继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Spring Boot(四)@EnableXXX注解分析

烙印99
关注TA
已关注
手记 352
粉丝 92
获赞 446

在学习使用springboot过程中,我们经常碰到以@Enable开头的注解,其实早在Spring3中就已经出现了类似注解,比如@EnableTransactionManagement、@ EnableWebMvc等,本文以@ EnableAutoConfiguration注解为例跟踪一下源码,分析实现过程。

@EnableAutoConfiguration注解

@EnableAutoConfiguration注解主要功能是启用自动配,将classpath中所有包下的META-INF/spring.factories配置文件中Key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的对应的符合自动配置条件的bean定义加载到Spring容器中。

查看源码可以看到@EnableAutoConfiguration是一个复合注解,自身也使用了其他注解,其中关键的是@Import(AutoConfigurationImportSelector.class)

@EnableAutoConfiguration注解实现代码

复制代码

@Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { //是否启用EnableAutoConfiguration注解,可在spring.factories中配置  spring.boot.enableautoconfiguration=true 或者false控制是否启用,默认为true    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";     //通过class指定需要排除的自动配置类:    Class<?>[] exclude() default {};    //通过类名指定需要排除的自动配置类:    String[] excludeName() default {}; }

复制代码

查看AutoConfigurationImportSelector.class源码发现这个继承接口ImportSelector,所以截取其中最重要的一个方法

复制代码

//实现接口ImportSelector的selectImports方法 ,该方法返回一个类名数组,数组中的类会被spring容器所托管起来
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { //判断是否启用自动配置    if (!isEnabled(annotationMetadata)) {       return NO_IMPORTS;    } //加载spring内部自动配置的默认原属性    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader          .loadMetadata(this.beanClassLoader);    AnnotationAttributes attributes = getAttributes(annotationMetadata); //关键加载配置实现: // getCandidateConfigurations方法实现加载需要自动配置的类,内部通过使用Spring framework提供的SpringFactoriesLoader类,可以从classpath中搜索所有META-INF/spring.factories配置文件,并读取配置    List<String> configurations = getCandidateConfigurations(annotationMetadata,          attributes); //去掉重复配置    configurations = removeDuplicates(configurations);    Set<String> exclusions = getExclusions(annotationMetadata, attributes); //检查需要排除的配置    checkExcludedClasses(configurations, exclusions);    configurations.removeAll(exclusions); //spring.factories加载一个org.springframework.boot.autoconfigure.AutoConfigurationImportFilter过滤器OnClassCondition //通过该过//滤器判断现有系统是否引入了某个组件,如果有则进行相关配置.    configurations = filter(configurations, autoConfigurationMetadata); //处理事件监听,具体监听器参见spring.factories中配置    fireAutoConfigurationImportEvents(configurations, exclusions); //返回最终需要托管的类    return StringUtils.toStringArray(configurations); }

复制代码

getCandidateConfigurations实现细节

复制代码

//实现加载META-INF/spring.factories配置文件,具体配置文件有什么类容,可以自行查看spring-boot-autoconfig-{version}.jar 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; }

复制代码

上面分析了EnableAutoConfiguration的实现过程,下面通过一个示例演示如果从外部项目中读取自动配置

定义一个外部项目autoconfigbean

pom.xml

复制代码

<project xmlns="http://maven.apache.org/POM/4.0.0"          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">     <modelVersion>4.0.0</modelVersion>     <groupId>com.sl.autoconfig</groupId>     <artifactId>autoconfig-bean</artifactId>     <version>1.0-SNAPSHOT</version>     <dependencies>         <dependency>             <groupId>org.springframework</groupId>             <artifactId>spring-context</artifactId>             <version>5.0.7.RELEASE</version>         </dependency>     </dependencies> </project>

复制代码

定义需要自动注入的类

复制代码

@Configuration public class TestConfig {     @Bean     public Configdemo configdemo(){         return new Configdemo();     } }
 public class Configdemo { }

复制代码

添加resources/META-INF/spring.factories配置文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ configtest.Configdem

在springboot项目中引用autoconfigbean

<dependency>    <groupId>com.sl.autoconfig</groupId>    <artifactId>autoconfig-bean</artifactId>    <version>1.0-SNAPSHOT</version> </dependency>

启动springboot项目可以通过System.out.println(context.getBeansOfType(Configdemo.class)) 查看是否已经注入到spring容器

@import注解

上面的@EnableAutoConfiguration注解通过@Import(AutoConfigurationImportSelector.class)实现了主要功能, 下面来看一下@import注解的作用

@import是spring framework提供的一个注解,是通过导入的方式把一个或多个bean或者bean的配置类注入到Spring容器中。

@import源码

复制代码

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import {    /**     * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}     * or regular component classes to import.     */    Class<?>[] value(); }

复制代码

ImportSelector接口:selectImports方法返回一个class名称数组,该class会被spring容器所托管起来

复制代码

public interface ImportSelector {    /**     * Select and return the names of which class(es) should be imported based on     * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.     */    String[] selectImports(AnnotationMetadata importingClassMetadata); }

复制代码

ImportBeanDefinitionRegistrar接口:registerBeanDefinitions方法的参数有一个BeanDefinitionRegistry,该register可以用来向spring容器中注入bean

复制代码

public interface ImportBeanDefinitionRegistrar {    /**     * Register bean definitions as necessary based on the given annotation metadata of     * the importing {@code @Configuration} class.     * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be     * registered here, due to lifecycle constraints related to {@code @Configuration}     * class processing.     * @param importingClassMetadata annotation metadata of the importing class     * @param registry current bean definition registry     */    public void registerBeanDefinitions(          AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry); }

复制代码

下面通过一个示例简单演示一下Import注解通过{@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}三种方式注入
定义配置类

复制代码

// ImportByConfiguration类定义的bean都将注入spring容器 public class ImportByConfiguration {     @Bean     public  ImportTestClass createImportTestClass(){         return new ImportTestClass();     } }

复制代码

定义ImportSelector接口实现类,

复制代码

//selectImports方法返回一个类数据,该数组中的类将会被spring容器所托管起来 public class ImportSelectorTest implements ImportSelector {     @Override     public String[] selectImports(AnnotationMetadata importingClassMetadata) {         /**          * xxxx逻辑          */         return new String[]{                 "com.sl.springbootdemo.EnableAnnotation.ImportTestClass"         };     } }

复制代码

定义ImportBeanDefinitionRegistrar接口实现类

复制代码

public class ImportBeanDefinitionRegistrarTest implements ImportBeanDefinitionRegistrar {     @Override     public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {         // new一个RootBeanDefinition         BeanDefinitionBuilder rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(ImportTestClass.class);         //RootBeanDefinition rootBeanDefinition2 = new RootBeanDefinition(ImportTestClass.class);         // 注册一个name为importTestClassInstance的bean         registry.registerBeanDefinition("importTestClassInstance", rootBeanDefinition.getBeanDefinition());         //registry.registerBeanDefinition("importTestClassInstance",rootBeanDefinition2);     } }

复制代码

输出到控制台:

复制代码

@Import({ImportByConfiguration.class, //导入bean配置类,则配置类中bean也将注入到spring容器       ImportSelectorTest.class,   //ImportSelector接口方式       ImportBeanDefinitionRegistrarTest.class  //ImportBeanDefinitionRegistrar接口方式 }) //@ComponentScan @SpringBootApplication public class SpringbootdemoApplication2 {    public static void main(String[] args) {       ConfigurableApplicationContext context = SpringApplication.run(SpringbootdemoApplication2.class,args);       System.out.println(context.getBeansOfType(ImportTestClass.class));     } }

复制代码

控制台打印信息如下:

复制代码

{createImportTestClass=com.sl.springbootdemo.EnableAnnotation.ImportTestClass@2bc0cde, com.sl.springbootdemo.EnableAnnotation.ImportTestClass=com.sl.springbootdemo.EnableAnnotation.ImportTestClass@ac16e08, importTestClassInstance=com.sl.springbootdemo.EnableAnnotation.ImportTestClass@210a40d6}

复制代码

@Import 注解并不是SpringBoot的功能,Spring 3.0开始就提供了@Import这个注解,并在后续版本中逐渐完善,主要用来支持在Configuration类中引入其它的配置类,包括Configuration类, ImportSelector和ImportBeanDefinitionRegistrar的实现类。

原文出处:https://www.cnblogs.com/ashleyboy/p/9562052.html

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP