现在的Java后端开发中,Spring Boot早已被广泛运用,运用它,咱们轻轻松松地就能够搭建起一个后端服务,发挥出你无限的创造力。

为什么Spring Boot能够这么便利呢?在Spring Boot问世之前,Spring为什么又会让人觉得繁琐呢?

这很大程度得益于Spring Boot的主动装备机制,而且在Spring Boot生态中,有着十分多的starter。

Spring Boot的Starter指的是运用Spring Boot主动装备机制,完结一些依靠的Bean的预先装备的一种依靠,能够协助咱们一键引进依靠,主动完结某些Bean的装备。

所以什么是主动装备机制?starter究竟做了什么?Spring Boot究竟便利在哪里?

或许刚刚开端学习后端开发,只是接触过Spring Boot而没有从事单纯的Spring开发的同学,脑袋里会有这些问号。

不要紧,今天咱们从零开端,了解一下Spring结构是怎么装备各种外部依靠的,以及Spring Boot的starter究竟省掉了那些事情。

在这之前,咱们需求先搞清楚Spring结构的一些中心概念例如依靠注入、控制反转、IoC容器是什么、Spring Bean是什么等等。

1,从编写一个外部库为例开端

无论是运用Spring结构开发,仍是Spring Boot,咱们都需求引进许多外部库依靠例如衔接数据库的、安全结构等等。引进依靠之后,要想将依靠中需求的类作为Bean交给IoC容器保管,就需求做一些装备。

这儿咱们自己开发一个简略的外部库,用作简略的日志打印功用,这个外部库有以下功用:

  • 输出infowarn类型的日志
  • 答应用户装备输出日志时是否显现时刻

开发了这个外部库之后,咱们来对比一下经过Spring引证并装备这个外部库,以及将其做成Starter后在Spring Boot引证,这两种情形下有什么区别。

言归正传,咱们开端第一步吧。

先创立一个空的Maven项目,不需求任何依靠,编写存放日志装备的类LogConfig

package com.gitee.swsk33.logcoredemo.config;
/**
 * 日志功用的装备类
 */
public class LogConfig {
	/**
	 * 是否显现时刻
	 */
	private boolean showTime;
	// 对应getter和setter办法
	public boolean isShowTime() {
		return showTime;
	}
	public void setShowTime(boolean showTime) {
		this.showTime = showTime;
	}
}

这便是一个简略的POJO类。

然后创立咱们的中心逻辑功用类LogService

package com.gitee.swsk33.logcoredemo.service;
import com.gitee.swsk33.logcoredemo.config.LogConfig;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
 * 日志功用类
 */
public class LogService {
	/**
	 * 日志装备字段(需求用户注入,因而这个字段要有Setter办法)
	 */
	private LogConfig config;
	// config字段的setter办法
	public void setConfig(LogConfig config) {
		this.config = config;
	}
	/**
	 * 工具类:获取当时时刻字符串
	 *
	 * @return 当时时刻字符串
	 */
	private String getTimeString() {
		// 自界说时刻格局:年/月/日-时/分/秒
		DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy/MM/dd-HH:mm:ss");
		// 时刻目标转换成自界说的字符串办法
		return LocalDateTime.now().format(format);
	}
	/**
	 * 输出告示类音讯
	 *
	 * @param message 日志音讯
	 */
	public void info(String message) {
		// 依据装备判别是否输出时刻
		String messageString = config.isShowTime() ? "[INFO] " + getTimeString() + " " + message : "[INFO] " + message;
		System.out.println(messageString);
	}
	/**
	 * 输出警告类音讯
	 *
	 * @param message 日志音讯
	 */
	public void warn(String message) {
		// 依据装备判别是否输出时刻
		String messageString = config.isShowTime() ? "[WARN] " + getTimeString() + " " + message : "[WARN] " + message;
		System.out.println(messageString);
	}
}

里边的代码很简略,这儿不再具体介绍了。

好的,咱们的外部库就开发完结了!现在在项目目录下履行mvn clean install指令将其装置至本地Maven库房,使得待会能够引证这个外部库。

在这儿这个外部库的groupIdcom.gitee.swsk33artifactIdlog-core-demoversion1.0.0,这儿咱们自己在pom.xml设定好即可。

2,在Spring项目中引证并装备这个外部库

好的,假定现在有一个一个运用Spring结构的开发者(下文将这个开发者称作外部库运用者),需求运用咱们的日志外部库,并将其间需求运用的服务类LogService交给Spring的IoC容器保管,这样除了引证这个外部库之外,还需求界说一些Bean的装备。

再创立一个空的Maven项目,引进Spring依靠以及咱们的日志外部库依靠等等:

<!-- Spring 上下文 -->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>6.0.9</version>
</dependency>
<!-- 调用咱们的日志中心外部库 -->
<dependency>
	<groupId>com.gitee.swsk33</groupId>
	<artifactId>log-core-demo</artifactId>
	<version>1.0.0</version>
</dependency>

只是是引进依靠,依靠中的服务类并不会被Spring结构实例化为Bean并放入IoC容器,由于外部库中的类不仅没有标示@Component等等注解,也没有说包括XML文件。

所以运用Spring结构的开发者在这时还需求手动地装备一下Bean,才能在后续开发时经过IoC容器取出对应的服务类的Bean并正常运用。

这位开发者或许运用XML的办法装备:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<!-- 界说日志库中的装备类的Bean -->
	<bean id="logConfig" class="com.gitee.swsk33.logcoredemo.config.LogConfig">
		<!-- 装备为显现时刻 -->
		<property name="showTime" value="true"/>
	</bean>
	<!-- 界说日志库中的服务类的Bean,并注入装备 -->
	<bean id="logService" class="com.gitee.swsk33.logcoredemo.service.LogService">
		<!-- 将上述的装备Bean注入进来 -->
		<property name="config" ref="logConfig"/>
	</bean>
</beans>

也可所以经过注解的办法,创立装备类进行装备:

package com.gitee.swsk33.springannotationbased.config;
import com.gitee.swsk33.logcoredemo.config.LogConfig;
import com.gitee.swsk33.logcoredemo.service.LogService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * 用于日志外部库的装备类:将日志库中需求的类注册为Bean
 */
@Configuration
public class LogServiceConfig {
	/**
	 * 实例化一个日志装备类,并设定装备,然后注册为Bean
	 */
	@Bean
	public LogConfig logConfig() {
		LogConfig config = new LogConfig();
		// 设定显现时刻
		config.setShowTime(true);
		return config;
	}
	/**
	 * 将LogService类实例化,并注册为Bean,并注入装备目标依靠
	 */
	@Bean
	public LogService logService(LogConfig logConfig) {
		LogService logService = new LogService();
		logService.setConfig(logConfig);
		return logService;
	}
}

无论怎么,也便是说假如要将对应的目标交给Spring结构保管,那么开发者需求为外部库中的类编写Bean装备,才能够运用

以注解的办法为例,装备完结后,才能从IoC容器中取出LogService类的Bean并运用:

package com.gitee.swsk33.springannotationbased;
import com.gitee.swsk33.logcoredemo.service.LogService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
public class Main {
	public static void main(String[] args) {
		// 创立IoC容器,基于注解的
		ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
		// 从容器中取出日志服务目标并调用
		LogService logService = context.getBean(LogService.class);
		logService.info("调用日志服务!");
		logService.warn("调用日志服务!");
	}
}

从零开始的Spring Boot自动配置学习和starter制作教程

咱们这儿的日志外部库中的类比较简略,实践开发中许多外部库中的类,及其依靠关系都是很杂乱的,因而开发者在运用Spring结构开发并引证它们的时分,都需求为这些外部库的对应类编写Bean装备,可见这是比较费事的。

毕竟你不知道你所运用的外部库中的类是否都标示了@Component等注解,也不知道外部库的开发者是否编写了XML装备,所以你需求自己为外部库中的类装备Bean的界说。

3,运用Spring Boot的主动装备机制处理上述费事

引进依靠后还要写装备,这实在是太费事了!能不能引进依靠后就直接运用呢?

当然,Spring Boot的主动装备机制就完结了这一点,当然,这是凭借starter依靠完结的。

(1) 主动装备机制概述

Spring Boot主动装备机制是测验依据开发者增加的jar依靠项,主动装备Spring应用程序。

例如前面咱们引进了日志外部库,那么主动装备机制就会主动地将这个外部库中的类初始化为Bean,而无需像前面相同先手动装备Bean。

主动装备是怎么完结的呢?

能够说,要想主动装备一个外部库中的类,至少需求下列两个东西:

  • 主动装备类
  • 主动装备候选类装备文件

这两样东西,一般就放在一个称作starter的依靠中,然后starter就会被打包发布,外部库的运用者引证即可。

下面,咱们先独自看看starter中的这两个东西是什么。

1. 主动装备类

在上述运用Spring结构引进外部库时,要手动地给外部库中的类LogServiceLogConfig编写Bean的界说装备,那么外部库的开发者能不能预先编写好这些Bean的装备呢而不是咱们运用者去编写呢?

当然能够,依据这个思路,外部库开发者能够界说一个装备类,在其间经过@Bean标示的办法,创立对应的类的Bean目标,以及约好好默许装备,然后交给IoC容器保管。外部库开发者完结了Bean的界说编写,是不是就不需求咱们外部库运用者去编写Bean的装备了呢?

这儿所说的装备类,便是starter中的主动装备类。主动装备类便是一个普通的标示了@Configuration的类,其间运用标示了@Bean的办法完结对Bean的界说,这便是主动装备类完结的作业,主动装备类由外部库开发者编写并放在starter中。

2. 主动装备候选类装备文件

到这儿又有一个问题了,开发者的确先界说好了一个主动装备类,但是咱们知道Spring结构并不是会扫描一切的类的,那是不是说明咱们还要经过@ComponentScan注解装备一下外部库的包途径呢?

当然不是了!不然怎么体现出主动装备中的“主动”这个特点呢?

所以外部库的开发者除了编写完结主动装备类之外,还需求编写一个主动装备候选类装备文件放在starter中,这个装备文件中便是声明哪些类是主动装备类,这样Spring Boot的主动装备机制会去先读取这些主动装备候选类的装备文件,找到一切的主动装备类后,再去加载这些主动装备类,完结主动装备。

主动装备候选类装备文件也是包括在starter中的,而且放在固定的方位。

在Spring Boot发动时,会扫描一切的外部库的classpath下一切的META-INF/spring.factories文件(Spring Boot 2.x版别),在这个文件中读取哪些类需求被读取以进行主动装备,可见这个META-INF/spring.factories文件便是咱们所说的主动装备候选类装备文件。

Spring Boot应用程序默许开启了主动装备功用,由于Spring Boot的主类上一般有@SpringBootApplication这个注解,而这个注解中包括了@EnableAutoConfiguration注解,这个注解便是用于开启主动装备功用的,至于其底层原理,就不在此赘述了!

这儿说明一下,Spring Boot 2.x版别和3.x版别的主动装备候选类的装备文件是不相同的:

  • Spring Boot 2.x发动时,是扫描一切的外部库classpath下一切的META-INF/spring.factories文件
  • Spring Boot 3.x发动时,是扫描一切的外部库classpath下一切的META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件

classpath表明类途径,能够理解为Java程序编译并打包为jar文件后jar包内的途径,在Maven项目中,项目目录下的src/main/resources目录就对应着classpath的根途径/

外部库开发者完结了主动装备类的编写以及主动装备候选类装备文件的编写,就能够将其打包为starter并发布,咱们引进starter即可!这样Spring Boot发动时,扫描到starter中的主动装备候选类装备文件并读取到需求加载的装备类,就能够完结装备类加载。外部库的运用者只需求引进starter作为依靠,然后直接就能够从IoC容器中获取外部库中需求用的类的Bean了!

(2) 为咱们的日志外部库制造一个starter

讲解了这么多的主动装备机制,咱们或许仍是不知道starter里边究竟装着啥,所以咱们现在就为咱们上述的日志外部库编写一个starter。

1. 创立starter工程

首先创立一个新的Spring Boot依靠,并勾选Spring Configuration Processor依靠:

从零开始的Spring Boot自动配置学习和starter制作教程

然后在pom.xml中,删除spring-boot-starter-test依靠,以及build部分,这些是不需求的:

从零开始的Spring Boot自动配置学习和starter制作教程

然后把项目中的主类和resources目录下的装备文件也删掉,这也是用不着的:

从零开始的Spring Boot自动配置学习和starter制作教程

这样,一个空的starter工程就创立完结了!

在这儿,starter工程中一般有两个要害依靠咱们能够看一下:

<!-- Spring Boot Starter 支撑 -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- Spring Boot Starter 装备生成器 -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-configuration-processor</artifactId>
	<optional>true</optional>
</dependency>

2. 参加日志外部库依靠

这个starter是为咱们的日志外部库制造的,当然要在这个工程中参加日志外部库作为依靠了!当然主要意图是咱们能够引证到外部库中的类并实例化为Bean然后交给IoC容器。

<!-- 引进咱们的外部库 -->
<dependency>
	<groupId>com.gitee.swsk33</groupId>
	<artifactId>log-core-demo</artifactId>
	<version>1.0.0</version>
</dependency>

3. 编写装备特点读取类

在上述日志外部库中,有LogConfig类专门用于存放用户的装备信息,这个类中的装备值是能够由外部库运用者自界说的。

咱们也知道在Spring Boot中能够让运用者把装备写在application.properties装备文件中,然后咱们读取,现在咱们就创立一个这样的装备读取类

package com.gitee.swsk33.logspringboot2starter.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
 * 用于读取装备文件(application.properties或application.yml)中的装备的特点装备类
 */
@Data
@ConfigurationProperties(prefix = "com.gitee.swsk33.log-core-demo")
public class LogConfigProperties {
	/**
	 * 是否显现时刻(默许为false)
	 */
	private boolean showTime = false;
}

运用@ConfigurationProperties注解,即可将Spring Boot装备文件中的对应装备读取并赋予到这个类的对应特点中,以完结咱们自界说装备值,这儿就不再过多赘述这个注解的作用了!

4. 编写主动装备类

这儿便是starter的中心了!创立上述所说的主动装备类,这个类便是用于在其间约好好对应的Bean目标,并交给IoC容器保管:

package com.gitee.swsk33.logspringboot2starter.autoconfigure;
import com.gitee.swsk33.logcoredemo.config.LogConfig;
import com.gitee.swsk33.logcoredemo.service.LogService;
import com.gitee.swsk33.logspringboot2starter.properties.LogConfigProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * 用于主动装备日志库中的服务类的主动装备类
 */
// 该类标记为装备类
@Configuration
// 经过@EnableConfigurationProperties注解指定咱们的特点装备类,才能在这个类中运用主动安装获取到特点装备类的Bean并读取装备
@EnableConfigurationProperties(LogConfigProperties.class)
public class LogServiceAutoConfiguration {
	/**
	 * 获取特点装备类以读取装备文件中的装备值
	 */
	@Autowired
	private LogConfigProperties logConfigProperties;
	/**
	 * 在这儿创立服务类LogService的实例,设定装备并注册为Bean
	 */
	@Bean
	public LogService logService() {
		// 以读取的装备值创立装备目标
		LogConfig config = new LogConfig();
		config.setShowTime(logConfigProperties.isShowTime());
		// 实例化日志服务类并设定装备
		LogService service = new LogService();
		service.setConfig(config);
		// 输出一个提示语
		System.out.println("------- LogService主动装备完结!-------");
		return service;
	}
}

这个类并不难,咱们来看一下其间的一些要点:

  • @EnableConfigurationProperties注解:表明加载一个装备特点读取类(标示了@ConfigurationProperties注解用于读取装备文件值的类),并将其实例化为Bean注册到IoC容器,这样就能够在该装备类中运用主动安装得到装备特点读取类,获取装备值
  • 在其间咱们写了一个带有@Bean的办法,@Bean注解的作用相信咱们都知道了,办法中咱们完结了最开端在Spring开发中运用者的Bean的界说作业,即创立好外部库的LogService类型的Bean并放入IoC容器中去

5. 编写主动装备候选类装备文件

上述了解了主动装备进程,咱们知道要想Spring Boot能够加载到上述的主动装备类LogServiceAutoConfiguration,还需求编写主动装备候选类装备文件并放在指定方位。

假定你要制造Spring Boot 2.x的starter,那就在resources目录下创立META-INF/spring.factories文件:

从零开始的Spring Boot自动配置学习和starter制作教程

在里边声明上述的主动装备类的全限制类名:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.gitee.swsk33.logspringboot2starter.autoconfigure.LogServiceAutoConfiguration

假如你有多个主动装备类,则以逗号,离隔,例如:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.mycorp.libx.autoconfigure.LibXAutoConfiguration,com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration

当然,这样写成一行不太美观,能够凭借\换行,如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.mycorp.libx.autoconfigure.LibXAutoConfiguration,\
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration

假如你是制造Spring Boot 3.x的starter,那就在resources目录下创立META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件:

从零开始的Spring Boot自动配置学习和starter制作教程

在其间直接声明主动装备类的全限制类名即可:

com.gitee.swsk33.logspringboot3starter.autoconfigure.LogServiceAutoConfiguration

多个主动装备类则每行一个,例如:

com.mycorp.libx.autoconfigure.LibXAutoConfiguration
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration

假如说你想要制造的starter即兼容Spring Boot 2.x版别也支撑3.x版别呢?那将上述两个版别的主动装备候选类装备文件都写一个即可!代码部分是相同的。

好的,到此咱们的starter就制造完结了!同样地,履行mvn clean install指令将其装置至本地Maven库房。

6. 【补充】starter的命名

starter的artifactId一般也是有所考究的,咱们一般将自己制造的starter命名为xxx-spring-boot-starter,例如log-spring-boot-starter,这个规范需求遵守。

咱们也能够发现Spring官方的starter的命名格局为spring-boot-starter-xxx,例如spring-boot-starter-web,和咱们自己的starter命名“相反”,可见上述规范也是为了将第三方starter和官方的区别开来。

(3) 运用Spring Boot调用咱们的starter

在Spring Boot项目中直接引进咱们的starter的工件坐标作为依靠即可,然后就能够在咱们需求运用的当地,直接经过@Autowired注解注入LogService类的目标即可运用!

package com.gitee.swsk33.springboot3use;
import com.gitee.swsk33.logcoredemo.service.LogService;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBoot3UseApplication {
	/**
	 * 在需求运用的当地主动安装一下即可!
	 */
	@Autowired
	private LogService logService;
	@PostConstruct
	private void init() {
		// 测试调用
		logService.info("调用日志服务!");
	}
	public static void main(String[] args) {
		SpringApplication.run(SpringBoot3UseApplication.class, args);
	}
}

这儿直接在主类调用,然后运行试试:

从零开始的Spring Boot自动配置学习和starter制作教程

可见控制台输出了对应音讯,说明咱们制造的starter主动装备成功!

除此之外,运用者还能够在装备文件application.properties中进行对应装备:

# 装备显现时刻
com.gitee.swsk33.log-core-demo.show-time=true

相信到这儿,咱们就知道starter是什么了!可见starter帮咱们完结了下列作业:

  • 导入一切所需的依靠:上述starter中引进了一切需求的依靠,包括日志外部库,这样开发者只需求引进starter作为依靠即可,不需求手动装备一切依靠
  • 完结了Bean的界说:Starter中现已完结了对外部库中运用的类的Bean的界说,而不需求运用者像最开端运用Spring结构开发时自己编写外部库中的Bean界说
  • 抽离出用户可自界说的装备部分:例如上述日志的装备部分,即装备是否显现时刻的部分,是能够由运用者自界说的,在starter中咱们用装备特点读取类LogConfigProperties抽离出了自界说的部分,使得运用者在Spring Boot的装备文件中界说自界说的装备值即可

可见,starter中一般并不包括一个库的中心功用或许业务代码,只包括主动装备类主动装备候选类装备文件,当然有时分也或许会包括装备特点读取类。但无论怎么,这说明starter应当是和外部库中心是分开的,例如上述日志外部库的中心功用的代码并没有包括在starter中,而是作为一个独自的项目,starter只是引证它罢了。

除此之外,咱们也学习到了Spring Boot中导入starter作为依靠时,主动装备的大致进程如下:

  1. 应用程序发动,Spring Boot扫描一切依靠的classpath途径下的主动装备候选类装备文件,在里边读取到哪些类是用于主动装备的类,其间:
    1. Spring Boot 2.x扫描的是classpath下一切的META-INF/spring.factories文件
    2. Spring Boot 3.x扫描的是classpath下一切的META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件
  2. 读取到一切主动装备类后,就会将这些类实例化为Bean并放入IoC容器(主动装备类要标示@Configuration注解),这个进程中,这些主动装备类中的一切@Bean办法也会被履行,这样开发者预先约好的Bean就被初始化好了并注册到IoC容器,后续开发者只需经过主动安装获取对应的类的Bean即可

可见,Spring Boot的主动装备机制并不是很难,制造starter也不是一件难事,关于Spring Boot 2.x和3.x版别的区别,也便是主动装备候选类装备文件的方位和表明办法不同罢了。

4,条件注解

上述咱们成功地制造了一个starter,不过咱们也发现这个starter中的装备,是必定会被加载的,咱们能不能设定条件,比如说用户不需求的时分就不加载它以节约内存呢?当然能够!Spring Boot还供给了许多条件加载Bean的注解。

(1) 类加载条件

Spring Boot供给了@ConditionalOnClass@ConditionalOnMissingClass这两个注解,咱们直接看比如:

// 省掉package和import
/**
 * 用于主动装备日志库中的服务类的主动装备类
 */
@Configuration
@EnableConfigurationProperties(LogConfigProperties.class)
@ConditionalOnClass(LogService.class)
public class LogServiceAutoConfiguration {
	// 省掉主动装备类的内容
}

咱们在上述的主动装备类上面标示了@ConditionalOnClass(LogService.class)注解,表明只要当加载到LogService这个类的时分,这个主动装备类LogServiceAutoConfiguration才会被加载并初始化为Bean。

不然这个类不会被加载,其间的@Bean办法也会不收效,这个装备也就不收效了!

咱们能够把上述starter中的日志外部库依靠删掉,然后加上@ConditionalOnClass(LogService.class)注解,最后在Spring Boot工程中引证这个starter,调查一下这个类会不会被加载。

那很简略,@ConditionalOnMissingClass便是和它相反,例如你标示@ConditionalOnMissingClass(LogService.class)就说明假如没有加载到LogService这个类,这个字段装备类才会被加载并初始化为Bean。

这两个注解只能用在类上面,而不能用在标示了@Bean的办法上!

(2) Bean条件

Spring Boot还能够依据是否存在或许不存在某个Bean作为条件,来初始化你的Bean,仍是看下列比如:

// 省掉package和import
/**
 * 用于主动装备日志库中的服务类的主动装备类
 */
@Configuration
@EnableConfigurationProperties(LogConfigProperties.class)
public class LogServiceAutoConfiguration {
	@Autowired
	private LogConfigProperties logConfigProperties;
	/**
	 * 在这儿创立服务类LogService的实例,设定装备并注册为Bean
	 */
	@Bean
	@ConditionalOnBean
	public LogService logService() {
		// 以读取的装备值创立装备目标
		LogConfig config = new LogConfig();
		config.setShowTime(logConfigProperties.isShowTime());
		// 实例化日志服务类并设定装备
		LogService service = new LogService();
		service.setConfig(config);
		// 输出一个提示语
		System.out.println("------- LogService主动装备完结!-------");
		return service;
	}
}

可见上述logService办法上,标示了@ConditionalOnBean,表明在主动装备时,IoC容器中存在LogService类型的Bean的时分,就会履行这个办法以生成Bean。

同样地,假如改成:

@Bean
@ConditionalOnMissingBean
public LogService logService() {
	// 省掉办法内容	
}

表明在主动装备时,IoC容器中不存在LogService类型的Bean的时分,才会履行这个办法以生成Bean。

可见,这两个注解直接标示(不传参)@Bean的办法上时,是判别这个办法的回来类型的Bean是否存在/不存在

当然,还能够这样:

@Bean
@ConditionalOnMissingBean(LogConfig.class)
public LogService logService() {
	// 省掉办法内容
}

上述指定了注解的value字段值,表明当IoC容器中不存在LogConfig类型的Bean的时分才会履行这个办法生成Bean。

还能够这样:

@Bean
@ConditionalOnMissingBean(name = "logService")
public LogService logService() {
	// 省掉办法内容
}

上述指定了注解的name字段值,表明当IoC容器中不存在**名(id)**为logService的Bean的时分才会履行这个办法生成Bean。

那么@ConditionalOnBean注解同理。

事实上,@ConditionalOnMissingBean这个注解是很常用的,运用这个注解,能够答应用户是自界说这个Bean仍是运用外部库开发者供给的默许的Bean

咱们来看看Redis的starter中,RedisTemplate类型的Bean:

从零开始的Spring Boot自动配置学习和starter制作教程

这是Spring Boot的Redis的starter中,用于主动装备RedisTemplate类型Bean的办法,这儿加上了@ConditionalOnMissingBean注解,指定当未找到名为redisTemplate的Bean的时分,就会履行这个办法将RedisTemplate类型Bean注册到IoC容器中。

这样,假如用户需求自行装备RedisTemplate,例如装备Redis的序列化办法时,用户会自己创立一个RedisTemplate类型Bean,装备好序列化办法后就注册到IoC容器,这时有了用户自己创立的RedisTemplate类型Bean,上述官方starter中的这个办法就不会被履行,就能够让用户运用自己自界说的Bean。

可见这种思路,能够使得用户去选择是运用自己自界说的Bean,仍是运用官方给出的默许的Bean

(3) 装备文件条件

官方还供给了@ConditionalOnProperty注解,表明当读取到装备文件application.properties中有特定的装备值的时分,才会实例化某个Bean,例如:

@Bean
@ConditionalOnProperty(prefix = "com.gitee.swsk33", name = "enable-log", havingValue = "true")
public LogService logService() {
	// 省掉办法内容
}

这表明只要装备文件中,存在装备项com.gitee.swsk33.enable-log而且其值为true时,这个办法才会被履行以生成Bean。

这个注解中,经过prefixname特点,指定具体的装备项名称,而havingValue表明指定这个装备的值是什么才收效。

加上上述注解,用户就能够经过装备文件来启用或许禁用日志功用:

# 启用日志功用
com.gitee.swsk33.enable-log=true

反之只需把装备值改成false,上述@Bean办法就不会被履行,这个装备不收效。

5,总结

可见Spring Boot的主动装备,大大当地便了咱们的开发,这也是为什么咱们平常引进依靠例如MongoDB的starter后,就能够直接主动安装MongoTemplate并运用了,十分便利。这些,都是主动装备机制,以及各个starter帮咱们简化了开发。

本文以制造一个简略的外部库为例,比较了Spring结构直接引证外部库并装备,以及制造为starter后运用Spring Boot引证这两种情形的区别,知道Spring Boot的主动装备机制帮咱们简化了哪些步骤,以及starter是由什么组成的,怎么制造。

这些关于初学者来说或许有些难以理解,期望咱们能够仔细阅读完结本文的每一个部分,一步步地知道到主动装备机制处理了什么问题,以及其大致进程。

本文的参考文献:

  • Spring Boot主动装备机制概述:传送门
  • Spring Boot 2.x Starter制造指引:传送门
  • Spring Boot 3.x Starter制造指引:传送门

本文的代码库房地址:传送门