关注“Java后端技术全栈”**
回复“面试”获取全套大厂面试资料
在文章中经常会看到一个-starter-,比如:
spring-boot-starter-quartz
spring-boot-starter-web
spring-boot-starter-jdbc
spring-boot-starter-data-jpa
...
很多人可能会觉得这种starter方式很牛B,添加一个starter就搞定了很多事情。今天咱们也来搞一个自己的starter。
starter的原理
先来说说starter的原理,我们知道使用一个公用的starter的时候,只需要将相应的依赖添加的Maven的配置文件当中即可,免去了自己需要引用很多依赖类,并且SpringBoot会自动进行类的自动配置。那么 SpringBoot 是如何知道要实例化哪些类,并进行自动配置的呢?下面简单说一下。
第一步,SpringBoot 在启动时会去依赖的starter包中寻找
resources/META-INF/spring.factories
文件,然后根据文件中配置的Jar包去扫描项目所依赖的Jar包,这类似于 Java 的 SPI 机制(后面会专门写一篇关于java 的SPI机制)。
第二步,根据 spring.factories
配置加载AutoConfigure
类。
最后,根据 @Conditional
注解的条件,进行自动配置并将Bean注入Spring Context 上下文当中。
我们也可以使用@ImportAutoConfiguration({MyAutoConfiguration.class})
指定自动配置哪些类。
starter的机制
SpringBoot中的starter是一种非常重要的机制,能够抛弃以前繁杂的配置,将其统一集成进starter,应用者只需要在maven中引入starter依赖,SpringBoot就能自动扫描到要加载的信息并启动相应的默认配置。starter让我们摆脱了各种依赖库的处理,需要配置各种信息的困扰。SpringBoot会自动通过classpath路径下的类发现需要的Bean,并注册进IOC容器。SpringBoot提供了针对日常企业应用研发各种场景的spring-boot-starter依赖模块。所有这些依赖模块都遵循着约定成俗的默认配置,并允许我们调整这些配置,也就是大家所说的“约定大于配置”。
starter的好处
在我们的日常开发工作中,经常会有一些独立于业务之外的配置模块,我们经常将其放到一个特定的包下,然后如果另一个工程需要复用这块功能的时候,需要将代码硬拷贝到另一个工程,重新集成一遍,麻烦至极。如果我们将这些可独立于业务代码之外的功配置模块封装成一个个starter,复用的时候只需要将其在pom中引用依赖即可,SpringBoot为我们完成自动装配,简直不要太爽。
实战自定义starter
创建一个项目名称为demo-spring-boot-starter
分别创建好对应目录。下面来说说几个类的内容。
pom.xml依赖
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.3.RELEASE
com.demo
demo-spring-boot-starter
1.0-SNAPSHOT
1.8
org.springframework.boot
spring-boot-configuration-processor
true
org.springframework.boot
spring-boot-autoconfigure
org.springframework.boot
spring-boot-starter
DemoProperties读取配置文件内容,前缀为demo
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "demo")
public class DemoProperties {
private String sayWhat;
private String toWho;
public String getSayWhat() {
return sayWhat;
}
public void setSayWhat(String sayWhat) {
this.sayWhat = sayWhat;
}
public String getToWho() {
return toWho;
}
public void setToWho(String toWho) {
this.toWho = toWho;
}
}
创建一个service类DemoService
public class DemoService {
public String sayWhat;
public String toWho;
public DemoService(String sayWhat, String toWho) {
this.sayWhat = sayWhat;
this.toWho = toWho;
}
public String say() {
return this.sayWhat + " " + toWho;
}
}
接下来的这个类是最关键的类
import com.demo.properties.DemoProperties;
import com.demo.service.DemoService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
@Configuration
@ConditionalOnClass(DemoService.class)
@EnableConfigurationProperties(DemoProperties.class)
public class DemoConfig {
@Resource
private DemoProperties demoProperties;
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "demo", value = "enabled", havingValue = "true")
public DemoService demoService() {
return new DemoService(demoProperties.getSayWhat(), demoProperties.getToWho());
}
}
解释一下代码中用到的几个注解:
@ConditionalOnClass
,当classpath
下发现该类的情况下进行自动配置。@ConditionalOnMissingBean
,当Spring Context
中不存在该Bean
时。@ConditionalOnProperty(prefix = "example.service",value = "enabled",havingValue = "true")
,当配置文件中example.service.enabled=true
时。
下面列举SpringBoot中的所有@Conditional注解及作用
@ConditionalOnBean:当容器中有指定的Bean的条件下
@ConditionalOnClass:当类路径下有指定的类的条件下
@ConditionalOnExpression:基于SpEL表达式作为判断条件
@ConditionalOnJava:基于JVM版本作为判断条件
@ConditionalOnJndi:在JNDI存在的条件下查找指定的位置
@ConditionalOnMissingBean:当容器中没有指定Bean的情况下
@ConditionalOnMissingClass:当类路径下没有指定的类的条件下
@ConditionalOnNotWebApplication:当前项目不是Web项目的条件下
@ConditionalOnProperty:指定的属性是否有指定的值
@ConditionalOnResource:类路径下是否有指定的资源
@ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者在有多个Bean的情况下,用来指定首选的Bean @ConditionalOnWebApplication:当前项目是Web项目的条件下
最后一步,在resources/META-INF/
下创建spring.factories
文件,并添加如下内容:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.demo.config.DemoConfig
至此,我们的一个Starter代码部分就是完成了,下面将项目mvn install安装到本地Maven仓库中。
测试自定义starter
在前面文章中的项目中添加咱们自定义的starter依赖
com.demo
demo-spring-boot-starter
1.0-SNAPSHOT
在application.properties中添加配置项
demo.isopen=true
demo.say-what=hello
demo.to-who=mystarter
定义一个DemoStarterController
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class DemoStarterController {
@Resource
private DemoService demoService;
@GetMapping("/test/starter")
public String sayWhat(){
return demoService.say();
}
}
启动项目。
请求:http:localhost:8080/test/starter
输出:hello mystarter
starter 的命名规范
a. spring 提供的 starter:
spring-boot-starter-XXX-x.y.z.jar
spring-boot-XXX-autoconfigure-x.y.z.jar
b. 第三方提供的 jar
XXX-spring-boot-starter-x.y.z.jar
XXX-spring-boot-autoconfigure-x.y.z.jar
ok,自定义starter就这么轻松的搞定了。码字不易,点个 在看 +分享 再走呗。感谢!!!
推荐阅读
一波骚操作,我把 SQL 执行效率提高了 10,000,000 倍!