Spring Property Injection Concepts: Difference between revisions
Line 49: | Line 49: | ||
environment.getProperty(propertyName); | environment.getProperty(propertyName); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Spring Boot offers a facility to inject property values into components coded using JavaBeans conventions: | |||
{{Internal|Spring Boot Property Injection|Spring Boot Property Injection}} | |||
=The PropertySource Abstraction= | =The PropertySource Abstraction= |
Revision as of 17:10, 3 December 2018
Internal
Overview
Spring has two different, but related kinds of configuration: bean wiring, which refers to declaring application components and their dependencies, and how those dependencies should be injected into each other, as described in the Dependency Injection and Inversion of Control Container section, and property injection, which is the process that allows external pieces of data, known as configuration properties, to be provided to the application runtime at startup or while it is running, in form of Java system properties, environment variables and by other means. This section addresses property injection concepts. These two concepts intermingle when we talk about profiles, as both bean wiring and property injection are subject to profiles.
The Environment Abstraction
The environment is an abstraction integrated in the container that models two key aspects of the application environment: configuration properties and profiles.
The environment implements Environment and can be obtained directly from the application context, as follows:
import org.springframework.core.env.Environment;
ApplicationContext applicationContext = ...
Environment environment = applicationContext.getEnvironment();
The environment can be injected into a component and accessed programmatically as follows:
@Autowired
private Environment environment;
Configuration Properties
Configuration properties are pieces of data coded into Spring components using the JavaBeans property conventions. They usually have a corresponding member variable, a getter and a setter. The Spring Framework provides conventions and mechanisms to automatically inject values into configuration properties, while those values come from several different property sources.
The Spring environment provides the user with a convenient service interface for configuring property sources and resolving properties from them.
The Spring environment is the only source of configuration properties for components needing them. The environment abstracts the origins of properties and applies precedence rules when the same property is specified in more than one source. It pulls property values from several sources: Java system properties, command-line arguments, environment variables, configuration files, etc. The property sources are described in detail in their corresponding sections, below.
Reading Properties from Environment
The availability of a property in one of the environment's property sources can be checked with:
if (environment.containsProperty(propertyName)) {
...
}
If the property is available, it can be obtained with:
environment.getProperty(propertyName);
Spring Boot offers a facility to inject property values into components coded using JavaBeans conventions:
The PropertySource Abstraction
The Spring environment abstraction integrates and searches over a configurable hierarchy of property sources. To figure out whether a specific property was declared and to obtain its value, the environment performs a search over a set of PropertySource objects.
The mechanism is configurable, custom property sources can be integrated.
Property Sources
Java System Properties
Java system properties are exposed to the environment via a PropertySource wrapped around System.getProperties(). Properties declared with -D on java command line are available via the environment abstraction.
Environment Variables
Environment variables are exposed to the environment via a PropertySource wrapped around System.getenv().
The naming style should accommodate restrictions placed on environment variable names by the operating system.
export SERVER_PORT=9999
Spring Boot Command-Line Arguments
This option to specify system property is only available to Spring Boot applications.
Do test.
java -jar ... --<property.name>=<value>
Example:
java -jar ... --server.port=9999
Property Configuration Files
application.properties
Map Objects
JNDI
Servlet Configuration
Servlet configuration is accessible through a StandardServletEnvironment.
Servlet Context Parameters
Servlet context parameters are accessible through a StandardServletEnvironment.
Configuration Service
The configuration service is also referred to as configuration server.
Custom Property Source
API
ConfigurableApplicationContext ctx = ...
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());
@PropertySource
Precedence
Property values are not merged but rather completely overridden by a preceding entry. The property sources are scanned in the descending order of their priority, and when a hit is encountered, the scan ends, and the found value is returned.
For a StandardEnvironment, the full hierarchy is as follows, with the highest-precedence entries at the top:
- JVM system properties (-D command-line arguments).
- OS environment variables.
For a StandardServletEnvironment, the full hierarchy is as follows, with the highest-precedence entries at the top:
- ServletConfig parameters.
- ServletContext parameters (web.xml context-param entries).
- JNDI environment variables (java:comp/env/ entries).
- JVM system properties (-D command-line arguments).
- OS environment variables.
Profiles
A profile is a named, logical group of bean definitions and configuration properties to be registered with the container only if the given profile is active. Beans may be assigned to a profile whether they are defined in XML or with annotations. The environment determines which profiles - if any - are currently active, and which profiles - if any - should be active by default.
Default Profile
A default profile represents the profile (or profiles) that are enabled by default. The default profile's name is "default". Components can be declared to be registered by default, probably redundantly with:
@Profile("default")
...
If no profile is active, all components belonging to the default profile are created and registered. The default profile is a way to provide a default definition for one or more beans. If any profile is enabled, the default profile does not apply. The name of the default profile can be changed by using setDefaultProfiles() on the Environment or declaratively by using the "spring.profiles.default" property.
Relationship between default and active profiles.
Active Profile
More than one profile can be active at a time.
A profile, or more than one profiles can be activated declaratively using the spring.profiles.active system property:
java -Dspring.profiles.active=red -cp ...
the SPRING_PROFILES_ACTIVE environment variable (note that SPRING_ACTIVE_PROFILES has no effect):
export SPRING_PROFILES_ACTIVE="blue"
servlet context parameters in web.xml, JNDI entries, etc.
Active profiles can also be activated programmatically via the API exposed by the Environment interface:
For the programmatic configuration to work, setActiveProfile() needs to be called before the components are registered with register(), as shown in the example below.
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.getEnvironment().setActiveProfiles("blue");
applicationContext.scan("playground.spring.profile");
applicationContext.refresh();
Active Profile Example
Active Profiles in Integration Tests
In integration tests, the association between a test and active profile (profiles) can be declared with the @ActiveProfiles annotation:
Bean Definition Profiles
Bean definition profiles provide a mechanism in the core of the container that allows for registration of different beans in different environments, or according to different conditions. Beans can be associated with profiles and they are only registered if the associated profile or profiles are active. Note that if more than one valid candidates are found associated with the same active profile, the runtime fails with:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'playground.spring.profile.ColorAware' available: expected single matching bean but found 2: componentA,componentB
@Profile
The @Profile annotation is used to associate the component with one or more active profiles: the component annotated as such becomes eligible for registration, and will be instantiated and registered only if at least one of the profiles is associated with it is active.
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
@Component
@Profile("red")
public class ComponentA implements ColorAware {
...
}
// two profiles
The association with one or more profile can be more complex when it is declared with a profile expression.