https://blog.csdn.net/csdn_20150804/article/details/103943435
文章目录
基于springboot2.1.4; springmvc执行,核心逻辑是在DispatchServlet中,其中的若干属性,初始值都是null,如下:
private MultipartResolver multipartResolver; private LocaleResolver localeResolver; private ThemeResolver themeResolver; //主要分析这个 private List<HandlerMapping> handlerMappings; private List<HandlerAdapter> handlerAdapters; private List<HandlerExceptionResolver> handlerExceptionResolvers; private RequestToViewNameTranslator viewNameTranslator; private FlashMapManager flashMapManager; private List<ViewResolver> viewResolvers; 12345678910
上篇文章中,DispatchServlet在执行相关逻辑时,都是直接使用了上边这些属性,本文分析下这些属性是何时在哪里被初始化的,并以handlerMappings为例,着重分析。
DispatchServlet类是个servlet,包含init方法,tomcat容器在启动时,会默认执行init方法,而DispatchServlet的父类HttpServletBean实现了Servlet接口的init方法。因此我们从HttpServletBean#init() 方法开始。
1.HttpServletBean#init()
public final void init() throws ServletException { // Set bean properties from init parameters. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { if (logger.isErrorEnabled()) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); } throw ex; } } // Let subclasses do whatever initialization they like. //此方法是个空方法,目的是留给子类自己去实现一些初始化动作 initServletBean(); } 123456789101112131415161718192021222324
上面方法,其中关键方法是最后一行 initServletBean();这是个空方法,实现在其子类FrameworkServlet中实现,FrameworkServlet是DispatchServlet的直接父类。继续看下initServletBean的实现,
protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'"); if (logger.isInfoEnabled()) { logger.info("Initializing Servlet '" + getServletName() + "'"); } long startTime = System.currentTimeMillis(); try { //初始化springmvc web容器 this.webApplicationContext = initWebApplicationContext(); // initFrameworkServlet(); } catch (ServletException | RuntimeException ex) { logger.error("Context initialization failed", ex); throw ex; } if (logger.isDebugEnabled()) { String value = this.enableLoggingRequestDetails ? "shown which may lead to unsafe logging of potentially sensitive data" : "masked to prevent unsafe logging of potentially sensitive data"; logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value); } if (logger.isInfoEnabled()) { logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms"); } } 123456789101112131415161718192021222324252627282930
其中initWebApplicationContext()表示初始化web容器,进入,
protected WebApplicationContext initWebApplicationContext() { //得到根容器,也就是springmvc的父容器,spring容器ApplicationContext WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); } if (wac == null) { // No context instance is defined for this servlet -> create a local one wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. //刷新容器 synchronized (this.onRefreshMonitor) { onRefresh(wac); } } if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); } return wac; } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
从上面方法可以看出,web容器实际上是spring容器的子容器,web容器内部包含了父容器的引用,然后再包含自己特有的一些内容,这些内容在onRefresh(wac);方法中初始化,继续进入onRefresh方法,
2.DispatcherServlet#onRefresh(ApplicationContext context)
protected void onRefresh(ApplicationContext context) { initStrategies(context); } 123
上述方法在DispatcherServlet类中实现的,继续进入initStrategies(context),
protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); //初始化HandlerMappings initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); } 123456789101112
这里使用了策略模式,其中initHandlerMappings(context); 就是用来初始化handlerMappings属性的。
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) {//默认是true,扫描所有 // 从ApplicationContext中,找类型是HandlerMapping的bean Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { //初始化handlerMappings属性 this.handlerMappings = new ArrayList<>(matchingBeans.values()); // We keep HandlerMappings in sorted order. AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } } // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. if (this.handlerMappings == null) { //如果容器中一个handlerMapping都没有,则给一个默认的,就是去DispatcherServlet.properties配置文件中读取 this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerMappings declared for servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } } 1234567891011121314151617181920212223242526272829303132333435
上述方法中,handlerMappings是从上下文容器中直接get出来的,如果get不到,会使用默认的配置:其实从上述方法中的log信息中可以看出,有个叫DispatcherServlet.properties的配置文件。 打开看下:
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager 1234567891011121314151617181920
从配置文件中我们可以发现,所有初始化所需要的内容都在这里了,其中包括HandlerMapping,共有两个。
但是,如果容器中有,就不会使用默认的,那么容器中是何时放进去的呢, 我们就直接分析springboot中的初始化逻辑。
3.WebMvcAutoConfiguration初始化配置
我们知道springboot在启动的时候,默认会加载spring.factories文件中的配置类,在spring-boot-autoconfigure.jar包下的spring.factories文件中,有如下内容: org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,
就是这个WebMvcAutoConfiguration,初始化了所有webmvc的配置,在这里面可以找到此配置类中还有个EnableWebMvcConfiguration内部类,可以发现他继承了WebMvcConfigurationSupport,所有HandlerMappings都能在这俩配置类中找到(代码比较多,就不贴出来了)。
4.RequestMappingHandlerMapping初始化
这里看下一个比较重要的HandlerMapping,RequestMappingHandlerMapping 的初始化代码,RequestMappingHandlerMapping专门用来处理@Controller修饰的bean,这种方式也是我们开发中最常用的。
@Bean public RequestMappingHandlerMapping requestMappingHandlerMapping() { RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping(); mapping.setOrder(0); mapping.setInterceptors(getInterceptors()); mapping.setContentNegotiationManager(mvcContentNegotiationManager()); mapping.setCorsConfigurations(getCorsConfigurations()); PathMatchConfigurer configurer = getPathMatchConfigurer(); Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch(); if (useSuffixPatternMatch != null) { mapping.setUseSuffixPatternMatch(useSuffixPatternMatch); } Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch(); if (useRegisteredSuffixPatternMatch != null) { mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch); } Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch(); if (useTrailingSlashMatch != null) { mapping.setUseTrailingSlashMatch(useTrailingSlashMatch); } UrlPathHelper pathHelper = configurer.getUrlPathHelper(); if (pathHelper != null) { mapping.setUrlPathHelper(pathHelper); } PathMatcher pathMatcher = configurer.getPathMatcher(); if (pathMatcher != null) { mapping.setPathMatcher(pathMatcher); } return mapping; } protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() { return new RequestMappingHandlerMapping(); } 1234567891011121314151617181920212223242526272829303132333435363738
上面这段代码,是告诉spring容器实例化一个RequestMappingHandlerMapping,并且设置了一些属性值。
而RequestMappingHandlerMapping类还实现了接口InitializingBean,所以在实例化后,会执行afterPropertiesSet()方法:
public void afterPropertiesSet() { //初始化RequestMappingInfo的config this.config = new RequestMappingInfo.BuilderConfiguration(); this.config.setUrlPathHelper(getUrlPathHelper()); this.config.setPathMatcher(getPathMatcher()); this.config.setSuffixPatternMatch(this.useSuffixPatternMatch); this.config.setTrailingSlashMatch(this.useTrailingSlashMatch); this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch); this.config.setContentNegotiationManager(getContentNegotiationManager()); //调用父类方法 super.afterPropertiesSet(); } 123456789101112
打开父类方法:
public void afterPropertiesSet() { initHandlerMethods(); } protected void initHandlerMethods() { //从spring容器中拿到所有bean,然后遍历 for (String beanName : getCandidateBeanNames()) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { //扫描所有handler方法,就是controller层的方法 processCandidateBean(beanName); } } //记录个日志 handlerMethodsInitialized(getHandlerMethods()); } 1234567891011121314
上述方法中,spring拿到容器中的诉苦有bean,然后遍历处理,让我们看下如何处理的,
protected void processCandidateBean(String beanName) { Class<?> beanType = null; try { beanType = obtainApplicationContext().getType(beanName); } catch (Throwable ex) { // An unresolvable bean type, probably from a lazy bean - let's ignore it. if (logger.isTraceEnabled()) { logger.trace("Could not resolve type for bean '" + beanName + "'", ex); } } if (beanType != null && isHandler(beanType)) { //探测寻找Handler方法 detectHandlerMethods(beanName); } } 1234567891011121314151617
上述代码逻辑就是判断一个bean是不是Handler(就是所谓的controller),如果是,那么就继续扫描Handler中的方法,并注册;其中判断isHandler(beanType)用来判断是否是handler。 因为我们这里分析的是RequestMappingHandlerMapping,所以isHandler方法在此类中实现:
protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); } 1234
就是判断bean上是否有@Controller注解或者@RequestMapping注解。
下面继续看下spring如何探测handler方法然后如何注册的,
protected void detectHandlerMethods(Object handler) { //handler对象就是我们定义的一个Controller Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { Class<?> userType = ClassUtils.getUserClass(handlerType); //扫描到Controller中的所有方法,key是方法名,value是路径名 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.isTraceEnabled()) { logger.trace(formatMappings(userType, methods)); } // methods.forEach((method, mapping) -> { //如果有代理,这里要找到被代理后的方法 Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); //注册Method registerHandlerMethod(handler, invocableMethod, mapping); }); } } 123456789101112131415161718192021222324252627282930
上述代码逻辑,首先拿到Controller中的所有方法以及方法上的路径,如果方法有被代理,要使用被代理后的方法;然后遍历进行注册; 下面看下注册的逻辑:
protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method); } 123
其中,MappingRegistry是AbstractHandlerMethodMapping的内部类,里面维护了一堆map,
class MappingRegistry { private final Map<T, MappingRegistration<T>> registry = new HashMap<>(); private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>(); private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>(); private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>(); private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>(); private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); ... } 123456789101112131415
注册就是往这些map中存东西:
public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = createHandlerMethod(handler, method); assertUniqueMethodMapping(handlerMethod, mapping); //存储请求路径(包括请求方式)和请求方法的映射关系 this.mappingLookup.put(mapping, handlerMethod); List<String> directUrls = getDirectUrls(mapping); for (String url : directUrls) { //存储url和请RequestMapping注解中的信息映射 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(); } } 12345678910111213141516171819202122232425262728293031
说下参数: mapping:是方法上的路径信息(@RequestMpping注解中的信息),包括请求方式GET/POST等; handler:就是Controller对象; method:Controller中的方法; 注册的过程就是MappingRegistry存请求路径处理方法等相关的映射关系,而这些东西最终会在DispatcherServlet处理前端请求时使用到,也就是上篇文章中,我们分析的内容。
本文先分析到这里,其他初始化内容也是和HandlerMapping一样的,具体代码就不再展示了,直接在配置类中搜索相关内容即可。