Springmvc注解驱动 注解驱动的解析 通过查看 spring-webmvc-xxx.jar 下的spring.handlers文件可以发现 mvc前缀的标签都是由 MvcNamespaceHandler 来进行解析的。
spring.handlers
1 http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class MvcNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser()); registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser()); registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser()); registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser()); registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser()); registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser()); } }
通过查看其内部唯一的方法 init() 的实现可以确定 <mvc:annotation-driven /> 的解析工作是由 AnnotationDrivenBeanDefinitionParser 类全权负责的。其实现了BeanDefinitionParser接口
唯一的parse()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { public static final String HANDLER_MAPPING_BEAN_NAME = RequestMappingHandlerMapping.class.getName(); public static final String HANDLER_ADAPTER_BEAN_NAME = RequestMappingHandlerAdapter.class.getName(); //....省略其他属性 public BeanDefinition parse(Element element, ParserContext parserContext) { //.....省略代码 //此处注册组件 parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME)); //注册RequestMappingHandlerMapping parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME)); //注册REquestMappingHandlerAdapter parserContext.registerComponent(new BeanComponentDefinition(uriCompContribDef, uriCompContribName)); parserContext.registerComponent(new BeanComponentDefinition(exceptionResolver, methodExceptionResolverName)); parserContext.registerComponent(new BeanComponentDefinition(statusExceptionResolver, statusExResolverName)); parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExResolverName)); parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName)); // Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off" MvcNamespaceUtils.registerDefaultComponents(parserContext, source); parserContext.popAndRegisterContainingComponent(); return null; } }
观察该类对所继承接口的实现可以发现:
向SpringMVC容器中注册了 ContentNegotiationManagerFactoryBean 向SpringMVC容器中注册了 RequestMappingHandlerMapping (间接实现了HandlerMapping接口), order为0 向SpringMVC容器中注册了 RequestMappingHandlerAdapter (间接实现了HandlerAdapter接口,直接实现了 InitializingBean接口,关于这个接口的实现,参见本人的另外一篇博客) 向SpringMVC容器中注册了 MappedInterceptor 向SpringMVC容器中注册了 ExceptionHandlerExceptionResolver (间接实现了HandlerExceptionResolver接口), order为0 向SpringMVC容器中注册了 ResponseStatusExceptionResolver (间接实现了HandlerExceptionResolver接口), order为1 向SpringMVC容器中注册了 DefaultHandlerExceptionResolver (间接实现了HandlerExceptionResolver接口), order为2
RequestMappingHandlerMapping 继承结构 public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping
public abstract class AbstractHandlerMethodMapping \<T> extends AbstractHandlerMapping implements InitializingBean
初始化 初始化接口 该类实现了InitializingBean接口,因此初始化bean时会调用afterPropertiesSet()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public interface InitializingBean { /** * Invoked by a BeanFactory after it has set all bean properties supplied * (and satisfied BeanFactoryAware and ApplicationContextAware). * <p>This method allows the bean instance to perform initialization only * possible when all bean properties have been set and to throw an * exception in the event of misconfiguration. * @throws Exception in the event of misconfiguration (such * as failure to set an essential property) or if initialization fails. */ void afterPropertiesSet() throws Exception; }
InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。
补充说明:
1、Spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法,或者在配置文件中通过init-method指定,两种方式可以同时使用。
2、实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率要高一点,但是init-method方式消除了对spring的依赖。
3、如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。
参考:https://www.cnblogs.com/weiqihome/p/8922937.html
具体实现接口 1)public abstract class AbstractHandlerMethodMapping 该类实现了InitializingBean的afterPropertiesSet()方法
AbstractHandlerMethodMapping—>afterPropertiesSet()
1 2 3 4 @Override public void afterPropertiesSet() { initHandlerMethods(); }
调用了initHandlerMethods();
2)AbstractHandlerMethodMapping–>initHandlerMethods();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 /** * Scan beans in the ApplicationContext, detect and register handler methods. * @see #isHandler(Class) * @see #getMappingForMethod(Method, Class) * @see #handlerMethodsInitialized(Map) */ protected void initHandlerMethods() { if (logger.isDebugEnabled()) { logger.debug("Looking for request mappings in application context: " + getApplicationContext()); } //获取容器中的bean名 String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) : obtainApplicationContext().getBeanNamesForType(Object.class)); //遍历 for (String beanName : beanNames) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { Class<?> beanType = null; try { //根据bean名获取该bean的类型(Class) beanType = obtainApplicationContext().getType(beanName); } catch (Throwable ex) { // An unresolvable bean type, probably from a lazy bean - let's ignore it. if (logger.isDebugEnabled()) { logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex); } } // isHandler在AbstractHandlerMethodMapping<T>类中抽象方法; //判断是否是一个Handler if (beanType != null && isHandler(beanType)) { //检测Handler中的方法 detectHandlerMethods(beanName); } } } handlerMethodsInitialized(getHandlerMethods()); }
3)String[] beanNames获取容器中的bean名称,然后对bean进行遍历
4)isHandler()方法
RequestMappingHandlerMapping类对父类AbstractHandlerMethodMapping\<T>类的实现
RequestMappingHandlerMapping——–>isHandler
1 2 3 4 5 @Override protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); }
分析:
判断该Bean是否属于Handler
判断依据:是否有Controller注解 ,或者是否有RequestMapping注解
返回一个boolean类型
5)如果是一个Handler,则执行 ————- detectHandlerMethods(beanName)
AbstractHandlerMethodMapping–>detectHandlerMethods(beanName)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 /** * Look for handler methods in a handler. * @param handler the bean name of a handler or a handler instance */ protected void detectHandlerMethods(final Object handler) { Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { final Class<?> userType = ClassUtils.getUserClass(handlerType); Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> { try { //获取映射方法 return getMappingForMethod(method, userType); } catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } }); if (logger.isDebugEnabled()) { logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods); } for (Map.Entry<Method, T> entry : methods.entrySet()) { Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType); T mapping = entry.getValue(); registerHandlerMethod(handler, invocableMethod, mapping); } } }
该方法检测Handler(一个Controller)中的方法并进行注册
该方法主要代码分析:(没有详细分析,下次分析)
1)获取userType中的映射方法放入Map集合中
1 2 3 4 5 6 7 8 9 10 Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> { try { return getMappingForMethod(method, userType); } catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } });
getMappingForMethod(method, userType); //返回一个请求映射信息
1)userType为我们自定义的, 被@Controller注解所修饰的类. 2)method为 java.lang.reflect.Method 类型, 为我们所自定义的userType类型中的方法. 3)该getMappingForMethod方法也是抽象的, 交由RequestMappingHandlerMapping类来实现.
1 2 3 4 5 6 7 8 9 10 11 12 13 protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { // 抽取该方法上注解的@RequestMapping RequestMappingInfo info = createRequestMappingInfo(method); if (info != null) { // 抽取handlerType(即我们自定义的Controller类)上注解的@RequestMapping RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType); if (typeInfo != null) { // 合并两个RequestMapping的信息(controller路径+method) info = typeInfo.combine(info); } } return info; }
createRequestMappingInfo()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 protected RequestMappingInfo createRequestMappingInfo( RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) { RequestMappingInfo.Builder builder = RequestMappingInfo /* 这里说明我们是可以在配置@RequestMapping时, 使用spel表达式的; 例如@RequestMapping("/yuyue/${url.mh}/login") */ .paths(resolveEmbeddedValuesInPatterns(requestMapping.path())) .methods(requestMapping.method()) .params(requestMapping.params()) .headers(requestMapping.headers()) .consumes(requestMapping.consumes()) .produces(requestMapping.produces()) .mappingName(requestMapping.name()); if (customCondition != null) { builder.customCondition(customCondition); } return builder.options(this.config).build(); }
2)注册获取到的映射方法
1 2 3 4 5 for (Map.Entry<Method, T> entry : methods.entrySet()) { Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType); T mapping = entry.getValue(); registerHandlerMethod(handler, invocableMethod, mapping); }
registerHandlerMethod(handler, invocableMethod, mapping);
1)andler为传入的bean name 2)method为 java.lang.reflect.Method 类型, 为我们所自定义的userType类型中的方法. 3)mappings.get(method)为我们使用getMappingForMethod 构造的RequestMappingInfo 实例
AbstractHandlerMethodMapping——->registerHandlerMethod()
1 2 3 protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method); }
AbstractHandlerMethodMapping———>register()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = createHandlerMethod(handler, method); assertUniqueMethodMapping(handlerMethod, mapping); if (logger.isInfoEnabled()) { logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod); } //mappin与hanlderMethod映射 this.mappingLookup.put(mapping, handlerMethod); List<String> directUrls = getDirectUrls(mapping); for (String url : directUrls) { //url与mapping映射 this.urlLookup.add(url, mapping); } String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); } CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { this.corsLookup.put(handlerMethod, corsConfig); } this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } }
//属性:private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
this.mappingLookup.put(mapping, handlerMethod);
mapping:映射路径(例如:/login)
handlerMethod: 包含了bean和方法信息
抽取出来RequestMappingInfo 信息 注册到 LinkedHashMap<RequestMappingInfo,HandlerMethod>类型的全局字段handlerMethods
//属性:private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
this.urlLookup.add(url, mapping);
url:浏览器请求路径(例如:/login)
mapping:映射方法路径(“例如: /login”)
将映射关系注册到urlLookup全局变量中
总结 1)context:component-scan 默认是将所有@Component注解的类扫描进容器;
@Controller自身就是被@Component修饰的
2)mvc:annotation-driven的解析工作是由 AnnotationDrivenBeanDefinitionParser 类全权负责的。 3)AnnotationDrivenBeanDefinitionParser会向SpringMVC容器中注册了 RequestMappingHandlerMapping (间接实现了HandlerMapping接口), order为0 4)查看RequestMappingHandlerMapping 类的继承链就会发现, 其祖先类 AbstractHandlerMethodMapping 实现了 InitializingBean 5)而对InitializingBean的实现中, 会回调AbstractHandlerMethodMapping–>initHandlerMethods()的实现,其中的isHandler()方法中有对@Controller,@RequestMapping注解的扫描
参考:https://blog.csdn.net/lqzkcx3/article/details/78159708