有时候我们需要在spring bean的生命周期开始前,初始化一些环境变量到Environment中,在bean生命周期的过程中,可以动态的注入这些环境变量。下面就介绍3种方式,配置环境变量。
自定义Environment属性有常用的3种方式,属性绑定有手动绑定和注解自动绑定2种方式。
源码
方式一:实现FactoryBean
我们常见的一些框架本身是可以单独运行的,而不是依赖于Spring。同时,有框架为了使用Spring的IOC,通常都提供了一个与Spring整合的jar包,例如:mybatis-spring,我们在xml中会配置一个SqlSessionFactoryBean,它是实现了FactoryBean的,SqlSessionFactoryBean类中完成了一些必要对象的初始化,通过FactoryBean#getObject方法,将SqlSessionFactory交给了Spring管理。通过这个例子,我可以告诉你,FactoryBean是很多框架整合到Spring IOC的桥梁。那我们也可以通过FactoryBean初始化环境变量。
步骤1:实现FactoryBean、EnvironmentAware
super.getDefaultProperties()方法是DefaultPropertiesConfig中的,本博客讲述了3种实现方式,就把要配置的环境变量抽出来了。
public class InjectEnvironmentFactoryBean extends DefaultPropertiesConfig implements FactoryBean<Properties>, EnvironmentAware {
private Environment environment;
@Override
public Properties getObject() throws Exception {
Properties p = new Properties();
Map<String, Object> defaultProperties = super.getDefaultProperties();
if (defaultProperties != null) {
MapPropertySource propertySource = new MapPropertySource(DefaultPropertiesConfig.ASSOCIATED_NAME, defaultProperties);
((ConfigurableEnvironment) environment).getPropertySources().addFirst(propertySource);
p.putAll(defaultProperties);
return p;
@Override
public Class<?> getObjectType() {
return Properties.class;
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
步骤2:context:property-placeholder配置
在xml中注入InjectEnvironmentFactoryBean,同时被context:property-placeholder引用,这样才可以在Spring bean生命周期开始前将环境变量设置在Environment中。
<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="environmentInject" class="com.example.environment.inject.env.InjectEnvironmentFactoryBean"/>
<!-- ContextNamespaceHandler -->
<context:property-placeholder properties-ref="environmentInject"/>
</beans>
公共的属性类
public class DefaultPropertiesConfig {
// 环境配置的关联key
public static final String ASSOCIATED_NAME = "configTestName";
// 环境变量的公共前缀
public static final String PREFIX = "config.test";
// name
public static final String ARG1 = "name";
// age
public static final String ARG2 = "age";
// address
public static final String ARG3 = "address";
public Map<String, Object> getDefaultProperties() {
Map<String, Object> environmentInject = new HashMap<>();
environmentInject.put(PREFIX + "." + ARG1, "大桥");
environmentInject.put(PREFIX + "." + ARG2, "18");
environmentInject.put(PREFIX + "." + ARG3, "ka");
return environmentInject;
}
测试
手动绑定环境变量
配置类
public class ConfigTest {
private String name;
private String age;
private String address;
}
测试方法
注意:DefaultPropertiesConfig中配置的key,带有config.test前缀, 使用Binder.get(environment).bind(“config.test”, ConfigTest.class)可以自动的匹配配置类。
// 使 PropertiesFactoryBean 生效
@ImportResource("classpath:access-config.xml")
public class EnvironmentInjectDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 注册 Configuration Class
context.register(EnvironmentInjectDemo.class);
// 启动 Spring 应用上下文
context.refresh();
ConfigurableEnvironment environment = context.getEnvironment();
try {
// 测试 IOC 容器没有 ConfigTest Bean,因为没有扫描这个包,也没有添加注解
ConfigTest configTest = context.getBean(ConfigTest.class);
} catch (Exception e) {
System.err.println("测试 IOC 容器没有 ConfigTest Bean: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.access.boot.sample.ConfigTest' available");
// 手动绑定 PropertiesFactoryBean 注入的环境变量
// 绑定环境变量前缀为 config.test 的属性到 ConfigTest 中
BindResult<ConfigTest> bind = Binder.get(environment).bind("config.test", ConfigTest.class);
if (bind.isBound()) {
ConfigTest configTest = bind.get();
System.out.println(configTest);
// 关闭 Spring 应用上下文
context.close();
}
注解自动绑定环境变量
自动配置类
@Configuration
@ConfigurationProperties(prefix = "config.test")
public class AutoConfigTest {
private String name;
private String age;
private String address;
}
测试方法
注意:AutoConfigTest配置类中使用了ConfigurationProperties注解,指定了prefix=“config.test”,通过注解的形式,可以自动绑定环境变量到AutoConfigTest配置类中。
@SpringBootApplication
@ImportResource("classpath:access-config.xml")
public class EnvironmentAutoInjectDemo {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(EnvironmentAutoInjectDemo.class, args);
// AutoConfigTest 自动配置的类,自动将手动注入到 Environment 中的环境变量注入到配置类
AutoConfigTest bean = run.getBean(AutoConfigTest.class);
System.out.println(bean);
run.close();
}
方式二:实现EnvironmentPostProcessor
步骤1:实现EnvironmentPostProcessor
/**
* 通过 spring.factories 配置
* org.springframework.boot.env.EnvironmentPostProcessor=\
* com.example.environment.inject.env.InjectEnvironmentPostProcessor
* 使用 @Configuration 等其他方式配置不生效
public class InjectEnvironmentPostProcessor extends DefaultPropertiesConfig implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
Map<String, Object> defaultProperties = super.getDefaultProperties();
MapPropertySource propertySource = new MapPropertySource(ASSOCIATED_NAME, defaultProperties);
environment.getPropertySources().addFirst(propertySource);
}
步骤2:spring.factories配置
在resources/META-INF文件夹下新建spring.factories文件,内容如下:
org.springframework.boot.env.EnvironmentPostProcessor=\
com.example.environment.inject.env.InjectEnvironmentPostProcessor
测试
注意:测试时,需要将方式一中的xml配置注释掉,测试方法见方式一中的测试方法。
方式三:实现ApplicationContextInitializer
步骤1:实现ApplicationContextInitializer
/**
* 通过 spring.factories 配置
* org.springframework.context.ApplicationContextInitializer=\
* com.example.environment.inject.env.InjectEnvironmentApplicationContextInitializer
public class InjectEnvironmentApplicationContextInitializer extends DefaultPropertiesConfig implements
ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) {
ConfigurableEnvironment environment = context.getEnvironment();
Map<String, Object> defaultProperties = super.getDefaultProperties();
MapPropertySource propertySource = new MapPropertySource(ASSOCIATED_NAME, defaultProperties);
environment.getPropertySources().addFirst(propertySource);
}
步骤2:spring.factories配置
在resources/META-INF文件夹下新建spring.factories文件,内容如下:
org.springframework.context.ApplicationContextInitializer=\
com.example.environment.inject.env.InjectEnvironmentApplicationContextInitializer
测试
注意:测试时,需要将方式一、方式二中的配置注释掉,测试方法见方式一中的测试方法。