• spring Environment上下文环境参数变量


    spring通过Environment对象来存储上下文环境变量信息,即包含当前系统环境变量也包含配置文件配置变量。Environment作为一个bean被存放在容器中,可以在需要的地方进行依赖注入直接使用。

    Environment的创建

    以AnnotationConfigApplicationContext容器类来看,在其构造函数总会初始化reader = new AnnotatedBeanDefinitionReader(this);然后会调用重载构造函数AnnotatedBeanDefinitionReader(registry, getOrCreateEnvironment(registry)),最后通过getEnvironment获取environment。

    AbstractApplicationContext#getEnvironment

    public ConfigurableEnvironment getEnvironment() {
       if (this.environment == null) {
          this.environment = createEnvironment();
       }
       return this.environment;
    }
    	protected ConfigurableEnvironment createEnvironment() {
    		return new StandardEnvironment();
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这里最后看到创建了一个StandardEnvironment类型的实例。

    Environment放入容器

    在使用中,可以直接通过注解注入的方式将environment注入直接使用,如下:

    @Autowired
    private Environment env;
    
    • 1
    • 2

    那么environment什么时候装载到容器中,成为一个bean的呢?

    这里还要回到容器初始化的Refresh方法,在初始化扫描到的bean前,会调用prepareBeanFactory()方法。

    AbstractApplicationContext#prepareBeanFactory

    //...
    // Register default environment beans.
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
       beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
       beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
       beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    }
    if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) {
       beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    这一步发生在所有的bean实例化之前,beanFactory.registerSingleton方法会将当前对象放到容器单例池里。也就是单例bean。可以说上面几个bean是最早一批bean。这里看到有environment、systemProperties、systemEnvironment和applicationStartup。

    属性文件加载

    在上一篇文章中介绍@Configuration时候可以通过@PropertySource进行属性配置文件的引入,引入的配置文件最终会放入environment中,可以通过environment对象获取属性文件内容。

    如下:

        @Autowired
        private Environment env;
    
        @PostConstruct
        public void init(){
            log.info("property name:{}",env.getProperty("name"));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    那么配置文件是怎么被存放到environment中的呢?在上面一步将environment放入spring容器的一步,所有的bean都还未实例化,@PropertySource也未初始化,这时候还未加载自定义引入的properties配置文件

    还是上一篇文章说的,容器会引入ConfigurationClassPostProcessor类来解析处理@Configuration注解,该类

    不仅是一个bean定义后置处理器,还继承了EnvironmentAware接口。这样当ConfigurationClassPostProcessor在被容器实例化的时候,initializeBean()方法会调用invokeAwareInterfaces方法,这里会调到setEnvironment方法将容器的environment传递给当前ConfigurationClassPostProcessor。这样ConfigurationClassPostProcessor就拥有了容器的environment。

    ApplicationContextAwareProcessor#invokeAwareInterfaces

    private void invokeAwareInterfaces(Object bean) {
       if (bean instanceof EnvironmentAware) {
          ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
       }
       //...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    下一步在ApplicationContextAwareProcessor中会使用ConfigurationClassParser类进行@PropertySource的解析,将解析到的property追加到environment的propertySources中。

    具体逻辑在ConfigurationClassParser#addPropertySource()中。

    最后来看下environment内部存储结构吧:

    {
       //存储Profile
      	private final Set activeProfiles = new LinkedHashSet<>();
    	private final Set defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());
    	//内部使用List> propertySourceList来存储多个PropertySource
    	private final MutablePropertySources propertySources;
    
    	private final ConfigurablePropertyResolver propertyResolver;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Profile怎么理解呢,可以理成剖面,环境。举例说明就好理解了。

    bean可以通过@Profile进行修饰,例如在数据源datasources初始化时候可以指定不同的Profile。

    @Profile("dev")
    @ConfigurationProperties(prefix="spring.datasource")
    public DataSource getDuridSource(){
        return new DruidDataSource();
    }
    @Profile("sit")
    @ConfigurationProperties(prefix="spring.datasource")
    public DataSource getDuridSource(){
        return new DruidDataSource();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    如果一个bean被@Profile修饰,则只有当只有@Profile指定的值在activeProfiles之内,bean才会被加载。

    profile的取值设定在

    AbstractEnvironment#doGetActiveProfiles

    protected Set doGetActiveProfiles() {
       synchronized (this.activeProfiles) {
          if (this.activeProfiles.isEmpty()) {
             String profiles = doGetActiveProfilesProperty();
             if (StringUtils.hasText(profiles)) {
                setActiveProfiles(StringUtils.commaDelimitedListToStringArray(
                      StringUtils.trimAllWhitespace(profiles)));
             }
          }
          return this.activeProfiles;
       }
    }
    	protected String doGetActiveProfilesProperty() {
    		return getProperty(ACTIVE_PROFILES_PROPERTY_NAME);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这里ACTIVE_PROFILES_PROPERTY_NAME = “spring.profiles.active"。可以在启动时候通过-Dspring.profiles.active=xx来指定,也可以配置到properties文件中。都可以解析的到。

  • 相关阅读:
    leetcode522. 最长特殊序列 II(java)
    【数据结构】树和二叉树以及经典例题
    【花书笔记|PyTorch版】手动学深度学习6:多层感知机
    职业技能鉴定服务中心前端静态页面(官网+证书查询)
    ruoyi-启动
    【学习笔记】Shiro安全框架(一)
    模块化---common.js
    token系统讲解及过期处理
    Java Design Patterns 之API 网关模式
    自学Python只看这个够不够........?
  • 原文地址:https://blog.csdn.net/sinat_16493273/article/details/133947559