How To Use Propertysource In Your Spring Application

Elena Vance
-
how to use propertysource in your spring application

Environment Abstraction The Environment interface is an abstraction integrated in the container that models two key aspects of the application environment: profiles and properties. A profile is a named, logical group of bean definitions to be registered with the container only if the given profile is active. Beans may be assigned to a profile whether defined in XML or with annotations.

The role of the Environment object with relation to profiles is in determining which profiles (if any) are currently active, and which profiles (if any) should be active by default. Properties play an important role in almost all applications and may originate from a variety of sources: properties files, JVM system properties, system environment variables, JNDI, servlet context parameters, ad-hoc Properties objects, Map objects, and so on.

The role of the Environment object with relation to properties is to provide the user with a convenient service interface for configuring property sources and resolving properties from them. Bean Definition Profiles Bean definition profiles provide a mechanism in the core container that allows for registration of different beans in different environments.

The word, “environment,” can mean different things to different users, and this feature can help with many use cases, including: - Working against an in-memory datasource in development versus looking up that same datasource from JNDI when in QA or production. - Registering monitoring infrastructure only when deploying an application into a performance environment. - Registering customized implementations of beans for customer A versus customer B deployments. Consider the first use case in a practical application that requires a DataSource .

In a test environment, the configuration might resemble the following: - Java - Kotlin @Bean public DataSource dataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.HSQL) .addScript("my-schema.sql") .addScript("my-test-data.sql") .build(); } @Bean fun dataSource(): DataSource { return EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.HSQL) .addScript("my-schema.sql") .addScript("my-test-data.sql") .build() } Now consider how this application can be deployed into a QA or production environment, assuming that the datasource for the application is registered with the production application server’s JNDI directory.

Our dataSource bean now looks like the following listing: - Java - Kotlin @Bean(destroyMethod = "") public DataSource dataSource() throws Exception { Context ctx = new InitialContext(); return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); } @Bean(destroyMethod = "") fun dataSource(): DataSource { val ctx = InitialContext() return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource } The problem is how to switch between using these two variations based on the current environment.

Over time, Spring users have devised a number of ways to get this done, usually relying on a combination of system environment variables and XML <import/> statements containing ${placeholder} tokens that resolve to the correct configuration file path depending on the value of an environment variable. Bean definition profiles is a core container feature that provides a solution to this problem.

If we generalize the use case shown in the preceding example of environment-specific bean definitions, we end up with the need to register certain bean definitions in certain contexts but not in others. You could say that you want to register a certain profile of bean definitions in situation A and a different profile in situation B. We start by updating our configuration to reflect this need. Using @Profile The @Profile annotation lets you indicate that a component is eligible for registration when one or more specified profiles are active.

Using our preceding example, we can rewrite the dataSource configuration as follows: - Java - Kotlin @Configuration @Profile("development") public class StandaloneDataConfig { @Bean public DataSource dataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.HSQL) .addScript("classpath:com/bank/config/sql/schema.sql") .addScript("classpath:com/bank/config/sql/test-data.sql") .build(); } } @Configuration @Profile("development") class StandaloneDataConfig { @Bean fun dataSource(): DataSource { return EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.HSQL) .addScript("classpath:com/bank/config/sql/schema.sql") .addScript("classpath:com/bank/config/sql/test-data.sql") .build() } } - Java @Configuration @Profile("production") public class JndiDataConfig { @Bean(destroyMethod = "") (1) public DataSource dataSource() throws Exception { Context ctx = new InitialContext(); return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); } } @Configuration @Profile("production") class JndiDataConfig { @Bean(destroyMethod = "") (1) fun dataSource(): DataSource { val ctx = InitialContext() return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource } } The profile string may contain a simple profile name (for example, production ) or a profile expression.

A profile expression allows for more complicated profile logic to be expressed (for example, production & us-east ). The following operators are supported in profile expressions: - ! : A logicalNOT of the profile - & : A logicalAND of the profiles - | : A logicalOR of the profiles You can use @Profile as a meta-annotation for the purpose of creating a custom composed annotation.

The following example defines a custom @Production annotation that you can use as a drop-in replacement for @Profile("production") : - Java - Kotlin @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Profile("production") public @interface Production { } @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.RUNTIME) @Profile("production") annotation class Production @Profile can also be declared at the method level to include only one particular bean of a configuration class (for example, for alternative variants of a particular bean), as the following example shows: - Java @Configuration public class AppConfig { @Bean("dataSource") @Profile("development") (1) public DataSource standaloneDataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.HSQL) .addScript("classpath:com/bank/config/sql/schema.sql") .addScript("classpath:com/bank/config/sql/test-data.sql") .build(); } @Bean("dataSource") @Profile("production") (2) public DataSource jndiDataSource() throws Exception { Context ctx = new InitialContext(); return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource"); } } @Configuration class AppConfig { @Bean("dataSource") @Profile("development") (1) fun standaloneDataSource(): DataSource { return EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.HSQL) .addScript("classpath:com/bank/config/sql/schema.sql") .addScript("classpath:com/bank/config/sql/test-data.sql") .build() } @Bean("dataSource") @Profile("production") (2) fun jndiDataSource() = InitialContext().lookup("java:comp/env/jdbc/datasource") as DataSource } XML Bean Definition Profiles The XML counterpart is the profile attribute of the <beans> element.

Our preceding sample configuration can be rewritten in two XML files, as follows: <beans profile="development" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xsi:schemaLocation="..."> <jdbc:embedded-database id="dataSource"> <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/> <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/> </jdbc:embedded-database> </beans> <beans profile="production" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="..."> <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/> </beans> It is also possible to avoid that split and nest <beans/> elements within the same file, as the following example shows: <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="..."> <!-- other bean definitions --> <beans profile="development"> <jdbc:embedded-database id="dataSource"> <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/> <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/> </jdbc:embedded-database> </beans> <beans profile="production"> <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/> </beans> </beans> The spring-bean.xsd has been constrained to allow such elements only as the last ones in the file.

This should help provide flexibility without incurring clutter in the XML files. Activating a Profile Now that we have updated our configuration, we still need to instruct Spring which profile is active. If we started our sample application right now, we would see a NoSuchBeanDefinitionException thrown, because the container could not find the Spring bean named dataSource . Activating a profile can be done in several ways, but the most straightforward is to do it programmatically against the Environment API which is available through an ApplicationContext .

The following example shows how to do so: - Java - Kotlin AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.getEnvironment().setActiveProfiles("development"); ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class); ctx.refresh(); val ctx = AnnotationConfigApplicationContext().apply { environment.setActiveProfiles("development") register(SomeConfig::class.java, StandaloneDataConfig::class.java, JndiDataConfig::class.java) refresh() } In addition, you can also declaratively activate profiles through the spring.profiles.active property, which may be specified through system environment variables, JVM system properties, servlet context parameters in web.xml , or even as an entry in JNDI (see PropertySource Abstraction).

In integration tests, active profiles can be declared by using the @ActiveProfiles annotation in the spring-test module (see context configuration with environment profiles ). Note that profiles are not an “either-or” proposition. You can activate multiple profiles at once. Programmatically, you can provide multiple profile names to the setActiveProfiles() method, which accepts String… varargs.

The following example activates multiple profiles: - Java - Kotlin ctx.getEnvironment().setActiveProfiles("profile1", "profile2"); ctx.getEnvironment().setActiveProfiles("profile1", "profile2") Declaratively, spring.profiles.active may accept a comma-separated list of profile names, as the following example shows: -Dspring.profiles.active="profile1,profile2" Default Profile The default profile represents the profile that is enabled by default.

Consider the following example: - Java - Kotlin @Configuration @Profile("default") public class DefaultDataConfig { @Bean public DataSource dataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.HSQL) .addScript("classpath:com/bank/config/sql/schema.sql") .build(); } } @Configuration @Profile("default") class DefaultDataConfig { @Bean fun dataSource(): DataSource { return EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.HSQL) .addScript("classpath:com/bank/config/sql/schema.sql") .build() } } If no profile is active, the dataSource is created. You can see this as a way to provide a default definition for one or more beans. If any profile is enabled, the default profile does not apply.

You can change the name of the default profile by using setDefaultProfiles() on the Environment or, declaratively, by using the spring.profiles.default property. PropertySource Abstraction Spring’s Environment abstraction provides search operations over a configurable hierarchy of property sources. Consider the following listing: - Java - Kotlin 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); val ctx = GenericApplicationContext() val env = ctx.environment val containsMyProperty = env.containsProperty("my-property") println("Does my environment contain the 'my-property' property?

$containsMyProperty") In the preceding snippet, we see a high-level way of asking Spring whether the my-property property is defined for the current environment. To answer this question, the Environment object performs a search over a set of PropertySource objects. A PropertySource is a simple abstraction over any source of key-value pairs, and Spring’s StandardEnvironment is configured with two PropertySource objects — one representing the set of JVM system properties (System.getProperties() ) and one representing the set of system environment variables (System.getenv() ).

Concretely, when you use the StandardEnvironment , the call to env.containsProperty("my-property") returns true if a my-property system property or my-property environment variable is present at runtime. Most importantly, the entire mechanism is configurable. Perhaps you have a custom source of properties that you want to integrate into this search. To do so, implement and instantiate your own PropertySource and add it to the set of PropertySources for the current Environment .

The following example shows how to do so: - Java - Kotlin ConfigurableApplicationContext ctx = new GenericApplicationContext(); MutablePropertySources sources = ctx.getEnvironment().getPropertySources(); sources.addFirst(new MyPropertySource()); val ctx = GenericApplicationContext() val sources = ctx.environment.propertySources sources.addFirst(MyPropertySource()) In the preceding code, MyPropertySource has been added with highest precedence in the search. If it contains a my-property property, the property is detected and returned, in favor of any my-property property in any other PropertySource . The MutablePropertySources API exposes a number of methods that allow for precise manipulation of the set of property sources.

Using @PropertySource The @PropertySource annotation provides a convenient and declarative mechanism for adding a PropertySource to Spring’s Environment .

Given a file called app.properties that contains the key-value pair testbean.name=myTestBean , the following @Configuration class uses @PropertySource in such a way that a call to testBean.getName() returns myTestBean : - Java - Kotlin @Configuration @PropertySource("classpath:/com/myco/app.properties") public class AppConfig { @Autowired Environment env; @Bean public TestBean testBean() { TestBean testBean = new TestBean(); testBean.setName(env.getProperty("testbean.name")); return testBean; } } @Configuration @PropertySource("classpath:/com/myco/app.properties") class AppConfig { @Autowired private lateinit var env: Environment @Bean fun testBean() = TestBean().apply { name = env.getProperty("testbean.name")!!

} } Any ${…} placeholders present in a @PropertySource resource location are resolved against the set of property sources already registered against the environment, as the following example shows: - Java - Kotlin @Configuration @PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties") public class AppConfig { @Autowired Environment env; @Bean public TestBean testBean() { TestBean testBean = new TestBean(); testBean.setName(env.getProperty("testbean.name")); return testBean; } } @Configuration @PropertySource("classpath:/com/\${my.placeholder:default/path}/app.properties") class AppConfig { @Autowired private lateinit var env: Environment @Bean fun testBean() = TestBean().apply { name = env.getProperty("testbean.name")!!

} } Assuming that my.placeholder is present in one of the property sources already registered (for example, system properties or environment variables), the placeholder is resolved to the corresponding value. If not, then default/path is used as a default. If no default is specified and a property cannot be resolved, an IllegalArgumentException is thrown. Placeholder Resolution in Statements Historically, the value of placeholders in elements could be resolved only against JVM system properties or environment variables. This is no longer the case.

Because the Environment abstraction is integrated throughout the container, it is easy to route resolution of placeholders through it. This means that you may configure the resolution process in any way you like. You can change the precedence of searching through system properties and environment variables or remove them entirely. You can also add your own property sources to the mix, as appropriate. Concretely, the following statement works regardless of where the customer property is defined, as long as it is available in the Environment : <beans> <import resource="com/bank/service/${customer}-config.xml"/> </beans>

People Also Asked

Mastering @PropertySourceinSpringBoot | Medium?

Environment Abstraction The Environment interface is an abstraction integrated in the container that models two key aspects of the application environment: profiles and properties. A profile is a named, logical group of bean definitions to be registered with the container only if the given profile is active. Beans may be assigned to a profile whether defined in XML or with annotations.

HowtoUse@PropertySourceinYourSpringApplication?

The role of the Environment object with relation to properties is to provide the user with a convenient service interface for configuring property sources and resolving properties from them. Bean Definition Profiles Bean definition profiles provide a mechanism in the core container that allows for registration of different beans in different environments.

Properties withSpringandSpringBoot | Baeldung?

Environment Abstraction The Environment interface is an abstraction integrated in the container that models two key aspects of the application environment: profiles and properties. A profile is a named, logical group of bean definitions to be registered with the container only if the given profile is active. Beans may be assigned to a profile whether defined in XML or with annotations.

Spring@PropertySourceannotation tutorial -using...?

In integration tests, active profiles can be declared by using the @ActiveProfiles annotation in the spring-test module (see context configuration with environment profiles ). Note that profiles are not an “either-or” proposition. You can activate multiple profiles at once. Programmatically, you can provide multiple profile names to the setActiveProfiles() method, which accepts String… varargs.

Environment Abstraction ::SpringFramework?

Environment Abstraction The Environment interface is an abstraction integrated in the container that models two key aspects of the application environment: profiles and properties. A profile is a named, logical group of bean definitions to be registered with the container only if the given profile is active. Beans may be assigned to a profile whether defined in XML or with annotations.