First, annotation usage
In our development test deployment, there are different environments, such as: development environment, test environment, production environment, different environments have different components, this sounds so much like the multi-environment configuration in SpringBoot? Today, let’s take a look at how this is implemented in Spring.
For multi-environment development, Spring provides the ability to dynamically activate and switch a series of components based on the current environment, such as the configuration of data source components. Different development environments may have different data sources connected to them, so you can use the @profile annotation to configure and dynamically switch data source components based on the environment.
Profile: Specifies the environment under which a component can be registered with the container. If this is not specified, any environment can register the component. Beans with an environment identifier can only be registered with the container if the environment is active
Ii. Case analysis
Take data source configuration as an example. Data source configuration in different environments is often different. How to use @profile annotation to register data sources in different environments is analyzed by examples.
[1] @Profile environment construction
// Start the test class
@Test
public void TestMain(a) {
// Create an IOC container
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class);
for (String string : namesForType) {
System.out.println(string);
}
// applicationContext.close();
}
/ / configuration class
@Configuration
public class AppConfig {
// Test the environment
// @Profile("test")
@Bean("testDataSource")
public DataSource dataSourceTest(a) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("806188");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/vhr");
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
return dataSource;
}
// Development environment
// @Profile("dev")
@Bean("devDataSource")
public DataSource dataSourceDev(a) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("806188");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/vhr");
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
return dataSource;
}
// Production environment
// @Profile("pro")
@Bean("proDataSource")
public DataSource dataSourcePro(a) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("806188");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/vhr");
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
returndataSource; }}Copy the code
As you can see, in the configuration class, have different data source configuration, test environment, the development environment, production environment, we can use @ Profile annotations to specify what kind of environment, when you don’t specify any environment can register the components, namely the above code, run the test class, the output is as follows, injection were carried out three sources set price:
The data source is identified with the @profile annotation, which means that the three @profile annotations in the above code are turned on. None of the three data sources can be injected because the registration environment is not enabled. Now let’s activate it.
[2] Activate the registration environment
- Default Default environment:
@Profile("default")
- Using command line dynamic parameters:
-Dspring.profiles.active=dev
- Manually activate the specified environment with code: use the no-argument constructor
- Configuration on the configuration class: All configurations of the entire configuration class take effect only when the environment is specified
- Default Specifies the default environment
@Profile("default")
, identifies the default current environment
// Test the environment
@Profile("default")
// @Profile("test")
@Bean("testDataSource")
public DataSource dataSourceTest(a) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("806188");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/vhr");
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
return dataSource;
}
Copy the code
- Using the command line dynamic parameters, edit the VM options for running the configuration as follows:
-Dspring.profiles.active=dev
, using this configuration to mark the environment
Run the startup class and you can see that the devDataSource is injected:
- This method does not allow the parameter constructor code to execute, because when the parameter constructor loads the configuration class, the container is refreshed by executing the refresh() method and the configuration is written to death. Therefore, the parameter constructor is used to manually activate the specified environment. Modify the startup class:
@Test
public void TestMain(a) {
// Create an IOC container
// AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// Set the environment to activate
applicationContext.getEnvironment().setActiveProfiles("pro");
// Register the master configuration class
applicationContext.register(AppConfig.class);
// Start the refresh container
applicationContext.refresh();
String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class);
for(String string : namesForType) { System.out.println(string); }}Copy the code
After modification, manually set the environment to be activated and run the startup class. The output is as follows:
- The @profile annotation on the configuration class will only take effect if the environment is specified, as follows:
@Profile("test")
@Configuration
public class AppConfig {... }Copy the code
Config @profile (“test”); config @profile (“test”);
Third, source tracking
If we look at the @profile annotation source, we can see that @profile is actually an @conditional annotation:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({ProfileCondition.class})
public @interface Profile {
String[] value();
}
Copy the code
The @Conditional annotation was explained in previous articles and can be referred to: @Conditional annotation – [Spring basic principle], here is a brief analysis
Conditinal is a conditional annotation that takes a class that implements the Condition interface, overriding the matches() method. For example, profilecondition.class in the sample code above
class ProfileCondition implements Condition {
ProfileCondition() {
}
// Spring takes the activated Profile from ConditionContext and compares it with the strings on the annotations to decide whether to instantiate the bean
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if(attrs ! =null) {
Iterator var4 = ((List)attrs.get("value")).iterator();
Object value;
do {
if(! var4.hasNext()) {return false;
}
value = var4.next();
} while(! context.getEnvironment().acceptsProfiles(Profiles.of((String[])((String[])value))));return true;
} else {
return true; }}}Copy the code
mathces()
The return value of the method is a Boolean, and spring creates the bean when it returns true, but not when it returns falsemathes()
The method takes two arguments, respectivelyConditionContext
andAnnotatedTypeMetadata
There is a lot of information in this single parameter,ConditionContext
There areEnvironment
.ClassLoader
Information such as,AnnotatedTypeMetadata
Annotated information is available- Spring from
ConditionContext
To determine whether to instantiate the bean
Four,
The @profile annotation is used to specify the context in which a component can be registered in a container, and only when that context is activated. Activation can be summarized in the following ways:
- Default Default environment:
@Profile("default")
- Using command line dynamic parameters:
-Dspring.profiles.active=dev
- Manually activate the specified environment with code: use the no-argument constructor
- Configuration on the configuration class: All configurations of the entire configuration class take effect only when the environment is specified