03 SpringBoot自动配置-Enable注解原理
SpringBoot是不能直接获取在其他工程中定义的Bean的。
新建模块springboot-enable-other和模块springboot-enable,在springboot-enable中引入springboot-enable-other坐标:
<dependency> <groupId>com.itheima</groupId> <artifactId>springboot-enable-other</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
在springboot-enable-other工程中定义bean:
package com.itheima.config; import com.itheima.domain.Role; import com.itheima.domain.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class UserConfig { @Bean public User user() { return new User(); } @Bean public Role role() { return new Role(); } }
尝试在springboot-enable中获取,并不能获取到user的bean:
//获取Bean Object user = context.getBean("user"); System.out.println(user);
解决方案:
1、使用@ComponentScan注解扫描指定包路径:
@ComponentScan("com.itheima.config")
2、使用@Import引入指定类:
@Import(UserConfig.class)
3、在springboot-enable-other中自定义注解@EnableUser:
package com.itheima.config; import org.springframework.context.annotation.Import; import java.lang.annotation.*; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(UserConfig.class) public @interface EnableUser { }
1. 导入Bean
@Import(User.class)
2. 导入配置类(被导入的配置类的@Configuration注解可以省略)
package com.itheima.config; import com.itheima.domain.Role; import com.itheima.domain.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; // @Configuration public class UserConfig { @Bean public User user() { return new User(); } @Bean public Role role() { return new Role(); } }
@Import(UserConfig.class)
3. 导入ImportSelector的实现类
自定义类MyImportSelector实现ImportSelector接口会导入selectImports方法返回的类:
package com.itheima.config; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"com.itheima.domain.User", "com.itheima.domain.Role"}; } }
@Import(MyImportSelector.class)
4. 导入ImportBeanDefinitionRegistrar实现类
自定义类MyImportBeanDefinitionRegistrar实现ImportBeanDefinitionRegistrar接口会自动执行registerBeanDefinitions方法注册BeanDefinition到容器中:
package com.itheima.config; import com.itheima.domain.User; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition(); registry.registerBeanDefinition("user", beanDefinition); } }
@Import({MyImportBeanDefinitionRegistrar.class})
SpringBoot自动装配的核心注解@EnableAutoConfiguration:
@EnableAutoConfiguration:基于你配置的依赖项,也就是引入的jar包,扫描所有jar包下面的META-INF/spring.factories,spring.factories中都是这个jar的配置类,配置类里面就有我们所需要的工具类。将所有复合自动配置条件的bean定义加载到ioc容器中,记住@EnableAutoConfiguration自动加载的是一些不需要我们自己去定义但是需要用到的“工具类”。
使用的是第三种ImportSelector接口的实现类方式
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.boot.autoconfigure; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.context.annotation.Import; @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中核心代码#selectImports方法:
public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return NO_IMPORTS; } else { AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } }
方法#getAutoConfigurationEntry:
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } else { AnnotationAttributes attributes = this.getAttributes(annotationMetadata); List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes); configurations = this.removeDuplicates(configurations); Set<String> exclusions = this.getExclusions(annotationMetadata, attributes); this.checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = this.filter(configurations, autoConfigurationMetadata); this.fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions); } }
核心方法#getCandidateConfigurations:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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; }
可知自动加载的配置文件在META-INF/spring.factories中。
当 SpringBoot 应用启动时,会自动加载这些配置类,初始化Bean,