我们把应用重置回刚初始化的状态,也或者新创建一个干净的工程,还是只导入
spring-boot-starter-web
依赖,接下来咱开始分析WebMvc的一些原理。之前分析自动装配时,咱们拿WebMvc来解析实例了。咱简单回顾一下WebMvc的自动装配都干了什么。
Converter
:@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) { this.messageConvertersProvider .ifAvailable((customConverters) -> converters.addAll(customConverters.getConverters())); }
ViewResolver
:// 最常用的视图解析器 @Beanpublic InternalResourceViewResolver defaultViewResolver() {} @Beanpublic BeanNameViewResolver beanNameViewResolver() {} @Beanpublic ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {} // 国际化组件 @Beanpublic LocaleResolver localeResolver() {}
webjars
映射:public void addResourceHandlers(ResourceHandlerRegistry registry) { // ...... // 映射webjars if (!registry.hasMappingForPattern("/webjars/**")) { customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/") .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); } // 映射静态资源路径 String staticPathPattern = this.mvcProperties.getStaticPathPattern(); if (!registry.hasMappingForPattern(staticPathPattern)) { customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern) .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations())) .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); } }
private Resource getIndexHtml(String location) { return this.resourceLoader.getResource(location + "index.html"); }
@Beanpublic SimpleUrlHandlerMapping faviconHandlerMapping() { // ...... mapping.setUrlMap(Collections.singletonMap("**/favicon.ico", faviconRequestHandler())); return mapping; }
@Beanpublic TomcatServletWebServerFactory tomcatServletWebServerFactory() { return new TomcatServletWebServerFactory(); }
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (this.beanFactory == null) { return; } // 编程式注入组件 registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class); registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class); }
An instance of the ServletContainerInitializer is looked up via the jar services API by the container at container / application startup time. The framework providing an implementation of the ServletContainerInitializer MUST bundle in the META-INF/services directory of the jar file a file called javax.servlet.ServletContainerInitializer, as per the jar services API, that points to the implementation class of the ServletContainerInitializer.
ServletContainerInitializer
的实现类。框架必须在jar包的 META-INF/services
的文件夹中提供一个名为 javax.servlet.ServletContainerInitializer
的文件,文件内容要写明 ServletContainerInitializer
的实现类的全限定名。ServletContainerInitializer
是一个接口,实现它的类必须实现一个方法:onStartUp
。public interface ServletContainerInitializer { void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException; }
onStartUp
方法。ServletContainerInitializer
的实现类上标注 @HandlesTypes
注解,在应用启动的时候自行加载一些附加的类,这些类会以字节码的集合形式传入 onStartup
方法的第一个参数中。SpringBootServletInitializer
。SpringBoot
应用打包启动的两种方式:JarLauncher
篇解析)SpringBoot
应用,然后才是创建IOC容器。SpringBootServletInitializer
。下面咱来看看外置Web容器是如何成功引导 SpringBoot
应用启动的:SpringBoot
应用中的每一个被依赖的jar中寻找 META-INF/services/javax.servlet.SpringBootServletInitializer
的文件。SpringServletContainerInitializer
)。@HandlesTypes
中标注的类型的所有普通实现类(也就是非抽象子类)都实例化出来,之后分别调他们自己的 onStartup
方法。@HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer { @Overridepublic void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { // SpringServletContainerInitializer会加载所有的WebApplicationInitializer类型的普通实现类 List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>(); if (webAppInitializerClasses != null) { for (Class<?> waiClass : webAppInitializerClasses) { // 如果不是接口,不是抽象类 if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) { try { // 创建该类的实例 initializers.add((WebApplicationInitializer) waiClass.newInstance()); } catch (Throwable ex) { throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex); } } } } if (initializers.isEmpty()) { servletContext.log("No Spring WebApplicationInitializer types detected on classpath"); return; } servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath"); AnnotationAwareOrderComparator.sort(initializers); // 调用各自的onStartup方法 for (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); } } }
onStartup
方法的文档注释原文翻译:Delegate the ServletContext to any WebApplicationInitializer implementations present on the application classpath. Because this class declares @HandlesTypes(WebApplicationInitializer.class), Servlet 3.0+ containers will automatically scan the classpath for implementations of Spring's WebApplicationInitializer interface and provide the set of all such types to the webAppInitializerClasses parameter of this method. If no WebApplicationInitializer implementations are found on the classpath, this method is effectively a no-op. An INFO-level log message will be issued notifying the user that the ServletContainerInitializer has indeed been invoked but that no WebApplicationInitializer implementations were found. Assuming that one or more WebApplicationInitializer types are detected, they will be instantiated (and sorted if the @@Order annotation is present or the Ordered interface has been implemented). Then the WebApplicationInitializer.onStartup(ServletContext) method will be invoked on each instance, delegating the ServletContext such that each instance may register and configure servlets such as Spring's DispatcherServlet, listeners such as Spring's ContextLoaderListener, or any other Servlet API componentry such as filters.将 ServletContext 委托给应用程序类路径上存在的任何 WebApplicationInitializer 实现。 因为此类声明了 @HandlesTypes(WebApplicationInitializer.class),所以 Servlet 3.0+ 容器将自动扫描类路径以查找 Spring 的 WebApplicationInitializer 接口的实现,并将所有此类的类型的集合提供给此方法的 webAppInitializerClasses 参数。 如果在类路径上没有找到 WebApplicationInitializer 实现,则此方法实际上是无操作的。将发出info级别的日志消息,通知用户确实已调用 ServletContainerInitializer,但是未找到 WebApplicationInitializer 实现。 假设检测到一个或多个 WebApplicationInitializer 类型,将对其进行实例化(如果存在 @Order 注解或已实现 Ordered 接口,则将对其进行排序)。然后将在每个实例上调用 WebApplicationInitializer.onStartup(ServletContext) 方法,委派 ServletContext,以便每个实例可以注册和配置 Servlet(例如 Spring 的 DispatcherServlet),监听器(例如 Spring 的 ContextLoaderListener)或任何其他 Servlet API组件(例如Filter)。
SpringBoot
工程会在启动类的同包下创建 ServletInitializer
,并且必须继承 SpringBootServletInitializer
,所以会被服务器创建对象。SpringBootServletInitializer
没有重写 onStartup
方法,去父类 SpringServletContainerInitializer
中寻找SpringServletContainerInitializer
中的 onStartup
方法中有一句核心源码:WebApplicationContextrootAppContext rootAppContext = createRootApplicationContext(servletContext);
@Overridepublic void onStartup(ServletContext servletContext) throws ServletException { // Logger initialization is deferred in case an ordered // LogServletContextInitializer is being used this.logger = LogFactory.getLog(getClass()); // 创建 父IOC容器 WebApplicationContext rootAppContext = createRootApplicationContext(servletContext); if (rootAppContext != null) { servletContext.addListener(new ContextLoaderListener(rootAppContext) { @Overridepublic void contextInitialized(ServletContextEvent event) { // no-op because the application context is already initialized } }); } else { this.logger.debug("No ContextLoaderListener registered, as " + "createRootApplicationContext() did not " + "return an application context"); } } protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) { // 使用Builder机制,前面也介绍过 SpringApplicationBuilder builder = createSpringApplicationBuilder(); builder.main(getClass()); ApplicationContext parent = getExistingRootWebApplicationContext(servletContext); if (parent != null) { this.logger.info("Root context already created (using as parent)."); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null); builder.initializers(new ParentContextApplicationContextInitializer(parent)); } // 设置Initializer builder.initializers(new ServletContextApplicationContextInitializer(servletContext)); // 在这里设置了容器启动类:AnnotationConfigServletWebServerApplicationContext builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class); // 【引导】多态进入子类(自己定义)的方法中 builder = configure(builder); builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext)); // builder.build(),创建SpringApplication SpringApplication application = builder.build(); if (application.getAllSources().isEmpty() && AnnotationUtils.findAnnotation(getClass(), Configuration.class) != null) { application.addPrimarySources(Collections.singleton(getClass())); } Assert.state(!application.getAllSources().isEmpty(), "No SpringApplication sources have been defined. Either override the " + "configure method or add an @Configuration annotation"); // Ensure error pages are registered if (this.registerErrorPageFilter) { application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class)); } // 启动SpringBoot应用 return run(application); }
SpringApplicationBuilder
应用构建器;builder = configure(builder);
ServletInitializer
)重写的方法;return builder.sources(DemoApplication.class);
SpringBootApplication
没什么区别了。SpringBootServletInitializer
的作用和原理。@Autowired
是什么时机被解析的:AutowiredAnnotationBeanPostProcessor
在 postProcessMergedBeanDefinition
中触发。AnnotationAwareAspectJAutoProxyCreator
负责创建代理对象 。@Controller
中 @RequestMapping
的时机可能也在这两种情况之内,暂且保存这个猜想。@RequestMapping
的解析时机。InitializingBean
,可是它为什么要这么干呢?咱来进到实现中:public void afterPropertiesSet() { 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(); }
afterPropertiesSet
中:public void afterPropertiesSet() { initHandlerMethods(); }
initHandlerMethods
方法,但这个方法的字面意思貌似就有些问题:初始化 HandlerMethod
?难不成这个方法有关键的含义吗?private static final String SCOPED_TARGET_NAME_PREFIX = "scopedTarget."; protected void initHandlerMethods() { for (String beanName : getCandidateBeanNames()) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { processCandidateBean(beanName); } } handlerMethodsInitialized(getHandlerMethods()); }
"scopedTarget."
的都拿出来,执行一个 processCandidateBean
方法。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)) { detectHandlerMethods(beanName); } }
isHandler
protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); }
@Controller
或 @RequestMapping
标注。@Controller
和 @RequestMapping
了!证明咱的寻找思路是正确的。@RequestMapping
的方法了。protected void detectHandlerMethods(Object handler) { Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { Class<?> userType = ClassUtils.getUserClass(handlerType); // 3.5 解析筛选方法 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)); } // 3.6 注册方法映射 methods.forEach((method, mapping) -> { Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); registerHandlerMethod(handler, invocableMethod, mapping); }); } }
MethodInterceptor
来筛选一些方法。public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) { final Map<Method, T> methodMap = new LinkedHashMap<>(); Set<Class<?>> handlerTypes = new LinkedHashSet<>(); Class<?> specificHandlerType = null; if (!Proxy.isProxyClass(targetType)) { specificHandlerType = ClassUtils.getUserClass(targetType); handlerTypes.add(specificHandlerType); } handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType)); for (Class<?> currentHandlerType : handlerTypes) { final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType); ReflectionUtils.doWithMethods(currentHandlerType, method -> { Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass); T result = metadataLookup.inspect(specificMethod); if (result != null) { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) { methodMap.put(specificMethod, result); } } }, ReflectionUtils.USER_DECLARED_METHODS); } return methodMap; }
MetadataLookup
类型来确定是否可以符合匹配条件。MetadataLookup
是一个函数式接口:@FunctionalInterfacepublic interface MetadataLookup<T> { T inspect(Method method); }
Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> { try { // 3.5 return getMappingForMethod(method, userType); } catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } });
getMappingForMethod
方法:protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { // 创建方法级别的RequestMappingInfo RequestMappingInfo info = createRequestMappingInfo(method); if (info != null) { // 创建类级别的RequestMappingInfo RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType); if (typeInfo != null) { info = typeInfo.combine(info); } // 拼接路径前缀 String prefix = getPathPrefix(handlerType); if (prefix != null) { info = RequestMappingInfo.paths(prefix).build().combine(info); } } return info; }
RequestMappingInfo
,创建类级别的 RequestMappingInfo
,拼接路径前缀。一步一步来看:private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); RequestCondition<?> condition = (element instanceof Class ? getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element)); return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); }
@RequestMapping
注解了!最终会把 @RequestMapping
及相关的属性封装到一个 RequestMappingInfo
对象中,逻辑比较简单。private Map<String, Predicate<Class<?>>> pathPrefixes = new LinkedHashMap<>(); String getPathPrefix(Class<?> handlerType) { for (Map.Entry<String, Predicate<Class<?>>> entry : this.pathPrefixes.entrySet()) { if (entry.getValue().test(handlerType)) { String prefix = entry.getKey(); if (this.embeddedValueResolver != null) { prefix = this.embeddedValueResolver.resolveStringValue(prefix); } return prefix; } } return null; }
pathPrefixes
。WebMvcAutoConfiguration
,初始化时调用过(不过它直接回调了父类 WebMvcConfigurationSupport
的方法)。public RequestMappingHandlerMapping requestMappingHandlerMapping() { RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping(); // ...... PathMatchConfigurer configurer = getPathMatchConfigurer(); // ...... Map<String, Predicate<Class<?>>> pathPrefixes = configurer.getPathPrefixes(); if (pathPrefixes != null) { mapping.setPathPrefixes(pathPrefixes); } return mapping; }
configurer.getPathPrefixes
,而 configurer
又来源于 getPathMatchConfigurer
。protected PathMatchConfigurer getPathMatchConfigurer() { if (this.pathMatchConfigurer == null) { this.pathMatchConfigurer = new PathMatchConfigurer(); configurePathMatch(this.pathMatchConfigurer); } return this.pathMatchConfigurer; }
configurePathMatch
方法用来配置 PathMatch
。protected void configurePathMatch(PathMatchConfigurer configurer) { this.configurers.configurePathMatch(configurer); } public void configurePathMatch(PathMatchConfigurer configurer) { for (WebMvcConfigurer delegate : this.delegates) { delegate.configurePathMatch(configurer); } }
WebMvcConfigurer
都拿出来回调 configurePathMatch
方法。(这也启发我们可以自定义一些配置类,实现 WebMvcConfigurer
接口来重写 configurePathMatch
方法,添加自定义规则)methods.forEach((method, mapping) -> { Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); registerHandlerMethod(handler, invocableMethod, mapping); });
HandlerMethod
。private final MappingRegistry mappingRegistry = new MappingRegistry(); protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method); }
register
中:(关键部分注释已标注在源码中)public void register(T mapping, Object handler, Method method) { // 读写锁加锁 this.readWriteLock.writeLock().lock(); try { // 将Controller的类型和Controller中的方法包装为一个HandlerMethod对象 HandlerMethod handlerMethod = createHandlerMethod(handler, method); assertUniqueMethodMapping(handlerMethod, mapping); // 将RequestMappingInfo和Controller的目标方法存入Map中 this.mappingLookup.put(mapping, handlerMethod); // 将注解中的映射url和RequestMappingInfo存入Map List<String> directUrls = getDirectUrls(mapping); for (String url : directUrls) { this.urlLookup.add(url, mapping); } String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); } // 将Controller目标方法和跨域配置存入Map CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { this.corsLookup.put(handlerMethod, corsConfig); } // uri 映射 HandlerMethod封装的MappingRegistration对象,存入Map中 this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } }
Controller
和它的方法,变成一个 HandlerMethod
对象,之后分别保存三组Map映射(源码中已标注注释),完成注册。@Controller
中的 @RequestMapping
信息已经被装载进 RequestMappingHandlerMapping
中。web.xml
,改用 ServletContainerInitializer
来接管应用启动。SpringBootServletInitializer
实现了 WebApplicationInitializer
,用于被 ServletContainerInitializer
引导 SpringBoot 应用启动。@RequestMapping
标注的方法装载时机是 RequestMappingHandlerMapping
的初始化阶段。DispatcherServlet
的工作原理,体会 WebMvc 中的设计】