自定义一个starter一般需要经过以下几个过程
步骤
完整项目会以免积分附件形式发布,有兴趣的可以down下来跟着试一下
或使用码云地址:https://gitee.com/master336/demo-spring-boot-starter
前提
SpringBoot项目开启自动配置 @EnableAutoConfiguration(默认开启了,参见@SpringBootApplication)
1. 创建项目
这一步就一个关注点,项目名建议**-spring-boot-starter,据说是springboot官方建议的命名方式,有只要此建议地址的朋友可以留言以方便补充。这里给两个常用starter命名示例
2. maven依赖倒入
首先再次提一下命名问题,再看一眼上一步。
引入依赖,核心依赖就一个,目的是为了使用其注解
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
</dependencies>
3. 定义自动装配属性对应的配置类(非必须)
如果有允许用户自定义配置的地方,可以借助Configurationproperties注解轻松将配置转换为Java对象,方便注入使用。
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "demo")
public class DemoProperties {
private String name = "default name";
private Integer age = 18;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
简单说一下ConfigurationProperties这个注解,这个用于声明该类为配置类,prefix声明配置项的开头,如上代码中,配置装载的是以demo开头的配置,yml(或peopreties)文件配置如下:
demo:
name: 张三
age: 22
注意: Springboot本着"约定大于配置"原则,请合理设置默认值。
4. 定义需要交由Spring管理的Bean类及其具体实现方法
通过定义已上一步配置类为入参的构造方法获取配置类信息,用以获取可由用户自定义的一些属性值。
import cn.com.demo.autoconfigure.properties.DemoProperties;
public class DemoService {
private DemoProperties demoProperties;
public DemoService(DemoProperties demoProperties) {
this.demoProperties = demoProperties;
}
public void printProperties(){
if ( demoProperties == null){
System.out.println("null propertoes");
}else {
System.out.println("name: "+demoProperties.getName() +"\t age:"+demoProperties.getAge());
}
}
}
看到这个类可能存在疑问:
- 这个类不是要注册成Bean交由Spring管理吗?为什么没有定义声明@Component@Bean等之类的标签标注一下?
其实这个问题是这样的,Springboot中声明Bean除了要使用Spring中符合要求的注解外,还需要定义@Configuration(不然会因为没在扫描路径(ComponentScan)下导致无法被Spring管理,为什么不会一会儿再第6步里说明),用于告诉SpringBoot你这是个Bean配置管理类,这样一来就成了下一步要干的事了,这里把Bean的声明和实现分开,更清晰的管理自己的Bean。
另外:值得注意的是@Configuration 标注的也是一个Bean~,只是有特殊含义,其在某种情况下也是可以被其他方案代替的(参见@Configuration)。
5. 定义Configuration类完成Bean声明
起始就是完成Bean的new过程,即告诉Spring这个Bean是怎么来的。
import cn.com.demo.autoconfigure.properties.DemoProperties;
import cn.com.demo.autoconfigure.service.DemoService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(DemoProperties.class)
public class DemoAutoConfigure {
/**属性自动从Springboot配置文件装配,DemoProperties里声明前缀*/
private DemoProperties demoProperties;
/* 如果无需(或不允许)用户修改配置,这里可以使用默认构造(简单来说就是别写任何构造方法了)
public DemoAutoConfigure(){
System.out.println("init no patam ");
}*/
public DemoAutoConfigure(DemoProperties demoProperties) {
this.demoProperties = demoProperties;
}
/**
* 自定义Bean以供外部使用
* @return
*/
@Bean
@ConditionalOnMissingBean
public DemoService demoService(){
return new DemoService(demoProperties);
}
}
@Bean用户告诉容器这是一个Bean,方法名为BeanName(可以通过name指定)
@ConditionalOnMissingBean 如果容器中没有这个Bean,会通过此定义创建改Bean,即如果不需要再次封装,可以使用这个Bean进行操作(相当于默认处理Bean,类似druid里定义datasource这个Bean一样,可参见:com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure)
6. 编写META-INF/spring.factories文件定义自动配置类
即告诉Spring自动装配的类路径,内容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.com.demo.autoconfigure.DemoAutoConfigure
这里有个疑问:为什么已经配置了@Configuration还要整个spring.factories文件呢?
这个问题在于@Configuration会不会生效的问题,如果@Configuration所在类在你的项目扫描路径下,就不需要配置,如果不在,那就需要配置(@Configuration)。如果你作为一个组件发布,你无法保证ComponentScan能扫描到你的组件,所以就有了配置这个的方法。
spring.factories文件的加载在:
new SpringApplication()
–> setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
–>private Collection getSpringFactoriesInstances(Class type, Class<?>[] parameterTypes, Object… args)
–>Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
–>private static Map<String, List> loadSpringFactories(@Nullable ClassLoader classLoader)
–>private static Map<String, List> loadSpringFactories(@Nullable ClassLoader classLoader)
spring.factories中配置生效(Bean被创建)是在:
SpringApplication.refreshContext(context),过程比较复杂,感兴趣可以深入跟踪一下。
以上为执行过程,对照代码可能更容易懂~
7. 打包发布
如果本地测试的话:
mvn clean compile install
即可被本地其他项目以来,如果需要发布仓库被别人共用的话请使用deploy
8. 需要的项目上引入依赖即使用
<dependency>
<groupId>cn.com.demo</groupId>
<artifactId>demo-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
作为一个Bean正常注入即可使用
// 需要用哪个注入那个即可
@Resource
DemoService demoService;
// 一般咱们不用这个
@Resource
DemoAutoConfigure demoAutoConfigure;