# spring-boot-2.2.9.RELEASE **Repository Path**: tca/spring-boot-2.2.9.-release ## Basic Information - **Project Name**: spring-boot-2.2.9.RELEASE - **Description**: springboot源码学习 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2021-11-22 - **Last Updated**: 2025-07-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: springboot源码学习 ## README # Springboot源码解析 ## 0.源码环境搭建 ### 下载地址 [github下载地址(2.2.9.RELEASE版本)](https://github.com/spring-projects/spring-boot/releases/tag/v2.2.9.RELEASE) ### 注意事项 #### 1.为什么选择2.2.9.RELEASE版本 ``` 2.2.9.RELEASE以上版本采用gradle进行编译打包, 我们希望采用maven的方式 ``` #### 2.主pom.xml报错解决方案 <\properties>标签下添加: ``` true ``` #### 3.环境依赖 ``` jdk -- jdk8+ maven -- 3.5+ ``` 在 spring-boot-project/spring-boot-parent/pom.xml文件中,依赖了一个plugin --> maven-enforcer-plugin,它有两个属性值:requireJavaVersion --> [1.8,) 以及 requireMavenVersion --> [3.5.0,) [参考](https://blog.csdn.net/chuanchengdabing/article/details/115871178) ## 1.准备 maven ```xml org.springframework.boot spring-boot-starter-web ``` 启动类 ```java @SpringBootApplication public class SpringBootLearningApplication { public static void main(String[] args) { SpringApplication.run(SpringBootLearningApplication.class, args); } } ``` ## 2.@SpringBootApplication ```java @Target(ElementType.TYPE) // 注解适用范围, TYPE表示注解可以描述在类、接口、注解、枚举上 @Retention(RetentionPolicy.RUNTIME) // 表示注解的生命周期 @Documented // 表示注解可以记录在javadoc中 @Inherited // 表示注解可以被子类继承 @SpringBootConfiguration // 表明该类为配置类 @EnableAutoConfiguration // 启动自动配置功能 (spring中有很多@Enable注解, 原理就是借助@Import等相关注解向spring容器注册相关的核心Bean) @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { @AliasFor(annotation = EnableAutoConfiguration.class) Class[] exclude() default {}; @AliasFor(annotation = EnableAutoConfiguration.class) String[] excludeName() default {}; @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default {}; @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") Class[] scanBasePackageClasses() default {}; @AliasFor(annotation = Configuration.class) boolean proxyBeanMethods() default true; } ``` 可以看到 @SpringBootApplication 注解上加了两个核心的自定义注解:@SpringBootConfiguration 和 @EnableAutoConfiguration,下面将介绍这两个注解的作用 ### 2.1 @SpringBootConfiguration ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { @AliasFor(annotation = Configuration.class) boolean proxyBeanMethods() default true; } ``` #### 总结 ``` 这个注解上加上了@Configuration注解,作用是标明当类上使用当前注解时,为主配置类 ``` ### 2.2 @EnableAutoConfiguration ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage // 自动配置包 @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class[] exclude() default {}; String[] excludeName() default {}; } ``` 这个注解是自动配置的核心注解,看到这个注解上又加了两个自定义注解,分别是 :@AutoConfigurationPackage 和 @Import(AutoConfigurationImportSelector.class),@AutoConfigurationPackage注解用来标识自动配置的包,@Import(AutoConfigurationImportSelector.class)是自动配置的核心 #### 2.2.1 @AutoConfigurationPackage ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) // 向spring容器中注入AutoConfigurationPackages.Registrar public @interface AutoConfigurationPackage { } ``` 查看当前注解,可以看到,当前注解上添加了@Import(AutoConfigurationPackages.Registrar.class),由spring-ioc源码,我们知道@Import注解用于向spring容器注册相关bean!ConfigurationClassPostProcessor会解析主配置类(@Configuration注解的类),解析时,会委托ConfigurationClassParser进行解析,(这一部分spring-ioc源码已经分析)使用ConfigurationClassParser会递归解析主配置类中@Import注解(递归解析是指会解析注解中的注解,只要包含@Import注解,都会被解析),下面再次展示处理@Import注解的代码 ##### ConfigurationClassParser#processImports(..) ```java // Collection importCandidates --> 使用@Import注解引用的类 private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection importCandidates, boolean checkForCircularImports) { // 1.校验非空 if (importCandidates.isEmpty()) { return; } // 2.跳过 if (checkForCircularImports && isChainedImportOnStack(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); } else { this.importStack.push(configClass); try { for (SourceClass candidate : importCandidates) { // 3.判断@Import的类是否为ImportSelector实现类 if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports // 3.1 当@Import引入的类实现了ImportSelector, 进行实例化 Class candidateClass = candidate.loadClass(); ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); // 3.2 回调Aware接口 ParserStrategyUtils.invokeAwareMethods( selector, this.environment, this.resourceLoader, this.registry); // 3.3 如果@Import引入的类实现了ImportSelector, 但是同时实现了 DeferredImportSelector 接口(DeferredImportSelector接口是ImportSelector的子接口), 调用deferredImportSelectorHandler#handle()处理 if (selector instanceof DeferredImportSelector) { // 这里handle方法, 将 selector 封装成 DeferredImportSelectorHolder, 添加到paser的deferredImportSelectors 列表属性中等待后续处理 this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { // 3.4 如果只是实现了@ImportSelector, 没有实现 eferredImportSelector, 则调用其selectImports方法, 向容器中引入需要注册的bean, 并且递归调用processImports // 本质上, @ImportSelector, 返回了A.class, B.class 相当于 @Import({A.class, B.class}), 仍然需要继续解析A.class, B.class, 判断其是否实现 @ImportSelector接口, 或者ImportBeanDefinitionRegistrar接口等!!!【这个非常重要, spring-tx就是采用这种方式】 String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection importSourceClasses = asSourceClasses(importClassNames); processImports(configClass, currentSourceClass, importSourceClasses, false); } } // 4.判断@Import的类是否为ImportBeanDefinitionRegistrar实现类 // 【重点】aop的核心注解@EnableAspectJAutoProxy 中 @Import(AspectJAutoProxyRegistrar.class) // AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口!!! else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // Candidate class is an ImportBeanDefinitionRegistrar -> // delegate to it to register additional bean definitions // 4.1 当@Import引入的类实现了ImportBeanDefinitionRegistrar, 进行实例化 // spring-aop、spring-transaction注解版都使用了 ImportBeanDefinitionRegistrar // 在spring-aop中: 在主配置类上添加@EnableAspectJAutoProxy注解, @EnableAspectJAutoProxy注解上使用了 // @Import(AspectJAutoProxyRegistrar.class), AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口 // 因此这里会实例化对象 AspectJAutoProxyRegistrar Class candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class); ParserStrategyUtils.invokeAwareMethods( registrar, this.environment, this.resourceLoader, this.registry); // 4.2 将ImportBeanDefinitionRegistrar的实现类实例化后添加到配置类的 importBeanDefinitionRegistrars属性中 // 等待后面处理 configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> // process it as an @Configuration class this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); // 5.如果@Import引入的类既不是 ImportSelector实现类, 也不是 ImportBeanDefinitionRegistrar实现类 // 将其封装成 ConfigurationClass, 递归解析, 后续会创建称为普通的bean对象 processConfigurationClass(candidate.asConfigClass(configClass)); } } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", ex); } finally { this.importStack.pop(); } } } ``` ##### AutoConfigurationPackages.Registrar ```java // 作用: 注册(org.springframework.boot.autoconfigure.AutoConfigurationPackages, BasePackages)核心bean, bean中添加一个参数: // packageName, 这里即为主配置类所在包名 static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { register(registry, new PackageImport(metadata).getPackageName()); } @Override public Set determineImports(AnnotationMetadata metadata) { return Collections.singleton(new PackageImport(metadata)); } } public static void register(BeanDefinitionRegistry registry, String... packageNames) { if (registry.containsBeanDefinition(BEAN)) { BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN); ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues(); constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames)); } else { GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(BasePackages.class); // 这里的packageNames是使用了@AutoConfigurationPackage注解的类所在的包名 // @SpringBootApplication注解上使用了@EnableAutoConfiguration注解, // @EnableAutoConfiguration注解上使用了@AutoConfigurationPackage注解, // 所以这里取的packageNames是使用@SpringBootApplication类所在的包名, 即主启动类所在包名 beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(BEAN, beanDefinition); } } ``` ##### 总结 ``` @AutoConfigurationPackage的作用就是向spring容器中注册了一个bean:BasePackages。它会记录最外层根包的位置,以便第三方框架整合使用。 使用AutoConfigurationPackages.get(beanFactory)可以获取根路径! ``` #### 2.2.2 AutoConfigurationImportSelector ##### @Import解析 @Import(AutoConfigurationImportSelector.class),这个是springboot自动配置的核心,由于前面的学习,我们这里再一次总结一下@Import注解的作用(当我们的配置类上使用了@Import注解),根据@Import导入的jclass,一共可能有四种情况: ###### 1.实现ImportBeanDefinitionRegistrar 接口 ``` 将ImportBeanDefinitionRegistrar的实现类实例化后添加到配置类的 importBeanDefinitionRegistrars属性中, 后期会调用其registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)方法, 向spring容器中注册相关的核心bean 我们在spring-aop, spring-mybatis源码中有实现 ``` spring-aop中,我们在主配置类中加了@EnableAspectJAutoProxy注解,,注解中引入了AspectJAutoProxyRegistrar ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { boolean proxyTargetClass() default false; boolean exposeProxy() default false; } ``` ```java class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); if (enableAspectJAutoProxy != null) { if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } } } ``` 在spring-mybatis,注解方式中,我们在主配置类中添加了@MapperScan注解,注解中@Import(MapperScannerRegistrar.class)向spring容器中导入了MapperScannerRegistrar,而MapperScannerRegistrar则实现了MapperScannerRegistrar则实现了ImportBeanDefinitionRegistrar接口 ````java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(MapperScannerRegistrar.class) public @interface MapperScan { String[] value() default {}; String[] basePackages() default {}; Class[] basePackageClasses() default {}; Class nameGenerator() default BeanNameGenerator.class; Class annotationClass() default Annotation.class; Class markerInterface() default Class.class; String sqlSessionTemplateRef() default ""; String sqlSessionFactoryRef() default ""; Class factoryBean() default MapperFactoryBean.class; } ```` ```java public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware { private ResourceLoader resourceLoader; /** * {@inheritDoc} */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); // this check is needed in Spring 3.1 if (resourceLoader != null) { scanner.setResourceLoader(resourceLoader); } Class annotationClass = annoAttrs.getClass("annotationClass"); if (!Annotation.class.equals(annotationClass)) { scanner.setAnnotationClass(annotationClass); } Class markerInterface = annoAttrs.getClass("markerInterface"); if (!Class.class.equals(markerInterface)) { scanner.setMarkerInterface(markerInterface); } Class generatorClass = annoAttrs.getClass("nameGenerator"); if (!BeanNameGenerator.class.equals(generatorClass)) { scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass)); } Class mapperFactoryBeanClass = annoAttrs.getClass("factoryBean"); if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) { scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass)); } scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef")); scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef")); List basePackages = new ArrayList(); for (String pkg : annoAttrs.getStringArray("value")) { if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } for (String pkg : annoAttrs.getStringArray("basePackages")) { if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } for (Class clazz : annoAttrs.getClassArray("basePackageClasses")) { basePackages.add(ClassUtils.getPackageName(clazz)); } scanner.registerFilters(); scanner.doScan(StringUtils.toStringArray(basePackages)); } /** * {@inheritDoc} */ @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } } ``` ###### 2.实现ImportSelector接口,但未实现DeferredImportSelector接口 ``` 将ImportSelector的实现类实例化后, 调用其String[] selectImports(AnnotationMetadata importingClassMetadata)方法, 向spring容器内导入相关的bean, 这里作用类似于@Import 我们在spring-transaction有实现 ``` 在spring-transaction中,我们在主配置类中加上了@EnableTransactionManagement注解,注解中引入了TransactionManagementConfigurationSelector,这个类实现了ImportSelector接口 ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(TransactionManagementConfigurationSelector.class) public @interface EnableTransactionManagement { boolean proxyTargetClass() default false; AdviceMode mode() default AdviceMode.PROXY; int order() default Ordered.LOWEST_PRECEDENCE; } ``` ```java public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector { @Override protected String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()}; case ASPECTJ: return new String[] {determineTransactionAspectClass()}; default: return null; } } private String determineTransactionAspectClass() { return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ? TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME : TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME); } } ``` 默认情况下,adviceMode == PROXY,所以导入了 AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration,这里相当于使用 @Import(AutoProxyRegistrar.class)和@Import(ProxyTransactionManagementConfiguration.class),我们仍然会递归处理AutoProxyRegistrar,ProxyTransactionManagementConfiguration,判断它们是否实现SelectImport接口,是否实现DefferedSelectorImport接口或是ImportBeanDefinitionRegistrar 接口等 ###### 3.实现实现ImportSelector接口,且实现DeferredImportSelector接口 ``` 这个是当前springboot中自动配置核心注解@EnableAutoConfiguration导入的AutoConfigurationImportSelector的情况 ``` ###### 4.未实现上述所有接口 ``` 将其解析成配置类, 并处理 在spring-tx中有相关实现 ``` 在spring-tx中,由上述我们知道先通过第二种方式,向spring容器中导入了 AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration,其中 ProxyTransactionManagementConfiguration 就是普通的配置类! ##### DeferredImportSelector解析 由上述我们知道,当前springboot中向容器中导入了AutoConfigurationImportSelector,这个类实现了DeferredImportSelector接口,属于上述第三种情况,由上述 ConfigurationClassParser#processImports(..) 代码解析得知,这里会实例化 AutoConfigurationImportSelector,回调其相关Aware接口方法,并添加到 ConfigurationClassParser 的 deferredImportSelectors 列表属性中等待后续处理 ###### ConfigurationClassParser#parse(Set configCandidates) ```java public void parse(Set configCandidates) { // 1.解析所有的配置类 for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); try { if (bd instanceof AnnotatedBeanDefinition) { parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) { parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName()); } else { parse(bd.getBeanClassName(), holder.getBeanName()); } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex); } } // 2.解析完所有的配置类后, 开始解析 deferredImportSelectors!!! this.deferredImportSelectorHandler.process(); } ``` ###### DeferredImportSelectorHandler#process() ```java public void process() { List deferredImports = this.deferredImportSelectors; this.deferredImportSelectors = null; try { // 1.这里 deferredImports!= null, 这里有 AutoConfigurationImportSelector 对应的 DeferredImportSelectorHolder if (deferredImports != null) { // 2.创建 DeferredImportSelectorGroupingHandler DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler(); deferredImports.sort(DEFERRED_IMPORT_COMPARATOR); // 3.调用 handler#register(DeferredImportSelectorHolder deferredImport)方法 deferredImports.forEach(handler::register); // 4.【核心】处理, 完成自动装配 handler.processGroupImports(); } } finally { this.deferredImportSelectors = new ArrayList<>(); } } ``` ###### DeferredImportSelectorGroupingHandler##register(DeferredImportSelectorHolder deferredImport) ```java public void register(DeferredImportSelectorHolder deferredImport) { // 1.获取 AutoConfigurationImportSelector 的 importGroup --> AutoConfigurationImportSelector$AutoConfigurationGroup Class group = deferredImport.getImportSelector().getImportGroup(); // 2.创建 DeferredImportSelectorGrouping, 添加到 groupings 中 DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent( (group != null ? group : deferredImport), key -> new DeferredImportSelectorGrouping(createGroup(group))); grouping.add(deferredImport); // 3.添加到 configurationClasses this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass()); } ``` ###### DeferredImportSelectorGroupingHandler#processGroupImports() ```java public void processGroupImports() { for (DeferredImportSelectorGrouping grouping : this.groupings.values()) { // 1.这里 this.groupings.values() 即为上述创建的 DeferredImportSelectorGrouping Predicate exclusionFilter = grouping.getCandidateFilter(); // 2.grouping.getImports() 特别重要, 获取需要自动装配的配置类(读取 spring.factories配置文件 并完成过滤) grouping.getImports().forEach(entry -> { ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata()); try { // 3.每个entry都对应着自动配置类, 这里递归处理自动配置类, 继续判断当前类是否实现 @ImportSelector、@ImportBeanDefinitionRegistry等 processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter), Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)), exclusionFilter, false); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]", ex); } }); } } ``` ###### DeferredImportSelectorGrouping#getImoprts() ```java public Iterable getImports() { for (DeferredImportSelectorHolder deferredImport : this.deferredImports) { // 1.process --> 这个方法及其重要, 它会扫描jar包内所有的 /META-INF/spring.factories 配置文件, 读取org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ , 下的所有配置, 一共124个自动配置类的全限定类名, 并进行过滤, 过滤条件即为 自动配置类的@ConditionalOnxxx条件是否满足, 只有满足条件的才会启用!!! this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector()); } // 2.getImports() --> 过滤并排序 return this.group.selectImports(); } ``` **@ConditionalOnxxx** ``` @ConditionalOnBean: 在当前spring容器中存在某个bean, 才会实例化当前Bean @ConditionalOnClass: 当类路径下存在某个类的Class文件时, 才会实例化当前Bean @ConditionalOnExpression: 当条件表达式为true时, 才会实例化当前Bean @ConditionalOnMissingBean: 当spring容器中不存在某个bean, 才会实例化当前Bean @ConditionalOnMissingClass: 当类路径下不存在某个类的Class文件时, 才会实例化当前Bean @ConditionalOnProperty: 当相关属性匹配时, 才会实例化当前Bean ``` ##### 总结 ``` @Import(AutoConfigurationImportSelector.class)注解的作用是, 向容器中导入了 AutoConfigurationImportSelector, 它是DefferedImportSelector的实现类, spring会实例化AutoConfigurationImportSelector, 同时实例化其内部类AutoConfigurationGroup, 调用其process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector)方法读取 META-INF/spring.factories配置文件, 读取自动配置类, 进行过滤; 最终递归解析过滤后的自动配置类, 完成扫描和注册!!! ``` ## 3.SpringApplication#run解析 ### 3.1 SpringApplication#run(Class primarySource, String... args) ```java public static ConfigurableApplicationContext run(Class primarySource, String... args) { // 调用其重载方法 return run(new Class[] { primarySource }, args); } public static ConfigurableApplicationContext run(Class[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); } ``` #### 总结 ``` 调用run方法, 核心方法包括两部分: 第一: new SpringApplication(Class clazz) , 构造方法, 入参是启动类, 在@3.2中介绍 第二: run(args) 方法, 在@3.3中介绍 ``` ### 3.2 SpringApplication(Class... primarySources) ```java public SpringApplication(Class... primarySources) { // 调用其构造方法 public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) this(null, primarySources); } ``` 这里重点看 public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) #### 3.2.1 SpringApplication(ResourceLoader resourceLoader, Class... primarySources) ```java public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { // 1.赋值 resourceLoader, 一般为null this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // 2.赋值 primarySources, 为启动类对应的Class this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 3.推断web应用类型, 有三个值: REACTIVE、SERVLET、NONE, 这里主要根据引用的jar包来判断, 见@3.2.2 this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 4.getSpringFactoriesInstances(ApplicationContextInitializer.class)很重要: 这里会读取 // 所有 META-INF/spring.factories 配置文件, 获取里面所有key == org.springframework.context.ApplicationContextInitializer, 进行实例化, 一般有7个, 实例化之后存到initializer属性中 // ApplicationContextInitializer的作用是什么?在IOC容器创建之后,refresh刷新之前进行回调。spring-boot-starter-web中的spring.factories中有一个经典的实现 --> ServletContextApplicationContextInitializer, 在其回调方法中, 将ApplicationContext放入到ServletContext容器中, 将ServletContext容器放入到ApplicationContext中。 // 见@3.2.3 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 5.同4, 这里读取的key == ApplicationListener, 是spring事件监听器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 6.推断main方法的类名, 即启动类, 赋值到 mainApplicationClass 属性中 this.mainApplicationClass = deduceMainApplicationClass(); } ``` #### 3.2.2 SpringApplication#deduceFromClasspath() ```java /** * 推断web应用类型 -- REACTIVE、 SERVLET、 NONE(非web容器) * @return */ static WebApplicationType deduceFromClasspath() { // 1.使用类加载器加载对应的类 // 能加载到: org.springframework.web.reactive.DispatcherHandler // 且不能加载到: org.springframework.web.servlet.DispatcherServlet, org.glassfish.jersey.servlet.ServletContainer // 满足以上三个条件, 即为REACTIVE if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { return WebApplicationType.REACTIVE; } // 2.加载不到 javax.servlet.Servlet 或 org.springframework.web.context.ConfigurableWebApplicationContext // 为NONE for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } // 3.不满足以上条件, 则为SERVLET, 这是springmvc环境中的 return WebApplicationType.SERVLET; } ``` #### 3.2.3 SpringApplication#getSpringFactoriesInstances(Class type) ```java private Collection getSpringFactoriesInstances(Class type) { // 调用重载方法 return getSpringFactoriesInstances(type, new Class[] {}); } ``` ```java private Collection getSpringFactoriesInstances(Class type, Class[] parameterTypes, Object... args) { // 1.获取类加载器 ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates // 2.读取 META-INF/spring.factories配置文件中, 根据key取相关的value Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 3.实例化读取的全限定类名的实例 List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 4.排序 AnnotationAwareOrderComparator.sort(instances); return instances; } ``` #### 总结 ``` SpringApplication构造方法中, 主要: 1.推断当前spring应用的类型, 类型有三个: reactive, servlet, none, 推断的依据是根据当前应用所引用的spring相关jar包中的类, 如引用springmvc相关jar包, 则为servlet 2.从 META-INF/spring.factories 配置文件中读取两个key对应的value: ApplicationContextInitializer 和 ApplicationListener 3.推断main函数的启动类 4.将以上信息存储到SpringApplication对应的属性上 ``` ### 3.3 SpringApplication#run(String... args) ```java // 创建应用并启动 public ConfigurableApplicationContext run(String... args) { // 1.记录运行时间, 暂略 StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); // 2.获取SpringApplicationRunListeners监听器 // 这里也是通过读取 META-INF/spring.factories配置文件, 读取 key == org.springframework.boot.SpringApplicationRunListener // 对应的value, 这里只有一个 SpringApplicationRunListeners listeners = getRunListeners(args); // 3.启动监听器 -- 这里的监听器是事件发布器 // 这里会发布 ApplicationStartingEvent 应用启动等相关事件 // 监听器监听事件后会进行对应处理, 监听器是在构造器中读取spring.factories配置文件, 进行实例化并存储在当前SpringApplication中的 // 如: 这里有一个 ConfigFileApplicationListener监听到此事件后, 会读取主配置文件 application.yml文件等 // 关于 SpringApplicationRunListeners、SpringApplicationRunListener、ApplicationListener, 可以参考@3.4 listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 4.构建 ConfigurableEnvironment, 这里会根据当前应用的类型创建不同类型的Environment对象 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); // 5.打印spring图标 Banner printedBanner = printBanner(environment); // 6.创建 ApplicationContext // 根据当前应用类型创建不同类型的ApplicationContext的子类: // servlet --> AnnotationConfigServletWebServerApplicationContext // reactive --> AnnotationConfigReactiveWebServerApplicationContext // none --> AnnotationConfigApplicationContext(这个是我们前期spring-ioc源码介绍中的核心类) context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); // 7.ApplicationContext 准备阶段, 见@3.3.1 // 这里将启动类封装成BeanDefinition, 注册到spring容器的 beanDefinitionMap 中 prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 8.【核心方法】refresh, 这里解析就是spring-ioc中的启动核心部分, 启动类上加了@SpringBootApplication中有@Configuration注解, 所以这里启动类会被封装成 主配置类 ConfigurationClass 进行解析! refreshContext(context); // 9.运行时间记录停止 afterRefresh(context, applicationArguments); // 10.发布容器启动完成事件 stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; } ``` #### 3.3.1 SpringApplication#prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) ```java private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 1.设置 environment context.setEnvironment(environment); // 2.设置beanFactory的 ConversionService 属性 postProcessApplicationContext(context); // 3.调用前期从 spring.factories 配置文件中读取的 ApplicationContextInitializer 实例化后的 initialize方法 applyInitializers(context); listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // Load the sources Set sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); // 4.创建并注册启动类的BeanDefinition到spring容器中BeanDefinitionMap中 load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); } ``` #### 总结 ``` 这里主要跟spring-ioc源码类似, 有两点不同: 1.这里的主配置类@Configuration对应的类为启动类 2.启动类中的@SpringBootApplication注解中引用了 AutoConfigurationImportSelector , 这个类实现了DefferedImportSelector接口, 完成了META-INF/spring.factories中自动配置类的扫描和初始化! ``` ### 3.4 关于springboot中的监听器 #### 3.4.1 SpringApplicationRunListeners ``` 1.它监听了各个springboot启动各个阶段的事件,在每个阶段都会调用其方法,如:starting、environmentPrepared、contextPrepared、started、running等 2.它内部封装了一个list,元素是:SpringApplicationRunListener,在springboot启动时,会去读取所有依赖的jar中的spring.factories文件中的SpringApplicationRunListener,实例化后存储到list中 3.调用各个方法的实质就是遍历list,依次调用各个SpringApplicationRunListener的对应方法 4.下面来逐一介绍下SpringApplicationRunListener的各个阶段的方法【其中一些标注了 @Deprecated 的方法,即表示当前版本废弃的方法】: starting:当 run 方法第一次被执行时,会被立即调用,可用于非常早期的初始化工作。 environmentPrepared:当 environment 准备完成,在 ApplicationContext 创建之前,该方法被调用。 contextPrepared:当 ApplicationContext 构建完成,资源还未被加载时,该方法被调用。 contextLoaded:当 ApplicationContext 加载完成,未被刷新之前,该方法被调用。 started:当 ApplicationContext 刷新并启动之后, CommandLineRunner 和 ApplicationRunner 未被调用之前,该方法被调用。 ready:当应用程序上下文已刷新,并且所有的 CommandLineRunner 和 ApplicationRunner 都已被调用后,run 方法完成之前,该方法被立即调用。 running:同 ready 方法,在当前版本已被废弃。 failed:当应用程序出现错误时,该方法被调用。 ``` #### 3.4.2 EventPublishingRunListener ``` 1.它是spring-boot.jar中的spring.factories文件中唯一的默认的SpringApplicationRunListener,所以它会被封装到SpringApplicationRunListeners中; 2.在实例化EventPublishingRunListener时,它有一个很重要的属性,SimpleApplicationEventMulticaster,即spring的事件广播器。它可以广播各种事件; 3.哪些实例可以监听上述SimpleApplicationEventMulticaster广播的事件呢??在实例化EventPublishingRunListener的时候,除了创建了SimpleApplicationEventMulticaster,还将之前从spring.factories文件中加载的ApplicationListener列表,作为SimpleApplicationEventMulticaster的核心属性,所以由它们去监听EventPublishingRunListener发布的各种事件 ``` #### 3.4.3 总结 ``` 综上:目前已经发现有两种Listener:1.SpringApplicationRunListeners、2.ApplicationListener。这两个listener有舍呢关系呢??? step1: springboot启动时,会从spring.factories文件中去加载所有的ApplicationListener(默认11个) step2: 会从spring.factories文件中加载所有的SpringApplicationRunListener(默认1个 --> EventPublishingRunListener),封装到SpringApplicationRunListeners中 step3: springboot启动的过程中会调用SpringApplicationRunListeners的各个方法:如starting、environmentPrepared、contextPrepared、started、running。也就是调用各个SpringApplicationRunListener的各个starting、environmentPrepared、contextPrepared、started、running方法。也就是调用EventPublishingRunListener的各个方法。EventPublishingRunListener中包含了一个事件广播器: SimpleApplicationEventMulticaster,在上述各个方法中(starting、environmentPrepared、contextPrepared、started、running)通过广播器进行广播,监听的对象是一开始加载的11个ApplicationListener。 所以,SpringApplicationRunListener(EventPublishingRunListener)是一开始的事件监听器,监听到之后,会再广播出去,由ApplicationListener进行监听和处理。其实,与其说SpringApplicationRunListener是事件监听器,倒不如说他更像一个事件发布器。它在springboot启动的各个阶段,调用对应的事件方法。 所以,我们一般可以自定义ApplicationListener,而不是SpringApplicationRunListeners ``` [参考1](https://cloud.tencent.com/developer/article/2468630) [参考2](https://www.cnblogs.com/kukuxjx/p/17373029.html) #### 3.4.4 遗留问题 Q1:SpringBoot中的配置文件是什么时候解析的?如何解析的? --> ConfigFileApplicationListener ## 4.SpringBoot Starter机制 ### 4.1 概念 ``` springboot中的starter是一种非常重要的机制, 能够抛弃以前繁杂的配置, 将其统一集成进starter, 应用者只需要在maven中引入starter依赖, springboot就能自动扫描到要加载的信息并启动相应的默认配置。springboot会自动通过classpath路径下的类发现需要的bean, 并注册进ioc容器 ``` ### 4.2 自定义starter springboot内置starter:spring-boot-starter-xxx 自定义starter:xxx-spring-boot-starter ## 5.内嵌tomcat ### 5.1 spring-boot-starter-web 我们在项目中引入了 spring-boot-starter-web 包 ```xml org.springframework.boot spring-boot-starter-web ``` 查看 spring-boot-starter-web 包,我们知道 spring-boot-starter-xxx 包中一般不会有java代码,都是引入spring和相关第三方包,并通过spring-boot自动配置机制,完成相关核心bean的注入 ```xml org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-json org.springframework.boot spring-boot-starter-tomcat org.springframework.boot spring-boot-starter-validation org.apache.tomcat.embed tomcat-embed-el org.springframework spring-web org.springframework spring-webmvc ``` ### 5.2 Tomcat内嵌原理 #### 5.2.1 说明 由之前的学习我们知道,springboot自动配置原理中,在ApplicationContext启动时,会调用核心方法refresh(),会使用DefferedImportSelector的实现类 --> AutoConfigurationImportSelector及其内部类 AutoConfigurationGroup 去读取所有依赖jar包中的 META-INF/spring.factories 配置文件,获取key == org.springframework.boot.autoconfigure.EnableAutoConfiguration的实现类,通过实现类中的@ConditionalOnxxx进行过滤,判断当前类是否需要注册到spring中!!! 其中就包含了 org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration和org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration #### 5.2.2 ServletWebServerFactoryAutoConfiguration ```java Configuration(proxyBeanMethods = false) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @ConditionalOnClass(ServletRequest.class) @ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(ServerProperties.class) @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ServletWebServerFactoryAutoConfiguration { ........ } ``` 查看其@Import注解,它向容器中导入了 ServletWebServerFactoryConfiguration的内部类 EmbeddedTomcat、EmbeddedJetty和EmbeddedUndertow三个servlet容器 #### 5.2.3 ServletWebServerFactoryConfiguration ```java @Configuration(proxyBeanMethods = false) class ServletWebServerFactoryConfiguration { @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) static class EmbeddedTomcat { @Bean TomcatServletWebServerFactory tomcatServletWebServerFactory( ObjectProvider connectorCustomizers, ObjectProvider contextCustomizers, ObjectProvider> protocolHandlerCustomizers) { TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); factory.getTomcatConnectorCustomizers() .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList())); factory.getTomcatContextCustomizers() .addAll(contextCustomizers.orderedStream().collect(Collectors.toList())); factory.getTomcatProtocolHandlerCustomizers() .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList())); return factory; } } /** * Nested configuration if Jetty is being used. */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) static class EmbeddedJetty { @Bean JettyServletWebServerFactory JettyServletWebServerFactory( ObjectProvider serverCustomizers) { JettyServletWebServerFactory factory = new JettyServletWebServerFactory(); factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList())); return factory; } } /** * Nested configuration if Undertow is being used. */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) static class EmbeddedUndertow { @Bean UndertowServletWebServerFactory undertowServletWebServerFactory( ObjectProvider deploymentInfoCustomizers, ObjectProvider builderCustomizers) { UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory(); factory.getDeploymentInfoCustomizers() .addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList())); factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList())); return factory; } } } ``` 通过上述可以看到具体使用哪个servlet容器,也是通过@ConditionalOnxxx注解实现的,因为spring-boot-starter-web中默认使用了 tomcat,所以这里向容器中注入了 TomcatServletWebServerFactory,这个类是tomcatserver的工厂类,用于创建tomcat实例 #### 5.3.4 DispatcherServletAutoConfiguration ```java @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) // 配置加载顺序 @Configuration(proxyBeanMethods = false) // 不需要进行 cglib 代理 @ConditionalOnWebApplication(type = Type.SERVLET) // Servlet 应用的类型才注入当前 Bean @ConditionalOnClass(DispatcherServlet.class) // 存在 DispatcherServlet 这个类才注入当前 Bean @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class) // 配置加载顺序, 需要在ServletWebServerFactoryAutoConfiguration加载之后 public class DispatcherServletAutoConfiguration { /* * The bean name for a DispatcherServlet that will be mapped to the root URL "/" */ public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet"; /* * The bean name for a ServletRegistrationBean for the DispatcherServlet "/" */ public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration"; @Configuration(proxyBeanMethods = false) @Conditional(DefaultDispatcherServletCondition.class) @ConditionalOnClass(ServletRegistration.class) @EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class }) protected static class DispatcherServletConfiguration { @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) { DispatcherServlet dispatcherServlet = new DispatcherServlet(); dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest()); dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest()); dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound()); dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents()); dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails()); return dispatcherServlet; } } @Configuration(proxyBeanMethods = false) @Conditional(DispatcherServletRegistrationCondition.class) @ConditionalOnClass(ServletRegistration.class) @EnableConfigurationProperties(WebMvcProperties.class) @Import(DispatcherServletConfiguration.class) protected static class DispatcherServletRegistrationConfiguration { @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) // 为 DispatcherServlet 定义一个 RegistrationBean 对象,目的是往 ServletContext 上下文中添加 DispatcherServlet @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) // 需要存在名称为 `dispatcherServlet` 类型为 DispatcherServlet 的 Bean public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider multipartConfig) { DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath()); registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup()); multipartConfig.ifAvailable(registration::setMultipartConfig); return registration; } } ...省略... } ``` ``` 这是另一个自动配置的核心类, 可以看到当同时满足 @ConditionalOnWebApplication(type = Type.SERVLET), @ConditionalOnClass(DispatcherServlet.class) 这两个条件后, 就会向spring中注册DispatcherServletAutoConfiguration, 同时其中有两个内部类, DispatcherServletConfiguration, DispatcherServletRegistrationConfiguration, 这两个内部类有两个@Bean方法, 它们分别向spring容器中注册了 DispatcherServlet、 DispatcherServletRegistrationBean, DispatcherServletRegistrationBean负责将DispatcherServlet注册到springmvc容器中 ``` #### 总结 ``` 1.首先我们在项目中导入 spring-boot-starter-web , 它引用了tomcat相关jar 2.在SpringApplication.run()方法中, 会根据当前应用的类型, 创建对应的ApplicationContext上下文, 并调用其refresh()方法, 由前面分析我们知道, refresh()方法中借助 AutoConfigurationImportSelector 完成了自动配置, 其核心是读取所有jar中的 META-INF/spring.factories 配置文件, 并根据@ConditionalOnxxx 注解判断当前spring容器是否需要注入当前bean, 我们在引用spring-boot-starter-web后, 会向spring容器中引入 ServletWebServerFactoryAutoConfiguration, 在这个类上又有@Import注解, 根据具体依赖的servlet容器, 判断具体引入的ServerFactory, 默认会向容器中导入 TomcatServletWebServerFactory, 它用于创建tomcat实例 3.同时, 向spring容器中注册了 DispatcherServlet 和 DispatcherServletRegistrationBean, 其中DispatcherServletRegistrationBean实现了 ServletContextInitializer 接口 ``` ### 5.3 tomcat启动 #### 5.3.1 说明 由上述,我们知道调用SpringApplication#run方法时会创建ApplicationContext对象(这里的ApplicationContext实现类为:AnnotationConfigServletWebServerApplicationContext),并向容器中导入TomcatServletWebServerFactory,用于创建tomcat容器,那么tomcat容器何时创建并启动? #### 5.3.2 SpringApplication#run(String... args) ```java // 创建应用并启动 public ConfigurableApplicationContext run(String... args) { // 1.记录运行时间, 暂略 StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); // 2.获取SpringApplicationRunListeners监听器 // 这里也是通过读取 META-INF/spring.factories配置文件, 读取 key == org.springframework.boot.SpringApplicationRunListener // 对应的value, 这里只有一个 SpringApplicationRunListeners listeners = getRunListeners(args); // 3.启动监听器 -- 这里的监听器是事件发布器 // 这里会发布 ApplicationStartingEvent 应用启动等相关事件 // 监听器监听事件后会进行对应处理, 监听器是在构造器中读取spring.factories配置文件, 进行实例化并存储在当前SpringApplication中的 // 如: 这里有一个 ConfigFileApplicationListener监听到此事件后, 会读取主配置文件 application.yml文件等 listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 4.构建 ConfigurableEnvironment, 这里会根据当前应用的类型创建不同类型的Environment对象 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); // 5.打印spring图标 Banner printedBanner = printBanner(environment); // 6.创建 ApplicationContext // 根据当前应用类型创建不同类型的ApplicationContext的子类: // servlet --> AnnotationConfigServletWebServerApplicationContext (springmvc使用的) // reactive --> AnnotationConfigReactiveWebServerApplicationContext // none --> AnnotationConfigApplicationContext(这个是我们前期spring-ioc源码介绍中的核心类) context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); // 7.ApplicationContext 准备阶段, 见@3.3.1 // 这里将启动类封装成BeanDefinition, 注册到spring容器的 beanDefinitionMap 中 prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 8.【核心方法】refresh, 这里解析就是spring-ioc中的启动核心部分, 启动类上加了@SpringBootApplication中有@Configuration注解, 所以这里启动类会被封装成 主配置类 ConfigurationClass 进行解析!, 见@5.3.3 refreshContext(context); // 9.运行时间记录停止 afterRefresh(context, applicationArguments); // 10.发布容器启动完成事件 stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; } ``` #### 5.3.3 AnnotationConfigServletWebServerApplicationContext架构图![AnnotationConfigServletWebServerApplicationContext](https://gitee.com/tca/img/raw/master/springboot/AnnotationConfigServletWebServerApplicationContext.png) #### 5.3.4 SpringApplication#refreshContext ```java private void refreshContext(ConfigurableApplicationContext context) { // 这里的context --> AnnotationConfigServletWebServerApplicationContext refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } } ``` SpringApplication#refresh ```java protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); // 这里的ApplicationContext --> AnnotationConfigServletWebServerApplicationContext ((AbstractApplicationContext) applicationContext).refresh(); } ``` ServletWebServerApplicationContext#refresh ```java public final void refresh() throws BeansException, IllegalStateException { try { // 这里会调用 ioc 源码解析中的核心类 AbstractApplicationContext#refresh super.refresh(); } catch (RuntimeException ex) { stopAndReleaseWebServer(); throw ex; } } ``` AbstractApplicationContext#refresh 这个方法我们在spring-ioc中已经解析过,这里重点看onRefresh()方法 ```java @Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. // 这里采用模板方法设计模式, 由子类 AnnotationConfigServletWebServerApplicationContext实现, 该类继承了 ServletWebServerApplicationContext, 具体由其实现, 见@5.3.5 onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } } ``` #### 5.3.5 ServletWebServerApplicationContext#onRefresh ```java protected void onRefresh() { // 1.调用父类方法, 一般为空实现 super.onRefresh(); try { // 2.创建server, 这里会创建servlet容器!见@5.3.6 createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } } ``` #### 5.3.6 ServletWebServerApplicationContext#createWebServer ```java private void createWebServer() { // 1.这里 webServer == null WebServer webServer = this.webServer; // 2.这里 servletContext == null ServletContext servletContext = getServletContext(); // 3.进入 if (webServer == null && servletContext == null) { // 4.获取WebServerFactory, 这里会从spring容器中获取, 由前面学习我们知道, 在 ServletWebServerFactoryAutoConfiguration 中默认会向spring容器中注入 TomcatServletWebServerFactory, 用来创建 tomcat 容器, 所以这里的factory == TomcatServletWebServerFactory ServletWebServerFactory factory = getWebServerFactory(); // 5.这里分两步, 都很重要: // 5.1 getSelfInitializer() 见@5.3.7, 这里使用了jdk8新特性, 方法引用, 即返回一个匿名内部类对象 // 5.2 factory#getWebServer 获取web容器, 并启动, 见@5.3.8 this.webServer = factory.getWebServer(getSelfInitializer()); } else if (servletContext != null) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } initPropertySources(); } ``` #### 5.3.7 ServletWebServerApplicationContext#getSelfInitializer ```java private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() { // 调用的是下面的方法 return this::selfInitialize; } ``` ```java private void selfInitialize(ServletContext servletContext) throws ServletException { // 1.将 servletContext 赋值到 WebApplicationContext属性上 prepareWebApplicationContext(servletContext); // 2.ServletContextScope处理, 暂略 registerApplicationScope(servletContext); // 3.将 ServletContext 及其相关属性对象注入到spring容器中 WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext); // 4.获取 ServletContextInitializer, 调用其startup方法 // 这里会获取 之前注入的 DispatcherServletRegistrationBean, 并调用其onStartup方法, 向ServletContext中注入 DispatcherServlet // 以及其他 Servlet 和 Filter 等, 完成 Servlet, Filter 等的注册!!! for (ServletContextInitializer beans : getServletContextInitializerBeans()) { beans.onStartup(servletContext); } } ``` ServletWebServerApplicationContext#getServletContextInitializerBeans() ```java protected Collection getServletContextInitializerBeans() { return new ServletContextInitializerBeans(getBeanFactory()); } ``` ServletContextInitializerBeans构造方法 ```java public ServletContextInitializerBeans(ListableBeanFactory beanFactory, Class... initializerTypes) { this.initializers = new LinkedMultiValueMap<>(); // 1.initializerTypes == ServletContextInitializer this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes) : Collections.singletonList(ServletContextInitializer.class); // 2.找到 spring 容器中所有 `ServletContextInitializer` 类型的 Bean, 并将这些信息添加到 `seen` 和 `initializers` 集合中 addServletContextInitializerBeans(beanFactory); // 3.找到 spring 容器中所有 Servlet or Filter or EventListener 类型的 Bean, 适配成 RegistrationBean 对象, 并将这些信息添加到 `seen` 和 `initializers` 集合中 addAdaptableBeans(beanFactory); List sortedInitializers = this.initializers.values().stream() .flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE)) .collect(Collectors.toList()); this.sortedList = Collections.unmodifiableList(sortedInitializers); logMappings(this.initializers); } ``` #### 5.3.8 TomcatServletWebServerFactory#getWebServer ```java public WebServer getWebServer(ServletContextInitializer... initializers) { // 1.创建tomcat对象 Tomcat tomcat = new Tomcat(); // 2.tomcat相关配置 File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); tomcat.getService().addConnector(connector); customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false); configureEngine(tomcat.getEngine()); for (Connector additionalConnector : this.additionalTomcatConnectors) { tomcat.getService().addConnector(additionalConnector); } prepareContext(tomcat.getHost(), initializers); // 3.创建 TomcatWebServer, 在其构造器方法中, 会调用init方法, 完成 tomcat容器的启动 // 同时也会回调 initializers 的 onStartup方法, @5.3.7中的方法 return getTomcatWebServer(tomcat); } ``` 上面只是介绍了ServletWebServer容器的创建和初始化,什么时候进行启动呢? #### 总结 ``` 1.通过自动配置类, 引入ServletWebServerFactoryConfiguration.* 2.在ServletWebServerFactoryConfiguration.*中根据ConditionalOnClass条件来决定注册哪种类型的ServletWebServerFactory, 默认注册的是TomcatServletWebServerFactory 3.通过自动配置类引入的BeanPostProcessorRegistrar, 注册WebServerFactoryCustomizerBeanPostProcessor到ApplicationContext中 4.在ServletWebServerApplicationContext上下文的onRefresh方法中, 调用createWebServer创建WebServer 5.在createWebServer方法中通过BeanFactory获取ServletWebServerFactory 6.在BeanFactory中获取ServletWebServerFactory时, 会对TomcatServletWebServerFactory进行初始化, 初始化过程中, 工厂会调用WebServerFactoryCustomizerBeanPostProcessor的postProcessBeforeInitialization方法 7.在postProcessBeforeInitialization方法中, 从工厂获取所有WebServerFactoryCustomizer类型的Bean, 调用它们的customize方法, 传入参数为此时生成的Bean 8.初始化完成后拿到ServletWebServerFactory, 调用getWebServer方法获取WebServer 9.在方法中创建Tomcat容器及其相关组件, 并对所有组件进行初始化和自定义配置 10.准备Servlet相关的Context上下文 11.把Tomcat包装为TomcatWebServer并返回 12.在ApplicationContext的finishRefresh方法中, 启用上一步返回的TomcatWebServer ``` ## 6.RequestMappingHandlerMapping 我们在springmvc源码中简单介绍了下RequestMappingHandlerMapping,现在再来详细讲解下 ### 6.1 RequestMappingHandlerMapping是如何注册到spring容器中的 接下来以RequestMappingHandlerMapping举例,讲解下核心组件是如何被注入到spring容器中的。 step1 当我们引入spring-boot-starter-web时,间接依赖了spring-boot-autoconfigure,在其spring.factories文件中引入了WebMvcAutoConfiguration,根据springboot自动装配的原理,会加载配置类:WebMvcAutoConfiguration step2 查看配置类 ```java @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }) @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class }) public class WebMvcAutoConfiguration { @Configuration(proxyBeanMethods = false) @Import(EnableWebMvcConfiguration.class) @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class }) @Order(0) public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer { ...... } /** * Configuration equivalent to {@code @EnableWebMvc}. */ @Configuration(proxyBeanMethods = false) public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware { ...... } } ``` * @ConditionalOnMissingBean(WebMvcConfigurationSupport.class),如果我们自定义了WebMvcConfigurationSupport,则这个配置类不生效(当我们使用@EnableWebMvc注解时,会引入WebMvcConfigurationSupport的子类 --> DelegatingWebMvcConfiguration),所以在spring-boot中,不要使用@EnableWebMvc注解 * 在这个配置类中定义了两个配置类,即:WebMvcAutoConfigurationAdapter 和 EnableWebMvcConfiguration,先看第一个配置类:WebMvcAutoConfigurationAdapter,它实现了WebMvcConfigurer接口,同时向容器中引入了EnableWebMvcConfiguration * 再看引入的第二个配置类:EnableWebMvcConfiguration,它是DelegatingWebMvcConfiguration的子类,即是WebMvcConfigurationSupport的子类,它重写了父类的部分方法! step3 EnableWebMvcConfiguration配置类,在这个配置类中,使用@Bean重新定义了requestMappingHandlerMapping方法,通过这个方法向容器中注册了RequestMappingHandlerMapping ```java @Bean @Primary @Override public RequestMappingHandlerMapping requestMappingHandlerMapping( @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, @Qualifier("mvcConversionService") FormattingConversionService conversionService, @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) { // Must be @Primary for MvcUriComponentsBuilder to work return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService, resourceUrlProvider); } ``` 总结 ``` 综上,WebMvcConfigurationSupport这个配置类是SpringMVC组件的核心配置类!关于接口WebMvcConfigurer,下次再聊。 ``` ### 6.2 @RequestMapping查找原理 先看RequestMappingHandlerMapping的类图 ![RequestMappingHandlerMapping结构图](https://gitee.com/tca/img/raw/master/springmvc/RequestMappingHandlerMapping.png) step1 查找入口:在请求入口方法,DispatcherServlet#doDispatch方法中 DispatcherServlet#doDispatch(HttpServletRequest request, HttpServletResponse response) ```java protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { ...... HandlerExecutionChain mappedHandler = null; ...... try { ...... try { ...... // 核心 --> 根据请求获取 HandlerExecutionChain, 这个对象 HandlerExecutionChain 封装了HandlerMethod和Interceptor列表。 mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } ...... } ``` 我们接着进入DispatcherServlet#getHandler(HttpServletRequest request)方法 ```java protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { // 遍历所有的HandlerMapping, 如果通过HandlerMapping的getHandler方法可以获取HandlerExecutionChain, 则结束, 否则通过下一个HandlerMapping去找, 直到遍历完所有HandlerMapping, 如果都没有找到, 则返回null。所以可以看到, HandlerMapping的顺序很重要! // 那么, 这里有哪些HandlerMapping呢?HandlerMapping又是如何排序的呢?这里直接解答吧。springmvc中默认有3个HandlerMapping, springboot在其基础上又添加了2个, 一共5个。排序的规则是: 1.如果实现了Order接口, 则通过Order进行排序, 数值小的优先级高; 2.对于没有实现Order接口的, 则通过@Order注解中的value进行判断; 3.再没有, 则通过@Priority注解中的value判断。 for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; } ``` 这里我们只关注@RequestMapping注解对应的RequestMappingHandlerMapping,且在springboot中, 其优先级最高。 step2 理论上应该调用RequestMappingHandlerMapping#getHandler方法。由其父类的父类的父类AbstractHandlerMapping来实现。AbstractHandlerMapping#getHandler代码如下: ```java public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // 1.先调用getHandlerInternal方法,获取 Object handler = getHandlerInternal(request); // 2.如果没有获取到, 则获取默认的Handler if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } // 3.将处理器封装成HandlerExecutionChain HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (logger.isTraceEnabled()) { logger.trace("Mapped to " + handler); } else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) { logger.debug("Mapped to " + executionChain.getHandler()); } // 4.跨域相关 if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); config = (config != null ? config.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; } ``` 这里又是一个模板方法设计模式,getHandlerInternal方法又由子类去实现。理论上应该是RequestMappingHandlerMapping,但是它本身没有实现这个方法,所以由其父类RequestMappingInfoHandlerMapping实现,代码如下 ```java protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); try { // 继续调用父类的getHandlerInternal方法, 其父类是: AbstractHandlerMethodMapping return super.getHandlerInternal(request); } finally { ProducesRequestCondition.clearMediaTypesAttribute(request); } } ``` 如上,我们继续查看AbstractHandlerMethodMapping#getHandlerInternal方法,源码如下: ```java protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 1.获取请求路径, 作为查找的路径 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); request.setAttribute(LOOKUP_PATH, lookupPath); // 2.获取映射注册器MappingRegistry的读锁, 说明这里支持线程安全的并发操作。这个映射注册器MappingRegistry十分十分重要, 后面会介绍到 this.mappingRegistry.acquireReadLock(); try { // 3.查找处理器方法 HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); // 4.如果处理器方法不为空, 还额外解析了处理器方法中的方法所在的Bean实例, 这里先跳过 return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } } ``` 如上,其实我们需要关注的是lookupHandlerMethod方法,同样的,理论上应该调用的是RequestMappingHandlerMapping的,但实际实现是由其父类的父类AbstractHandlerMethodMapping实现,源代码如下: ```java protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { // 1.创建匹配结果列表,用于保存多个匹配结果,Match是匹配的映射信息与处理器方法的封装!!! List matches = new ArrayList<>(); // 2.这里通过请求url从映射注册器MappingRegistry获取到RequestMappingInfo列表 List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { // 3.遍历上面匹配的RequestMappingInfo列表, 获取全部匹配的映射信息与处理器方法, 继而封装成Match, 放到匹配列表中 // 上面的RequestMappingInfo是@RequestMapping的封装, 具体信息后面介绍, 需要注意的是比如我们的@RequestMapping(method = {GET, POST}) // 调用这个方法时会根据实际请求和匹配到的RequestMappingInfo列表作进一步封装, 所以封装后的RequestMappingInfo中只会有GET和POST其中的一个 addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { // No choice but to go through all mappings... // 如果没有通过直接路径找到, 则尝试遍历所有RequestMappingInfo信息, 执行与上面相同的逻辑 addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request); } // 如果有匹配上 if (!matches.isEmpty()) { Match bestMatch = matches.get(0); // 找到最佳匹配 if (matches.size() > 1) { Comparator comparator = new MatchComparator(getMappingComparator(request)); matches.sort(comparator); bestMatch = matches.get(0); if (logger.isTraceEnabled()) { logger.trace(matches.size() + " matching mappings: " + matches); } if (CorsUtils.isPreFlightRequest(request)) { return PREFLIGHT_AMBIGUOUS_MATCH; } Match secondBestMatch = matches.get(1); if (comparator.compare(bestMatch, secondBestMatch) == 0) { Method m1 = bestMatch.handlerMethod.getMethod(); Method m2 = secondBestMatch.handlerMethod.getMethod(); String uri = request.getRequestURI(); throw new IllegalStateException( "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}"); } } request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod); // 匹配上了调用的逻辑 handleMatch(bestMatch.mapping, lookupPath, request); return bestMatch.handlerMethod; } else { // 没有匹配上调用的逻辑 return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); } } ``` 上面的方法中,有两个核心方法:1.mappingRegistry#getMappingsByUrl; 2.addMatchingMappings,接下来依次介绍这两个方法。 step3 MappingRegistry#getMappingsByUrl(String urlPath) 要介绍这个方法之前,需要先了解:MappingRegistry,即映射注册器。接下来,详细介绍下这个类的和核心方法。 MappingRegistry ```java class MappingRegistry { // 核心属性如下:registry、mappingLookup、urlLookup、nameLookup(用的少,后面不单独介绍了)、corsLookup(跨域相关,暂时不介绍了) // 所以下面的文档中, 我们优先介绍这三个: registry, mappingLookup, urlLookup // 一个请求url -> 一个或多个RequestMappingInfo, 一个RequestMappingInfo对应一个HandlerMethod // key -> RequestMappingInfo, value -> MappingRegistration private final Map> registry = new HashMap<>(); // key -> RequestMappingInfo, value -> controller里面的含@RequestMapping注解的方法对应的封装:HandlerMethod(处理器方法) private final Map mappingLookup = new LinkedHashMap<>(); // key -> url, value -> List, 为什么一个url可能会对应多个RequestMappingInfo, 因为一个url可能对应多个Http method, 每个Http method都可能对应一个方法, 每个方法上都有个@RequestMapping private final MultiValueMap urlLookup = new LinkedMultiValueMap<>(); private final Map> nameLookup = new ConcurrentHashMap<>(); private final Map corsLookup = new ConcurrentHashMap<>(); private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); public Map getMappings() { return this.mappingLookup; } @Nullable public List getMappingsByUrl(String urlPath) { return this.urlLookup.get(urlPath); } /** * 注册方法, 入参: RequestMappingInfo, handler(一般是Method所在的controller的beanName, String类型), Method(RequestMapping对应的Method) */ public void register(T mapping, Object handler, Method method) { // Assert that the handler method is not a suspending one. if (KotlinDetector.isKotlinType(method.getDeclaringClass())) { Class[] parameterTypes = method.getParameterTypes(); if ((parameterTypes.length > 0) && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) { throw new IllegalStateException("Unsupported suspending handler method detected: " + method); } } this.readWriteLock.writeLock().lock(); try { // 1.封装HandlerMethod HandlerMethod handlerMethod = createHandlerMethod(handler, method); // 2.唯一性校验, 确保 RequestMappingInfo对应的HandlerMethod是唯一的, 这里先留个问题: 这里既然已经做了唯一性校验, 为什么后面还会存在多个匹配, 需要找最佳匹配的情况??? validateMethodMapping(handlerMethod, mapping); this.mappingLookup.put(mapping, handlerMethod); // 3.这里的getDirectUrls, 会从@RequestMapping对应的RequestMapping中获取多个直接匹配路径, 不包含正则匹配的 List 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); } // 跨域相关,暂略吧 CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { this.corsLookup.put(handlerMethod, corsConfig); } // 这里的MappingRegistration封装了RequestMappingInfo, url, HandlerMethod this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } } /** * 这里是注销方法, 且和register方法一样, 都是public, 所以可以让大家动态地配置注册表, 所以就更需要支持线程安全 */ public void unregister(T mapping) { this.readWriteLock.writeLock().lock(); try { MappingRegistration definition = this.registry.remove(mapping); if (definition == null) { return; } this.mappingLookup.remove(definition.getMapping()); for (String url : definition.getDirectUrls()) { List list = this.urlLookup.get(url); if (list != null) { list.remove(definition.getMapping()); if (list.isEmpty()) { this.urlLookup.remove(url); } } } removeMappingName(definition); this.corsLookup.remove(definition.getHandlerMethod()); } finally { this.readWriteLock.writeLock().unlock(); } } // ...... 注意:这里有些方法省略了,这里暂时不介绍,比如:nameLookup, corsLookup相关 } ``` 根据上面的源码介绍,我们可以了解到 MappingRegistry#getMappingsByUrl(String urlPath) 这个方法的作用了。 接下来,我们看第二个核心方法: AbstractHandlerMethodMapping#addMatchingMappings(Collection mappings, List matches, HttpServletRequest request) ```java /** * Collection mappings --> List, 即根据请求url从MappingRegistry中获取到的 List * List --> 响应结果, 将匹配的HandlerMethod和对应的RequestMappingInfo封装成Match对象 * request --> 请求 */ private void addMatchingMappings(Collection mappings, List matches, HttpServletRequest request) { for (T mapping : mappings) { // 这个方法相当重要!!! // 入参是 RequestMappingInfo 和request, 响应结果还是RequestMappingInfo, 是啥含义??? // 简单解释下, 这个后面会详细介绍 // 这里的实现是调用入参的RequestMappingInfo#getMatchingCondition(HttpServletRequest request)方法, 这个方法我们会在step5中介绍 // 作用有2: // 1.判断当前请求request是否真的和这个RequestMappingInfo匹配, 因为之前注册的时候只是通过url来注册到MappingRegistry的, 并不能说明完全匹配上 // 2.结合RequestMappingInfo和request封装成一个新的RequestMappingInfo并添加到Match列表中, why??? 因为假设我们的方法上使用的@RequestMapping(method = {RequestMethod.GET, RequestMethod.POST}), 那么注册到MappingRegistry中的RequestMappingInfo中的方法是包含GET和POST的, 但是我实际的request请求如果是GET, 那么匹配封装之后的新的RequestMappingInfo中的method就只包含GET, 没有POST!!!关于getMatchingMapping方法,它本质上调用的是入参里的mapping, 调用它的RequestMappingInfo.getMatchingCondition(request)方法,我们在后面step5介绍吧。 T match = getMatchingMapping(mapping, request); if (match != null) { matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping))); } } } ``` 这里插播个话题,这个话题也很重要:上面的MappingRegistry方法是什么时候调用的?即什么时候解析@RequestMapping和其对应的Controller中的Method,将其封装并注册到MappingRegistry中的???答案还是RequestMappingHandlerMapping。 RequestMappingHandlerMapping实现了InitializingBean接口,这个接口不多介绍了,作用是在Bean的初始化过程中会通过BeanPostProcessor调用当前Bean的afterPropertiesSet方法 RequestMappingHandlerMapping#afterPropertiesSet ```java public void afterPropertiesSet() { this.config = new RequestMappingInfo.BuilderConfiguration(); this.config.setUrlPathHelper(getUrlPathHelper()); this.config.setPathMatcher(getPathMatcher()); this.config.setSuffixPatternMatch(useSuffixPatternMatch()); this.config.setTrailingSlashMatch(useTrailingSlashMatch()); this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch()); this.config.setContentNegotiationManager(getContentNegotiationManager()); // 这里会调用父类的afterPropertiesSet方法, 实际上是其父类的父类 AbstractHandlerMethodMapping super.afterPropertiesSet(); } ``` AbstractHandlerMethodMapping#afterPropertiesSet ```java public void afterPropertiesSet() { initHandlerMethods(); } ``` AbstractHandlerMethodMapping#initHandlerMethods ```java protected void initHandlerMethods() { // getCandidateBeanNames() -> 获取BeanFactory中的所有beanName for (String beanName : getCandidateBeanNames()) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { // 核心 processCandidateBean(beanName); } } // 这个方法只是输出日志, 忽略 handlerMethodsInitialized(getHandlerMethods()); } ``` 综上,所以核心代码在: processCandidateBean(beanName),其实现的核心方法是:AbstractHandlerMethodMapping#processCandidateBean(String beanName),接下来看看这个方法 ```java protected void processCandidateBean(String beanName) { Class beanType = null; try { // 1.根据beanName获取当前bean的实际类型 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); } } // 2.isHandler是判断当前bean是否是处理器, 该方法由子类RequestMappingHandlerMapping实现。如何判断 -> 当前类上是否有@Controller注解或者@RequestMapping注解 if (beanType != null && isHandler(beanType)) { // 3.如果是处理器, 则进行核心注册操作 detectHandlerMethods(beanName); } } ``` 上面代码是判断当前bean是不是处理器,如果是,则进行核心操作:AbstractHandlerMethodMapping#detectHandlerMethods(Object handler),代码如下: ```java protected void detectHandlerMethods(Object handler) { // 1.获取处理器的Class对象 Class handlerType = (handler instanceof String ?obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { // 2.如果是代理对象, 则获取到该代理对象的目标对象 Class userType = ClassUtils.getUserClass(handlerType); // 3.这里有个匿名内部类 -> MetadataLookup // 这个方法是核心, 作用是:遍历当前处理器的所有方法, 生成一个Map, key是当前方法的Method对象, value是当前方法的@RequestMapping对应的RequestMappingInfo对象 Map methods = MethodIntrospector.selectMethods(userType,(MethodIntrospector.MetadataLookup) 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); // 4.注册, 这里实际调用的就是MappingRegistry#register方法, 上面有介绍 registerHandlerMethod(handler, invocableMethod, mapping); }); } } ``` step4 让我们看下核心方法getMappingForMethod,该方法由子类RequestMappingHandlerMapping实现 RequestMappingHandlerMapping#getMappingForMethod(Method method, Class handlerType) ```java protected RequestMappingInfo getMappingForMethod(Method method, Class handlerType) { // 1.解析方法上的@RequestMapping, 创建RequestMappingInfo对象 RequestMappingInfo info = createRequestMappingInfo(method); if (info != null) { // 2.解析类上的@RequestMapping, 创建RequestMappingInfo对象 RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType); if (typeInfo != null) { // 3.合并两个RequestMappingInfo, 这个合并方法, 即RequestMappingInfo#combine(RequestMappingInfo)方法, 我们会在后面step5中介绍。 info = typeInfo.combine(info); } // 4.暂略 String prefix = getPathPrefix(handlerType); if (prefix != null) { info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info); } } return info; } ``` 接着看上面的createRequestMappingInfo方法,由RequestMappingHandlerMapping实现,RequestMappingHandlerMapping#createRequestMappingInfo(AnnotatedElement element),这里的入参AnootatedElement既可以是方法,也可以是类 ```java private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { // 1.获取@RequestMapping属性 RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); // 2.判断是类还是方法, 获取自定义Condition, 默认实现都是null, 但是NGFS里面有定义RequestMappingHandlerMapping的子类来实现 RequestCondition condition = (element instanceof Class ? getCustomTypeCondition((Class) element) : getCustomMethodCondition((Method) element)); // 3.创建 RequestMappingInfo return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); } ``` 接下来看看,是如何创建RequestMappingInfo对象的。RequestMappingHandlerMapping#createRequestMappingInfo(RequestMapping requestMapping, @Nullable RequestCondition customCondition) ```java protected RequestMappingInfo createRequestMappingInfo( RequestMapping requestMapping, @Nullable RequestCondition customCondition) { // 使用build模式, 根据@RequestMapping注解, 创建RequestMappingInfo对象, 这里是合并完之后的RequestMappingInfo RequestMappingInfo.Builder builder = RequestMappingInfo .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(); } ``` 接下来就是分析 RequestMappingInfo 对象了!!! step5 RequestMappingInfo 解析 ```java public final class RequestMappingInfo implements RequestCondition { ...... @Nullable private final String name; private final PatternsRequestCondition patternsCondition; private final RequestMethodsRequestCondition methodsCondition; private final ParamsRequestCondition paramsCondition; private final HeadersRequestCondition headersCondition; private final ConsumesRequestCondition consumesCondition; private final ProducesRequestCondition producesCondition; private final RequestConditionHolder customConditionHolder; private final int hashCode ...... } ``` 从上面代码,可以看到: * 它实现了接口:RequestCondition,这个接口很重要,我们接下来说 * RequestMappingInfo是@RequestMapping封装对应的对象 * RequestMappingInfo对象中的核心属性,就是@RequestMapping注解里的相关配置,如RequestMethodsRequestCondition对应的就是@RequestMapping注解中的method属性 * 它的几个核心属性xxxRequestCondition也实现了RequestCondition接口。 接下来,我们先看RequestCondition接口 ```java public interface RequestCondition { /** * Combine this condition with another such as conditions from a * type-level and method-level {@code @RequestMapping} annotation. * @param other the condition to combine with. * @return a request condition instance that is the result of combining * the two condition instances. * 翻译下就是: 将当前controller类上的@RequestMapping对应的RequestMappingInfo和方法上的@RequestMapping对应的RequestMappingInfo对象进行合 并 */ T combine(T other); /** * Check if the condition matches the request returning a potentially new * instance created for the current request. For example a condition with * multiple URL patterns may return a new instance only with those patterns * that match the request. *

For CORS pre-flight requests, conditions should match to the would-be, * actual request (e.g. URL pattern, query parameters, and the HTTP method * from the "Access-Control-Request-Method" header). If a condition cannot * be matched to a pre-flight request it should return an instance with * empty content thus not causing a failure to match. * @return a condition instance in case of a match or {@code null} otherwise. * 翻译下: 获取与请求匹配的条件实例, 即与当前条件匹配的结果。举例: 一个方法上使用的@RequestMapping注解上配置的method属性为(method = {RequestMethod.GET, RequestMethod.POST}), 那么注册到MappingRegistry中的RequestMappingInfo中的方法是包含GET和POST的, 但是我实际的request请求如果是GET, 那么匹配封装之后的新的RequestMappingInfo中的method就只包含GET, 没有POST!!! */ @Nullable T getMatchingCondition(HttpServletRequest request); /** * Compare this condition to another condition in the context of * a specific request. This method assumes both instances have * been obtained via {@link #getMatchingCondition(HttpServletRequest)} * to ensure they have content relevant to current request only. * 当我们一个请求匹配多个RequestMappingInfo时, 就需要对这些RequestMappingInfo进行排序, 判断哪个跟请求是最匹配的。 */ int compareTo(T other, HttpServletRequest request); } ``` RequestCondition的几个核心方法见上。接下来我们分析RequestMappingInfo对于上述接口的具体实现。以及在何处使用? 1.首先看RequestMappingInfo#combine方法 ```java public RequestMappingInfo combine(RequestMappingInfo other) { String name = combineNames(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(name, patterns, methods, params, headers, consumes, produces, custom.getCondition()); } ``` 可以看到,该方法的实现是针对对其属性的合并。它的属性xxxRequestCondition,也都实现了RequestCondition接口,所以也是实现了combine方法。那么combine方法是在什么场景下使用的呢?答案其实在step4中已经介绍了。在初始化Bean -> RequestMappingHandlerMapping 时,调用其afterPropertiesSet方法,在该方法中会扫描含@RequestMapping或@Controller注解的class,并解析,将其注册到MappingRegistry中,其中就包含了url -> Collection<\RequestMappingInfo>的,map,这里就涉及到RequestMappingInfo的创建了。这里会先后创建class上@RequestMapping注解对应的RequestMappingInfo以及方法上@RequestMapping注解对应的RequestMappingInfo对象,并将二者合并!关于它其中的属性是如何合并的,我们后面再分析。 2.再看RequestMappingInfo#getMatchingCondition方法 ```java public RequestMappingInfo getMatchingCondition(HttpServletRequest request) { RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request); if (methods == null) { return null; } ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request); if (params == null) { return null; } HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request); if (headers == null) { return null; } ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request); if (consumes == null) { return null; } ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request); if (produces == null) { return null; } PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request); if (patterns == null) { return null; } RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request); if (custom == null) { return null; } return new RequestMappingInfo(this.name, patterns, methods, params, headers, consumes, produces, custom.getCondition()); } ``` 可以看到,该方法的实现,本质上也是调用其各个属性的getMatchingCondition方法。这个方法在何时调用的,又有什么作用呢?在step2和step3中,如何根据请求获取到对应的处理器方法中,首先通过url从MappingRegistry中查询对应的Collection<\RequestMappingInfo>,这里Collection中的RequestMappingInfo是上面combine方法调用之后的RequestMappingInfo。再获取到该Collection之后,会遍历当前Collection,取出每一个RequestMappingInfo对象,调用其getMatchingCondition方法,获取一个新的匹配的RequestMappingInfo!假设我们的合并之后的@RequestMapping(method = {RequestMethod.GET, RequestMethod.POST}), 那么注册到MappingRegistry中的RequestMappingInfo中的方法是包含GET和POST的, 但是我实际的request请求如果是GET, 那么匹配封装之后的新的RequestMappingInfo中的method就只包含GET, 没有POST!!! 3.最后看RequestMappingInfo#compareTo方法 ```java public int compareTo(RequestMappingInfo other, HttpServletRequest request) { int result; // Automatic vs explicit HTTP HEAD mapping // 先忽略 if (HttpMethod.HEAD.matches(request.getMethod())) { result = this.methodsCondition.compareTo(other.getMethodsCondition(), request); if (result != 0) { return result; } } result = this.patternsCondition.compareTo(other.getPatternsCondition(), request); if (result != 0) { return result; } result = this.paramsCondition.compareTo(other.getParamsCondition(), request); if (result != 0) { return result; } result = this.headersCondition.compareTo(other.getHeadersCondition(), request); if (result != 0) { return result; } result = this.consumesCondition.compareTo(other.getConsumesCondition(), request); if (result != 0) { return result; } result = this.producesCondition.compareTo(other.getProducesCondition(), request); if (result != 0) { return result; } // Implicit (no method) vs explicit HTTP method mappings result = this.methodsCondition.compareTo(other.getMethodsCondition(), request); if (result != 0) { return result; } result = this.customConditionHolder.compareTo(other.customConditionHolder, request); if (result != 0) { return result; } return 0; } ``` 可以看到,RequestMappingInfo的优先级比较也是其属性的优先级比较。比较的先后顺序是:PatternsRequestCondition> ParamsRequestCondition > HeadersRequestCondition > ConsumesRequestCondition > ProducesRequestCondition > RequestMethodsRequestCondition > customConditionHolder(自定义的,默认为null)。那么这个方法在何时调用呢?在step2中,当我们通过请求和getMatchingCondition方法获取到多个RequestMappingInfo时,就需要找到最佳匹配的RequestMappingInfo。如果排序后的前两个匹配的compareTo方法返回0,说明匹配度相同,那么直接抛异常! 接下来,我们介绍下属性中复杂度最高的PatternsRequestCondition(请求路径条件) step6 PatternsRequestCondition 先看它的几个核心属性: ```java public class PatternsRequestCondition extends AbstractRequestCondition { // 路径模式集合, 可以为空, 为空时匹配所有请求路径 private final Set patterns; // 路径工具类, 用于获取请求的请求路径, 实现类为: UrlPathHelper private final UrlPathHelper pathHelper; // 路径匹配器, 用于执行路径匹配逻辑, 实现类为: AntPathMatcher(很重要!) private final PathMatcher pathMatcher; // 是否启用匹配后缀(.*), 默认为false private final boolean useSuffixPatternMatch; // 是否启用匹配不上时添加/后缀尝试匹配, 默认为true private final boolean useTrailingSlashMatch; // 已知的文件扩展名列表, 默认为空, 不执行文件扩展匹配 private final List fileExtensions = new ArrayList<>(); } ``` 前面已经介绍过,当前当前class也实现了RequestCondition接口,而且RequestMappingInfo的三个核心方法的实现,也就是其各个xxxRequestCondition属性的三个核心方法的调用,所以我们接下来再分析PatternsRequestCondition是如何实现这三个方法的即可。 PatternsRequestCondition#combine ```java // 合并 public PatternsRequestCondition combine(PatternsRequestCondition other) { if (isEmptyPathPattern() && other.isEmptyPathPattern()) { return this; } else if (other.isEmptyPathPattern()) { return this; } else if (isEmptyPathPattern()) { return other; } Set result = new LinkedHashSet<>(); if (!this.patterns.isEmpty() && !other.patterns.isEmpty()) { for (String pattern1 : this.patterns) { for (String pattern2 : other.patterns) { result.add(this.pathMatcher.combine(pattern1, pattern2)); } } } return new PatternsRequestCondition(result, this); } ``` PatternsRequestCondition#getMatchingCondition ```java public PatternsRequestCondition getMatchingCondition(HttpServletRequest request) { // 从请求中获取查找的路径 String lookupPath = this.pathHelper.getLookupPathForRequest(request, HandlerMapping.LOOKUP_PATH); // 这个方法下面介绍 List matches = getMatchingPatterns(lookupPath); return !matches.isEmpty() ? new PatternsRequestCondition(new LinkedHashSet<>(matches), this) : null; } // 路径匹配并排序 public List getMatchingPatterns(String lookupPath) { List matches = null; for (String pattern : this.patterns) { String match = getMatchingPattern(pattern, lookupPath); if (match != null) { matches = (matches != null ? matches : new ArrayList<>()); matches.add(match); } } if (matches == null) { return Collections.emptyList(); } if (matches.size() > 1) { matches.sort(this.pathMatcher.getPatternComparator(lookupPath)); } return matches; } ``` PatternsRequestCondition#compareTo ```java public int compareTo(PatternsRequestCondition other, HttpServletRequest request) { String lookupPath = this.pathHelper.getLookupPathForRequest(request, HandlerMapping.LOOKUP_PATH); Comparator patternComparator = this.pathMatcher.getPatternComparator(lookupPath); Iterator iterator = this.patterns.iterator(); Iterator iteratorOther = other.patterns.iterator(); while (iterator.hasNext() && iteratorOther.hasNext()) { int result = patternComparator.compare(iterator.next(), iteratorOther.next()); if (result != 0) { return result; } } if (iterator.hasNext()) { return -1; } else if (iteratorOther.hasNext()) { return 1; } else { return 0; } } ``` 综上,我们已经分析完了如何根据请求获取HandlerMethod的整个过程。这里遗留一个问题:如何将HandlerMethod封装成HandlerExecutionChain。接下来简单讲解下。 step7 HandlerExecutionChain 我们知道,根据请求获取到处理器方法HandlerMethod之后,会将HandlerMethod和HandlerInterceptor进行封装成对象HandlerExecutionChain。那么,有哪些HandlerInterceptor呢?如何封装呢? 首先,在springboot中,RequestMappingHandlerMapping的@Bean构造器中,会创建添加两个HandlerInterceptor。如下: ```java public RequestMappingHandlerMapping requestMappingHandlerMapping( @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, @Qualifier("mvcConversionService") FormattingConversionService conversionService, @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) { RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping(); mapping.setOrder(0); // 这里创建并添加了HandlerInterceptor mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider)); mapping.setContentNegotiationManager(contentNegotiationManager); 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); } Map>> pathPrefixes = configurer.getPathPrefixes(); if (pathPrefixes != null) { mapping.setPathPrefixes(pathPrefixes); } return mapping; } ``` 其次,RequestMappingHandlerMapping的祖先类ApplicationObjectSupport,实现了ApplicationContextAware接口,在spring容器初始化RequestMappingHandlerMapping对象的过程中,会在第七次使用BeanPostProcessor时回调ApplicationContextAware接口的setApplicationContext方法,如下: ```java public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException { if (context == null && !isContextRequired()) { // Reset internal context state. this.applicationContext = null; this.messageSourceAccessor = null; } else if (this.applicationContext == null) { // Initialize with passed-in context. if (!requiredContextClass().isInstance(context)) { throw new ApplicationContextException( "Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]"); } this.applicationContext = context; this.messageSourceAccessor = new MessageSourceAccessor(context); // 这里是重点!!! initApplicationContext(context); } else { // Ignore reinitialization if same context passed in. if (this.applicationContext != context) { throw new ApplicationContextException( "Cannot reinitialize with different application context: current one is [" + this.applicationContext + "], passed-in one is [" + context + "]"); } } } ``` 在这个方法中,会调用initApplicationContext(ApplicationContext context)方法,这个方法最终会调用子类的AbstractHandlerMapping的initApplicationContext方法。接下来分析下AbstractHandlerMapping#initApplicationContext方法 AbstractHandlerMapping#initApplicationContext ```java protected void initApplicationContext() throws BeansException { // 调用这个方法之前this.interceptors中已经包含了2个HandlerInterceptor // 这里的extendInterceptors, 默认是空实现 extendInterceptors(this.interceptors); // 检测并添加MappedInterceptor, 默认也为空 detectMappedInterceptors(this.adaptedInterceptors); // 添加到adaptedInterceptors列表中 initInterceptors(); } ``` 当前interceptor都存储在AbstractHandlerMapping父类中,如下: ```java public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered, BeanNameAware { @Nullable private Object defaultHandler; private UrlPathHelper urlPathHelper = new UrlPathHelper(); private PathMatcher pathMatcher = new AntPathMatcher(); private final List interceptors = new ArrayList<>(); private final List adaptedInterceptors = new ArrayList<>(); @Nullable private CorsConfigurationSource corsConfigurationSource; private CorsProcessor corsProcessor = new DefaultCorsProcessor(); private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered @Nullable private String beanName; ...... } ``` 默认的HandlerInterceptor有2个:ConversionServiceExposingInterceptor,ResourceUrlProviderExposingInterceptor。有了这个,就可以将找到的HandlerMethod,封装成HandlerExecutionChain了。 ## 7.RequestMappingHandlerAdapter 上面介绍了RequestMappingHandlerMapping的工作原理。我们再回到上面的DispatcherServlet的doDispatch入口方法中。 DispatcherServlet#doDispatch(HttpServletRequest request, HttpServletResponse response) ```java protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { ...... HandlerExecutionChain mappedHandler = null; ...... try { ...... try { ...... // 这个是上述6.2核心部分 --> 根据请求获取 HandlerExecutionChain, 这个对象 HandlerExecutionChain 封装了HandlerMethod和Interceptor列表 mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // 1.这里是接下来的第一部分-> 根据上面获取到的处理器执行链, 获取到对应的处理器适配器 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } // 2.调用HandlerInterceptor的preHandle方法, 这个暂略 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. // 3.这是核心部分, 处理器适配器处理请求 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); // 4.调用HandlerInterceptor的postHandle方法, 这个暂略 mappedHandler.applyPostHandle(processedRequest, response, mv); ...... } ``` ### 7.1 DispatcherServlet#getHandler(Object handler) ```java protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { if (this.handlerAdapters != null) { for (HandlerAdapter adapter : this.handlerAdapters) { if (adapter.supports(handler)) { return adapter; } } } throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); } ``` 这里跟之前介绍HandlerMapping类似,会遍历容器中所有的HandlerAdapter,直到找到对应的处理器适配器。这里采用适配器模式。这个方法比较简单,就不介绍了,会找到对应的处理器适配器 -> RequestMappingHandlerAdapter。 ### 7.2 RequestMappingHandlerAdapter#handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) 上述调用ha.handle方法,RequestMappingHandlerAdapter的handle方法由其父类AbstractHandlerMethodAdapter实现,在该方法中实际调用了RequestMappingHandlerAdapter#handleInternal方法 ```java protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; // 请求校验: 1.判断本实例中的supportedMethods是否包含请求方法, 默认支持所有的请求方法; 2.如果配置的需要session属性为true, 检查请求是否包含session, 默认不需要session, 所以这段也暂略 checkRequest(request); // Execute invokeHandlerMethod in synchronized block if required. // 默认 this.synchronizeOnSession == false // 所以这段暂略 if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // No HttpSession available -> no mutex necessary mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // No synchronization on session demanded at all... // 这个是核心方法!!! mav = invokeHandlerMethod(request, response, handlerMethod); } // 缓存相关, 暂略 if (!response.containsHeader(HEADER_CACHE_CONTROL)) { if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { prepareResponse(response); } } return mav; } ``` ### 7.3 RequestMappingHandlerAdapter#invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) ```java protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { // 1.构建 ServletWebRequest ServletWebRequest webRequest = new ServletWebRequest(request, response); try { // 2.准备组件工厂 // 2.1 获取处理器方法对应的WebDataBinderFactory, 该工厂用于获取处理器方法对应的 WebDataBinder 组件 WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); // 2.2 获取处理器方法对应的ModelFactory, 该工厂用于获取处理器方法对应的Model ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); // 3.封装并配置ServletInvocableHandlerMethod // 3.1 将handlerMethod封装成ServletInvocableHandlerMethod(servlet下可调用处理器方法) ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); // 3.2 配置ServletInvocableHandlerMethod的参数解析器 if (this.argumentResolvers != null) { invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } // 3.3 配置ServletInvocableHandlerMethod的返回值处理器 if (this.returnValueHandlers != null) { invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); } // 3.4 配置ServletInvocableHandlerMethod的WebDataBinderFactory invocableMethod.setDataBinderFactory(binderFactory); // 3.5 配置ServletInvocableHandlerMethod的parameterNameDiscoverer, 这个组件用于获取方法上的参数名 invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); // 4.创建并配置ModelAndViewContainer ModelAndViewContainer mavContainer = new ModelAndViewContainer(); mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); modelFactory.initModel(webRequest, mavContainer, invocableMethod); mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); // 5.准备异步请求处理组件 // 5.1 创建并配置AsyncWebRequest AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response); asyncWebRequest.setTimeout(this.asyncRequestTimeout); // 5.2 创建并配置WebAsyncManager WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.setTaskExecutor(this.taskExecutor); asyncManager.setAsyncWebRequest(asyncWebRequest); asyncManager.registerCallableInterceptors(this.callableInterceptors); asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors); if (asyncManager.hasConcurrentResult()) { Object result = asyncManager.getConcurrentResult(); mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0]; asyncManager.clearConcurrentResult(); LogFormatUtils.traceDebug(logger, traceOn -> { String formatted = LogFormatUtils.formatValue(result, !traceOn); return "Resume with async result [" + formatted + "]"; }); invocableMethod = invocableMethod.wrapConcurrentResult(result); } // 6.调用处理器方法并处理返回值 invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; } // 7.获取ModelAndView结果 return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); } } ``` 接下来依次介绍各个步骤。 1.准备组件工厂 因为我们代码中暂时没有用到@InitBind注解和@ModelAndView注解,所以这块暂时可以忽略。 2.封装并配置ServletInvocableHandlerMethod,这里有几个核心组件:参数解析器,返回值解析器等 3.创建并配置ModelAndViewContainer,暂略 4.准备异步请求处理组件,暂略 5.调用处理器方法并处理返回值,这个方法是核心 ServletInvocableHandlerMethod#invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) ```java public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { // 1.解析参数, 调用处理器方法, 后面详细介绍 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); // 2.设置响应状态码 // 状态码来自处理器方法上的@ResponseStatus注解中的code, 如果@ResponseStatus注解中还指定了reason, 则会直接把响应转发到错误处理逻辑, 该注解多用于异常处理方法, 我们一般用统一异常处理器, 这里很少使用, 暂略吧 setResponseStatus(webRequest); // 3.这里先跳过 if (returnValue == null) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { disableContentCachingIfNecessary(webRequest); mavContainer.setRequestHandled(true); return; } } else if (StringUtils.hasText(getResponseStatusReason())) { mavContainer.setRequestHandled(true); return; } // 4.标记请求为未处理状态 mavContainer.setRequestHandled(false); Assert.state(this.returnValueHandlers != null, "No return value handlers"); try { // 5.执行返回值处理器的处理返回值逻辑 this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } // 6.如果出现异常, 则抛出, 交给异常处理器 catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(formatErrorForReturnValue(returnValue), ex); } throw ex; } } ``` 接下来,我们先看第一步,即解析参数, 调用处理器方法,该方法由其父类**InvocableHandlerMethod**实现,如下 InvocableHandlerMethod#invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) ```java public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { // 1.解析入参 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { logger.trace("Arguments: " + Arrays.toString(args)); } // 2.调用处理器方法并返回结果, 这里就是调用开发者开发的@RequestMapping下的方法 return doInvoke(args); } ``` 看看参数解析是如何处理的,实现是**InvocableHandlerMethod**,如下 InvocableHandlerMethod#getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) ```java protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { // 1.获取方法上的参数数组 MethodParameter[] parameters = getMethodParameters(); if (ObjectUtils.isEmpty(parameters)) { return EMPTY_ARGS; } // 2.遍历参数 Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; // 3.初始化参数的参数名获取器, 以用来获取参数名 parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); args[i] = findProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } if (!this.resolvers.supportsParameter(parameter)) { throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver")); } try { // 4.使用参数解析器解析参数 args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); } catch (Exception ex) { // Leave stack trace for later, exception may actually be resolved and handled... if (logger.isDebugEnabled()) { String exMsg = ex.getMessage(); if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) { logger.debug(formatArgumentError(parameter, exMsg)); } } throw ex; } } return args; } ``` ### 7.4 RequestMappingHandlerAdapter的初始化 类似于@6.1中介绍的RequestMappingHandlerMapping的创建过程,RequestMappingHandlerAdapter也类似。 step1 当我们引入spring-boot-starter-web时,间接依赖了spring-boot-autoconfigure,在其spring.factories文件中引入了WebMvcAutoConfiguration,根据springboot自动装配的原理,会加载配置类:WebMvcAutoConfiguration step2 查看WebMvcAutoConfiguration配置类 ```java @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }) @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class }) public class WebMvcAutoConfiguration { @Configuration(proxyBeanMethods = false) @Import(EnableWebMvcConfiguration.class) @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class }) @Order(0) public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer { ...... } /** * Configuration equivalent to {@code @EnableWebMvc}. */ @Configuration(proxyBeanMethods = false) public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware { ...... } } ``` * @ConditionalOnMissingBean(WebMvcConfigurationSupport.class),如果我们自定义了WebMvcConfigurationSupport,则这个配置类不生效(当我们使用@EnableWebMvc注解时,会引入WebMvcConfigurationSupport的子类 --> DelegatingWebMvcConfiguration),所以在spring-boot中,不要使用@EnableWebMvc注解 * 在这个配置类中定义了两个配置类,即:WebMvcAutoConfigurationAdapter 和 EnableWebMvcConfiguration,先看第一个配置类:WebMvcAutoConfigurationAdapter,它实现了WebMvcConfigurer接口,同时向容器中引入了EnableWebMvcConfiguration * 再看引入的第二个配置类:EnableWebMvcConfiguration,它是DelegatingWebMvcConfiguration的子类,即是WebMvcConfigurationSupport的子类,它重写了父类的部分方法!这里看下EnableWebMvcConfiguration的类结构 ![EnableWebMvcConfiguration](./img/EnableWebMvcConfiguration.png) step3 EnableWebMvcConfiguration配置类,在这个配置类中,使用@Bean重新定义了requestMappingHandlerAdapter方法,通过这个方法向容器中注册了RequestMappingHandlerAdapter ```java @Bean @Override public RequestMappingHandlerAdapter requestMappingHandlerAdapter( @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, @Qualifier("mvcConversionService") FormattingConversionService conversionService, @Qualifier("mvcValidator") Validator validator) { // 我们接着看父类的实现, 它的父类是DelegatingWebMvcConfiguration RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(contentNegotiationManager, conversionService, validator); adapter.setIgnoreDefaultModelOnRedirect( this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect()); return adapter; } ``` DelegatingWebMvcConfiguration#requestMappingHandlerAdapter ```java @Bean public RequestMappingHandlerAdapter requestMappingHandlerAdapter( @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, @Qualifier("mvcConversionService") FormattingConversionService conversionService, @Qualifier("mvcValidator") Validator validator) { // 1.这里调用RequestMappingHandlerAdapter的无参构造器 RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter(); adapter.setContentNegotiationManager(contentNegotiationManager); // 2.配置MessageConverters adapter.setMessageConverters(getMessageConverters()); adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator)); // 3.配置自定义argumentResolvers adapter.setCustomArgumentResolvers(getArgumentResolvers()); // 4.配置自定义returnValueHandlers adapter.setCustomReturnValueHandlers(getReturnValueHandlers()); if (jackson2Present) { adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice())); adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice())); } AsyncSupportConfigurer configurer = new AsyncSupportConfigurer(); configureAsyncSupport(configurer); if (configurer.getTaskExecutor() != null) { adapter.setTaskExecutor(configurer.getTaskExecutor()); } if (configurer.getTimeout() != null) { adapter.setAsyncRequestTimeout(configurer.getTimeout()); } adapter.setCallableInterceptors(configurer.getCallableInterceptors()); adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors()); return adapter; } ``` 下面来逐一分析上面的四个步骤 1.RequestMappingHandlerAdapter的无参构造器 ```java public RequestMappingHandlerAdapter() { this.messageConverters = new ArrayList<>(4); this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(new StringHttpMessageConverter()); try { this.messageConverters.add(new SourceHttpMessageConverter<>()); } catch (Error err) { // Ignore when no TransformerFactory implementation is available } this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); } ``` 构造器方法中只是配置了当前RequestMappingHandlerAdapter的messageConverters组件,添加了4个默认组件。 2.getMessageConverters(),该方法由其父类WebMvcConfigurationSupport实现,即:WebMvcConfigurationSupport#getMessageConverters ```java protected final List> getMessageConverters() { if (this.messageConverters == null) { this.messageConverters = new ArrayList<>(); // 1.配置当前WebMvcConfigurationSupport的messageConverters, 这里configureMessageConverters方法由子类DelegatingWebMvcConfiguration实现 configureMessageConverters(this.messageConverters); if (this.messageConverters.isEmpty()) { // 2.配置默认messageConverters, 由父类自己实现 addDefaultHttpMessageConverters(this.messageConverters); } // 3.拓展messageConverters, 也由子类DelegatingWebMvcConfiguration实现 extendMessageConverters(this.messageConverters); } return this.messageConverters; } ``` 可以看到,我们的配置类WebMvcConfigurationSupport中也有messageConverters属性。可以通过几个方法对其进行配置,其中添加默认的messageConverters由其自己实现,剩下的两个配置方法由其子类DelegatingWebMvcConfiguration实现。 2.1 先看第一个方法,DelegatingWebMvcConfiguration#configureMessageConverters ```java protected void configureMessageConverters(List> converters) { // 通过configurers属性的方法进行配置, 那么这个configurers是啥呢??? // 这个 configurers 是DelegatingWebMvcConfiguration的一个核心属性: private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite(); // 这个 WebMvcConfigurerComposite 是WebMvcConfigurer接口的集合的封装, 即List this.configurers.configureMessageConverters(converters); } ``` WebMvcConfigurerComposite#[configureMessageConverters]() ```java @Override public void configureMessageConverters(List> converters) { for (WebMvcConfigurer delegate : this.delegates) { // 遍历WebMvcConfigurerComposite中的每个WebMvcConfigurer, 调用它们的configureMessageConverters方法 delegate.configureMessageConverters(converters); } } ``` 默认情况下,DelegatingWebMvcConfiguration中的private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite()包含哪些WebMvcConfigurer呢?可以看到,在DelegatingWebMvcConfiguration中包含一个@Autowired的方法,如下 ```java @Autowired(required = false) public void setConfigurers(List configurers) { if (!CollectionUtils.isEmpty(configurers)) { this.configurers.addWebMvcConfigurers(configurers); } } ``` 该方法会在DelegatingWebMvcConfiguration的初始化过程中调用setConfigurers方法,参数中的configurers从spring容器中获取进行注入!springboot中的默认的实现类是:WebMvcAutoConfiguration中的内部类WebMvcAutoConfigurationAdapter,它的configureMessageConverters方法实现为: WebMvcAutoConfigurationAdapter#configureMessageConverters ```java public void configureMessageConverters(List> converters) { // 向容器中添加部分MessageConverter this.messageConvertersProvider .ifAvailable((customConverters) -> converters.addAll(customConverters.getConverters())); } ``` 再回到上面的方法,3和4步骤中配置自定义参数解析器和返回值处理器,默认都为空实现。 如果采用debug模式,我们会发现一个问题:RequestMappingHandlerAdapter的构造器方法执行完成后,它的很多重要属性都是空的,比如参数解析器、返回值处理解析器,这一块的赋值是在RequestMappingHandlerAdapter#afterPropertiesSet方法中完成,代码如下 RequestMappingHandlerAdapter#afterPropertiesSet ```java public void afterPropertiesSet() { // Do this first, it may add ResponseBody advice beans // 1.初始化ControllerAdvice缓存 initControllerAdviceCache(); // 2.添加默认的参数解析器 if (this.argumentResolvers == null) { List resolvers = getDefaultArgumentResolvers(); this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } if (this.initBinderArgumentResolvers == null) { List resolvers = getDefaultInitBinderArgumentResolvers(); this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } // 3.添加默认的返回值处理器 if (this.returnValueHandlers == null) { List handlers = getDefaultReturnValueHandlers(); this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers); } } ``` 总结 ``` 1.WebMvcConfigurationSupport及其子类DelegatingWebMvcConfiguration, 以及子类的子类EnableWebMvcConfiguration, 它们三都用于支持WebMvc中的相关组件配置的类, 这里我们先关注RequestMappingHandlerAdapter中常用的一些组件: MessageConverters, argumentResolvers, returnValueHandlers等 2.DelegatingWebMvcConfiguration中包含了一个核心属性: WebMvcConfigurerComposite, 它封装了List 3.接下来我们看看,RequestMappingHandlerAdapter是如何初始化的: a.在WebMvcAutoConfiguration中, 引入了这个配置类:EnableWebMvcConfiguration, 在这个配置类中定义了@Bean方法 -> requestMappingHandlerAdapter(xxx), 在这个方法中创建 requestMappingHandlerAdapter 对象, 同时作一些初步的配置 b.配置的过程中会获取spring容器中的WebMvcConfigurer, 调用它们的核心方法对组件进行配置。 c.以MessageConverters组件为例: 一开始messageConverters为空, 此时会通过DelegatingWebMvcConfiguration中的WebMvcConfigurerComposite(List)对其进行封装, 会获取容器中所有的WebMvcConfigurer实现类, 封装到WebMvcConfigurerComposite中, 然后依次对messageConverters进行配置, 比如添加部分messageConverter。如果没有, 则会使用默认的messageConverter进行配置。 现在, 我们知道WebMvcConfigurationSupport、WebMvcConfigurer、MessageConverter、RequestMappingHandlerAdapter 之间的关系了。以及RequestMappingHandlerAdapter是如何初始化的。 ``` ### 7.5 参数解析器 再回到上述@7.3的方法中,即: InvocableHandlerMethod#getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) ```java protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { // 1.获取方法上的参数数组 MethodParameter[] parameters = getMethodParameters(); if (ObjectUtils.isEmpty(parameters)) { return EMPTY_ARGS; } // 2.遍历参数 Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; // 3.初始化参数的参数名获取器, 以用来获取参数名 parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); args[i] = findProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } // 4.当前参数解析器是否支持当前参数, 这里的resolvers是HandlerMethodArgumentResolverComposite, 是List的封装 if (!this.resolvers.supportsParameter(parameter)) { throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver")); } try { // 5.使用参数解析器解析参数 args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); } catch (Exception ex) { // Leave stack trace for later, exception may actually be resolved and handled... if (logger.isDebugEnabled()) { String exMsg = ex.getMessage(); if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) { logger.debug(formatArgumentError(parameter, exMsg)); } } throw ex; } } return args; } ``` 参数解析器HandlerMethodArgumentResolver接口有2个核心方法,如下: ```java public interface HandlerMethodArgumentResolver { /** * Whether the given {@linkplain MethodParameter method parameter} is * supported by this resolver. * @param parameter the method parameter to check * @return {@code true} if this resolver supports the supplied parameter; * {@code false} otherwise */ boolean supportsParameter(MethodParameter parameter); /** * Resolves a method parameter into an argument value from a given request. * A {@link ModelAndViewContainer} provides access to the model for the * request. A {@link WebDataBinderFactory} provides a way to create * a {@link WebDataBinder} instance when needed for data binding and * type conversion purposes. * @param parameter the method parameter to resolve. This parameter must * have previously been passed to {@link #supportsParameter} which must * have returned {@code true}. * @param mavContainer the ModelAndViewContainer for the current request * @param webRequest the current request * @param binderFactory a factory for creating {@link WebDataBinder} instances * @return the resolved argument value, or {@code null} if not resolvable * @throws Exception in case of errors with the preparation of argument values */ @Nullable Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception; } ``` 这里,我们以核心的参数解析器 -> RequestResponseBodyMethodProcessor为例,该参数解析器内部执行了较为复杂的逻辑,获取请求体的输入流,并通过内置的各种消息转换器 **HttpMessageConverter** 把输入流转换为目标参数类型。 先来看看RequestResponseBodyMethodProcessor的类图 ![RequestResponseBodyMethodProcessor](./img/RequestResponseBodyMethodProcessor.png) 在RequestMappingHandlerAdapter#getDefaultArgumentResolvers方法中,会创建RequestResponseBodyMethodProcessor,传入的参数包括:所有的MessageConverters,全部RequestBodyAdvice、ResponseBodyAdvice类型的@ControllerAdvice ```java private List getDefaultArgumentResolvers() { List resolvers = new ArrayList<>(30); // Annotation-based argument resolution ...... resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); ...... return resolvers; } ``` ```java public RequestResponseBodyMethodProcessor(List> converters, @Nullable List requestResponseBodyAdvice) { // 调用其父类构造器 super(converters, null, requestResponseBodyAdvice); } protected AbstractMessageConverterMethodProcessor(List> converters, @Nullable ContentNegotiationManager manager, @Nullable List requestResponseBodyAdvice) { // 继续调用其父类构造器 super(converters, requestResponseBodyAdvice); this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager()); this.safeExtensions.addAll(this.contentNegotiationManager.getAllFileExtensions()); this.safeExtensions.addAll(SAFE_EXTENSIONS); } public AbstractMessageConverterMethodArgumentResolver(List> converters, @Nullable List requestResponseBodyAdvice) { Assert.notEmpty(converters, "'messageConverters' must not be empty"); this.messageConverters = converters; this.allSupportedMediaTypes = getAllSupportedMediaTypes(converters); // 创建增强器链 this.advice = new RequestResponseBodyAdviceChain(requestResponseBodyAdvice); } ``` RequestResponseBodyAdviceChain ```java // 该方法将有@ControllerAdvice注解的类且实现接口RequestBodyAdvice或ResponseBodyAdvice添加到对应的属性中, 分别是请求体的增强和响应体的增强 public RequestResponseBodyAdviceChain(@Nullable List requestResponseBodyAdvice) { this.requestBodyAdvice.addAll(getAdviceByType(requestResponseBodyAdvice, RequestBodyAdvice.class)); this.responseBodyAdvice.addAll(getAdviceByType(requestResponseBodyAdvice, ResponseBodyAdvice.class)); } @SuppressWarnings("unchecked") static List getAdviceByType(@Nullable List requestResponseBodyAdvice, Class adviceType) { if (requestResponseBodyAdvice != null) { List result = new ArrayList<>(); for (Object advice : requestResponseBodyAdvice) { Class beanType = (advice instanceof ControllerAdviceBean ? ((ControllerAdviceBean) advice).getBeanType() : advice.getClass()); if (beanType != null && adviceType.isAssignableFrom(beanType)) { result.add((T) advice); } } return result; } return Collections.emptyList(); } ``` #### 7.5.1 HttpMessageConverter - 消息转换器 消息转换器主要包括两个功能,即读取HTTP请求的请求体并将其序列化为目标类的实例对象,把对象实例反序列化后写入到HTTP的响应体中。 ```java public interface HttpMessageConverter { // 是否支持把mediaType类型的请求体序列化为clazz类型对象 boolean canRead(Class clazz, @Nullable MediaType mediaType); // 是否支持把clazz类型对象反序列化为内容类型为mediaType的响应中 boolean canWrite(Class clazz, @Nullable MediaType mediaType); // 该消息类型转换器支持读取与写入的所有mediaType List getSupportedMediaTypes(); // 执行读取操作, 把HTTP输入消息读取为clazz对象 T read(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException; // 执行写入操作, 将对象写入到HTTP响应流中 void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException; } ``` #### 7.5.2 RequestBodyAdvice - 请求体增强器 ```java public interface RequestBodyAdvice { // 根据方法参数、参数类型、用于读取请求体的消息转换器类型来确定该增强器是否对其进行增强 boolean supports(MethodParameter methodParameter, Type targetType, Class> converterType); // 在读取消息体前执行的增强方法, 传入原HTTP输入消息、方法参数、方法类型与消息转换器类型, 可返回一个新的HTTP输入信息, 用于后续读取 HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class> converterType) throws IOException; // 在请求体读取完成后执行的增强方法, 传入读取消息体后转换的结果、HTTP输入消息、方法参数、参数类型与消息转换器类型, 返回一个结果, 此结果会替代消息转换器转换后的原始结果 Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class> converterType); // 当请求体为空时, 执行此增强方法, 传入空的body、HTTP消息输入、方法参数、参数类型、消息转换器类型, 返回一个对象实例, 用于作为消息转换器的读取结果 @Nullable Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class> converterType); } ```