目录
方式一:使用spring.factories文件,通过配置enable实现开关
https://github.com/HandsomeMars/springboot-autoconfig-demo
一 前言
1、今天想写一个自动配置的模块,参考了一系列文章,感觉非常失望。
问题如下:
1、混淆了自动配置注解:@EanbleXXX注解 加与不加都会生效配置 其实现关键是自动配置上通过其他条件加载完成
2、无效代码:太多的模仿代码,让人云里雾里;以及不完整测试,只测开启不测关闭
2、自己看了springboot官方文档,写了两种形式的自动配置
环境 | 版本 |
springboot | 2.0.3.release |
maven | 3.5.2 |
jdk | 1.8 |
二 实现原理
(1)springboot启动类注解解析
springboot启动类中@SpringBootApplication
包含:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
其中@EnableAutoConfiguration
包含:
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
其中
AutoConfigurationImportSelector
实现了从所有包下的 META-INF/spring.factories
读取自动配置类信息,通过类加载初始化所描述的bean
由上即可知自动配置的最简单的实现方式
1、新建META-INFO/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.zyj.config.Custom2AutoConfig #需要被初始化的类
2、查询api可知,等效写法:实现如下接口,通过@Import等方式初始化
org.springframework.context.annotation.ImportSelector
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//逻辑判断 返回对应的配置类包全路径
if(annotationMetadata.getAnnotationTypes().contains(EnableCustomAutoConfig.class.getName())){
System.out.println("没错你是最棒的");
return new String[]{CustomAutoConfig.class.getName()};
}else{
return new String[0];
}
}
(2)参考
三 实现
方式一:使用spring.factories文件,通过配置enable实现开关
1、目录结构
─src
├─main
│ ├─java
│ │ └─com
│ │ └─zyj
│ │ │ SpringbootAutoconfigByfileApplication.java ##可忽略
│ │ │
│ │ └─config
│ │ Custom2AutoConfig.java ##关键配置类
│ │ Custom2Properties.java ##参数配置类
│ │ Custom2Server.java ##模拟服务(可忽略)
│ │
│ └─resources
│ │ application.properties ##可忽略
│ │
│ └─META-INF
│ spring.factories ##关键配置文件,指定初始化关键配置类
│
└─test
└─java
└─com
└─zyj
SpringbootAutoconfigByfileApplicationTests.java ##可忽略
2、关键代码
pom依赖
<!--关键依赖 @ConfigurationProperties-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!--更加自动配置框架而定-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Custom2Properties.java
package com.zyj.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author by zyj
* @version V1.0
* @Description:
* @Date 2019/8/5 14:35
*/
/**解析spring配置文件中custom2开头的配置信息*/
@ConfigurationProperties(prefix="custom2")
public class Custom2Properties {
private boolean enable;
private String test;
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
@Override
public String toString() {
StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append("[");
stringBuilder.append("enable:"+enable);
stringBuilder.append(",");
stringBuilder.append("test:"+test);
stringBuilder.append("]");
return stringBuilder.toString();
}
}
Custom2AutoConfig.java
package com.zyj.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
/**
* @author by zyj
* @version V1.0
* @Description:
* @Date 2019/8/5 11:29
*/
/**类似@Import 加载属性文件(相当于属性文件@Component)*/
/**与下面@Autowired配合*/
@EnableConfigurationProperties(Custom2Properties.class)
public class Custom2AutoConfig {
@Autowired
Custom2Properties custom2Properties;
@Bean
/**不存在实例才加载,避免重复*/
@ConditionalOnMissingBean(Custom2Server.class)
/**classpath出现Custom2Server才加载*/
@ConditionalOnClass(Custom2Server.class)
/**配置enable true 默认true才加载*/
@ConditionalOnProperty(prefix="custom2", value="enable", matchIfMissing = true)
public Custom2Server custom2Server(){
Custom2Server testServer=new Custom2Server(custom2Properties);
return testServer;
}
}
Custom2Server.java
package com.zyj.config;
/**
* @author by zyj
* @version V1.0
* @Description:
* @Date 2019/8/5 14:11
*/
public class Custom2Server {
/**
* 初始化
* @param custom2Properties
*/
public Custom2Server(Custom2Properties custom2Properties) {
System.out.println(this.getClass().getName()+"初始化了:打印配置"+ custom2Properties.toString());
}
}
spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.zyj.config.Custom2AutoConfig
3、测试
step1:pom引入自动配置的包
<dependency>
<groupId>com.zyj</groupId>
<artifactId>springboot-autoconfig-byfile</artifactId>
</dependency>
step2:配置application.properties
server.port=8099
#次自动配置方式通过enable控制开关 不写设置为true
custom2.test=测试文件方式自动配置
custom2.enable=true
step3:启动
step4:取消自动配置
server.port=8099
#次自动配置方式通过enable控制开关 不写设置为true
custom2.test=test-auto-by-file
custom2.enable=false
方式二:使用注解@EnableXXX实现开关
1、目录结构
src
├─main
│ ├─java
│ │ └─com
│ │ └─zyj
│ │ │ SpringbootAutoconfigByAnnoApplication.java ##可忽略
│ │ │
│ │ ├─auto
│ │ │ CustomAutoConfigSelect.java ##自定义配置类过滤 等价spring.factories
│ │ │ EnableCustomAutoConfig.java ##自定义配置注解
│ │ │
│ │ └─config
│ │ CustomAutoConfig.java ##自定配置类
│ │ CustomProperties.java ##配置属性(可忽略)
│ │ CustomServer.java ##配置服务(可忽略)
│ │
│ └─resources
│ application.properties ##可忽略
│
└─test
└─java
└─com
└─zyj
SpringbootAutoconfigApplicationTests.java ##可忽略
2、关键代码
pom依赖
<!--关键依赖 @ConfigurationProperties-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!--更加自动配置框架而定-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
EnableCustomAutoConfig.java
package com.zyj.auto;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
/**
* @author by zyj
* @version V1.0
* @Description:
* @Date 2019/8/5 11:26
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
/**如果使用注解则引入CustomAutoConfigSelect*/
@Import(CustomAutoConfigSelect.class)
public @interface EnableCustomAutoConfig {
}
CustomAutoConfigSelect.java
package com.zyj.auto;
import com.zyj.config.CustomAutoConfig;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
/**
* @author by zyj
* @version V1.0
* @Description:
* @Date 2019/8/5 11:28
*/
public class CustomAutoConfigSelect implements ImportSelector{
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
/** 打印springboot使用的注解annotationMetadata.getAnnotationTypes().forEach(System.out::println);*/
if(annotationMetadata.getAnnotationTypes().contains(EnableCustomAutoConfig.class.getName())){
//配置自动配置需要加载的类
return new String[]{CustomAutoConfig.class.getName()};
}else{
//没有自动配置则不加载
return new String[0];
}
}
}
CustomProperties.java
package com.zyj.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author by zyj
* @version V1.0
* @Description:
* @Date 2019/8/5 14:35
*/
/**解析spring配置文件中custom开头的配置信息*/
@ConfigurationProperties(prefix="custom")
public class CustomProperties {
private boolean enable;
private String test;
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
@Override
public String toString() {
StringBuilder stringBuilder=new StringBuilder();
stringBuilder.append("[");
stringBuilder.append("enable:"+enable);
stringBuilder.append(",");
stringBuilder.append("test:"+test);
stringBuilder.append("]");
return stringBuilder.toString();
}
}
CustomAutoConfig.java
package com.zyj.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
/**
* @author by zyj
* @version V1.0
* @Description:
* @Date 2019/8/5 11:29
*/
/**类似@Import 加载属性文件(相当于属性文件@Component)*/
/**与下面@Autowired配合*/
@EnableConfigurationProperties(CustomProperties.class)
public class CustomAutoConfig {
@Autowired
CustomProperties customProperties;
@Bean
/**不存在实例才加载,避免重复*/
@ConditionalOnMissingBean(CustomServer.class)
/**classpath出现CustomServer才加载*/
@ConditionalOnClass(CustomServer.class)
/**配置enable true 默认true才加载*/
@ConditionalOnProperty(prefix="custom", value="enable", matchIfMissing = true)
public CustomServer customServer(){
CustomServer testServer=new CustomServer(customProperties);
return testServer;
}
}
CustomServer.java
package com.zyj.config;
/**
* @author by zyj
* @version V1.0
* @Description:
* @Date 2019/8/5 14:11
*/
public class CustomServer {
/**
* 初始化
* @param customProperties
*/
public CustomServer(CustomProperties customProperties) {
System.out.println(this.getClass().getName()+"初始化了:打印配置"+ customProperties.toString());
}
}
3、测试
step1:pom引入自动配置的包
<dependency>
<groupId>com.zyj</groupId>
<artifactId>springboot-autoconfig-byanno</artifactId>
</dependency>
step2:springboot加注解 (配置application.properties(可忽略) )
@SpringBootApplication
@EnableCustomAutoConfig/**添加此注解实现自动配置*/
public class SpringbootAutoconfigTestApplication {
//省略
}
server.port=8099
#次自动配置方式通过enable注解即可,enable为再扩展
custom.test=test-auto-by-anno
custom.enable=true
step3:启动
step4:关闭自动配置
去除注解即可
不去注解设置false
server.port=8099
#次自动配置方式通过enable注解即可,enable为再扩展
custom.test=test-auto-by-anno
custom.enable=false