Back to Javatutorial

Spring中的Environment环境变量

docs/Spring全家桶/Spring/Spring中的Environment环境变量.md

1.0.034.9 KB
Original Source

ڰøٵĴ롢дϵͳ Spring Boot ȻΪ Java Ӧÿʵ׼ Spring Boot ṩڶУԶǶһԣSpring Boot һΪԱԶɿ伴á߱ijһܵ Bean£Զõ Bean պҵ󣬵ijЩ£òظǣʱֻҪ͵ Bean ɣΪԶõ Bean @ConditionalOnMissingBeanעΡ˵ǣֻ΢һЩϸڣĸĶ˿ں (server.port) Դ URL (spring.datasource.url) ѹûҪServerProperties``DataSourceProperties Bean Զõ Bean Spring Boot ΪԶõ Bean ṩ1000΢ԣҪʱֻҪڻвļ (application.properties/application.yml) нָɣ Spring Boot Externalized Configuration (⻯) ԡ

ȻⲿԴڻвļ֣ȤĶ߿Ķ Spring Boot ٷĵ Spring УBeanFactory Bean ĽɫEnvironmentͬλΪһⲿԴеԶᱻӵ Environment С**΢Ľ죬ⲿԴ_Disconf__Apollo_ Nacos ȷֲʽģ Spring ĵ̣ҪףжȡȻᱻ׷ӵ Environment **

֮дƪ£jasyptһνӴ2018꣬ʱͺܺʵֶԼӽܵģҪʵôһҪϤ Bean ڡIoC չ (IoC Container Extension Points) Spring Boot ̵֪ʶҪ Environment

jasypt ʮּ򵥡ͨjasypt-maven-pluginһ maven ΪֵģȻENC()滻ֵɡ£

jasypt.encryptor.password=crimson_typhoon

spring.datasource.url=jdbc:mysql://HOST:PORT/db_sql_boy?characterEncoding=UTF-8
spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.hikari.username=root
spring.datasource.hikari.password=ENC(qS8+DEIlHxvhPHgn1VaW3oHkn2twrmwNOHewWLIfquAXiCDBrKwvIhDoqalKyhIF)
ƴ

1 ʶ Environmnent

ʵʹУ Environment 򽻵ĻᲢࣻҵ Bean ȷʵҪȡⲿԴеijһֵֶ Environment ע뵽ҵ Bean УҲֱʵEnvironmentAwareӿڣõ Environment ͵ Bean ʵ֮ͨgetProperty()ȡֵ_Environment_ ӿʾ

public interface Environment extends PropertyResolver {
    String[] getActiveProfiles();
    String[] getDefaultProfiles();
    boolean acceptsProfiles(Profiles profiles);
}

public interface PropertyResolver {
    boolean containsProperty(String key);
    String getProperty(String key);
    String getProperty(String key, String defaultValue);
    <T> T getProperty(String key, Class<T> targetType);
    <T> T getProperty(String key, Class<T> targetType, T defaultValue);
    String resolvePlaceholders(String text);
}
ƴ

ҲҪ Environment getProperty() 󵼣ⲿԴеԲԵΪάȱӵ Environment еģPropertySourceΪάPropertySource ǶԴƺ͸ԴһԵijMapPropertySourceһ򵥵ʵ֣ͨ Map<String, Object> صԡ_PropertySource_ £

public abstract class PropertySource<T> {
    protected final String name;
    protected final T source;

    public PropertySource(String name, T source) {
        this.name = name;
        this.source = source;
    }

    public String getName() { return this.name; }
    public T getSource() { return this.source; }
    public abstract Object getProperty(String name);
}
ƴ

PropertySource PropertySource Ǿ߱ȡֵһġ

getProperty()ڲִ߼

һ㣬_Environment_ ʵлһPropertyResolver͵ijԱ PropertyResolver ִ getProperty() ߼_PropertyResolver_ ʵֻԱֱǣConversionService``PropertySourcesȣ_PropertyResolver_ PropertySources е _PropertySource_ȡԭֵȻί ConversionService ԭֵת (бҪĻ)Ȼ PropertySource Ǿ߱ȡֵһģ߱ռλתм߱ PropertyResolver Ҳӡ֤һӣڼѧУûʲôмһ˵ģУǾټһ

PropertySourceڲ߼

Environment ʵг˳PropertyResolver͵ijԱ⣬һMutablePropertySources͵ijԱṩֱӲ MutablePropertySources ķֻͨgetPropertySources()ȡ MutablePropertySources ʵȻ MutablePropertySources еaddFirst()``addLast()``replace()ȷȥ PropertySource__MutablePropertySources PropertySources Ψһһʵ࣬ͼʾ

ܵ˵Environment Ƕ PropertySource Profile Ķ Profile ĸӦóҪ𵽲ͬлʱһЩͨͬ磬Դ URL ڿͲԻͻ᲻һSpring 3.1汾ʼֻ֧ Profile á

Profile in Spring 3.1

Spring 3.1汾ʱSpring Boot δ˵ʱ Profile ԻЩģ覲褡Ҫڣͬһ͵ BeanΡһС覴ã

@Configuration(proxyBeanMethods = false)
public class DataSourceConfig {
    @Bean
    @Profile("dev")
    public DataSource devDataSource () {
        return DataSourceBuilder.create()
                .driverClassName("com.mysql.jdbc.Driver")
                .url("jdbc:mysql://DEV_HOST:PORT/db_sql_boy?characterEncoding=UTF-8")
                .username("dev")
                .password("dev")
                .build();
    }

    @Bean
    @Profile("test")
    public DataSource testDataSource () {
        return DataSourceBuilder.create()
                .driverClassName("com.mysql.jdbc.Driver")
                .url("jdbc:mysql://TEST_HOST:PORT/db_sql_boy?characterEncoding=UTF-8")
                .username("test")
                .password("test")
                .build();
    }
}
ƴ

Profile in Spring Boot

Spring Boot @Profileעӵˡٷп϶Ҳʶ Profile in Spring 3.1 覴ã Spring Boot ĵһ汾 (1.0.0.RELEASE) оȲ֧Ϊ application.properties application.yml Profile ˡζһţ

@Configuration(proxyBeanMethods = false)
public class DataSourceConfig {
    @Bean
    public DataSource devDataSource (DataSourceProperties dataSourceProperties) {
        return DataSourceBuilder.create()
                .driverClassName(dataSourceProperties.getDriverClassName())
                .url(dataSourceProperties.getUrl())
                .username(dataSourceProperties.getUsername())
                .password(dataSourceProperties.getPassword())
                .build();
    }
}
ƴ

application-dev.properties £

spring.datasource.url=jdbc:mysql://DEV_HOST:PORT/db_sql_boy?characterEncoding=UTF-8
spring.datasource.hikari.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.hikari.password=dev
spring.datasource.hikari.username=dev
ƴ

application-test.properties £

spring.datasource.url=jdbc:mysql://TEST_HOST:PORT/db_sql_boy?characterEncoding=UTF-8
spring.datasource.hikari.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.hikari.password=test
spring.datasource.hikari.username=test
ƴ

ԭ Spring 3.1 Spring Boot Уͨspring.profiles.activeΪ Environment ָ Profile__Environment Ĭϼ Profile ΪdefaultдԺһ⣺һ㣬@Profile עҪ @Configuration ע @Bean עʹã spring.profiles.active ֵΪ dev ʱôЩ @Configuration @Bean ע (û@ProfileעӰ) Bean ᱻΪBeanDefinitionʵ𣿴ǻġConfigurationClassPostProcessor @Configuration Ϊ _BeanDefinition_ڴ˹лִConditionEvaluator``shouldSkip()Ҫ£

public class ConditionEvaluator {
    public boolean shouldSkip(AnnotatedTypeMetadata metadata, ConfigurationCondition.ConfigurationPhase phase) {
        if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
            return false;
        }

        if (phase == null) {
            if (metadata instanceof AnnotationMetadata &&
                    ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
                return shouldSkip(metadata, ConfigurationCondition.ConfigurationPhase.PARSE_CONFIGURATION);
            }
            return shouldSkip(metadata, ConfigurationCondition.ConfigurationPhase.REGISTER_BEAN);
        }

        List<Condition> conditions = new ArrayList<>();
        for (String[] conditionClasses : getConditionClasses(metadata)) {
            for (String conditionClass : conditionClasses) {
                Condition condition = getCondition(conditionClass, this.context.getClassLoader());
                conditions.add(condition);
            }
        }

        AnnotationAwareOrderComparator.sort(conditions);

        for (Condition condition : conditions) {
            ConfigurationCondition.ConfigurationPhase requiredPhase = null;
            if (condition instanceof ConfigurationCondition) {
                requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
            }
            if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
                return true;
            }
        }

        return false;
    }
}
ƴ

shouldSkip()һ if Ǵ𰸣@Profileע@Conditional(ProfileCondition.class)ΣһͷûConditionӰֱӷfalseˣǾDz˼ඣ

Environment еЩ PropertySource ɶðȻΪ Bean ඣϻ˵ͼ

ǰ visio processOn ͼһ draw.ioû뵽㣬ǿҰһ

2 Environmnent ʼ

Ҫ Spring Boot Environmnt оעЩ _PropertySource_λSpringApplicationеrun(String... args)£

public class SpringApplication {
    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        DefaultBootstrapContext bootstrapContext = createBootstrapContext();
        ConfigurableApplicationContext context = null;
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting(bootstrapContext, this.mainApplicationClass);
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            configureIgnoreBeanInfo(environment);
            Banner printedBanner = printBanner(environment);
            context = createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
            prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            listeners.started(context);
            callRunners(context, applicationArguments);
        } catch (Throwable ex) {
            handleRunFailure(context, ex, listeners);
            throw new IllegalStateException(ex);
        }

        try {
            listeners.running(context);
        } catch (Throwable ex) {
            handleRunFailure(context, ex, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }
}
ƴ

Կ_Environmnt_ ijʼrefreshContext(context)֮ǰɵģǺʵġ_run()_ ܸӣ뱾ϵ߼ֻһ

prepareEnvironment(listeners, bootstrapContext, applicationArguments);
ƴ

ֱ߼

2.1 prepareEnvironment()

ȻݶprepareEnvironment()ڣСһ

public class SpringApplication {
    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
                                                       DefaultBootstrapContext bootstrapContext,
                                                       ApplicationArguments applicationArguments) {
        // 2.1.1
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        // 2.1.2
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        // 2.1.3
        ConfigurationPropertySources.attach(environment);
        // 2.1.4
        listeners.environmentPrepared(bootstrapContext, environment);
        DefaultPropertiesPropertySource.moveToEnd(environment);
        bindToSpringApplication(environment);
        ConfigurationPropertySources.attach(environment);
        return environment;
    }
}
ƴ

2.1.1 getOrCreateEnvironment()

getOrCreateEnvironment()Ҫ𹹽 Environment ʵǰӦǻͬI/Oģ͵ģ Environment ѡApplicationServletEnvironment෴أǰӦǻ첽I/Oģ͵ģ Environment ѡApplicationReactiveWebEnvironmentǹлǻ Spring MVC ӦãSpring MVC һServlet API֮ϡͬ I/O ģ͵ Java Web ܣ I/O ģζһ HTTP Ӧһ̣߳ÿһ HTTP ڸ߳ɴġ_ApplicationServletEnvironment_ ̳йϵͼʾ

ͼԿ ApplicationServletEnvironment ൱Ӵִ ApplicationServletEnvironment 췽ʱȻᴥ๹췽е߼Ϊ

public abstract class AbstractEnvironment implements ConfigurableEnvironment {
    public AbstractEnvironment() {
        this(new MutablePropertySources());
    }

    protected AbstractEnvironment(MutablePropertySources propertySources) {
        this.propertySources = propertySources;
        // createPropertyResolver(propertySources)
        // |___ ConfigurationPropertySources.createPropertyResolver(propertySources)
        //      |___ new ConfigurationPropertySourcesPropertyResolver(propertySources)
        this.propertyResolver = createPropertyResolver(propertySources);
        customizePropertySources(propertySources);
    }
}
ƴ
public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {
    @Override
    protected void customizePropertySources(MutablePropertySources propertySources) {
        propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
        propertySources.addLast(new StubPropertySource("servletContextInitParams"));
        super.customizePropertySources(propertySources);
    }
}
ƴ
public class StandardEnvironment extends AbstractEnvironment {
    @Override
    protected void customizePropertySources(MutablePropertySources propertySources) {
        propertySources.addLast(
                new PropertiesPropertySource("systemProperties", (Map) System.getProperties()));
        propertySources.addLast(
                new SystemEnvironmentPropertySource("systemEnvironment", (Map) System.getenv()));
    }
}
ƴ

ApplicationServletEnvironment 췽ִУʱ Environment MutablePropertySources ͵ijԱpropertySourcesѾ**** PropertySource ˣǣservletConfigInitParams``servletContextInitParams``systemProperties``systemEnvironment⣬ҲҪס ApplicationServletEnvironment еҪԱMutablePropertySources``ConfigurationPropertySourcesPropertyResolver

2.1.2 configureEnvironment()

configureEnvironment()е߼Ҳܼ򵥹ȣΪ Environment е PropertySourcesPropertyResolver 趨 _ConversionService_Ȼ Environment е MutablePropertySources ׷һΪcommandLineArgs PropertySource ʵעʹõaddFirst()ŶζΪcommandLineArgs PropertySource ȼߵġҪ߼£

public class SpringApplication {
    protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
        if (this.addConversionService) {
            environment.getPropertyResolver().setConversionService(new ApplicationConversionService());
        }
        if (this.addCommandLineProperties && args.length > 0) {
            MutablePropertySources sources = environment.getPropertySources();
            sources.addFirst(new SimpleCommandLinePropertySource(args));
        }
    }
}
ƴ

SimpleCommandLinePropertySource

public class SimpleCommandLinePropertySource extends CommandLinePropertySource<CommandLineArgs> {
    public SimpleCommandLinePropertySource(String... args) {
        // 丸๹췽Ϊsuper("commandLineArgs", source)
        super(new SimpleCommandLineArgsParser().parse(args));
    }
}
ƴ

вDZȽϳõģ Spring Boot Ӧʱвjava -jar app.jar --server.port=8088

2.1.3 ConfigurationPropertySources.attach()

attach()Ҫ Environment MutablePropertySources ͷλòһΪconfigurationProperties PropertySource ʵҪ߼£

public final class ConfigurationPropertySources {
    public static void attach(org.springframework.core.env.Environment environment) {
        MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
        PropertySource<?> attached = getAttached(sources);
        if (attached != null && attached.getSource() != sources) {
            sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
            attached = null;
        }
        if (attached == null) {
            sources.addFirst(new ConfigurationPropertySourcesPropertySource("configurationProperties", new SpringConfigurationPropertySources(sources)));
        }
    }

    static PropertySource<?> getAttached(MutablePropertySources sources) {
        return (sources != null) ? sources.get("configurationProperties") : null;
    }
}
ƴ

߶˺þãѹûΪconfigurationProperties PropertySource ɶá󣬻ڹٷĵйRelaxed Binding (ɰ) в³ЩߡͨȽֱӡȣ application.properties ׷һa.b.my-first-key=hello spring environmentȻͨ Environment ȡֵ£

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(DemoApplication.class, args);
        ConfigurableWebEnvironment environment = (ConfigurableWebEnvironment)
                configurableApplicationContext.getBean(Environment.class);
        System.out.println(environment.getProperty("a.b.my-first-key"));
    }
}
ƴ

Ӧú󣬿̨ӡ hello spring environment Ԥġɵͨenvironment.getProperty("a.b.myfirstkey")``environment.getProperty("a.b.my-firstkey")Ȼܹȡݡa.b.myfirstkey``a.b.my-firstkeyļеƣֻƶѣȷ****ȤĶ߿ DEBUG еԭ

2.1.4 listeners.environmentPrepared()

úڰ壬λУҪ environmentPrepared()㲥һApplicationEnvironmentPreparedEvent¼EnvironmentPostProcessorApplicationListenerӦ¼Ӧǵ͵۲ģʽҪ£

public class SpringApplicationRunListeners {
    private final List<SpringApplicationRunListener> listeners;

    void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
        doWithListeners("spring.boot.application.environment-prepared",
                (listener) -> listener.environmentPrepared(bootstrapContext, environment));
    }

    private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) {
        StartupStep step = this.applicationStartup.start(stepName);
        this.listeners.forEach(listenerAction);
        step.end();
    }
}

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
    @Override
    public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
                                    ConfigurableEnvironment environment) {
        this.initialMulticaster.multicastEvent(
                new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
    }
}

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    @Override
    public void multicastEvent(ApplicationEvent event) {
        multicastEvent(event, resolveDefaultEventType(event));
    }

    @Override
    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        Executor executor = getTaskExecutor();
        for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            } else {
                invokeListener(listener, event);
            }
        }
    }
}
ƴ

һEnvironmentPostProcessorApplicationListener®ɽĿ

public class EnvironmentPostProcessorApplicationListener implements SmartApplicationListener, Ordered {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
        }
        if (event instanceof ApplicationPreparedEvent) {
            onApplicationPreparedEvent();
        }
        if (event instanceof ApplicationFailedEvent) {
            onApplicationFailedEvent();
        }
    }
    private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        SpringApplication application = event.getSpringApplication();
        for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(), event.getBootstrapContext())) {
            postProcessor.postProcessEnvironment(environment, application);
        }
    }
}
ƴ

EnvironmentPostProcessor Spring Boot Ϊ Environment չ㡣ùٷĵбȽϾһ仰_Allows for customization of the application's Environment prior to the application context being refreshed__EnvironmentPostProcessor_ һԽӿڣ£

public interface EnvironmentPostProcessor {
    void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application);
}
ƴ

EnvironmentPostProcessorApplicationListener ¼߼УgetEnvironmentPostProcessorsسе EnvironmentPostProcessor һڲ߼

public interface EnvironmentPostProcessorsFactory {
    static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) {
        return new ReflectionEnvironmentPostProcessorsFactory(
                classLoader, 
                SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, classLoader)
        );
    }
}
ƴ

SpringFactoriesLoaderһ̽

public final class SpringFactoriesLoader {

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

    public static List<String> loadFactoryNames(Class<?> factoryType, ClassLoader classLoader) {
        ClassLoader classLoaderToUse = classLoader;
        if (classLoaderToUse == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }
        String factoryTypeName = factoryType.getName();
        return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
        Map<String, List<String>> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }

        result = new HashMap<>();
        try {
            Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                    for (String factoryImplementationName : factoryImplementationNames) {
                        result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                                .add(factoryImplementationName.trim());
                    }
                }
            }
            result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
                    .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
            cache.put(classLoader, result);
        } catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
        return result;
    }
}
ƴ

Spring SPI

SpringFactoriesLoader һ߼ Spring еSPIƣֱ׵˵ǴclasspathµMETA-INF/spring.factories ļм EnvironmentPostProcessor ͽԼʵֵ EnvironmentPostProcessor ŵļоˡʵJDKеSPIƺƹ

ڵǰ汾Spring Boot 7 EnvironmentPostProcessor ʵࡣȽϵ͵ķ¡

RandomValuePropertySourceEnvironmentPostProcessor

RandomValuePropertySourceEnvironmentPostProcessor Environment ׷һΪrandom PropertySourceRandomValuePropertySource£

public class RandomValuePropertySourceEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
    public static final int ORDER = Ordered.HIGHEST_PRECEDENCE + 1;
    private final Log logger;

    public RandomValuePropertySourceEnvironmentPostProcessor(Log logger) {
        this.logger = logger;
    }

    @Override
    public int getOrder() {
        return ORDER;
    }

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        RandomValuePropertySource.addToEnvironment(environment, this.logger);
    }
}
ƴ

ô RandomValuePropertySource ɶأҪ磺environment.getProperty("random.int(5,10)")Իȡһrandom.intΪԻȡһ int ͵random.longΪԻȡһ long ͵random.int(5,10)ΪԻȡһ [5, 10} int ͵淨̽

SystemEnvironmentPropertySourceEnvironmentPostProcessor

ǰ_Environment_ ѾһΪsystemEnvironment PropertySourceSystemEnvironmentPropertySource``SystemEnvironmentPropertySourceEnvironmentPostProcessorڽ SystemEnvironmentPropertySource 滻ΪOriginAwareSystemEnvironmentPropertySourceզе㡰ѿӷƨһ١ĸоأ

public class SystemEnvironmentPropertySourceEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
    public static final int DEFAULT_ORDER = SpringApplicationJsonEnvironmentPostProcessor.DEFAULT_ORDER - 1;
    private int order = DEFAULT_ORDER;

    @Override
    public int getOrder() {
        return this.order;
    }

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        String sourceName = "systemEnvironment";
        PropertySource<?> propertySource = environment.getPropertySources().get(sourceName);
        if (propertySource != null) {
            replacePropertySource(environment, sourceName, propertySource, application.getEnvironmentPrefix());
        }
    }
    private void replacePropertySource(ConfigurableEnvironment environment, String sourceName,
                                       PropertySource<?> propertySource, String environmentPrefix) {
        Map<String, Object> originalSource = (Map<String, Object>) propertySource.getSource();
        SystemEnvironmentPropertySource source = new OriginAwareSystemEnvironmentPropertySource(sourceName, originalSource, environmentPrefix);
        environment.getPropertySources().replace(sourceName, source);
    }
}
ƴ

SpringApplicationJsonEnvironmentPostProcessor

ͨjava -jar -Dspring.application.json={"name":"duxiaotou"} app.jar Spring Boot Ӧõʱ򣬸ԻᱻԶӵ JVM ϵͳ (ʵ -Dkey=value ʽԾ)ЧSystem.setProperty(key, value)``SPRING_APPLICATION_JSONһϵͳʱȻҲSystem.getenv()г֡ǰᵽSystem.getProperties()``systemPropertiesһ PropertySourceSystem.getenv()``systemEnvironmentһ PropertySourceSpringApplicationJsonEnvironmentPostProcessorڴ PropertySource гȡ spring.application.json SPRING_APPLICATION_JSON JSON Environment ׷һΪspring.application.json PropertySourceJsonPropertySource

ConfigDataEnvironmentPostProcessor

ConfigDataEnvironmentPostProcessor``optional:classpath:/``optional:classpath:/config/``optional:file:./``optional:file:./config/``optional:file:./config/*/ЩĿ¼µ application.properties ļسָ spring.profiles.active_ĻͬʱҲὫЩĿ¼µ application-{profile}.properties ļسգ_ConfigDataEnvironmentPostProcessor Environment ׷OriginTrackedMapPropertySource PropertySource λ Environment β application-{profile}.properties OriginTrackedMapPropertySource application.properties OriginTrackedMapPropertySource ǰģһͦҪ

3 jasypt ԭ

jasypt``jasypt-spring-boot-starterDzͬдģֻΪ jasypt Spring Boot ѡʵ

application.properties ļйԴһܺģ£

spring.datasource.hikari.password=ENC(4+t9a5QG8NkNdWVS6UjIX3dj18UtYRMqU6eb3wUKjivOiDHFLZC/RTK7HuWWkUtV)
ƴ

HikariDataSource󣬸 Bean password ֶεֵզͱΪܺ qwe@1234 һأȻSpring Boot Ϊ EnvironmentEnvironmentPostProcessorһչʵ͵컻գûʹ Spring еһ IoC չBeanFactoryPostProcessorҲȫԵģΪִе BeanFactoryPostProcessor еpostProcessBeanFactory()߼ʱֻBeanDefinitionļأûʵ BeanDefinition Ӧ Bean

濴һEnableEncryptablePropertiesBeanFactoryPostProcessorеݣ

public class EnableEncryptablePropertiesBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered {

    private final ConfigurableEnvironment environment;
    private final EncryptablePropertySourceConverter converter;

    public EnableEncryptablePropertiesBeanFactoryPostProcessor(ConfigurableEnvironment environment, EncryptablePropertySourceConverter converter) {
        this.environment = environment;
        this.converter = converter;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        MutablePropertySources propSources = environment.getPropertySources();
        converter.convertPropertySources(propSources);
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE - 100;
    }
}
ƴ

Դ BeanFactoryPostProcessor EncryptablePropertySourceConverter MutablePropertySources һתôתɶأ

ţ EncryptablePropertySourceConverter£

public class EncryptablePropertySourceConverter {

    public void convertPropertySources(MutablePropertySources propSources) {
        propSources.stream()
                .filter(ps -> !(ps instanceof EncryptablePropertySource))
                .map(this::makeEncryptable)
                .collect(toList())
                .forEach(ps -> propSources.replace(ps.getName(), ps));
    }

    public <T> PropertySource<T> makeEncryptable(PropertySource<T> propertySource) {
        if (propertySource instanceof EncryptablePropertySource 
                || skipPropertySourceClasses.stream().anyMatch(skipClass -> skipClass.equals(propertySource.getClass()))) {
            return propertySource;
        }
        PropertySource<T> encryptablePropertySource = convertPropertySource(propertySource);
        return encryptablePropertySource;
    }

    private <T> PropertySource<T> convertPropertySource(PropertySource<T> propertySource) {
        PropertySource<T> encryptablePropertySource;
        if (propertySource instanceof SystemEnvironmentPropertySource) {
            encryptablePropertySource = (PropertySource<T>) new EncryptableSystemEnvironmentPropertySourceWrapper((SystemEnvironmentPropertySource) propertySource, propertyResolver, propertyFilter);
        } else if (propertySource instanceof MapPropertySource) {
            encryptablePropertySource = (PropertySource<T>) new EncryptableMapPropertySourceWrapper((MapPropertySource) propertySource, propertyResolver, propertyFilter);
        } else if (propertySource instanceof EnumerablePropertySource) {
            encryptablePropertySource = new EncryptableEnumerablePropertySourceWrapper<>((EnumerablePropertySource) propertySource, propertyResolver, propertyFilter);
        } else {
            encryptablePropertySource = new EncryptablePropertySourceWrapper<>(propertySource, propertyResolver, propertyFilter);
        }
        return encryptablePropertySource;
    }
}
ƴ

Ȼԭ PropertySource תΪһEncryptablePropertySourceWrapper϶ʵĽܣģ

EncryptablePropertySourceWrapper£

public class EncryptablePropertySourceWrapper<T> extends PropertySource<T> implements EncryptablePropertySource<T> {
    private final CachingDelegateEncryptablePropertySource<T> encryptableDelegate;

    public EncryptablePropertySourceWrapper(PropertySource<T> delegate, EncryptablePropertyResolver resolver, EncryptablePropertyFilter filter) {
        super(delegate.getName(), delegate.getSource());
        encryptableDelegate = new CachingDelegateEncryptablePropertySource<>(delegate, resolver, filter);
    }

    @Override
    public Object getProperty(String name) {
        return encryptableDelegate.getProperty(name);
    }

    @Override
    public PropertySource<T> getDelegate() {
        return encryptableDelegate;
    }
}
ƴ

ʧûɶ߼ getProperty ߼ίɸCachingDelegateEncryptablePropertySource

û취ֻܵ CachingDelegateEncryptablePropertySource һ̽ˣ

public class CachingDelegateEncryptablePropertySource<T> extends PropertySource<T> implements EncryptablePropertySource<T> {
    private final PropertySource<T> delegate;
    private final EncryptablePropertyResolver resolver;
    private final EncryptablePropertyFilter filter;
    private final Map<String, Object> cache;

    public CachingDelegateEncryptablePropertySource(PropertySource<T> delegate, EncryptablePropertyResolver resolver, EncryptablePropertyFilter filter) {
        super(delegate.getName(), delegate.getSource());
        this.delegate = delegate;
        this.resolver = resolver;
        this.filter = filter;
        this.cache = new HashMap<>();
    }

    @Override
    public PropertySource<T> getDelegate() {
        return delegate;
    }

    @Override
    public Object getProperty(String name) {
        if (cache.containsKey(name)) {
            return cache.get(name);
        }
        synchronized (name.intern()) {
            if (!cache.containsKey(name)) {
                Object resolved = getProperty(resolver, filter, delegate, name);
                if (resolved != null) {
                    cache.put(name, resolved);
                }
            }
            return cache.get(name);
        }
    }
}
ƴ

ڣEncryptablePropertySourceп˽ܵ߼УEncryptablePropertyDetector̽ǷҪܣҪͨжϸֵǷENC()

public interface EncryptablePropertySource<T> extends OriginLookup<String> {
    default Object getProperty(EncryptablePropertyResolver resolver, EncryptablePropertyFilter filter, PropertySource<T> source, String name) {
        Object value = source.getProperty(name);
        if (value != null && filter.shouldInclude(source, name) && value instanceof String) {
            String stringValue = String.valueOf(value);
            return resolver.resolvePropertyValue(stringValue);
        }
        return value;
    }
}

public class DefaultPropertyResolver implements EncryptablePropertyResolver {

    private final Environment environment;
    private StringEncryptor encryptor;
    private EncryptablePropertyDetector detector;

    @Override
    public String resolvePropertyValue(String value) {
        return Optional.ofNullable(value)
                .map(environment::resolvePlaceholders)
                .filter(detector::isEncrypted)
                .map(resolvedValue -> {
                    try {
                        String unwrappedProperty = detector.unwrapEncryptedValue(resolvedValue.trim());
                        String resolvedProperty = environment.resolvePlaceholders(unwrappedProperty);
                        return encryptor.decrypt(resolvedProperty);
                    } catch (EncryptionOperationNotPossibleException e) {
                        throw new DecryptionException("Unable to decrypt property: " + value + " resolved to: " + resolvedValue + ". Decryption of Properties failed,  make sure encryption/decryption " +
                                "passwords match", e);
                    }
                })
                .orElse(value);
    }
}
ƴ

4 ܽ

ܽԵ־Ͳ˵ˣ˼Ȫӿˮ300֡ϣҼסڵǰ Spring Boot 汾УApplicationServletEnvironment _Environment_սίConfigurationPropertySourcesPropertyResolverȥȡֵ

ߣԳСͷ ӣhttps://juejin.cn/post/7098299623759937543 Դϡ ȨСҵתϵ߻Ȩҵתע

ο

https://www.w3cschool.cn/wkspring https://www.runoob.com/w3cnote/basic-knowledge-summary-of-spring.html http://codepub.cn/2015/06/21/Basic-knowledge-summary-of-Spring https://dunwu.github.io/spring-tutorial https://mszlu.com/java/spring http://c.biancheng.net/spring/aop-module.html