Loading

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 {
}

@Import注解的四种用法:

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,并不是所有的Bean都会被初始化,在配置类中使用Condition来加载满足条件的Bean。

posted @ 2023-02-11 19:00  1640808365  阅读(97)  评论(0编辑  收藏  举报