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框架更加灵活方便,其中通过注解控制类是否注册到容器中,也很使用.在实际使用中,更能体会作者的伟大.

猜你喜欢

转载自blog.csdn.net/ABestRookie/article/details/120028894