@ConditionalOn…注解 参考文章 https://zhuanlan.zhihu.com/p/78251301
源码分析 在Spring中,如果想要满足一定条件才加载某个bean到IoC容器,只能使用@Conditional
注解
1 2 3 4 5 6 7 8 public @interface Conditional { Class<? extends Condition >[] value(); }
可以看到@Conditional
接收的参数是实现了Condition
接口的类,所以只需要重写matches()
方法即可
1 2 3 4 5 6 7 8 9 @FunctionalInterface public interface Condition { boolean matches (ConditionContext context, AnnotatedTypeMetadata metadata) ; }
而在SpringBoot中已经为我们封装好了@ConditionalOn...
接口,这些接口上都有@Conditional
注解
比如@ConditionalOnClass
上有@Conditional(OnClassCondition.class)
,下面是OnClassCondition
的继承关系
.
1 2 3 4 5 6 ... @Target({ ElementType.TYPE, ElementType.METHOD }) @Conditional(OnClassCondition.class) public @interface ConditionalOnClass { ... }
ElementType.TYPE
表明Spring 自动扫描的一切类@Component
(@Configuration, @Service, @Repository, @Controller
) 都可以通过添加相应的 @ConditionalOnClass
来判断是否加载
ElementType.METHOD
表明@ConditionalOnClass
还可以应用在具有@Bean
的方法上,实现粒度更小的控制
使用方法 具体各注解如下:
@ConditionalOnProperty
:在application.yml
中是否有对应属性,如果无对应属性则默认不加载,因为
1 2 3 4 5 6 7 8 9 10 11 public @interface ConditionalOnProperty { ... boolean matchIfMissing () default false ; } @Component @ConditionOnProperty(value="mybean.enabled",havingValue="true",matchIfMissing=true) public class MyBean { ... }
@ConditionalOnExpression
:使用SpEL表达式用于多个配置属性联合判断
1 @ConditionOnExpression("${mybean.enabled:true} and ${otherbean.enabled:true}")
这里的:true
意思是如果没有该属性,默认值为true
@ConditionalOnResource
:要加载的Bean依赖于某个资源
1 @ConditionalOnResource(resources="/logback.xml")
@ConditionalOnBean
:当容器里有指定 Bean 的条件下
@ConditionalOnMissingBean
:当容器里没有指定 Bean 的情况下
@ConditionalOnClass
:当类路径下有指定类的条件下
@ConditionalOnMissingClass
:当类路径下没有指定类的条件下
…
使用多个@ConditionalOn...
默认是且 的关系,如果要实现或 的关系,需要继承AnyNestedCondition
类
非 的关系也有NoneNestedCondition
类
自动装配原理★ 参考文章:https://zhuanlan.zhihu.com/p/345895748
SpringBoot 定义了一套接口规范,规定:SpringBoot 在启动时会扫描外部 jar 包中的META-INF/spring.factories
文件,将文件中配置的类型信息加载到 Spring 容器,一般在SpringBoot Starter 中,并执行类中定义的各种操作
首先来看@SpringBootApplication
注解
1 2 3 4 5 6 7 8 @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { ... }
其中,@EnableAutoConfiguration
是实现自动装配的核心注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration" ; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
可以看到,自动装配的功能实际上是由AutoConfigurationImportSelector
实现的
1 2 3 public class AutoConfigurationImportSelector implements DeferredImportSelector , BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { ... }
这里只需要关心DeferredImportSelector
接口
1 2 3 public interface DeferredImportSelector **extends** ImportSelector { ... }
.
selectImports
方法用于获取所有符合条件的类的全限定类名 ,这些类需要被加载到 IoC 容器中
AutoConfigurationImportSelector
的实现方式如下
1 2 3 4 5 6 7 8 9 10 11 12 private static final String[] NO_IMPORTS = new String [0 ];public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!this .isEnabled(annotationMetadata)) { return NO_IMPORTS; } else { AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } }
首先看一眼isEnabled
方法
1 2 3 4 5 6 7 8 protected boolean isEnabled (AnnotationMetadata metadata) { if (getClass() == AutoConfigurationImportSelector.class) { return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true ); } return true ; }
然后重点关注getAutoConfigurationEntry()
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 protected AutoConfigurationEntry getAutoConfigurationEntry (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 = getConfigurationClassFilter().filter(configurations); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry (configurations, exclusions); }
.
.
可以看到,这些全部都是以AutoConfiguration
为后缀的类名,这些类中配置了要注入到IoC容器中的bean,例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Configuration @ConditionalOnClass(DruidDataSource.class) @AutoConfigureBefore(DataSourceAutoConfiguration.class) @EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class}) @Import({DruidSpringAopConfiguration.class, DruidStatViewServletConfiguration.class, DruidWebStatFilterConfiguration.class, DruidFilterConfiguration.class}) public class DruidDataSourceAutoConfigure { private static final Logger LOGGER = LoggerFactory.getLogger(DruidDataSourceAutoConfigure.class); @Bean(initMethod = "init") @ConditionalOnMissingBean public DataSource dataSource () { LOGGER.info("Init DruidDataSource" ); return new DruidDataSourceWrapper (); } }
大致流程过完之后,继续深入,查看Step2的getCandidateConfigurations()
方法
1 2 3 4 5 6 7 protected List<String> getCandidateConfigurations (AnnotationMetadata metadata, AnnotationAttributes attributes) { 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; }
显然,核心是SpringFactoriesLoader.loadFactoryNames()
方法,参数中使用到了ClassLoader
1 2 3 4 public static List<String> loadFactoryNames (Class<?> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); }
可以继续深入,但先摆了
另外,Step3的过滤也可以深入,也先摆了