docs/Spring全家桶/Spring/Spring中的Environment环境变量.md
ڰøٵĴ롢дϵͳ 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) ƴ
ʵʹУ 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 Ǿ߱ȡֵһġ
һ㣬_Environment_ ʵлһPropertyResolver͵ijԱ PropertyResolver ִ getProperty() _PropertyResolver_ ʵֻԱֱǣConversionService``PropertySourcesȣ_PropertyResolver_ PropertySources е _PropertySource_ȡԭֵȻί ConversionService ԭֵת (бҪĻ)Ȼ PropertySource Ǿ߱ȡֵһģ߱ռλתм߱ PropertyResolver Ҳӡ֤һӣڼѧУûʲôмһ˵ģУǾټһ
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û뵽㣬ǿҰһ
Ҫ 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);
ƴ
ֱ
Ȼݶ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;
}
}
ƴ
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
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
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 еԭ
úڰ壬λУҪ 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 ǰģһͦҪ
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 Ϊ Environment ṩEnvironmentPostProcessorһչʵ͵컻գûʹ 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);
}
}
ƴ
ܽԵ־Ͳ˵ˣ˼Ȫӿˮ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