Spring Boot系列3
1 关于Spring Boot自动配置的理解
Spring框架提供了多种方式来配置一个Bean对象,XML配置,注解配置,和JavaConfig配置类. Spring随着项目功能的增加,配置也越来越庞大,不好管理.所以Spring Boot中采用自动配置的特性,来解决Spring框架中配置过多的管理问题.
1 Java配置
Spring中简化XML配置的解决方案有:
-
组件扫描(Component Scan): Spring去自动发现应用上下文中创建的Bean.
-
自动装配(AutoWired): Spring自动创建Bean之间的依赖.
-
通过JavaConfig方式实现Java代码配置Bean.
使用一个自定义的配置类为列:
@Configuration
public class WebConfigurer implements WebMvcConfigurer {
/**
* 用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 列如
// addPathPatterns("/**") 表示拦截所有的请求
// excludePathPatterns("/login", "/register") 表示除了登陆与注册之外
registry.addInterceptor(this.getMyInterceptor()).addPathPatterns("/**").excludePathPatterns("/login", "/register");
}
@Bean
public MyInterceptor getMyInterceptor() {
return new MyInterceptor();
}
/**
* 用来配置静态资源的,比如html,js,css,等等,列如在使用swagger文档时
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
}
}
1 @Configuration
注解标记该类为Spring的配置类.
2 通过@Bean
注解标记将拦截器类交给Spring容器管理
2 条件化Bean
Spring Boot可以根据不同的条件,来控制Bean注册到容器.
1 注解@Conditional
条件注解可以根据不同的条件,来做不同的Bean对象注入.类似设计模式中的状态模式.
常用注解说明:
条件注解 | 条件说明 |
---|---|
@ConditionalOnBean | 上下文存在某个对象时,才会实例化一个Bean |
@ConditionalOnClass | 当class位于类路径上,才会实例化一个Bean |
@ConditionalOnExpression | 当表达式为True的时候,才会实例化一个Bean |
@ConditionalOnMissingBean | 上下文中不存在某个对象时,才会实例化一个Bean |
@ConditionalOnMissingClass | 当类路径上不存在某个class的时候,才会实例化一个Bean |
@ConditionalOnNotWebApplication | 当不是一个Web应用时,才会实例化一个Bean |
2 条件注解使用案列
目标:
当application.properties中存在password配置项,且值为true时,创建目标Bean对象.
password=123456
自定义类实现Condition接口
public class MyController implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
if (environment.containsProperty("password")) {
String password = environment.getProperty("password");
System.out.println("配置文件application中存在属性password = " + password);
return "123456".equals(password);
}
return false;
}
}
第一个参数ConditionContext,是一个上下文接口.源码如下
public interface ConditionContext {
/**
返回Bean定义的BeanDefinitionRegistry对象,可以使用BeanDefinitionRegistry检查Bean的定义
*/
BeanDefinitionRegistry getRegistry();
/**
返回ConfigurableListableBeanFactory对象,用来检查B二胺是否存在,以及检查Bean的属性
*/
@Nullable
ConfigurableListableBeanFactory getBeanFactory();
/**
返回Environment检查环境变量是否存在以及读取它的值
*/
Environment getEnvironment();
/**
读取并检查它返回的ResourceLoader所加载的资源
*/
ResourceLoader getResourceLoader();
/**
返回类加载器对象ClassLoader来加载并检查类是否存在
*/
@Nullable
ClassLoader getClassLoader();
}
第二个参数AnnotatedTypeMetadata.
public interface AnnotatedTypeMetadata {
MergedAnnotations getAnnotations();
// 判断有@Bean注解的方法是否有其他特定的注解
default boolean isAnnotated(String annotationName) {
return this.getAnnotations().isPresent(annotationName);
}
@Nullable
default Map<String, Object> getAnnotationAttributes(String annotationName) {
return this.getAnnotationAttributes(annotationName, false);
}
@Nullable
default Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString) {
MergedAnnotation<Annotation> annotation = this.getAnnotations().get(annotationName, (Predicate)null, MergedAnnotationSelectors.firstDirectlyDeclared());
return !annotation.isPresent() ? null : annotation.asAnnotationAttributes(Adapt.values(classValuesAsString, true));
}
@Nullable
default MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName) {
return this.getAllAnnotationAttributes(annotationName, false);
}
@Nullable
default MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) {
Adapt[] adaptations = Adapt.values(classValuesAsString, true);
return (MultiValueMap)this.getAnnotations().stream(annotationName).filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes)).map(MergedAnnotation::withNonMergedAttributes).collect(MergedAnnotationCollectors.toMultiValueMap((map) -> {
return map.isEmpty() ? null : map;
}, adaptations));
}
}
Spring4使用ProfileCondition类对多环境部署配置文件
class ProfileCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) {
return true;
}
}
return false;
}
return true;
}
}
3 条件配置类ConditionalConfig
使用@Conditionnal注解方法,在value中给出条件.当Spring容器满足条件时才会去实例化这个Bean,否则不注册这个Bean.
4 组合注解
组合注解就是将现有的注解进行组合,生成一个新的注解.使用这个新的注解就相当于使用了该组合注解中所有的注解.
如启动类上注解@Spring BootApplication.
@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 {
...
3 Spring Boot自动配置过程
Spring Boot内置自动配置原理主要是依赖@EnableAutoConfiguration
注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
@EnableAutoConfiguration
注解中主要的是@Import(AutoConfigurationImportSelector.class)
注解,借助Enable-AutoConfigurationImportSelector、@EnableAutoConfiguration、Spring Boot应用将所有符合条件的@Configuration配置类都加载到当前Spring容器中.
1 @EnableAutoConfiguration 注解说明
Spring Boot通过@EnableAutoConfiguration
启动Spring程序上下文的自动配置,导入一个AutoConfigurationImportSelector类,AutoConfigurationImportSelector会去读取spring.factories下key为EnableAutoConfiguration对应的类全限定名的值.
部分源码:
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;
}
spring.factories里面配置的那些类,主要作用是告诉Spring Boot这个stareter所需要加载的那些*AutoConfiguration类,也就是你真正的要自动注册的那些Bean或功能。然后,再实现一个spring.factories指定的类,标上@Configuration注解,一个starter就定义完了。
其中getSpringFactoriesLoaderFactoryClass()方法直接返回了EnableAutoConfiguration类.
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
所以getCandidateConfigurations()方法会过滤出key为org.springframework.boot.autoconfigure.EnableAutoConfiguration
的全限定名对应的值.
SpringFactoriesLoader主要用来查询META-INF/spring. factories的properties配置中指定class对应的所有实现类
2 spring. factories文件
SpringFactoriesLoader类是加载spring. factories文件的,
会根据文件中的AutoConfiguration上的@ConditionalOnClass等条件,判断是否加载
SpringFactoriesLoader会加载classpath下所有JAR文件里面的META-INF/spring.factories文件。
其中加载spring.factories文件的代码在loadFactoryNames()方法里。
4 总结
Spring Boot中自动配置实现极简化配置,让使用Spring框架更加灵活方便,其中通过注解控制类是否注册到容器中,也很使用.在实际使用中,更能体会作者的伟大.