轉載地址 :http://blog.csdn.net/j080624/article/details/56278461
爲了下降文章篇幅,使得文章更目標化,簡潔化,咱們就不例舉各類@RequestMapping的用法等內容了.
文章主要說明如下問題:html
-
-
Spring怎樣處理@RequestMapping(怎樣將請求路徑映射到控制器類或方法)java
-
Spring怎樣將請求分派給正確的控制器類或方法web
-
Spring如何實現靈活的控制器方法的spring
在Spring MVC 3.1 以前的版本中,Spring默認使用 DefaultAnnotationHandlerMapping,AnnotationMethodHandlerAdapter來處理 @RequestMapping註解和請求方法調用,而從3.1開始提供了一組新的API完成這些工做。相比之下,新的API更加的合理完善,開放,易拓 展,面向對象。這篇文章即是基於3.1的新API進行剖析的。api
1、概念解析
在開始以前咱們先了解下新的API中引入的新接口或者類,這會有助於後面的處理過程的理解。不得不說新的API提供了更多漂亮的抽象,你能感覺到面向對象的魅力。數組
-
RequestMappingInfo 這個類是對請求映射的一個抽象,它包含了請求路徑,請求方法,請求頭等信息。其實能夠看作是@RequestMapping的一個對應類。緩存
-
HandlerMethod這個類封裝了處理器實例(Controller Bean)和 處理方法實例(Method)以及方法參數數組(MethodParameter[])安全
-
MethodParameter 這個類從2.0就有了,它封裝了方法某個參數的相關信息及行爲,如該參數的索引,該參數所屬方法實例或構造器實例,該參數的類型等。mvc
-
HandlerMapping 該接口的實現類用來定義請求和處理器以前的映射關係,其中只定義了一個方法getHandler。app
-
AbstractHandlerMethodMapping 這是HandlerMapping的一個基本實現類,該類定義了請求與HandlerMethod實例的映射關係。
-
RequestMappingInfoHandlerMapping這個是AbstractHandlerMethodMapping的實現類,他維護了一個RequestMappingInfo和HandlerMethod的Map屬性。
-
RequestMappingHandlerMapping 這個是RequestMappingInfoHandlerMapping的子類,它將@RequestMapping註解轉化爲RequestMappingInfo實例,併爲父類使用。也就是咱們處理@RequestMapping的終點。
-
InitializingBean 這個接口定義了其實現Bean在容器完成屬性設置後能夠執行自定義初始化操做,咱們的AbstractHandlerMethodMapping便實現了這個接口,而且定義了一組自定義操做,就是用來檢測處理咱們的@RequestMapping註解。
概念講的太多總不是什麼好事。但明白了上述概念基本上就成功一半了,其中的實現相對@Autowired那篇簡單多了。
2、InitialiZingBean.afterPropertySet()
咱們從頭開始,看看到底Spring是怎樣檢測並處理咱們@RequestMapping註解的。不知你們還記不記的這段代碼:
- Object exposedObject = bean;
- try {
- populateBean(beanName, mbd, instanceWrapper);
- if (exposedObject != null) {
- exposedObject = initializeBean(beanName, exposedObject, mbd);
- }
- }
這是BeanFactory建立Bean過程當中須要執行的一段代碼,其中populateBean方法即是@Autowired註解的處理過程,執行的屬性的自動注入等操做。由於initializeBean方法當時與主題無關沒有講,不過這時它即是咱們關注的焦點了。
上面概念中咱們講到InitiaizingBean接口,它的實現Bean會在容器完成屬性注入後執行一個自定義操做,這不就知足initializeBean方法的執行喚醒嘛,咱們來看它的實現:
- protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
- if (System.getSecurityManager() != null) {
- AccessController.doPrivileged(new PrivilegedAction<Object>() {
- public Object run() {
- invokeAwareMethods(beanName, bean);
- return null;
- }
- }, getAccessControlContext());
- }
- else {
- invokeAwareMethods(beanName, bean);
- }
-
- Object wrappedBean = bean;
- if (mbd == null || !mbd.isSynthetic()) {
- wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
- }
-
- try {
- invokeInitMethods(beanName, wrappedBean, mbd);
- }
-
- if (mbd == null || !mbd.isSynthetic()) {
- wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
- }
- return wrappedBean;
- }
咱們接着來看下invokeInitMethods方法的實現:
- protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
- throws Throwable {
-
- boolean isInitializingBean = (bean instanceof InitializingBean);
- if (isInitializingBean &&
- (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
- if (System.getSecurityManager() != null) {
- try {
- AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
- public Object run() throws Exception {
- ((InitializingBean) bean).afterPropertiesSet();
- return null;
- }
- }, getAccessControlContext());
- }
- }
- else {
- ((InitializingBean) bean).afterPropertiesSet();
- }
- }
-
- }
上一篇關於<mvc:annotation-driven/>的文章,咱們說過了,當在配置文件中加上該標記後,Spring(3.1後)會默認爲咱們註冊RequestMappingHandlerMapping
等Bean定義。而RequestMappingHandlerMapping
實現了InitializingBean接口,所以,在初始化並裝配該Bean實例時,執行到上述代碼是,便會執行他的afterPropertySet方法。咱們接下來看看他的afterPropertySet方法:
- public void afterPropertiesSet() {
- initHandlerMethods();
- }
-
-
- protected void initHandlerMethods() {
-
- String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
- BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(),
- Object.class) : getApplicationContext().getBeanNamesForType(Object.class));
-
- for (String beanName : beanNames) {
- if (isHandler(getApplicationContext().getType(beanName))){
- detectHandlerMethods(beanName);
- }
- }
-
- handlerMethodsInitialized(getHandlerMethods());
- }
它直接調用了initHandlerMethods()方法,而且該方法被描述爲:掃描ApplicationContext中的beans,檢測並註冊處理器方法。we are close。
3、檢測@RequestMapping
咱們再看它是怎樣判斷是不是處理器的,以及怎麼detect Handler Methods 的:
- @Override
- protected boolean isHandler(Class<?> beanType) {
- return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
- (AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
- }
啊哈,很簡單,就是看看有沒有被@Controller或者@RequestMapping註解標記
- protected void detectHandlerMethods(final Object handler) {
- Class<?> handlerType = (handler instanceof String) ?
- getApplicationContext().getType((String) handler) : handler.getClass();
- final Class<?> userType = ClassUtils.getUserClass(handlerType);
- Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter(){
- public boolean matches(Method method) {
- return getMappingForMethod(method, userType) != null;
- }
- });
-
- for (Method method : methods) {
-
- T mapping = getMappingForMethod(method, userType);
-
- registerHandlerMethod(handler, method, mapping);
- }
- }
整個的檢測過程大體清楚了:1)遍歷Handler中的全部方法,找出其中被@RequestMapping註解標記的方法。2)而後遍歷這些方法,生成RequestMappingInfo實例。3)將RequestMappingInfo實例以及處理器方法註冊到緩存中。
下面咱們看看細節:
- @Override
- protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
- RequestMappingInfo info = null;
-
- RequestMapping methodAnnotation =
- AnnotationUtils.findAnnotation(method, RequestMapping.class);
- if (methodAnnotation != null) {
- RequestCondition<?> methodCondition = getCustomMethodCondition(method);
- info = createRequestMappingInfo(methodAnnotation, methodCondition);
-
- RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType,
- RequestMapping.class);
- if (typeAnnotation != null) {
- RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
-
- info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
- }
- }
- return info;
- }
很清晰吧,先獲取方法上的@RequestMapping信息,而後獲取類級別上的@RequestMapping 信息,而後將二者結合,這裏咱們有必要再瞭解下怎樣建立RequestMappingInfo對象的(包括他的內部結構),以及怎樣將類級別的request mapping信息和方法級別的進行結合的?
- private RequestMappingInfo createRequestMappingInfo(RequestMapping annotation,
- RequestCondition<?> customCondition) {
- return new RequestMappingInfo(
- new PatternsRequestCondition(annotation.value(), getUrlPathHelper(), getPathMatcher(),
- this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions),
- new RequestMethodsRequestCondition(annotation.method()),
- new ParamsRequestCondition(annotation.params()),
- new HeadersRequestCondition(annotation.headers()),
- new ConsumesRequestCondition(annotation.consumes(), annotation.headers()),
- new ProducesRequestCondition(annotation.produces(), annotation.headers(),
- getContentNegotiationManager()),
- customCondition
- );
- }
其中涉及到了幾個類,咱們大體瞭解下含義:
-
PatternRequestCondition 它其實就是URL模式的封裝,它包含了一個URL模式的Set集合。其實就是@RequestMapping註解中的value值得封裝。
-
RequestMethodRequestCondition 它是@RequestMapping 註解中method屬性的封裝
-
ParamsRequestCondition 它是@RequestMapping註解中params屬性的封裝
等等,依次類推。所以RequestMappingInfo其實就是對@RquestMapping 的封裝。
下面咱們再看看怎樣進行Combine的:
- public RequestMappingInfo combine(RequestMappingInfo other) {
- PatternsRequestCondition patterns = this.patternsCondition.combine(other.patternsCondition);
- RequestMethodsRequestCondition methods = this.methodsCondition.combine(other.methodsCondition);
- ParamsRequestCondition params = this.paramsCondition.combine(other.paramsCondition);
- HeadersRequestCondition headers = this.headersCondition.combine(other.headersCondition);
- ConsumesRequestCondition consumes = this.consumesCondition.combine(other.consumesCondition);
- ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition);
- RequestConditionHolder custom = this.customConditionHolder.combine(other.customConditionHolder);
-
- return new RequestMappingInfo(patterns, methods, params, headers, consumes,
- produces, custom.getCondition());
- }<span style="white-space:pre;"> </span>
很清晰,對每個元素都進行combine操做,咱們這裏只看PatternRequestCondition是怎麼結合的,就是看看怎樣合併url的。其餘沒太大必要。
- public PatternsRequestCondition combine(PatternsRequestCondition other) {
- Set<String> result = new LinkedHashSet<String>();
- if (!this.patterns.isEmpty() && !other.patterns.isEmpty()) {
- for (String pattern1 : this.patterns) {
- for (String pattern2 : other.patterns) {
- result.add(this.pathMatcher.combine(pattern1, pattern2));
- }
- }
- }
- else if (!this.patterns.isEmpty()) {
- result.addAll(this.patterns);
- }
- else if (!other.patterns.isEmpty()) {
- result.addAll(other.patterns);
- }
- else {
- result.add("");
- }
- return new PatternsRequestCondition(result, this.urlPathHelper, this.pathMatcher,
- this.useSuffixPatternMatch,this.useTrailingSlashMatch, this.fileExtensions);
- }
1)兩個pattern都存在是,調用PathMatcher的combine方法合併兩個pattern。
2)只有一個有時,使用這個。
3)兩個都沒有時,爲空「」。
如今真正的url拼接是由PathMatcher來完成的了。咱們就不看他的代碼了就是一串if else的組合,重點是考慮進各類狀況,咱們來看下方法的註釋吧:

清晰,全面吧,有興趣的能夠看一下代碼,這裏不講了。
4、註冊請求映射
上面咱們已經講了@RequestMapping的檢測和處理,而且根據@RequestMapping生成了RequestMappingInfo實例,那Spring一定須要將這些信息保存起來,以處理咱們的請求。
第三節中咱們提到一個方法尚未分析,就是registerHandlerMethod 方法:
- protected void registerHandlerMethod(Object handler, Method method, T mapping) {
- HandlerMethod handlerMethod;
- if (handler instanceof String) {
- String beanName = (String) handler;
- handlerMethod = new HandlerMethod(beanName, getApplicationContext(), method);
- }
- else {
- handlerMethod = new HandlerMethod(handler, method);
- }
-
-
- HandlerMethod oldHandlerMethod = handlerMethods.get(mapping);
- if (oldHandlerMethod != null && !oldHandlerMethod.equals(handlerMethod)) {
- throw new IllegalStateException();
- }
-
-
- this.handlerMethods.put(mapping, handlerMethod);
-
- Set<String> patterns = getMappingPathPatterns(mapping);
- for (String pattern : patterns) {
- if (!getPathMatcher().isPattern(pattern)) {
-
-
- this.urlMap.add(pattern, mapping);
- }
- }
- }
這裏可能稍微有點繞,其實道理很簡單,當請求到達時,去urlMap中需找匹配的url,以及獲取對應mapping實例,而後去handlerMethods中獲取匹配HandlerMethod實例。
5、承上啓下
篇幅有些長了,超出字數限制了,只能分紅兩篇了..........................
這章只分析了咱們前面三個問題中的第一個,可是已經至關接近了。下一篇咱們來說,Spring怎樣處理客戶發來的請求,以及方法調用的。