前言
之前介绍到了把启动类
封装成BeanDefinition
注入进IOC容器,那么这个启动类就会跟普通的bean
一样在refresh()
中被实例化,那么显而易见作为启动类这个实例化并不简单,肯定会存在一些特殊处理,那么就需要研究一下其注解@SpringBootApplication
一、@SpringBootApplication
根注解概述
@SpringBootApplication
是SpringBoot实现自动配置的入口,它里面包含另外3个注解:
@SpringBootConfiguration
: 将启动类标注为@Configuration
,毕竟只有被标记为配置类,才能被扫描进IOC容器@EnableAutoConfiguration
: 自动配置机制,包括:引入自动配置包 + 扫描自动配置机制用到的类@ComponentScan
:扫描bean,默认会扫描启动类同级包和子包下开发人员写的bean
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration // 根注解组成部分一
@EnableAutoConfiguration // 根注解组成部分二
@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;
}
根注解组成部分一:@SpringBootConfiguration
【核心功能】:把启动类标记为@Configuration
,这样才能被Spring
框架扫描到
@SpringBootConfiguration
这个注解包含了@Configuration
,@Configuration
里面又包含了一个@Component
注解
之前介绍我们需要把启动类
注入到IOC容器中,那它也需要被@Component
标记
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration // 只是标注启动类同样为配置类
@Indexed
public @interface SpringBootConfiguration {@AliasFor(annotation = Configuration.class)boolean proxyBeanMethods() default true;}
根注解组成部分二:@EnableAutoConfiguration
该注解负责的功能也很简单,通过名字就可以看出来,开启自动配置机制
,那么如何开启?
它由@AutoConfigurationPackage
和@Import(AutoConfigurationImportSelector.class)
两部分组成:
@Import(AutoConfigurationImportSelector.class)
引入类负责按需加载自动配置类@EnableAutoConfiguration
允许自动配置类扫描开发人员写的类
这里只写了几句话介绍的很简单,下面会分开详细介绍
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 自动配置包规则的注解
@Import(AutoConfigurationImportSelector.class) // 引入的类
public @interface EnableAutoConfiguration {/*** Environment property that can be used to override when auto-configuration is* enabled.*/String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";/*** Exclude specific auto-configuration classes such that they will never be applied.* @return the classes to exclude*/Class<?>[] exclude() default {};/*** Exclude specific auto-configuration class names such that they will never be* applied.* @return the class names to exclude* @since 1.3.0*/String[] excludeName() default {};
}
1.加载自动配置类
1) 功能介绍
在SpringBoot
出现之后,我们使用某个功能,无须在经历引入jar包、写配置文件这样的路径,直接把对应的spring-boot-starter-xxx
这样的依赖引入就可以了,那么想一下,其中肯定有对应的自动配置类来负责实现,而且每个依赖肯定有各自的自动配置类,只有这些自动配置类被执行,我们才能调用对应的功能
那么引入的这个类AutoConfigurationImportSelector
就是负责读取每个依赖中的自动配置类
2) 获取自动配置类
上面看到了引入类AutoConfigurationImportSelector
的类结构,实现了ImportSelector
,那么肯定就要看selectImports()
这个方法
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {// <1>. 判断自动装配开关是否打开 if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);// <2>. 获取所有需要装配的beanAutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata);// <3> 转换成数组返回 return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}}
<1> 自动配置类加载入口
// 按需加载自动配置类
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,AnnotationMetadata annotationMetadata) {// 判断自动装配开关是否打开,默认spring.boot.enableautoconfiguration=trueif (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}// 获取EnableAutoConfiguration注解的exclude和excludeName属性值AnnotationAttributes attributes = getAttributes(annotationMetadata);// <1> 获取spring.factories中EnableAutoConfiguration的配置值List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);// 去重,原因是上面会读取所有spring-boot-starter的META-INF/spring.factories文件,可能会存在重复的,需要保证唯一configurations = removeDuplicates(configurations);// 获取限制候选配置的所有排除项(找到不希望自动装配的配置类)Set<String> exclusions = getExclusions(annotationMetadata, attributes);// 对参数exclusions进行验证,exclusion必须为自动装配的类,否则抛出异常checkExcludedClasses(configurations, exclusions);// 移除exclusionsconfigurations.removeAll(exclusions);// <2> 过滤出需要导入的配置类configurations = filter(configurations, autoConfigurationMetadata);// 配置监听事件fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);
}
<2> 加载全部自动配置类
由上面getAutoConfigurationEntry()
进入到该getCandidateConfigurations()
,负责获取全部的自动配置的类,
下面显示了3个方法,进行了很多方法的调用
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {// getSpringFactoriesLoaderFactoryClass() 就是获取@EnableAutoConfiguration注解类List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "+ "are using a custom packaging, make sure that file is correct.");return configurations;
}public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {String factoryTypeName = factoryType.getName();return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {String factoryTypeName = factoryType.getName();return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
loadSpringFactories()
由上面loadFactoryNames()
调用到方法loadSpringFactories()
,该方法就负责读取配置文件,方法代码没有展示,知道做了什么就可以
读取的配置文件路径:META-INF/spring.factories
,下面截图只展示了一个配置文件,实际不只是这一个配置文件,下面会继续介绍
通过上面截图了解到会读取配置文件,那么配置文件中有好多内容,怎么知道读取的就是org.springframework.boot.autoconfigure.EnableAutoConfiguration
在下面截图中会看到,其实很简单,这个路径就是@EnableAutoConfiguration
这个注解的路径
通过Debug,可以看到读取到的配置类有一百多个
【注意】
读取到配置类不光是这个依赖下的META-INF/spring.factories
被读取到,所有spring-boot-starter-xxx
下的META-INF/spring.factories
都会被读取到。
所以,你可以清楚滴看到,druid
数据库连接池的SpringBoot Starter
就创建了META-INF/spring.factories
文件。
如果,我们自己要创建一个spring-boot-starter
,这一步是必不可少的。
<3> 筛选自动配置类
@Conditional 条件注解
spring.factories
中这么多配置,每次启动都要全部加载么?很明显,这是不现实的。我们Debug
到后面你会发现,configurations
的值变小了。
这是因为有了这一步的筛选,才能保证按需加载
,那么如何实现,想想都知道肯定是类似于if判断的筛选,具体的实现是借助SpringBoot
提供的条件注解
- @ConditionalOnBean:当容器里有指定
Bean
的条件下 - @ConditionalOnMissingBean:当容器里没有指定
Bean
的情况下 - @ConditionalOnSingleCandidate:当指定
Bean
在容器中只有一个,或者虽然有多个但是指定首选Bean
- @ConditionalOnClass:当类路径下有指定类的条件下
- @ConditionalOnMissingClass:当类路径下没有指定类的条件下
- @ConditionalOnProperty:指定的属性是否有指定的值
- @ConditionalOnResource:类路径是否有指定的值
- @ConditionalOnExpression:基于
SpEL
表达式作为判断条件 - @ConditionalOnJava:基于
Java
版本作为判断条件 - @ConditionalOnJndi:在
JNDI
存在的条件下差在指定的位置 - @ConditionalOnNotWebApplication:当前项目不是
Web
项目的条件下 - @ConditionalOnWebApplication:当前项目是
Web
项目的条件下
示例1:SpringMVC自动配置
如下为了适应SpringMVC
,需要满足下面的条件
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET) // 是Servlet原生式Web
@ConditionalOnClass(DispatcherServlet.class) // 存在DispatcherServlet这个类
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
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)// 这里会从WebMvcProperties.class获取配置// WebMvcProperties.class又和配置文件application.properties绑定@EnableConfigurationProperties(WebMvcProperties.class)protected static class DispatcherServletConfiguration {@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {DispatcherServlet dispatcherServlet = new DispatcherServlet();dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());return dispatcherServlet;}// 这个比较有意思// 当存在这个组件,并且没有名字 然后方法就是直接返回 其实它就是为了保证注入的文件上传组件必须是按照这个名字,防止乱改名字@Bean@ConditionalOnBean(MultipartResolver.class) // 存在MultipartResolver这个Bean@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)// 当没有这个multipartResolver名字的Beanpublic MultipartResolver multipartResolver(MultipartResolver resolver) {// Detect if the user has created a MultipartResolver but named it incorrectly// 直接返回return resolver;}}// 省略代码...
}
示例2:Aop自动配置
package org.springframework.boot.autoconfigure.aop;import org.aspectj.weaver.Advice;import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration(proxyBeanMethods = false)
// 必须存在spring.aop这个值才可以
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnClass(Advice.class) // 必须包含Advice这个组件static class AspectJAutoProxyingConfiguration {@Configuration(proxyBeanMethods = false)@EnableAspectJAutoProxy(proxyTargetClass = false)@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false")static class JdkDynamicAutoProxyConfiguration {}@Configuration(proxyBeanMethods = false)@EnableAspectJAutoProxy(proxyTargetClass = true)@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",matchIfMissing = true)static class CglibAutoProxyConfiguration {}}@Configuration(proxyBeanMethods = false)@ConditionalOnMissingClass("org.aspectj.weaver.Advice")@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",matchIfMissing = true)static class ClassProxyingConfiguration {@Beanstatic BeanFactoryPostProcessor forceAutoProxyCreatorToUseClassProxying() {return (beanFactory) -> {if (beanFactory instanceof BeanDefinitionRegistry) {BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}};}}
}
筛选执行 filter()
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {long startTime = System.nanoTime();String[] candidates = StringUtils.toStringArray(configurations);boolean[] skip = new boolean[candidates.length];boolean skipped = false;// 获取筛选器,也是从META-INF/spring.factories中获取,会获取到3个for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {invokeAwareMethods(filter);// 这里会调用接口的match()方法进行匹配boolean[] match = filter.match(candidates, autoConfigurationMetadata);for (int i = 0; i < match.length; i++) {if (!match[i]) {skip[i] = true;candidates[i] = null;skipped = true;}}}if (!skipped) {return configurations;}// 获取筛选后的自动配置类List<String> result = new ArrayList<>(candidates.length);for (int i = 0; i < candidates.length; i++) {if (!skip[i]) {result.add(candidates[i]);}}if (logger.isTraceEnabled()) {int numberFiltered = configurations.size() - result.size();logger.trace("Filtered " + numberFiltered + " auto configuration class in "+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");}return new ArrayList<>(result);
}
2.自动配置包
1) 注入bean
【提前声明】:首先说结果,@AutoConfigurationPackage
这个注解折腾了一大圈,最终只是向容器中注入了一个BeanDefinition
通过名字可以看到该注释只是@Import
了一个类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {String[] basePackages() default {};Class<?>[] basePackageClasses() default {};
}
进入到这个类中,看到是个静态内部类,那么肯定关注registerBeanDefinitions()
这个方法了
// 实现了 ImportBeanDefinitionRegistrar 注册类
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {// 参数:new PackageImports(metadata).getPackageNames().toArray(new String[0]) 会获取到启动类所在的包路径register(registry, new PackageImport(metadata).getPackageName());}@Overridepublic Set<Object> determineImports(AnnotationMetadata metadata) {return Collections.singleton(new PackageImport(metadata));}}
进入到register()
,发现它只是向容器中注入了一个BeanDefinition
- 对应的bean名称为那个静态常量:
BEAN
- 会把启动类的包路径存入
BeanDefinition
// 这个 BEAN 为需要注入的 BeanDefinition 的名称
// AutoConfigurationPackages.class.getName() = org.springframework.boot.autoconfigure.AutoConfigurationPackages
private static final String BEAN = AutoConfigurationPackages.class.getName();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 的类型为 BasePackages.classbeanDefinition.setBeanClass(BasePackages.class);// 由于上面是 BeanDefinition,还没有实例化BasePackages,所以这里定义在实例化的时候,使用对应的构造方法,把包路径传进去beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);registry.registerBeanDefinition(BEAN, beanDefinition);}
}
通过Debug看一下
2) 思路分析
上面可能看的比较懵,注入的这个BeanDefinition
有啥用?猜猜都知道肯定和那个包路径有关
举个例子:例如最常见的Mybatis
,我们使用的时候它会扫描被@Mapper
标记的接口,这是看到的效果,如何实现的呢?首先Mybatis
的自动配置类会通过这个之前注入的自动化配置bean来决定是否自动扫描mapper,也就是把注入的那个BeanDefinition
当作一个开关,代表允许扫描开发人员写的类,那么接下来就顺理成章了,那么包路径肯定是扫描哪个路径下的类
下面截图Debug展示一下Mybatis
的自动配置类MybatisAutoConfiguration
,在这之前肯定先导入对应的依赖mybatis-spring-boot-starter
通过这个例子应该就明白了,其实把这个包路径包装成BeanDefinition
存入IOC容器,我觉得是变相的把IOC容器当作是一个缓存了,因为IOC容器是始终存在的,贯穿这个项目的
那么在总结一下,@AutoConfigurationPackage
的作用是允许其它自动配置类去扫描开发人员写的代码,然后根据不同的扫描规则来执行,可能有人好奇Spring
框架不是都扫描了嘛,咋这还给扫描呢,肯定是有区分的,Spring
框架扫描的@Component
注解,Mybatis
扫描的是@Mapper
注解,自己定义的注解自己扫描
3) 理解误区
在网上也搜了一些文章,也包括自己最开始的错误想法,认为这个是扫描包路径下那些被@Component
标记的需要注入的IOC容器的bean,我们之所以在里面写个类,加个@Component
就能注入到IOC容器中,就是因为这个@AutoConfigurationPackage
,但这样理解是错误的,网上很多文章也是错误的
而且通过直观的角度来看,它是放在@EnableAutoConfiguration
这个注解里面的,父注解是开启自动配置机制,那么这个肯定也是和自动配置机制有关的了
那么扫描包路径下的@Component
,这个是怎么实现呢,是通过下面要介绍的组成部分三@ComponentScan
来实现的
3.总结
上面介绍了2个注解以后,应该整体就清晰了,自动配置机制
,首先会通过@Import(AutoConfigurationImportSelector.class)
按需加载需要的自动配置类,如果某些自动配置类需要扫描自己写的类,那么会通过@AutoConfigurationPackage
来获取允许扫描的路径,这样就保证将依赖的jar包会注入到IOC容器中,自己写的类会和jar包绑定起来
根注解组成部分三:@ComponentScan
1.功能介绍
在这里会负责扫描启动类同级和子包下的bean
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}
2.TypeExcludeFilter 没啥用
它是一种扩展机制,子类可以继承TypeExcludeFilter
,重写match()
,自定义将某些类排除注册,平常开发用不到
public class TypeExcludeFilter implements TypeFilter, BeanFactoryAware {private BeanFactory beanFactory;private Collection<TypeExcludeFilter> delegates;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory = beanFactory;}@Overridepublic boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)throws IOException {if (this.beanFactory instanceof ListableBeanFactory && getClass() == TypeExcludeFilter.class) {// 从容器中获取TypeExcludeFilter,然后遍历匹配for (TypeExcludeFilter delegate : getDelegates()) {if (delegate.match(metadataReader, metadataReaderFactory)) {return true;}}}return false;} // 从容器中获取TypeExcludeFilterprivate Collection<TypeExcludeFilter> getDelegates() {Collection<TypeExcludeFilter> delegates = this.delegates;if (delegates == null) {delegates = ((ListableBeanFactory) this.beanFactory).getBeansOfType(TypeExcludeFilter.class).values();this.delegates = delegates;}return delegates;}}
3.AutoConfigurationExcludeFilter
如果类上同时有@Configuration
和@EnableAutoConfiguration
自动配置类,那么该类需要被排除
public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {private ClassLoader beanClassLoader;private volatile List<String> autoConfigurations;@Overridepublic void setBeanClassLoader(ClassLoader beanClassLoader) {this.beanClassLoader = beanClassLoader;}// 匹配方法@Overridepublic boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)throws IOException {// 1.判断类是否有 @Configuration 注解// 2.判断是否有 @EnableAutoConfiguration 注解return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);}private boolean isConfiguration(MetadataReader metadataReader) {return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());}private boolean isAutoConfiguration(MetadataReader metadataReader) {return getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());}protected List<String> getAutoConfigurations() {if (this.autoConfigurations == null) {this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,this.beanClassLoader);}return this.autoConfigurations;}}
二、部分逻辑点Debug介绍
在上面的章节中我们介绍了主启动类启动SpringApplication.run(DroolsWys4822Application.class, args);
以及根注解@SpringBootApplication
,那么这里再次对一些逻辑点进行Debug介绍,会展示一些重要的截图
1.启动类注入IOC容器
SpringBoot
会把启动类封装成BeanDefinition
注入到IOC容器,然后以配置类的方式被初始化,执行代码是在SpringApplication -> run() -> prepareContext() -> load()
,这个之前已经介绍了
2.初始化启动类
之前介绍过启动类就是一个配置类,这个已经重复多次了,那么负责解析它的是ConfigurationClassPostProcessor
,这个研习过Spring
框架的肯定知道
首先回忆一下代码在refresh() -> invokeBeanFactoryPostProcessors()
,这里执行后置处理器,其中就有ConfigurationClassPostProcessor
,当然这个后置处理器是Spring框架自带的,启动以后默认就会注入到容器中的
接下来肯定就是执行ConfigurationClassPostProcessor
中的方法,负责解析各个配置类的内容
到这里以后启动类就会像普通的配置类一样,被逐步解析,具体解析步骤在Spring
中已经介绍了
3.自动扫描注入bean
都知道SpringBoot
默认会加载启动类同级和子包下的bean注入到IOC容器,那么Debug展示一下:
三、自动配置总结
- 在
SpringBoot
启动的时候会创建一个SpringApplication
对象,在对象的构造方法里面会进行一些参数的初始化工作,最主要的是判断当前应用程序的类型以及设置初始化器以及监听器,并在这个过程中会加载整个应用程序的spring.factories
文件,将文件中的内容放到缓存当中,方便后续获取; SpringApplication
对象创建完成之后会执行run()
方法来完成整个应用程序的启动,启动的过程中有两个最主要的方法prepareContext()
和refreshContext()
,在这两个方法中完成了自动装配的核心功能,在run()
方法里还执行了一些包括上下文对象的创建,打印banner
图,异常分析器的准备等各个准备工作,方便后续进行调用;- 在
prepareContext()
中主要完成的是对上下文对象的初始化操作,包括属性的设置,比如设置环境变量。在整个过程中有一个load()
方法,它主要是完成一件事,那就是将启动类作为一个beanDefinition
注册到registry
,方便后续在进行BeanFactoryPostProcessor
调用执行的时候,可以找到对应执行的主类,来完成对@SpringBootApplication
、@EnableAutoConfiguration
等注解的解析工作; - 在
refreshContext()
方法中会进行整个容器的刷新过程,会调用spring
中的refresh()
方法,refresh()
方法中有13个非常关键的方法,来完成整个应用程序的启动。而在refresh()
中会调用的关键的一个方法就是invokeBeanFactoryPostProcessors()
方法,在这个方法中主要是对ConfigurationClassPostProcessor
类的处理,这个类负责解析被@Configuration
标记的配置类(这里需要研习过refresh()方法),之前介绍过启动类是被@Configuration
标记了,所以会在这里进行处理解析,会解析处理各种的注解,包含@PropertySource
、@ComponentScan
、@Bean
、@Import
等注解,最主要的是对@Import
注解的解析 - 总结一下,我之前的错误思想以为
SpringBoot
和Spring
是两码事,而且主启动类很神秘,仔细研究了原理之后,发现SpringBoot
的启动类它就是一个@Configuration
配置类,在refresh()
的时候也会像我们自己写的配置类一样被初始化,然后SpringBoot
就是利用这点引入了很多默认配置,所以需要领悟的是SpringBoot
和Spring
是包含关系,SpringBoot = Spring + 默认配置
,它利用了Spring框架提供的良好扩展性,封装出这一套框架协助开发人员快速开发,再来体会一下SpringBoot的优点开箱即用
,当然也要感叹一下作者Pivotal团队
强大的创造力。
esh()方法中有13个非常关键的方法,来完成整个应用程序的启动。而在
refresh()中会调用的关键的一个方法就是
invokeBeanFactoryPostProcessors()方法,在这个方法中主要是对
ConfigurationClassPostProcessor类的处理,这个类负责解析被
@Configuration标记的配置类(这里需要研习过refresh()方法),之前介绍过启动类是被
@Configuration标记了,所以会在这里进行处理解析,会解析处理各种的注解,包含
@PropertySource、
@ComponentScan、
@Bean、
@Import等注解,最主要的是对
@Import`注解的解析 - 总结一下,我之前的错误思想以为
SpringBoot
和Spring
是两码事,而且主启动类很神秘,仔细研究了原理之后,发现SpringBoot
的启动类它就是一个@Configuration
配置类,在refresh()
的时候也会像我们自己写的配置类一样被初始化,然后SpringBoot
就是利用这点引入了很多默认配置,所以需要领悟的是SpringBoot
和Spring
是包含关系,SpringBoot = Spring + 默认配置
,它利用了Spring框架提供的良好扩展性,封装出这一套框架协助开发人员快速开发,再来体会一下SpringBoot的优点开箱即用
,当然也要感叹一下作者Pivotal团队
强大的创造力。