SpringBoot —— 关于 Starter 和 自动装配

前言

Spring Boot 的快速发展与流行,很大程度上依赖于 Starter 的出现。Starter 方便了 Spring 各项依赖的集成,通过 Starter 可以在 Spring Boot 中获取到所需相关技术的一站式支持(依赖、相关的自动配置文件和相关的 Bean),而无需通过示例代码和复制粘贴来获取依赖

例如,需要 Spring Web 支持时,可以通过引入 spring-boot-starter-web 依赖,它将自动为项目配置一个内嵌的 Tomcat 以及开启 Spring WebMvc 的功能

常用的 Starter

  • spring-boot-starter:核心 Starter,包括自动配置的支持、日志以及 YAML 解析等
  • spring-boot-starter-aop:提供 Spring AOPAspectJ 的面向切面编程支持
  • spring-boot-starter-jdbc:提供 JDBC 支持(由 Tomcat JDBC 连接池提供支持)
  • spring-boot-starter-actuatorSpring BootActuator 支持,其提供了生产就绪功能,帮助开发者监控管理应用
  • 等等

自制一个 Starter

Starter 主要由两部分组成

  • auto-configure,该模块主要提供我们要导入功能的实现代码
  • starter 模块主要管理相关的依赖,比如 auto-configure 依赖

实际上,这两个模块完全可以合并为一个模块,但是基于 Spring 职责分明 的理念,建议分开

auto-configure 模块

接下来我们开发一个简易的 auto-configure

  • 常常利用 @ConfigurationProperties @EnableConfigurationProperties 将当前模块的属性绑定到 Environment
  • 常常利用各种 @Conditional 相关注解比如 @ConditionalOnClass @ConditionalOnMissingBean 等注解控制组件的注册条件
  • 常常利用 @AutoConfigureBefore @AutoConfigureAfter 等注解控制组件的依赖关系

示例如下:

MyTestProperties

/**
 * 前缀:my.test
 * 属性:name
 */
@ConfigurationProperties(prefix = "my.test")
public class MyTestProperties {

    private String name = "default";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

MyAutoConfiguration

@Configuration
// 类路径存在 MyCommandLinerRunner.class
@ConditionalOnClass(MyCommandLinerRunner.class)
// 当前容器不存在 CommandLineRunner 类型的 bean
@ConditionalOnMissingBean(CommandLineRunner.class)
// MyTestProperties 属性配置
@EnableConfigurationProperties(MyTestProperties.class)
public class MyAutoConfiguration {

    /**
     * 注册一个 MyCommandLinerRunner
     * @return
     */
    @Bean
    public CommandLineRunner myCommandLineRunner() {

        return new MyCommandLinerRunner();
    }
}

MyCommandLinerRunner

/**
 * 输出配置的 MyTestProperties.name 属性
 */
public class MyCommandLinerRunner implements CommandLineRunner {

    @Autowired
    MyTestProperties myTestProperties;

    @Override
    public void run(String... args) throws Exception {
        System.out.println("&&&&&&&&&");
        System.out.println(myTestProperties.getName());
        System.out.println("&&&&&&&&&");
    }
}

最后,作为一个自定义的 moudleSpring 自然无法加载这个配置类,因此我们借用 Spring Boot自动装配 机制,在 src\main\resources\META-INF\spring.factories 文件中添加自动配置信息:org.springframework.boot.autoconfigure.EnableAutoConfiguration=my.autoconfigure.config.MyAutoConfiguration
则该类会在 Spring Boot 应用启动时被加载

starter 模块

该模块管理依赖即可,在此示例中我们仅需要引入 auto-configure 依赖即可

pom.xml

	<dependencies>
        <dependency>
            <groupId>com.xsn</groupId>
            <artifactId>my-spring-boot-autoconfigure</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

app 模块

此处的 app 模块即代表我们使用 auto-configure 功能的模块,此处直接引入 starter 模块依赖,然后启动 Spring Boot 应用即可,同时我们还可以在 yaml 文件里自定义 auto-configure 模块的配置属性

pom.xml

	<dependencies>
        <dependency>
            <groupId>com.xsn</groupId>
            <artifactId>my-spring-boot-starter</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

application.yml

my:
  test:
    name: dd

AppApplication

@SpringBootApplication
public class AppApplication {

    public static void main(String[] args) {
        SpringApplication.run(AppApplication.class, args);
    }
}

启动模块,定义的属性值成功输出

&&&&&&&&&
dd
&&&&&&&&&

至此,我们实现了一个简易的 Starter,其中有一个步骤,我们将自定义的配置类名添加到 src\main\resources\META-INF\spring.factories 文件中,这便是依赖于 Spring Boot 提供的 自动装配 机制

自动装配

在我们的 Spring Boot 启动类上的 @SpringBootApplication 注解中,我们可以看到一个 @EnableAutoConfiguration 注解

@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

进入该注解,可以看到 @Import({AutoConfigurationImportSelector.class})

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration

该注解 @Import 可以搭配 普通配置类 ImportSelector ImportBeanDefinitionRegistrar 在容器中注册对应的 BeanDefinition

关于 @Import,可以阅读下面文章

Spring —— 关于 @Configuration @Import ImportSelector ImportBeanDefinitionRegistrar

此处 ImportAutoConfigurationImportSelector 就是一个 ImportSelector,因此 ConfigurationClassPostProcessor 会在解析主配置类时,执行 AutoConfigurationImportSelector#selectImports 方法

AutoConfigurationImportSelector

	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}

		/**
		 * 获取所有符合条件的 EnableAutoConfiguration 装配类
		 */
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

	------------- getAutoConfigurationEntry ------------

	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);

		/**
		 * 获取所有备选装配类 (EnableAutoConfiguration)
		 */
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

		// 去重
		configurations = removeDuplicates(configurations);

		/**
		 * 从 exclude excludeName 和 Environment 的 spring.autoconfigure.exclude 属性
		 * 		中解析需要排除的类
		 */
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);

		// AutoConfigurationImportFilter 过滤
		configurations = getConfigurationClassFilter().filter(configurations);

		/**
		 * 获取自动装配的 AutoConfigurationImportListener 并发布
		 * 		AutoConfigurationImportEvent
		 */
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

所以,当我们将 auto-configure 的配置类名配置到对应的 spring.factories 文件中后,Spring Boot 并加载了该配置类

总结

本文从一个简单的 Starter Demo 入手,简单了解了 Starter 的使用机制,并同时对 自动装配 做了初步了解,这也是 Spring Boot 及其重要的机制

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值