今天的博客主题
SpringBoot ——》SpringBoot高级篇
springBoot的自动装配原理
AutoConfigurationImportSelector.class 究竟做了什么?
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
....
}
DeferredImportSelector 继承了 ImportSelector。ImportSelector应该还有印象吧。
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);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
主要是扫描spring-boot-autoconfigure\2.2.2.RELEASE\spring-boot-utoconfigure2.2.2.RELEASE.jar!
META-INF\spring.factories 中 EnableAutoConfiguration 对应的全类名
# 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,\
......
这些 xxxAutoConfiguration 都是一个个的自动配置类。
那这些自动配置类是如何工作的呢?
我们还是用 RedisAutoConfiguration 来分析下
// 标识是一个自动配置类
@Configuration(proxyBeanMethods = false)
// 判断当前环境中是否有这个类
@ConditionalOnClass(RedisOperations.class)
// 启动指定类的配置功能。并把配置文件中的属性和RedisProperties进行映射
@EnableConfigurationProperties(RedisProperties.class)
// 导入指定类
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
// 配置Bean
@Bean
// 容器是否存在 redisTemplate Bean. 存在就不创建,不存在就进行创建。
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
// 同上面一样
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
RedisProperties 配置类
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
...
}
按照指定的前缀,在配置文件所配置的属性值都会映射到 RedisProperties 配置类里
基本上就是通过 AutoConfigurationImportSelector 为我们容器中注册组件,然后根据maven依赖导入的jar包,根据条件注解装配来指定哪些组件起作用,哪些组件不起作用。
springBoot的条件注解
条件注解:@ConditionalOnBean
处理的类:OnBeanCondition
处理逻辑:Spring容器中是否存在对应的实例。可以通过实例的类型、类名、注解、昵称去容器中查找(可以配置从当前容器中查找或者父容器中查找或者两者一起查找)这些属性都是数组,通过"与"的关系进行查找
举例说明:@ConditionalOnBean(javax.sql.DataSource.class); Spring容器或者所有父容器中需要存在至少一个javax.sql.DataSource类的实例
条件注解:@ConditionalOnClass
处理的类:OnClassCondition
处理逻辑:类加载器中是否存在对应的类。可以通过Class指定(value属性)或者Class的全名指定(name属性)。如果是多个类或者多个类名的话,关系是"与"关系,也就是说这些类或者类名都必须同时在类加载器中存在
举例说明:@ConditionalOnClass({ Configuration.class,FreeMarkerConfigurationFactory.class }) 类加载器中必须存在Configuration和FreeMarkerConfigurationFactory这两个类
条件注解:@ConditionalOnExpression
处理的类:OnExpressionCondition
处理逻辑:判断SpEL 表达式是否成立
举例说明:@ConditionalOnExpression("'${server.host}'=='localhost'") server.host配置项的值需要是localhost
条件注解:@ConditionalOnJava
处理的类:OnJavaCondition
处理逻辑:指定Java版本是否符合要求。内部有2个属性value和range。value表示一个枚举的Java版本,range表示比这个老或者新于等于指定的Java版本(默认是新于等于)。内部会基于某些jdk版本特有的类去类加载器中查询,比如如果是jdk9,类加载器中需要存在java.security.cert.URICertStoreParameters;如果是jdk8,类加载器中需要存在java