视频讲解地址 https://www.bilibili.com/video/BV18b4y147YV/
之所以想要学习这个starter并不是对技术的热爱,而是来自一次领导的鞭策,不过通过这次的学习倒是收获良多。
一、理论 1、什么是starter
我们在进行Java开发的时候要引入各种依赖,比如JDBC、MySQL、Redis、MyBatis 等等,但各种依赖包本身又各自依赖了自己的依赖,在很久以前没有各种starter的时候,我们要频繁的解决各种依赖包冲突的问题,这是件很头疼的事。
为了解决这种依赖冲突问题,就出现了各种 spring-boot-starter-xxxx 、xxxx-spring-boot-starter 我们只需要导入对应的starter,并且版本和SpringBoot版本一致就不会出现依赖冲突的问题。
我们去看各种starter的pom文件就会发现,其实所谓的各种starter也是引入了最基本的依赖(比如 mybatis-plus-boot-starter 里面就引入了 mybatis-plus)然后再做一些基本的配置注入就完成了(这个自动配置就是starter的核心)。
注:spring-boot-starter-xxxx 这是官方提供的命名规则,xxxx-spring-boot-starter这是非官方提供的。
2、项目命名带starter和不带的区别
我在学习starter的时候有一个问题,比如我在pom里面引入了下面两个依赖
它们俩有啥区别呢?反正我们在打包的时候都是 mvn install ,当然我们不能说它们的功能不一样,这毫无意义。
什么时候取名需要带上这个starter呢,首先这个取名你随意,其次我觉得如果你是要和整合SpringBoot的时候可以带上这个starter,并且你整合的SpringBoot版本也要和你定义的版本一致。
二、实现一个自己的Starter
我在很早之前就背了一个面试题,并在很多次面试中也被问到了,下面是问题与我的答案(答案不一定对)
Spring Boot 自动配置原理是什么?
注解 @EnableAutoConfiguration, @Configuration, @ConditionalOnClass 就是自动配置的核心,首先它得是一个配置文件,其次根据类路径下是否有这个类去自动配置。
虽然我很早之前就会背了,但一直不得其中意,下面跟着我来实现一个自己的starter你就会理解:什么是自动配置,以及它的原理是什么
我们的代码分为两部分
sayhello-spring-boot-starter 我们今天的主角,自己的starterxdx-test 一个测试starter的项目
1、sayhello-spring-boot-starter 1-1、pom.xml
要看一个项目的技术,最快捷的就是去看看pom里面引入了哪些依赖
<?xml version="1.0" encoding="UTF-8"?>
1-2、pojo
这里就是简单的实体,它会从配置文件里面读取相应的配置信息,为了代码的简洁就只复制一个
package com.xdx97.sayhello.pojo;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;@Component@ConfigurationProperties(prefix = "sayhello.chinese")public class Chinese { private String say; private String city; public String getSay() { return say; } public void setSay(String say) { this.say = say; } public String getCity() { return city; } public void setCity(String city) { this.city = city; }}
1-4、service
这就是我们对外提供的“实用”方法了,可以看到我并没有使用@Service注解,这个和starter没有关系,只是我单纯的想要控制什么时候注入这个bean,后面会详细说明。
package com.xdx97.sayhello.service;import com.xdx97.sayhello.pojo.Chinese;import org.springframework.beans.factory.annotation.Autowired;public class ChineseService { @Autowired private Chinese chinese; public String show() { return "我说 " + chinese.getSay() + "我来自 " + chinese.getCity(); }}
1-5、spring-configuration-metadata.json
平时我们在yml文件里面写配置的时候会有提示,有时候我们没有配置,也会有默认值,全都来自这个配置文件
{ "properties": [ { "name": "sayhello.chinese.chineseService", "type": "java.lang.String", "description": "desc", "defaultValue": "false" }, { "name": "sayhello.chinese.say", "type": "java.lang.String", "description": "desc", "defaultValue": "汉语" }, { "name": "sayhello.chinese.city", "type": "java.lang.String", "description": "desc", "defaultValue": "中国" }, { "name": "sayhello.english.englishService", "type": "java.lang.String", "description": "desc", "defaultValue": "false" }, { "name": "sayhello.english.speak", "type": "java.lang.String", "description": "desc", "defaultValue": "english" }, { "name": "sayhello.english.eat", "type": "java.lang.String", "description": "desc", "defaultValue": "eat" } ]}
1-6、spring.factories
这个就是自动配置的核心配置文件,告诉我们的系统我们需要自动配置的文件所在位置
org.springframework.boot.autoconfigure.EnableAutoConfiguration= com.xdx97.sayhello.AutoConfigurationSayHello
1-7、AutoConfigurationSayHello
这个就是我们最终要自动配置的bean了
package com.xdx97.sayhello;import com.xdx97.sayhello.service.ChineseService;import com.xdx97.sayhello.service.EnglishService;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;// 表示我们这个是一个javaconfig配置@Configurationpublic class AutoConfigurationSayHello { @Bean // 去配置文件里面读取sayhello.chinese.chineseService 的值,如果等于 havingValue 就注入 @ConditionalOnProperty(prefix = "sayhello.chinese", name = "chineseService", havingValue = "true") // 它会保证你的bean只有一个,即你的实例只有一个 @ConditionalOnMissingBean(ChineseService.class) public ChineseService chineseService(){ return new ChineseService(); } @Bean @ConditionalOnProperty(prefix = "sayhello.english", name = "englishService", havingValue = "true") @ConditionalOnMissingBean(EnglishService.class) public EnglishService englishService(){ return new EnglishService(); }}
注1:这里我想让使用这个starter的人手动控制这个bean是否需要初始化,所以没有使用注解@ConditionalOnClass而是使用ConditionalOnProperty
注2:在前面我们说到我们会自动配置的核心是三个注解,现在还少了一个@EnableAutoConfiguration 那是因为我们的这个starter并不是在当前项目运行,而是在别的项目中运行,而我们的SpringBoot项目都有一个启用注解@SpringBootApplication这个里面就包含了上述的这个。
1-8、打包
这里我只是在本地使用这个starter所以我使用 mvn install 把它安装到本地仓库,如果你需要使用远程仓库,可以先配置maven远程仓库地址,然后使用 mvn deploy
这就是一个普通的项目,只有一个要求,它的maven仓库和上面这个starter是一个即可。
2-1、pom因为在starter里面已经引入了springboot,所以我这里可以不用引入直接使用
<?xml version="1.0" encoding="UTF-8"?>
2-2、controller
这就是一个简单的controller
package com.xdx97.controller;import com.xdx97.sayhello.service.ChineseService;import com.xdx97.sayhello.service.EnglishService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class TestController { @Autowired private ChineseService chineseService; @Autowired private EnglishService englishService; @GetMapping("/funTestOne") public String funTestOne(){ return chineseService.show(); } @GetMapping("/funTestTwo") public String funTestTwo(){ return englishService.show(); }}
2-3、application.yml
这个配置文件,算是我们的一个核心了,我们通过它来控制我们的实体bean是否注入,同时也用它来设置属性值
server: port: 8888sayhello: chinese: # 只有为true的时候才注入这个bean chineseService: true say: 我是小道仙 english: englishService: true
可以看到,是有提示的
3、其它
项目启动就可以运行了,结果就不展示了,很简单。
3-1、这里因为我想使用配置文件来控制bean的注入,所以我没有在 ChineseService.class 上面使用 @Service 注解,如果使用了,那么我们的配置文件就不生效了,就一定可以注入了。
这个思路来源于我老大写的一个starter,只不过他那个的bean是openfeign,倒不是使用的new,而是基于jdk代理。
3-2、
starter的核心其实就是自动注入,也就是几个配置文件和注解,重要的是我们要控制bean在什么调节下进行注入,相对应的就有很多相关注解了
太多了也记不住,只要记住是在xx调节下进行bean的注入,用到的时候再去搜索一下
三、简单看一下spring-boot-starter的自动注入
只要我们的代码里面引入了spring-boot-starter就可以看到如下的依赖
我们可以去spring.factories 里面找到我们想要了解的starter在自动配置的时候做了什么操作
四、源码获取
如果对你有帮助,可以关注我的微信公众号一起成长:小道仙97
回复关键字获取源码:xdx-spring-boot-starter