项目里需要添加个监控mysql的功能 打算使用spring-boot-starter-jdbc 项目启动的时候就会去加载数据源 并不是所有服务器上都有mysql 而我又不想配置个默认的数据源 所有那种没有mysql的服务器 服务启动时就会报错 导致启动失败
经过分析是由于@EnableAutoConfiguration注解导致的
该注解一般配置在启动类上或SpringBootApplication上 在启动时候会去执行EnableAutoConfigurationImportSelector类的selectImports方法 该方法会返回一批要被spring管理的类名
这批类名获取流程
获取spring-boot-autoconfigure jar包里META-INF/spring.factories里面配置的类名列表 在获取enableAutoConfiguration里配置的拒绝类名列表 与spring.autoconfigure.exclude里配置的拒绝类名列表 来求类名列表和拒绝类名列表的差集
获取spring.factories的方法调用链
EnableAutoConfigurationImportSelector.selectImports-->getCandidateConfigurations-->SpringFactoriesLoader.loadFactoryNames
Enumeration urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
通过分析得知 只需要把org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration添加到决绝列表就可以禁止项目启动时候去启动mysql相关组件了 如数据源
添加到决绝列表可以通过下面的方式
1.配置@EnableAutoConfiguration里的exclude相关属性
2.可以在application相关属性文件里配置spring.autoconfigure.exclude属性
禁止mysql和redis启动
spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,RedisRepositoriesAutoConfiguration
但是上面的方法还是有点麻烦 如果需要禁止某个组件 还需要去分析spring.factories和类里面的定义 可以封装下提前分析好 在使用的时候只需要通过配置禁止启动的组件就好了
封装后的使用方式
@EnableDiscoveryClient
@ComponentScan(basePackages={"com.nailsoul"})
@EnableAutoConfiguration
@AutoConfigurationStrategy(disables = "mysql,redis")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
上面标示禁止启动redis和mysql
@EnableAutoConfiguration
@AutoConfigurationStrategy(disables = "mysql")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
app:
autoconfig:
disables: redis
enables: mysql
上面代表禁止启动redis
源码如下
package com.nailsoul.common.annotation;
import com.nailsoul.common.util.Validator;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.Ordered;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.context.support.StandardServletEnvironment;
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.util.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationStrategy.AutoConfigurationDisableStrategyImpl.class)
public @interface AutoConfigurationStrategy {
String disables() default "";
class AutoConfigurationDisableStrategyImpl implements EnvironmentAware, ImportSelector, Ordered {
private StandardServletEnvironment environment;
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
Set disableCache = getDisableConfigurations(annotationMetadata);
String disables = handleDisables(disableCache);
addApplicationProperties(disables);
return ArrayUtils.EMPTY_STRING_ARRAY;
}
private Set getDisableConfigurations(AnnotationMetadata annotationMetadata) {
String annotationName = AutoConfigurationStrategy.class.getName();
Map attributes = annotationMetadata.getAnnotationAttributes(annotationName, true);
StringBuilder disablesBuilder = new StringBuilder(((String) attributes.get("disables")).trim());
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment,"app.autoconfig.");
String tmp = resolver.getProperty("disables","").trim();
if(Validator.notEmpty(tmp)){
if(disablesBuilder.length()>0){
disablesBuilder.append(",");
}
disablesBuilder.append(tmp);
}
String enablesStr = resolver.getProperty("enables","").trim();
if(Validator.isBlank(disablesBuilder)){
return Collections.emptySet();
}
Set disables = tokenizeToSet(disablesBuilder.toString(), ",",
true, true);
if(Validator.notBlank(enablesStr)){
Set enables = tokenizeToSet(enablesStr, ",");
if(enables != null){
disables.removeAll(enables);
}
}
return disables;
}
private String handleDisables(Set disables) {
Map componentMapping = new HashMap<>();
componentMapping.put("mysql",new String[]{
"org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration"
});
componentMapping.put("redis",new String[]{
"org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration",
"org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration"
});
StringBuilder builder = new StringBuilder();
String[] array;
for (String disable : disables) {
array = componentMapping.get(disable);
if(Validator.isEmpty(array)){
continue;
}
for (String str : array) {
builder.append(str).append(",");
}
}
if(builder.length() > 0){
builder.deleteCharAt(builder.length()-1);
}
return builder.toString();
}
public Set tokenizeToSet(String str, String delimiters) {
return tokenizeToSet(str, delimiters, true, true);
}
private Set tokenizeToSet(String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {
if (Validator.isEmpty(str)) {
return Collections.emptySet();
}
StringTokenizer st = new StringTokenizer(str, delimiters);
Set tokens = new LinkedHashSet();
while(true) {
String token;
do {
if (!st.hasMoreTokens()) {
return tokens;
}
token = st.nextToken();
if (trimTokens) {
token = token.trim();
}
} while(ignoreEmptyTokens && token.length() <= 0);
tokens.add(token);
}
}
private void addApplicationProperties(String disables) {
if(Validator.isBlank(disables)){
return;
}
MutablePropertySources propertySources = environment.getPropertySources();
PropertySource> applicationPropertySources = propertySources.get("applicationConfigurationProperties");
Collection> applicationPropertiesCache = (Collection) applicationPropertySources.getSource();
PropertySource> applicationProperties = (PropertySource>) applicationPropertiesCache.iterator().next();
Set target = (Set) applicationProperties.getSource();
PropertySource
如果使用的spring boot 2.0以上版本请修改addApplicationProperties方法
private void addApplicationProperties(String disables) {
if(Validator.isBlank(disables)){
return;
}
//spring boot 2.0 MapPropertySource {name='springCloudDefaultProperties'}
MutablePropertySources propertySources = environment.getPropertySources();
PropertySource> applicationPropertySources = propertySources.get("springCloudDefaultProperties");
Map map = (Map) applicationPropertySources.getSource();
map.put("spring.autoconfigure.exclude",disables);
}