正文
SpringBoot是如何将相关组件装配到IOC的容器里面的?这个是核心的问题。
一、主启动类
/** * @author :breakpoint/赵立刚 * @date : 2020/08/03 */ @EnableAsync // 开启是否支持异步的操作 @SpringBootApplication // 声明当前的应用是SpringBoot的应用 public class StartMain { public static void main(String[] args) { SpringApplication.run(StartMain.class, args); } }
二、@SpringBootApplication注解的功能
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration // SpringBoot的配置 与 @Configuration的作用类似,都是可以自动发现配置的 @EnableAutoConfiguration // 开启自动的装配的功能个 @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) // 上面的 @ComponentScan 是包扫描方案,扫描哪些包 public @interface SpringBootApplication {
三、@EnableAutoConfiguration的功能
一共提供了@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)两个核心的注解
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage // @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {
3.1 @AutoConfigurationPackage注解的功能
自动的配置我们的主启动类上面的包名
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { }
AutoConfigurationPackages.Registrar.class的主要的作用就是注册一个GenericBeanDefinition的bean的定义给IOC的容器。
将主配置类(@SpringBootApplication标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器;
3.2 AutoConfigurationImportSelector的功能
AutoConfigurationImportSelector:导入哪些组件的选择器;
将所有需要导入的组件以全类名的方式返回;这些组件就会被添加到容器中;
会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件, 并配置好这些组件;
// org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } // 家在自动配置的属性 AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); // 获取到自己配置的相关信息,这里面也返回了我们需要自动配置的类的信息 AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
接下来分析:getAutoConfigurationEntry方法
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } // 获取到注解的属性信息 AnnotationAttributes attributes = getAttributes(annotationMetadata); // 获取到候选的配置信息,这一步也就是加载了所有的自动配置的信息,一会主要分析的就是这个方法 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 去除掉重复的配置信息 configurations = removeDuplicates(configurations); // 根据以前的配置,获取到所有需要排除的信息 Set<String> exclusions = getExclusions(annotationMetadata, attributes); // 校验需要排除的配置类的信息 checkExcludedClasses(configurations, exclusions); // 执行移除的操作 configurations.removeAll(exclusions); // 过滤一下配置的信息与获取到的后信息是否有问题 configurations = filter(configurations, autoConfigurationMetadata); // 首先获取到所有的配置的监听器,之后配置listener.onAutoConfigurationImportEvent(event); fireAutoConfigurationImportEvents(configurations, exclusions); //返回操作的对象 return new AutoConfigurationEntry(configurations, exclusions); }
3.3 getCandidateConfigurations
这个是主要的返回我们候选配置类的基本信息:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { // 显然,和我们以前分析的一样,都是从META-INF/spring.factories获取到所有的EnableAutoConfiguration的组件并且进行返回。 List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); // 判断是否为空,默认肯定不是空的,因为spring-boot-autoconfigure-2.2.2.RELEASE.jar已经为我们配置了一些主要的的自动配置类。 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; }
四、如何将组件加入到IOC容器呢?
4.1 META-INF/spring.factories文件到底是什么样的
首先,在spring-boot-autoconfigure-2.2.2.RELEASE.jar里面,我们可以找到下面的文件
查看一下内容
# Auto Configure # 可以看到一些自动配置的信息 # 全类名=自动配置类的集合 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ ...
4.2 举例说明如何自动配置
我们用org.springframework.boot.autoconfigure.aop.AopAutoConfiguration为例子,来看一看如何的自动配置
// 说明当前是一个配置类 @Configuration(proxyBeanMethods = false) // 判断当前是否配置了 spring.aop 的相关的配置 @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true) public class AopAutoConfiguration { @Configuration(proxyBeanMethods = false) // 当 Advice 存在的是欧,这个配置是生效的 @ConditionalOnClass(Advice.class) static class AspectJAutoProxyingConfiguration { @Configuration(proxyBeanMethods = false) @EnableAspectJAutoProxy(proxyTargetClass = false) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = 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) // 不存在 org.aspectj.weaver.Advice这个类,是生效的 @ConditionalOnMissingClass("org.aspectj.weaver.Advice") @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true) static class ClassProxyingConfiguration { ClassProxyingConfiguration(BeanFactory beanFactory) { if (beanFactory instanceof BeanDefinitionRegistry) { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } } } }
通过上面的分析,其实我们可以发现,我们可以根据特定的环境分别的配置我们的组件,何时生效以及何时不生效,也就是这样,实现了SpringBoot的自动装配的最终的效果,实现了SpringBoot的精髓功能。
五、后记
@Conditional派生注解:
必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;
@Conditional扩展注解 | 作用(判断是否满足当前指定条件) |
@ConditionalOnJava | 系统的java版本是否符合要求 |
@ConditionalOnBean | 容器中存在指定Bean; |
@ConditionalOnMissingBean | 容器中不存在指定Bean; |
@ConditionalOnExpression | 满足SpEL表达式指定 |
@ConditionalOnClass | 系统中有指定的类 |
@ConditionalOnMissingClass | 系统中没有指定的类 |
@ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或者这个Bean是首选Bean |
@ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
@ConditionalOnResource | 类路径下是否存在指定资源文件 |
@ConditionalOnWebApplication | 当前是web环境 |
@ConditionalOnNotWebApplication | 当前不是web环境 |
@ConditionalOnJndi | JNDI存在指定项 |