SpringBoot 自动装配
- 传统的
Spring
项目会有很多的配置文件,比如我们要使用Redis
,一般除了对应的依赖的jar
包我们还需要在application.xml
里面配置JedisConnectionFactory
、JedisPoolConfig
、RedisTemplate
。但是如果使用SpringBoot
的话,系统会根据pom.xml
里面的jar
包,自动生成这些类并且注入到IOC
容器当中。
自动装配的过程
-
SpringApplication.run(Application.class, args)
在运行时,会读取spring-boot-autoconfigure.jar
里面的spring.factories
配置文件,配置文件中有所有自动装配类的配置类的className
,然后生成对应功能的Configuration
类,这些功能配置类要生效的话,会去classpath
中找是否有该类的依赖类(也就是pom.xml
必须有对应功能的jar包才行),然后配置类里再通过判断生成最后的功能类,并且配置类里面注入了默认属性值类,功能类可以引用并赋默认值。生成功能类的原则是自定义优先,没有自定义时才会使用自动装配类。 -
要想自动装配一个类需要满足2个条件:
spring.factories
里面有这个类的配置类(一个配置类可以创建多个围绕该功能的依赖类)pom.xml
里面需要有对应的jar
包
-
整个过程的结果是两件事情:
- 根据各种判断和依赖,最终生成了业务需要的类并且注入到
IOC
容器当中了 - 自动装配生成的类赋予了一些默认的属性值
- 根据各种判断和依赖,最终生成了业务需要的类并且注入到
SpringBootApplication注解
EnableAutoConfiguration
代表开启springboot
的自动装配@ComponentScan
的功能其实就是自动扫描并加载符合条件的组件或bean
定义,最终将这些bean
定义加载到容器中@Configuration
注解这个注解实际上就是代表了一个配置类
@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 {}
EnableAutoConfiguration注解
@EnableAutoConfiguration
:Springboot
应用启动自动装配的功能(包括加载对应的Bean
到IOC
容器中,且根据默认配置对属性赋值)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
-
@Import(AutoConfigurationImportSelector.class)
,可以帮助SpringBoot
应用将所有符合条件的@Configuration
配置都加载到当前SpringBoot
创建并使用的IoC
容器。同时借助于Spring
框架原有的一个工具类:SpringFactoriesLoader
-
@EnableAutoConfiguration
作用就是从classpath
中搜寻所有的META-INF/spring.factories
配置文件,并将其中org.springframework.boot.autoconfigure
对应的配置项通过反射(Java Refletion
)实例化为对应的标注了@Configuration
的JavaConfig
形式的IoC
容器配置类,然后汇总为一个bean
加载到IoC
容器
RedisTemplate 的自动配置
- 从
spring-boot-autoconfigure.jar/META-INF/spring.factories
中获取默认功能配置类
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration=
RestTemplateAutoConfiguration
配置类生效的一个条件是RestTemplate.class
所以会去classpath
下去查找对应的class
文件RestTemplateAutoConfiguration
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter(HttpMessageConvertersAutoConfiguration.class)
@ConditionalOnClass(RestTemplate.class)
@Conditional(NotReactiveWebApplicationCondition.class)
public class RestTemplateAutoConfiguration {
@Bean
@Lazy
@ConditionalOnMissingBean
public RestTemplateBuilderConfigurer restTemplateBuilderConfigurer(
ObjectProvider<HttpMessageConverters> messageConverters,
ObjectProvider<RestTemplateCustomizer> restTemplateCustomizers,
ObjectProvider<RestTemplateRequestCustomizer<?>> restTemplateRequestCustomizers) {
RestTemplateBuilderConfigurer configurer = new RestTemplateBuilderConfigurer();
configurer.setHttpMessageConverters(messageConverters.getIfUnique());
configurer.setRestTemplateCustomizers(restTemplateCustomizers.orderedStream().collect(Collectors.toList()));
configurer.setRestTemplateRequestCustomizers(
restTemplateRequestCustomizers.orderedStream().collect(Collectors.toList()));
return configurer;
}
@Bean
@Lazy
@ConditionalOnMissingBean
public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer restTemplateBuilderConfigurer) {
RestTemplateBuilder builder = new RestTemplateBuilder();
return restTemplateBuilderConfigurer.configure(builder);
}
static class NotReactiveWebApplicationCondition extends NoneNestedConditions {
NotReactiveWebApplicationCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnWebApplication(type = Type.REACTIVE)
private static class ReactiveWebApplication {
}
}
}
RedisAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
return new StringRedisTemplate(redisConnectionFactory);
}
}
相关注解
@ConditionalOnProperty
:如果有指定的配置,条件生效;@ConditionalOnBean
:如果有指定的Bean,条件生效;@ConditionalOnMissingBean
:如果没有指定的Bean,条件生效;@ConditionalOnMissingClass
:如果没有指定的Class,条件生效;@ConditionalOnWebApplication
:在Web环境中条件生效;@ConditionalOnExpression
:根据表达式判断条件是否生效。
@ConditionalOnMissingBean
-
@ConditionalOnMissingBean
,它是修饰bean
的一个注解,主要实现的是,当你的bean
被注册之后,如果注册相同类型的bean
,就不会成功,它会保证你的bean
只有一个,即你的实例只有一个,当你注册多个相同的bean
时,会出现异常,以此来告诉开发人员。 -
对于自定义的配置类,我们应该加上
@ConditionalOnMissingBean
注解,以避免多个配置同时注入的风险。
@ConditionalOnBean
- 判断当前需要注册的
bean
的实现类否被spring
管理,如果被管理则注入,反之不注入 condition2
注册的时候发现有condition1
那么就不会注册
@Configuration
public class CustomConditionOnBean {
private static final Logger looger = LoggerFactory.getLogger(CustomConditionOnBean.class);
@Bean(name = "condition1")
public ConditionTestBean getConditionBean2() {
ConditionTestBean conditionTestBean = new ConditionTestBean();
conditionTestBean.setId("2");
conditionTestBean.setName("Condition12");
looger.info("conditionTestBean2 注册了...");
return conditionTestBean;
}
@Bean(name = "condition2")
@ConditionalOnMissingBean(name = "condition1")
public ConditionTestBean getConditionBean1() {
ConditionTestBean conditionTestBean = new ConditionTestBean();
conditionTestBean.setId("1");
conditionTestBean.setName("Condition1");
looger.info("conditionTestBean1 注册了...");
return conditionTestBean;
}
}