禁止spring-boot-starter-xxx组件启动

项目里需要添加个监控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> mapSource = (PropertySource>) target.iterator().next();
            Map properties = mapSource.getSource();

            Field field = ReflectionUtils.findField(applicationPropertySources.getClass(), "names");
            field.setAccessible(true);
            String[] propertyNames = (String[]) ReflectionUtils.getField(field, applicationPropertySources);
            propertyNames = Arrays.copyOf(propertyNames, propertyNames.length + 1);
            propertyNames[propertyNames.length-1] = "spring.autoconfigure.exclude";
            ReflectionUtils.setField(field,applicationPropertySources,propertyNames);

            properties.put("spring.autoconfigure.exclude", disables);
        }

        @Override
        public int getOrder() {
            return Ordered.HIGHEST_PRECEDENCE - 2;
        }

        @Override
        public void setEnvironment(Environment environment) {
            this.environment = (StandardServletEnvironment) environment;
        }

    }
}

如果使用的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);
}

 

你可能感兴趣的:(禁止spring-boot-starter-xxx组件启动)