Spring series 15: Environment abstraction

Python wechat ordering applet course video

https://edu.csdn.net/course/detail/36074

Python actual combat quantitative transaction financial management system

https://edu.csdn.net/course/detail/35475

Content of this article

  1. Two important concepts of Environment abstraction
  2. @Use of Profile
  3. @Use of PropertySource

Two important concepts of Environment abstraction

The Environment interface represents the interface of the current application running Environment. Model two key aspects of the application Environment: profiles and properties. Methods related to property access are exposed through the PropertyResolver super interface. The configuration of Environment objects must be completed through the ConfigurableEnvironment interface, which is returned from all AbstractApplicationContext subclasses getEnvironment() method

Environment and configuration files

A configuration file is a named, logical group of bean definitions that are registered with the container only when a given configuration file is active. Beans can be assigned to configuration files, whether defined in XML or through annotation @ Profile; The role of the environment objects associated with profiles is to determine which profiles (if any) are currently active and which profiles (if any) should be active by default.

Environment and attributes

Attributes play an important role in almost all applications and can be derived from a variety of sources: attribute files, JVM system attributes, system environment variables, JNDI, servlet context parameters, attribute objects, map s, etc. The function of environment objects related to attributes is to provide users with a convenient service interface for configuring attribute sources and parsing attributes from them.

Beans managed in ApplicationContext can be registered as EnvironmentAware or @ Inject Environment to directly query the configuration file status or resolve properties. However, in most cases, application level beans do not need to interact directly with the Environment. Instead, they may have to replace the ${...} property value with a property placeholder configurator, such as PropertySourcesPlaceholderConfigurer, which itself is EnvironmentAware and is registered by default when using context: Property placeholder from Spring 3.1, Or register in the container through Java beans.

For the analysis of propertysources placeholderconfigurer, you can read the previous article: Spring series 14: extension point of IoC container

Interface source code overview

Interface inheritance relationship

The interface source code is as follows, which provides the interface methods related to the configuration file, and its inherited PropertyResolver provides the interface related to properties.

public interface Environment extends PropertyResolver {
    // List of currently active profiles
    // Set the system property value spring profiles. Active = XXX to activate
    // Or call ConfigurableEnvironment#setActiveProfiles(String...) activation
	String[] getActiveProfiles();

	// When the active profile is not explicitly set, the default profile set returns to the active state.
	String[] getDefaultProfiles();

	// Returns whether the activity profile matches the given Profiles
	boolean acceptsProfiles(Profiles profiles);

}

PropertyResolver is an interface that resolves properties for any underlying source. The main interface methods are as follows. A very important implementation class is PropertySourcesPlaceholderConfigurer.

public interface PropertyResolver {

	// Include attributes
	boolean containsProperty(String key);
	// Get property value
	String getProperty(String key);
	// Get attribute value with default value
	String getProperty(String key, String defaultValue);
	// Get property value
	 T getProperty(String key, Class targetType);
 // Get attribute value with default value
  T getProperty(String key, Class targetType, T defaultValue);

 // Get property value
 String getRequiredProperty(String key) throws IllegalStateException;
 // Get property value
  T getRequiredProperty(String key, Class targetType) throws IllegalStateException;

 // Parse ${...} in the given text placeholder 
 String resolvePlaceholders(String text);

 // Parse ${...} in the given text placeholder 
 String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;

}

ConfigurablePropertyResolver is the configuration interface that most PropertyResolver types will implement. Provides tools for accessing and customizing the ConversionService used when converting attribute values from one type to another.

public interface ConfigurablePropertyResolver extends PropertyResolver {

   
   ConfigurableConversionService getConversionService();

  
   void setConversionService(ConfigurableConversionService conversionService);

   // How does the default "${" of placeholder prefix come from
   void setPlaceholderPrefix(String placeholderPrefix);

   // Set the placeholder suffix. How does the default "}" come from
   void setPlaceholderSuffix(String placeholderSuffix);

  // Set placeholder value separator. How does the default ":" come from
   void setValueSeparator(@Nullable String valueSeparator);

   void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders);

   void setRequiredProperties(String... requiredProperties);

   void validateRequiredProperties() throws MissingRequiredPropertiesException;

}

ConfigurableEnvironment is the configuration interface that most environment types will implement. Provides tools for setting up activity and default profiles and operating the underlying property sources. The client is allowed to set and verify the required properties, custom transformation services, etc. through the ConfigurablePropertyResolver super interface.

public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {

	void setActiveProfiles(String... profiles);

	void addActiveProfile(String profile);

	void setDefaultProfiles(String... profiles);

	MutablePropertySources getPropertySources();

	// Key system properties ()
	Map getSystemProperties();

 // Critical system environment System#getenv()
 Map getSystemEnvironment();

 void merge(ConfigurableEnvironment parent);
 }

@Use of Profile

@Profile indicates that a component is eligible to register when one or more profiles are active. You can set one or more active profiles in the following ways:

  • Programming method: ConfigurableEnvironment#setActiveProfiles(String...)
  • Startup parameters: - dspring profiles. active=“profile1,profile2”
  • xml configuration method:

Use case

Let's look at a practical scenario: different environments require different types of data sources to be injected into the container. The dev environment uses H2, the production environment prod uses Mysql, and the default environment uses HSQL.

Define data sources in different environments and identify @ Profile

@Configuration
@ComponentScan
public class AppConfig {

    // Test environment data source H2
    @Profile("dev")
    @Bean
    public DataSource devDataSource() {
        DataSource dataSource = new DataSource();
        dataSource.setType("H2");
        dataSource.setUrl("jdbc:h2:xxxxxx");
        return dataSource;
    }
    // Production environment data source mysql
    @Profile("prod")
    @Bean
    public DataSource prodDataSource() {
        DataSource dataSource = new DataSource();
        dataSource.setType("mysql");
        dataSource.setUrl("jdbc:mysql:xxxxxx");
        return dataSource;
    }

    // HSQL for default environment
    @Profile("default")
    @Bean
    public DataSource defaultDataSource() {
        DataSource dataSource = new DataSource();
        dataSource.setType("HSQL");
        dataSource.setUrl("jdbc:HSQL:xxxxxx");
        return dataSource;
    }

}

To test the program, first do not specify the profile

@org.junit.Test
public void test\_profile() {
    AnnotationConfigApplicationContext context =
            new AnnotationConfigApplicationContext();
// context.getEnvironment().setActiveProfiles("prod");
    context.register(AppConfig.class);
    context.refresh();
    DataSource dataSource = context.getBean(DataSource.class);
    System.out.println(dataSource.getType());
    context.close();
}
// Output results
HSQL

The results show that the HSQL corresponding to the default environment registered in the container

Specify profile as prod and observe the output

context.getEnvironment().setActiveProfiles("prod")
// result
mysql

From the results, we can see that the mysql corresponding to the prod environment registered in the container.

Support logical operators

Support and or non operation combination

  • !
  • &
  • |

Combination & and | must use parentheses

Counterexample: Production & US East | EU Central

Positive example: Production & (US East | EU central)

Use @ Profile to customize composite annotations

Define combined annotation

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("production")
public @interface Production {
}

use

@Configuration
@Production
public class MyConfiguration {
}


If the @ Configuration class is marked with @ Profile, all @ Bean methods and @ Import annotations associated with the class will be bypassed unless one or more specified profiles are active.

Specify a profile using xml

The of the profile element in the tag can specify the profile.

xml version="1.0" encoding="UTF-8"?
<beans profile="prod"
 xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.crab.spring.ioc.demo13.DataSource" id="dataSource">
        <property name="type" value="mysql"/>
        <property name="url" value="jdbc:mysql/xxxxx"/>
    bean>

beans>


PropertySource abstraction

Spring's Environment abstraction provides search operations on a configurable attribute source hierarchy. Let's see how the case obtains properties from the spring container.

@org.junit.Test
public void test\_property\_source() {
    ApplicationContext ctx = new GenericApplicationContext();
    Environment env = ctx.getEnvironment();
    boolean containsMyProperty = env.containsProperty("my-property");
    System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty);
}

PropertySource is a simple abstraction of any key value pair source. Spring's StandardEnvironment is configured with two PropertySource objects:

  • One represents a set of JVM system properties (System.getProperties())
  • One represents a set of system environment variables (System.getenv())
public class StandardEnvironment extends AbstractEnvironment {

	/** System environment property source name: {@value}. */
	public static final String SYSTEM\_ENVIRONMENT\_PROPERTY\_SOURCE\_NAME = "systemEnvironment";

	/** JVM system properties property source name: {@value}. */
	public static final String SYSTEM\_PROPERTIES\_PROPERTY\_SOURCE\_NAME = "systemProperties";


	// Customize a set of attribute sources suitable for any standard
	@Override
	protected void customizePropertySources(MutablePropertySources propertySources) {
		propertySources.addLast(
				new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
		propertySources.addLast(
				new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
	}

}

The order of priority for finding whether an attribute exists in the attribute source is as follows, from high to low:

  1. ServletConfig parameters (web context)
  2. ServletContext parameters (web.xml context-param entries)
  3. JNDI environment variables (java:comp/env/ entries)
  4. JVM system properties (-D command-line arguments)
  5. JVM system environment (operating system environment variables)

Custom PropertySource

Custom MyPropertySource implementation Property provides a Property source based on the Map Property key value pair

/**
 * Custom PropertySource
 * @author zfd
 * @version v1.0
 * @date 2022/1/22 22:13
 * @For me, please pay attention to the official account of crab Java notes for more technical series.
 */
public class MyPropertySource extends PropertySource> {


 public MyPropertySource(String name, Map source) {
 super(name, source);
 }

 public MyPropertySource(String name) {
 super(name);
 }

 @Override
 public Object getProperty(String name) {
 return this.source.get(name);
 }
 }

Add to the Spring container environment with the highest priority

@org.junit.Test
    public void test\_custom\_property\_source() {

    ConfigurableApplicationContext ctx = new GenericApplicationContext();
    MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
    Map map = new HashMap<>();
 map.put("my-property", "xxx");
 sources.addFirst(new MyPropertySource("myPropertySource",map));
 // true
 boolean containsMyProperty = ctx.getEnvironment().containsProperty("my-property");
 System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty);
}

@PropertySource usage

Compared with the above programmatic addition of PropertySource, the @ PropertySource annotation provides a convenient and declarative mechanism for adding PropertySource to the Spring environment. Look directly at the case.

app.properties configuration

testBean.name=xxx

Configuration class

@Configuration
// Injection profile
@PropertySource("classpath:demo13/app.properties")
public class AppConfig3 {

    @Autowired
    private Environment env;

    @Bean
    public TestBean testBean() {
        TestBean testBean = new TestBean();
        testBean.setName(env.getProperty("testBean.name"));
        return testBean;
    }
}

Observation of test results

    @org.junit.Test
    public void test\_property\_source\_annotation() {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(AppConfig3.class);
        TestBean testBean = context.getBean(TestBean.class);
        System.out.println(testBean.getName());
    }
// result
xxx

@The configuration file specified in PropertySource can also use the placeholder ${...}. If the attribute value in the environment is my config. If the path already exists, it will be parsed. Otherwise, the default value demo13 will be used.

@Configuration
// Injection profile
@PropertySource("classpath:${my.config.path:demo13}/app.properties")
public class AppConfig3 {}

summary

This paper introduces two important concepts of Environment abstraction in Spring: Bean definition, configuration file and attribute source. It also introduces the use of @ Profile and @ PropertySource.

Source address of this article: https://github.com/kongxubihai/pdf-spring-series/tree/main/spring-series-ioc/src/main/java/com/crab/spring/ioc/demo13
Knowledge sharing, please indicate the source of reprint. There is no order in learning, and the one who reaches is the first!

Keywords: Java Spring Back-end computer

Added by joshgarrod on Fri, 18 Feb 2022 14:28:06 +0200