# Springboot 自动装配

# 核心注解 @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 {}

@SpringBootConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {}

@SpringBootApplication 组合了很多注解:

  • @EnableAutoConfiguration :启用 SpringBoot 的自动配置
    • @AutoConfigurationPackage:主配置类(@SpringBootApplication 标注的类)的所在包以及下面所有子包里面的所有组件扫描到 Spring 容器。
    • @Import(AutoConfigurationImportSelector.class):自动装配核心功能的实现
  • @ComponentScan:扫描 @Component(@Controller、@Service、@Repository 都包含此注解)并注入容器
  • @SpringConfiguration
    • Configuration:允许在 ApplicationContext 中注册额外的 Bean 或导入其他配置类
    • Indexed: 项目编译打包时,会在自动生成 META-INF/spring.components 文件

# @EnableAutoConfiguration

@EnableAutoConfiguration 注解中导入了 AutoConfigurationImportSelector 类。

AutoConfigurationImportSelector 类实现了 ImportSelector 接口,也就实现了这个接口中的 selectImports 方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IOC 容器中

@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
        // 判断自动装配是否启动
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
        // 获取所有自动装配的 bean
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

其中 getAutoConfigurationEntry(annotationMetadata)

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    	// 判断是否开启自动装配(spring.boot.enableautoconfiguration 默认为 true)
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
    	// 获取 EnableAutoConfiguration 注解中的属性 exclude 和 excludeName 排除
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		// 拿到 starter 包中 Auto 配置模块的	META-INF/spring.factories 文件下的自动配置类列表(条件装配)
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    	// 去重
		configurations = removeDuplicates(configurations);
    	// 获取注解的 exclude 和 excludeName 属性配置的需要排除的自动配置类全限定名的集合
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		// 检查并排除类(如果类不在 configurations 中报错此类不是自动配置类)
    	checkExcludedClasses(configurations, exclusions);
    	// 移除所有 exclude 和 excludeName 属性配置的需要排除的自动配置类
		configurations.removeAll(exclusions);
    	// 对 configurations 进行过滤,剔除掉条件不成立的配置类,@Conditional 有关
		configurations = getConfigurationClassFilter().filter(configurations);
    	// 监听器 import 事件回调
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

最后方法返回了一个 AutoConfigurationEntry ,它是 AutoConfigurationImportSelector 的内部类

protected static class AutoConfigurationEntry {
		private final List<String> configurations;
		private final Set<String> exclusions;
		private AutoConfigurationEntry() {
			this.configurations = Collections.emptyList();
			this.exclusions = Collections.emptySet();
		}
		/**
		 * Create an entry with the configurations that were contributed and their
		 * exclusions.
		 * @param configurations the configurations that should be imported
		 * @param exclusions the exclusions that were applied to the original list
		 */
		AutoConfigurationEntry(Collection<String> configurations, Collection<String> exclusions) {
			this.configurations = new ArrayList<>(configurations);
			this.exclusions = new HashSet<>(exclusions);
		}
		public List<String> getConfigurations() {
			return this.configurations;
		}
		public Set<String> getExclusions() {
			return this.exclusions;
		}
	}

最后 selectImports(AnnotationMetadata annotationMetadata) 方法返回一个配置类的全限定名数组。

# 自定义 Starter

代码参考:尚硅谷 springboot2 课程

image-20230228212127676

  1. 准备一个空工程

  2. 新建一个 Maven 模块,命名为 XXX-spring-boot-start 充当启动器

    pom 文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <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>per.windlinxy</groupId>
        <artifactId>windlinxy-spring-boot-starter</artifactId>
        <version>1.0-SNAPSHOT</version>
        <dependencies>
            <dependency>
                <groupId>per.windlnxy</groupId>
                <artifactId>windlinxy-spring-boot-starter-autoconfigure</artifactId>
                <version>0.0.1-SNAPSHOT</version>
            </dependency>
        </dependencies>
        
    </project>
  3. 再使用 Spring Initializr 创建一个模块,命名为 XXX-spring-boot-start-autoconfigure

    <?xml version="1.0" encoding="UTF-8"?>
    <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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.7.4</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>per.windlnxy</groupId>
        <artifactId>windlinxy-spring-boot-starter-autoconfigure</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>windlinxy-spring-boot-starter-autoconfigure</name>
        <description>windlinxy-spring-boot-starter-autoconfigure</description>
        <properties>
            <java.version>8</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
        </dependencies>
    </project>
  4. XXX-spring-boot-start-autoconfigure 模块编写代码

  5. 使用 maven 进行 install 将两个模块注入到本地 maven 仓库

    per
    ├─windlinxy
    │  └─windlinxy-spring-boot-starter
    │      └─1.0-SNAPSHOT
    └─windlnxy
        └─windlinxy-spring-boot-starter-autoconfigure
            └─0.0.1-SNAPSHOT
  6. 在另外的项目进行注册

    <dependency>
                <groupId>per.windlinxy</groupId>
                <artifactId>windlinxy-spring-boot-starter</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
  7. 使用

    配置文件:

    hello:
      prefix: 11
      suffix: 666

    Controller:

    @RestController
    public class HelloController {
        @Resource
        private HelloService helloService;
        @GetMapping("/hello")
        public String sayHello() {
            return helloService.sayHello("张三");
        }
    }

HelloServiceAutoConfiguration 自动配置类

@Configuration
@ConditionalOnMissingBean(HelloService.class)
// 会将 HelloProperties 自动注入容器
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {
    @Bean
    public HelloService helloService() {
        return new HelloService();
    }
}

HelloProperties

@ConfigurationProperties("hello")
public class HelloProperties {
    private String prefix;
    private String suffix;
    public String getPrefix() {
        return prefix;
    }
    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }
    public String getSuffix() {
        return suffix;
    }
    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }
}

HelloService

// 默认不放入容器
public class HelloService {
    @Autowired
    private HelloProperties helloProperties;
    public String sayHello(String username) {
        return helloProperties.getPrefix() + ": " + username + helloProperties.getSuffix();
    }
}

resources/META-INF/spring.factiories 文件

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
per.windlnxy.auto.HelloServiceAutoConfiguration
更新于 阅读次数 本文阅读量:

请我喝[茶]~( ̄▽ ̄)~*

Windlinxy 微信支付

微信支付

Windlinxy 支付宝

支付宝