SpringBoot--自定义自动配置和Starter

众所周知,SpringBoot能简化大部分配置过程,而且快速搭建起应用程序。主要的功劳在于自动装配和起步依赖。

起步依赖 (starter)

把一些具有某些功能的自研或三方依赖坐标打包到一块,使用的时候不需要再导入包含的依赖。一般情况下,官方starter命名格式一般是 spring-boot-starter-* ,比如 spring-boot-starter-web 、spring-boot-starter-test 。自定义starter一般的命名格式是 项目名称-starter。

比如spring-boot-starter-web依赖,其中包含了很多有关于web的依赖坐标,如下图所示:

 自动配置

不需要手动配置,由starter帮我们完成,就跟装修房子差不多,SpringBoot不仅给我们送装修工具,而且还顺带把装修的活也干了,让我们享受了一条龙服务,我们不需要考虑整体过程,只需要装修完以后直接使(添加业务逻辑)就行。

要想自定义一个起步依赖,那么就得先知道SpringBoot是怎么进行自动配置的:

首先就是启动类,这个类不仅是应用程序的入口,还具有着其他隐藏功能。下面是一个普通的启动类:

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class);
    }
}

进入@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 {
    @AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};

    @AliasFor(annotation = EnableAutoConfiguration.class)
	String[] excludeName() default {};

    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
	String[] scanBasePackages() default {};

    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};
}

先简单解释一下另外两个注解@SpringBootConfiguration和@ComponentScan。前面这个就相当于@Configuration注解,主要功能就是标记这个类,而后面这个主要功能就是开启自动扫描包。

其中重点就是@EnableAutoConfiguration注解,主要功能就是开启SpringBoot的自动配置功能,进入源码可以看到:

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

发现这个注解也是个组合注解,简单说一下@AutoConfigurationPackage注解,这个注解的主要功能就是把和启动类同级以及子级包中带有@Configuration、@Component的注解的类注册到容器里。

重点是@Import(AutoConfigurationImportSelector.class)注解,其中@Import注解的作用就是把组件导入到容器里,进一步进入这个组件类的源码里:

public String[] selectImports(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return NO_IMPORTS;
	}
	AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
			.loadMetadata(this.beanClassLoader);
	AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
			autoConfigurationMetadata, annotationMetadata);
	return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

protected AutoConfigurationEntry getAutoConfigurationEntry(
		AutoConfigurationMetadata autoConfigurationMetadata,
		AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return EMPTY_ENTRY;
	}
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	List<String> configurations = getCandidateConfigurations(annotationMetadata,
			attributes);
	configurations = removeDuplicates(configurations);
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
	checkExcludedClasses(configurations, exclusions);
	configurations.removeAll(exclusions);
	configurations = filter(configurations, autoConfigurationMetadata);
	fireAutoConfigurationImportEvents(configurations, exclusions);
	return new AutoConfigurationEntry(configurations, exclusions);
}

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

其中有一个selectImports方法,其中loadMetadata方法,就是让类加载器去找到元数据地址,然后调用了getAutoConfigurationEntry方法,把类路径下所有jar包的spring.factories文件里键为EnableAutoConfiguration的值取出来,这些值也就是全限定名,SpringBoot会通过反射机制来添加到容器里。获取spring.factories里值的过程是通过getCandidateConfigurations方法来实现的。

我们在类路径下找一下这个spring.factories文件:

自定义起步依赖

了解了自动配置的过程,自定义一个起步依赖,项目名称以starter结尾。

配置POM

需要在POM文件里导入两个依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

定义属性类

把未来需要调用的属性封装到属性类里:

@Data
@ConfigurationProperties(prefix = "myproperties") // 属性配置的前缀
public class MyProperties {
    private boolean ready;
    private String info;
}

定义业务类

自定义starter的主要目的就是把某些业务抽取出来,方便其他地方能直接调用:

public class MyService {

    @Autowired
    private MyProperties myProperties;

    // 具体业务
    public String doSome() {
        return "you can" + myProperties.getInfo();
    }
}

定义自动配置类

SpringBoot会先读取 META-INF\spring.factories 文件,之后再根据自动配置类里的注解来决定是否生效。以下就是自动配置类的实现:

/**
 * @Configuration: 声明该类为配置类
 * @EnableConfigurationProperties: 绑定属性类,并放入容器
 * @ConditionalOnProperty: 检查指定属性是否具有特定值的条件
 */
@Configuration
@EnableConfigurationProperties(MyProperties.class)
@ConditionalOnProperty(prefix = "myproperties",name = "ready", havingValue = "true")
public class MyAutoConfigure {

    @Bean(name = "myService")
    public MyService myConfigBean() {
        return new MyService();
    }
}

因为会读取 META-INF\spring.factories 文件,所以需要在resource文件夹底下创建这个文件(文件名必须要一致):

org.springframework.boot.autoconfigure.EnableAutoConfiguration = \
  com.doyens.file.config.MyAutoConfigure

starter编写完之后,用maven命令打包到本地仓库,就可以开始测试了。

测试

在另一个项目里导入starter依赖的坐标:

<dependency>
    <groupId>com.auto.test</groupId>
    <artifactId>my-auto-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

在配置文件定义属性:

myconfigbean:
  ready: true
  info: 666

编写测试类,需要设置由SpringBoot来运行测试方法:

@SpringBootTest(classes = DemoApplication.class)
@RunWith(SpringRunner.class)
public class StarterTest {

    @Resource(name = "myService")
    private MyService myService;

    @Test
    public void test() {
        String str= myService.doSome();
        System.out.println(str);
    }
}

最后,测试结果:

  • 56
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
自定义Spring Boot Starter是一种用于简化Spring Boot应用程序配置和依赖管理的机制。它由几个组件组成,包括starter包、autoconfiguration包和配置文件。 首先,你需要创建一个Maven工程,并创建三个模块。其中,starter包负责引入依赖,autoconfiguration包负责实现装配。在autoconfiguration包中,你需要定义对应的properties类、configuration类和核心业务类。此外,你还需要在autoconfiguration包的/resources/META-INF/目录下添加spring.factories文件,并配置org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.qiejk.demo.springboot.autoconfiguration.DemoAutoConfiguration。这一步是Spring Boot装配的核心,只有添加了这个内容,Spring Boot才会进行自动装配。\[1\] 在父模块的pom文件中,你需要引入Spring Boot的依赖,例如spring-boot-starter-web,以及指定Spring Boot的版本。这样,你的自定义starter模块就可以基于Spring Boot进行开发和使用。\[2\] 最后,你需要创建一个配置文件,用于告诉Spring Boot在启动时扫描该配置文件,并将其中的配置类加载到Spring容器中。这个配置文件的作用是指定自动配置类的位置。\[3\] 通过以上步骤,你就可以创建自定义Spring Boot Starter,并在应用程序中使用它来简化配置和依赖管理。 #### 引用[.reference_title] - *1* [如何自定义springboot-starter](https://blog.csdn.net/sinat_29434159/article/details/123995794)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [SpringBoot自定义Starter篇](https://blog.csdn.net/m0_46571920/article/details/122910726)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值