自动装配通过分析类路径下的依赖、Bean的定义以及其他配置信息,自动配置Spring应用程序的配置类和Bean,以减少开发人员的配置工作,提高开发效率。
Spring Boot的自动装配实际上是从 META-INF/spring.factories 文件中获取到对应的需要进行自动装配的类,并生成相应的Bean对象,然后将它们交给Spring容器进行管理。
没有 Spring Boot 的情况下,如果我们需要引入第三方依赖,需要手动配置。
通过SpringBoot项目启动类上的@SpringBootApplication注解来实现。
@SpringBootApplication注解核心源码:
//自定义注解相关注解 @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 { }
@SpringBootApplication主要由 @SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan这三个注解组成。
@EnableAutoConfiguration 是实现自动装配的主要注解。
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class>[] exclude() default {}; String[] excludeName() default {}; }
由源码可见自动装配的核心功能与引入的 AutoConfigurationImportSelector 类有关。
public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return NO_IMPORTS; } else { AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } }
在AutoConfigurationImportSelector 类中发现它实现了ImportSelector接口,也就实现了这个接口中的 selectImports 方法,该方法主要用于获取所有符合条件的类的全限定类名,并以字符串数组返回,这些类需要被加载到 IoC 容器中。
该方法主要通过调用**getAutoConfigurationEntry()**方法获取AutoConfigurationEntry对象,这个方法主要负责加载自动配置类的。
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } else { AnnotationAttributes attributes = this.getAttributes(annotationMetadata); List configurations = this.getCandidateConfigurations(annotationMetadata, attributes); configurations = this.removeDuplicates(configurations); Set exclusions = this.getExclusions(annotationMetadata, attributes); this.checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = this.getConfigurationClassFilter().filter(configurations); this.fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); } }
该方法通过调用 List configurations = getCandidateConfigurations(annotationMetadata, attributes) 获取到所有需要导入到容器中的配置类。
具体是在该类的 getCandidateConfigurations 方法中调用了 SpringFactoriesLoader类的 loadFactoryNames 方法获取所有自动转配类名,loadSpringFactories() 方法从META-INF/spring.factories加载自动装配类。
最后按照条件装配(@Conditional)最终会按需配置。
加载 spring.factories 中的配置,但不是每次启动都会加载其中的所有配置,会有一个筛选的过程,去掉重复的配置。
Condition 是在Spring 4.0 增加的条件判断功能,通过这个可以功能可以实现选择性的创建 Bean 操作。
@Conditional 有一个属性 value,其类型是 Condition 数组。组件必须匹配数组中所有的 Condition,才可以被注册。
Condition 是一个函数式接口,只有一个 matches 方法,返回 true 则表示条件匹配。matches 方法的两个参数分别是上下文信息和注解的元信息,从这两个参数中可以获取到 IOC 容器和当前组件的信息,从而判断条件是否匹配。
redis.clients jedis
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(UserConfig.class) public @interface EnableUser { } @Configuration public class UserConfig { @Bean public User user(){ return new User(); } } @SpringBootApplication @EnableScheduling @Import(UserConfig.class) @EnableAsync public class Springbootcondition03Application { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(Springbootcondition03Application.class, args); User user = context.getBean(User.class); System.out.println(user); } }
@Configuration public class UserConfig { @Bean // @Conditional(value = ClassCondition.class) // public User user(){ // return new User(); // } @ConditionOnClass(value = {"com.alibaba.fastjson.JSON","redis.clients.jedis.Jedis"}) public User user(){ return new User(); } @Bean @ConditionalOnProperty(name = "k1",havingValue = "v2") public User user2(){ return new User(); } } public class ClassCondition implements Condition { //Params: //context – 上下文对象。用于获取环境,IOC容器,ClassLoader对象 metadata – 注解元对象。 可以用于获取注解定义的属性值 //Returns: @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // boolean flag = true; // try { // Class> cls = Class.forName("redis.clients.jedis.Jedis"); // } catch (ClassNotFoundException e) { // flag = false; // } // return flag; Map map = metadata.getAnnotationAttributes(ConditionOnClass.class.getName()); System.out.println(map); String[] value = (String[]) map.get("value"); boolean flag = true; try { for(String className : value){ Class> cls = Class.forName(className); } } catch (ClassNotFoundException e) { flag = false; } return flag; } } @Target({ElementType.TYPE,ElementType.METHOD})//可以修饰在类与方法上 @Retention(RetentionPolicy.RUNTIME)//注解生效节点runtime @Documented//生成文档 @Conditional(value = ClassCondition.class) public @interface ConditionOnClass { String[] value();//设置此注解的属性redis.clients.jedis.Jedis } @SpringBootApplication public class SpringbootCondition01Application { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(SpringbootCondition01Application.class, args); Object user = context.getBean("user"); System.out.println(user); } }
Condition – 小结
自定义条件:
定义条件类:自定义类实现Condition接口,重写 matches 方法,在 matches 方法中进行逻辑判断,返回 boolean值 。
matches 方法两个参数:
判断条件: 在初始化Bean时,使用 @Conditional(条件类.class)注解
SpringBoot 提供的常用条件注解:
以下注解在SpringBoot-Autoconfigure的condition包下
上一篇:被攻击了,动漫攻击动作
下一篇:U盘通常采用何种文件格式?