添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
快乐的水煮肉  ·  基里连科兔_百度百科·  6 月前    · 
健壮的热带鱼  ·  .net core WebApi ...·  6 月前    · 
失恋的青椒  ·  20.5 OpenSSL ...·  7 月前    · 


有时候我们需要在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

测试

注意:测试时,需要将方式一、方式二中的配置注释掉,测试方法见方式一中的测试方法。