Spring master's road 9 - master the secret weapon of Spring conditional assembly

1. Conditional assembly

1.1 Understand conditional assembly and its important role in Spring

  In Springthe framework, conditional assembly ( Conditional Configuration) is a very important feature, which allows developers to dynamically Beanregister or create objects based on satisfied conditions. This allows you to create different instances based on different environments or configurations Bean, which is very useful for creating configurable and modular applications.

SpringA series of annotations are provided to implement conditional assembly, including:

  • @Profile : This is Springthe annotation. This annotation means that only when a specific profile Profileis activated, an profile with this annotation will be created Bean. We can set the activation profile in the application's configuration file Profile.

  • @Conditional : This is Springan annotation that accepts one or more Conditionclasses that need to implement Conditionthe interface and override its matchesmethods. Only when all methods of Conditionthe class return will the annotated ones be created.matchestrue@ConditionalBean

The following annotations are Spring Bootprovided and are mainly used for automatic configuration functions:

  • @ConditionalOnProperty : This annotation indicates that an annotated property will only be created if one or more given properties have a specific value Bean.

  • @ConditionalOnClass and @ConditionalOnMissingClass : These two annotations indicate that only Classpathwhen there (or not) a specific class is created with this annotation Bean.

  • @ConditionalOnBean and @ConditionalOnMissingBean : These two annotations mean that only Spring ApplicationContextwhen there is (or is not) specific Bean, the annotation will be created Bean.

By combining these annotations, developers can implement complex conditional assembly logic and flexibly control Springapplication configuration and behavior.


2. @Profile

  In Spring, Profileit is used to address the need for different configurations in different environments. It can assemble applications according to the requirements of specific environments. For example, we may have one set of configurations for the development environment and another set of configurations for the production environment, Profilewhich can be used to determine which environment is active at runtime and thus decide which ones to register bean.

2.1 Practical application scenarios based on @Profile

  For example, we might need to use a different database or a different service endpoint.

  Here we can take database configuration as an example. The databases in the development environment, test environment and production environment may be different. We can @Profileconfigure the databases of these environments respectively through annotations.

@Configuration
public class DataSourceConfiguration {
    
    
    @Value("${spring.datasource.username}")
    private String username;

    @Value("${spring.datasource.password}")
    private String password;

    @Value("${spring.datasource.url}")
    private String url;

    @Bean
    @Profile("dev")
    public DataSource devDataSource() {
    
    
        return DataSourceBuilder.create()
                .username(username)
                .password(password)
                .url(url + "?useSSL=false&serverTimezone=Asia/Shanghai")
                .driverClassName("com.mysql.cj.jdbc.Driver")
                .build();
    }

    @Bean
    @Profile("test")
    public DataSource testDataSource() {
    
    
        return DataSourceBuilder.create()
                .username(username)
                .password(password)
                .url(url + "?useSSL=false&serverTimezone=Asia/Shanghai")
                .driverClassName("com.mysql.cj.jdbc.Driver")
                .build();
    }

    @Bean
    @Profile("prod")
    public DataSource prodDataSource() {
    
    
        return DataSourceBuilder.create()
                .username(username)
                .password(password)
                .url(url + "?useSSL=true&serverTimezone=Asia/Shanghai")
                .driverClassName("com.mysql.cj.jdbc.Driver")
                .build();
    }
}

  In actual development, different environments have different Apolloconfigurations. ApolloThe database connection configuration is written on it. There is no need for multiple codes for production and testing Bean. You only need to load different Apolloconfigurations to establish a database connection.

  ApolloIt is a distributed configuration center open sourced by Ctrip Framework Department, which can centrally manage the configuration information of applications. ApolloThe main goal is to enable applications to dynamically adjust their configuration at runtime without the need for redeployment.

  Apolloand solve the same problem - how to manage the configuration of different environments, but Springprovide more powerful functions, especially in large-scale and complex distributed systems. In addition, it also supports configuration version control, audit logs, permission management and other functions, providing a comprehensive solution for configuration management.ProfileApolloApollo

2.2 Understand the working principle and purpose of @Profile

Let's use the library opening hours example and use Spring Profilesto control the configuration of opening hours.

The entire code is as follows:

First, we need a class that represents opening hours LibraryOpeningHours:

package com.example.demo.bean;

public class LibraryOpeningHours {
    
    
    private final String openTime;
    private final String closeTime;

    public LibraryOpeningHours(String openTime, String closeTime) {
    
    
        this.openTime = openTime;
        this.closeTime = closeTime;
    }

    @Override
    public String toString() {
    
    
        return "LibraryOpeningHours{" +
                "openTime='" + openTime + '\'' +
                ", closeTime='" + closeTime + '\'' +
                '}';
    }
}

Then, we need a Libraryclass to hold this opening time:

package com.example.demo.bean;

public class Library {
    
    
    private final LibraryOpeningHours openingHours;

    public Library(LibraryOpeningHours openingHours) {
    
    
        this.openingHours = openingHours;
    }

    public void displayOpeningHours() {
    
    
        System.out.println("Library opening hours: " + openingHours);
    }
}

Next, we need to define two different configurations, representing the opening hours on weekdays and weekends:

package com.example.demo.configuration;

import com.example.demo.bean.Library;
import com.example.demo.bean.LibraryOpeningHours;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
@Profile("weekday")
public class WeekdayLibraryConfiguration {
    
    
    @Bean
    public LibraryOpeningHours weekdayOpeningHours() {
    
    
        return new LibraryOpeningHours("8:00 AM", "6:00 PM");
    }

    @Bean
    public Library library(LibraryOpeningHours openingHours) {
    
    
        return new Library(openingHours);
    }
}

Opening hours on weekdays are morning 8and evening 6.

package com.example.demo.configuration;

import com.example.demo.bean.Library;
import com.example.demo.bean.LibraryOpeningHours;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
@Profile("weekend")
public class WeekendLibraryConfiguration {
    
    
    @Bean
    public LibraryOpeningHours weekendOpeningHours() {
    
    
        return new LibraryOpeningHours("10:00 AM", "4:00 PM");
    }

    @Bean
    public Library library(LibraryOpeningHours openingHours) {
    
    
        return new Library(openingHours);
    }
}

Opening hours on weekends are 10morning and afternoon 4.

Finally we run in the main program and Profilechange the library opening hours by selecting a different :

package com.example.demo.application;

import com.example.demo.bean.Library;
import com.example.demo.configuration.WeekdayLibraryConfiguration;
import com.example.demo.configuration.WeekendLibraryConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class DemoApplication {
    
    
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        context.getEnvironment().setActiveProfiles("weekday");
        context.register(WeekdayLibraryConfiguration.class, WeekendLibraryConfiguration.class);
        context.refresh();

        Library library = context.getBean(Library.class);
        library.displayOpeningHours();
    }
}

  Some friends here may be wondering, why is there setActiveProfilesstill a method in the future?registerrefresh

  setActiveProfilesThe method is used to specify which ones Profileare active, and just setting the active ones Profiledoes not trigger Springthe container to instantiate the corresponding ones bean.

  registerThe method is to register the configuration class into Springthe application context. It does not immediately create the configuration class declared in the application context bean. These creation processes are performed beanin the context phase. refreshAt this stage, Springthe container will read all configuration classes and @Beanparse the annotated methods in the configuration classes before creating and initializing them bean.

  So, if you refreshtry to get one of the configuration classes before calling the method bean, you won't be able to find it because it beanmay not have been created yet. Only after the context is refreshed (that is, the method is called refresh) are all beancreated and registered in Springthe container, and then can they be obtained through the context bean.

operation result:

Insert image description here

  If we choose "weekday" Profile, the library's opening hours will be on weekdays; if we choose "weekend" Profile, the library's opening hours will be on weekends.

Note:
  registerMethods, @Component, @Bean, @Importare all ways to register Beanto Springthe container. They have different applicable scenarios and working methods:

  • Register method : This method is used to register one or more configuration classes (that is, classes marked with @Configurationannotations) Springinto ApplicationContext. This process adds the metainformation of the configuration class to the context, but it is not instantiated immediately Bean. The actual Beaninstantiation process will occur at ApplicationContextrefresh time (that is, refreshwhen the method is called), and this process may be affected by conditional assembly annotations such as @Profile.@Conditional

  • @Component : This is a general annotation that can be used to mark any class as Springa component. If an annotated @Componentclass is Springin the component scan path, Springthe class will be automatically created Beanand added to the container.

  • @Bean : This annotation is usually used on methods in configuration classes. The annotated @Beanmethods represent how to instantiate, configure, and initialize a new Beanobject. Spring IoCThe container will be responsible for calling these methods at the appropriate time (during ApplicationContextthe refresh phase) to create Beanthe instance.

  • @Import : This annotation is used to import other configuration classes in a configuration class. By using this annotation, we can Beanspread the definition across multiple configuration classes for better modularity and organization.

  In Springthe framework, the above methods and annotations work together to provide powerful dependency injection and management capabilities, allowing us to create complex, modular applications.

  In Springthe framework, refreshmethods are used to start Springthe life cycle of the application context, which dominates ApplicationContextthe Beanparsing, creation, and initialization processes. The following are refreshthe main steps of the method in practice:

  1. Read all registered configuration classes: refreshThe method first scans ApplicationContextall registered configuration classes (usually @Configurationclasses marked with annotations). @BeanIt will look for all annotated methods in these configuration classes .

  2. Parsing @Beanmethod: For each @Beanmethod, how to create and configure the corresponding method will be determined Springbased on the method's signature, return type, and possible other annotations (such as @Scope, @Lazy, etc.) .@ProfileBean

  3. BeanCreation and dependency injection: Based on the parsed information, Spring IoCthe container will create Beaninstances on demand. After instantiation Bean, this dependency Springwill also be processed , that is, it will automatically inject other dependencies into its properties or constructor parameters.BeanBeanBean

  4. Initialization: If the interface Beanis implemented InitializingBeanor annotated methods are defined , these initialization methods will be called @PostConstructafter all dependency injection is completed .Spring

  Therefore, after calling refreshthe method, we can be sure that everything defined in the configuration class Beanhas been correctly parsed, created, and registered in the Springconfiguration class ApplicationContext. This includes Beaninstantiation, dependency injection, and possibly initialization.

  The above steps are not necessarily performed in order. In fact, Springthe IoCcontainer may make some optimizations and adjustments when processing these steps. The specific processing order may be affected by the following factors:

  • Bean dependencies : If one Bean Adepends on another Bean B, it Springneeds to be created and initialized Bean Bbefore it can be created Bean A. This may result in Beanthe objects being created in a different order than the order in which they are defined in the configuration class.

  • Bean lifecycle and scope : For example, if a bean Beanis singleton (default scope), then it will usually be created when the container starts. But if it is prototyped, it will only be created when it is needed.

  • Conditional annotations : Conditional annotations like @Profileand may also affect the creation of . If the condition is not met, the corresponding one will not be created.@ConditionalBeanBean

  Although in general, the life cycle will be processed according to the steps of Spring"reading configuration class - parsing @Beanmethod - creation - dependency injection - initialization" , the specific processing process may be affected by the above mentioned The influence of various factors.BeanBean

Please remember this example of library opening hours. There are many examples that follow this example.

2.3 Why is there @Profile? Doesn’t application have configuration files for various environments?

  application-dev.yml, application-test.ymland application-prod.ymlThese configuration files can be used to specify different configuration parameters in different environments, such as database connection strings, service addresses, etc.

  Compared with applicationconfiguration files, @Profileannotations Springprovide a higher level of environmental differentiation control in applications. They can control the entire Beanor configuration class, not just the configuration parameters. With this @Profile, we can make the entire Beanor configuration class only effective in a specific environment, which means we can use completely different Beanimplementations or different configuration methods depending on the environment.

  For example, consider an email service. We may use a simulated email service in the development environment and simply print out the email content, while in the production environment we may need to use an actual email service. We can create two Bean, one @Profile("dev")annotated with and one @Profile("prod")annotated with . In this way, we can choose which mail service to use according to the environment without changing other code.

  In general, application-{profile}.ymlfiles and @Profileannotations are Springenvironment difference management tools provided by . They are used to manage configuration parameters and Bean/or configuration classes respectively, and can be selected according to the specific needs of the application.

2.4 How to determine the active Profile in Spring?

SpringThe current activity can be specified in a variety of ways Profile, with priority ordering from high to low as follows:

  1. ConfigurableEnvironment.setActiveProfiles
  2. JVM's -Dspring.profiles.active parameter or environment variable SPRING_PROFILES_ACTIVE (only available with Spring Boot)
  3. SpringApplicationBuilder.profiles (available only with Spring Boot)
  4. SpringApplication.setDefaultProperties (available only with Spring Boot)
  5. Profile property spring.profiles.active

If there are configurations above, the one with higher priority will overwrite the configuration with lower priority.

Let's take a look at them separately. Here we use 2.1the example in the section as the basis. We only modify the main program and do not put other repeated codes:

  1. ConfigurableEnvironment.setActiveProfiles

  This is Springa framework API, so it can be Spring Bootused not only in APP, but also in native applications. We can set the activity Springthrough the obtained environment .ApplicationContextProfile

package com.example.demo.application;

import com.example.demo.configuration.WeekdayLibraryConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;


public class DemoApplication {
    
    
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.getEnvironment().setActiveProfiles("weekday");
        context.register(WeekdayLibraryConfiguration.class);
        context.refresh();
        // 下面这行调试使用
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
    }
}

operation result:

Here you can see WeekdayLibraryConfigurationthat it has been created, but WeekendLibraryConfigurationnot created.

  1. JVM's -Dspring.profiles.active parameter and environment variable SPRING_PROFILES_ACTIVE (only available with Spring Boot)

Both of these are features, and there is no built-in support in Spring Bootnative applications. The main program we use here demonstratesSpringSpring Boot

  • Configuration-Dspring.profiles.active parameter

For example, when launching an Spring Bootapplication, we can use the following command:

-Dspring.profiles.active=weekend

In this example, -Dspring.profiles.active=weekendit is the part that sets the system properties, indicating that only those marked @Profile("weekend")will Beanbe created and used.

Let’s use 2.1the example in the above section to modify the code and set the system properties.

Insert image description here

Or configure it like this

Insert image description here

Spring BootMain program:

package com.example.demo.application;

import com.example.demo.configuration.WeekendLibraryConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan("com.example")
public class DemoApplication {
    
    
    public static void main(String[] args) {
    
    
        ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);
        WeekendLibraryConfiguration bean = context.getBean(WeekendLibraryConfiguration.class);
        System.out.println(bean.weekendOpeningHours());
    }
}

ProfileThere is only Wei weekend's here bean, but ProfileWei weekday's beanhas not been created. You can debug and verify it by yourself.

The running results are as follows:

Insert image description here

  • Configure the environment variable SPRING_PROFILES_ACTIVE

We can set it in the environment variables of the operating system SPRING_PROFILES_ACTIVE.

In Unix/Linuxthe system, we can use commands to set environment variables before starting the application export. For example:

export SPRING_PROFILES_ACTIVE=dev
java -jar myapp.jar

In Windowsthe system, we can use setthe command to set environment variables:

set SPRING_PROFILES_ACTIVE=dev
java -jar myapp.jar
  1. SpringApplicationBuilder.profiles (available only with Spring Boot)
package com.example.demo.application;

import com.example.demo.configuration.WeekendLibraryConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan("com.example")
public class DemoApplication {
    
    
    public static void main(String[] args) {
    
    
        ConfigurableApplicationContext context = new SpringApplicationBuilder(DemoApplication.class)
                .profiles("weekend")
                .run(args);

        WeekendLibraryConfiguration bean = context.getBean(WeekendLibraryConfiguration.class);
        System.out.println(bean.weekendOpeningHours());
    }
}

operation result:

Insert image description here

  1. SpringApplication.setDefaultProperties (available only with Spring Boot)

  SpringApplication.setDefaultPropertiesThe method is used to set default properties. It can set a series of default properties, including spring.profiles.activeproperties. When spring.profiles.activea property is set, Springit is considered the current activation Profile.

Main program:

package com.example.demo.application;

import com.example.demo.configuration.WeekdayLibraryConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

import javax.annotation.Resource;
import java.util.Properties;

@SpringBootApplication
@ComponentScan(basePackages = "com.example.demo")
public class DemoApplication {
    
    
    @Resource
    private WeekdayLibraryConfiguration weekdayLibraryConfiguration;

    public static void main(String[] args) {
    
    
        SpringApplication application = new SpringApplication(DemoApplication.class);
        Properties props = new Properties();
        props.put("spring.profiles.active", "weekday");
        application.setDefaultProperties(props);
        ConfigurableApplicationContext context = application.run(args);
        // 验证当前激活Profile
        String[] activeProfiles = context.getEnvironment().getActiveProfiles();
        for (String profile : activeProfiles) {
    
    
            System.out.println("当前活动Profile:" + profile);
        }
    }
}

operation result:

Insert image description here

  1. Profile property spring.profiles.active profile

In application.propertiesor application.ymlfiles we can set spring.profiles.activeproperties.

For example, in application.propertiesthe file we can add the following lines:

spring.profiles.active=weekday

In application.ymlthe file we can add the following:

spring:
  profiles:
    active: weekday

3. @Conditional

3.1 @Conditional annotation and its uses

  @Conditionalis Spring 4.0a core annotation introduced in , used to Beanassociate the creation of with specific conditions. This feature Spring Bootis used a lot in order to create and assemble when specific conditions are met Bean.

  @ConditionalThe annotation accepts one or more classes that implement Conditionthe interface as parameters. ConditionThe interface has only one matchesmethod named which is required to return a boolean value to indicate whether the condition is met. If matchesthe method returns ,true the marked as will be created and assembled ; if it returns , this will not be created and assembled .Spring@ConditionalBeanfalseBean

  @ConditionalThe application of annotations is very flexible. It can be used to mark classes that use annotations directly or indirectly @Component, including but not limited to @Configurationclasses. In addition, it can be used to mark @Beanmethods, or as meta-annotations to compose custom annotations.

  If a @Configurationclass is marked with an annotation, then all methods @Conditionalassociated with that class , as well as any and annotations, are also subject to the same conditions. This means that these methods and annotations will only be processed when the conditions are met.@Bean@Import@ComponentScan@Conditional

  Overall, @Conditionalthis provides a powerful mechanism for controlling Beancreation and assembly based on specific conditions. By using it, we can more flexibly control Springthe configuration of the application to adapt to various operating environments and needs.

3.2 Use @Conditional to implement conditional assembly

  Suppose we have a library application with two classes, Librarianand Library, and we want Librarianthe class to be created only if it exists Library. This @Profileis impossible to achieve at this time, because @Profileit is impossible to check whether other beanexists.

The entire code is as follows:

First, we create Librarianthe class:

package com.example.demo.bean;

public class Librarian {
    
    
}

Create Libraryclass

package com.example.demo.bean;

public class Library {
    
    
    // Library class
    private final String libraryName;

    public Library(String libraryName) {
    
    
        this.libraryName = libraryName;
    }

    public void showLibraryName() {
    
    
        System.out.println("Library name: " + libraryName);
    }
}

Next, we create a conditional class to check Librarianif beanthe definition exists:

package com.example.demo.condition;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class LibrarianCondition implements Condition {
    
    

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    
    
        return context.getBeanFactory().containsBeanDefinition("librarian");
    }
}

  A condition class named is defined here LibrarianCondition, implements Conditionthe interface and overrides matchesthe method. In matchesthe method, it was checked Springwhether a definition named " librarian" exists in the application context Bean.

  Then, we need to create a configuration class that defines a condition that will only be created if Librarian beanit exists :Library bean

package com.example.demo.configuration;

import com.example.demo.bean.Librarian;
import com.example.demo.bean.Library;
import com.example.demo.condition.LibrarianCondition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class LibraryConfiguration {
    
    

    /**
     * 通过注释或取消注释librarian方法,来改变Librarian bean的存在状态,从而观察对Library bean创建的影响。
     *
     * @return
     */
    @Bean
    public Librarian librarian() {
    
    
        return new Librarian();
    }

    @Bean
    @Conditional(LibrarianCondition.class)
    public Library library() {
    
    
        return new Library("The Great Library");
    }
}

  In the above code, annotations Library Beanare added to the created method @Conditional(LibrarianCondition.class), specifying that it will only be created when it LibrarianConditionreturns . We can then change the existence state of the by annotating or uncommenting the method to observe its impact on the creation.trueLibrary beanlibrarian()Librarian beanLibrary bean

  Finally, in the main program, we initialize Springthe context and Libraryobtain bean:

package com.example.demo;

import com.example.demo.configuration.LibraryConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class DemoApplication {
    
    
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(LibraryConfiguration.class);
        context.refresh();

        System.out.println("IOC容器是否有librarian?" + context.containsBean("librarian"));
        System.out.println("IOC容器是否有library?" + context.containsBean("library"));
    }
}

In this example, will be created Libraryonly if beanalso exists.Librarianbean

When Librarianpresent, the output is:

Insert image description here

When Librarianit does not exist, the output is:

Insert image description here

3.2 Application of @Conditional in Spring Boot

  Spring Bootis used in many places @Conditionalto implement conditional configuration, let's take a look at them separately.

3.2.1 @ConditionalOnBean 和 @ConditionalOnMissingBean

  @ConditionalOnBeanand @ConditionalOnMissingBeanare Spring Boota pair of conditional annotations provided for conditional creation Spring Beans, which can check Springwhether a specific object exists in the container bean. If such one exists (or does not exist) bean, then the corresponding configuration will be enabled (or ignored).

Specifically:

  • @ConditionalOnBean : When Springthe specified type exists in the container Bean, the currently annotated Beanwill be created.

  • @ConditionalOnMissingBean : When Springthe specified type does not exist in the container Bean, the currently annotated Beanwill be created.

  Here we will select @ConditionalOnBeanannotations for explanation. Modify our configuration class just now. We delete Conditionthe conditional judgment class that implements the interface LibrarianConditionand @Conditionalchange it to@ConditionalOnBean

package com.example.demo.configuration;

import com.example.demo.bean.Librarian;
import com.example.demo.bean.Library;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class LibraryConfiguration {
    
    

    /**
     * 通过注释或取消注释librarian方法,来改变Librarian bean的存在状态,从而观察对Library bean创建的影响。
     *
     * @return
     */
    @Bean
    public Librarian librarian() {
    
    
        return new Librarian();
    }

    @Bean
    @ConditionalOnBean(Librarian.class)
    public Library library() {
    
    
        return new Library("The Great Library");
    }
}

As shown below:

Insert image description here

Then, change the main program Spring Bootto restart

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class DemoApplication {
    
    
    public static void main(String[] args) {
    
    
        ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class);

        System.out.println("IOC容器是否有librarian?" + context.containsBean("librarian"));
        System.out.println("IOC容器是否有library?" + context.containsBean("library"));
    }
}

The running results are as follows:

When Librarianpresent, the output is:

Insert image description here

When Librarianit does not exist, the output is:

Insert image description here

Some people may wonder, is it possible to register later, causing this condition to be considered to Librariannot exist?LibraryLibrarian

  The answer is no. SpringWhen processing @Configurationa class, all @Beanmethods will be parsed in advance, all definition information will be collected , and then these will be instantiated Beanaccording to dependencies .Bean

If it Librarianis not written in the configuration class, but modified by @Componentannotations, will the condition be judged to not exist due to order issues?

  Even if Librarianthe definition of the class is @Componentdecorated with annotations and registered to Springthe container through component scanning, Springthe dependency can still be handled correctly, it depends on Beanthe definition of , not Beanthe instantiation of .

  When Springthe container starts, it will first scan all Beandefinitions and collect information, including those defined by @Configurationclass methods, as well as those registered through , , and other annotations and component scanning mechanisms.@Bean@Component@Service@Repository

  When executing the conditional judgment of @ConditionalOnBeanor @ConditionalOnMissingBean, Springyou already have a global view and know all Beanthe definitions of . Therefore, even if a Beanis defined through @Componentannotations and registered by the component scanning mechanism, the judgment will not fail due to order issues. Similarly, @Conditionalconditional judgment does not have this problem.

  In general, Springit provides powerful dependency management and automatic assembly functions, which can ensure Beanthe judgment of various conditions, no matter Beanhow it is defined and registered.

3.2.2 @ConditionalOnProperty

  This annotation indicates that an object with this annotation will only be created if one or more of the given properties have a specific value Bean.

  @ConditionalOnPropertyIt is Spring Bootan annotation in the class that is used to check whether a certain configuration attribute exists or has a specific value. Only when the conditions are met, the class or method marked by the annotation will be created or executed. This annotation can be used to turn on or off certain features in different environments, or adjust the behavior of features.

  In actual business, we may decide whether to enable a certain Beanfunction or functions based on different configurations. Take the following code as an example:

@Configuration
public class MyConfiguration {
    
    

    @Bean
    @ConditionalOnProperty(name = "my.feature.enabled", havingValue = "true", matchIfMissing = true)
    public MyFeature myFeature() {
    
    
        return new MyFeature();
    }
}

  In this example, MyFeaturethis will only be created if the value of the property Beanin the configuration file is . If this attribute does not exist in the configuration file , it will still be created because the attribute's value is .my.feature.enabledtruemy.feature.enabledmatchIfMissingtrueMyFeatureBean

  In this way, we can flexibly turn on or off a function by modifying the configuration file without modifying the code. For example, we might have features that behave differently in development and production environments, or optional features that can be turned on or off based on demand. These functions can also be considered for use in actual development Apollo. You only need to configure the corresponding environment Apolloto obtain the corresponding attribute values ​​to achieve different functions.

3.2.3 @ConditionalOnClass 和 @ConditionalOnMissingClass

  These two annotations can check Classpathwhether a specific class exists or not.

  For example, we may have a service that requires Classpatha specific class to work properly. We can configure it like this:

@Service
@ConditionalOnClass(name = "com.example.SomeClass")
public class MyService {
    
    
    // ...
}

  In this example, if com.example.SomeClassit exists Classpathin , MyServiceit will be created and added Springto ApplicationContext. If the class does not exist, it MyServicewill not be created.

  Similarly, we can also use the annotation to create @ConditionalOnMissingClassa specific class if it does not exist , just replace the with in the above code .ClasspathBean@ConditionalOnClass@ConditionalOnMissingClass

  This 2annotation is rarely used in actual business development, because it is mainly used to handle some common logic of the underlying framework or library. But it is indeed very useful in the development of frameworks or libraries, allowing the framework or library to be more flexibly adapted to different usage environments and configurations.

  For example, in Spring Boot, many automatic configuration classes use conditional assembly. For example DataSourceAutoConfiguration, class, this class is responsible for automatically configuring the database connection pool. It uses @ConditionalOnClassannotations to determine Classpathwhether there is a relevant database driver class in the class. Automatic configuration will only occur when a relevant database driver class exists.

  For another example, we may have developed a powerful logging library that can log to a database, but if the user's project does not include a driver, then JDBCour library should degrade to only log to a file. At this time, you can use @ConditionalOnClassto check whether JDBCa driver exists. If it exists, create one that records logs to the database Bean. Otherwise, create one that records logs to a file Bean.



Welcome to the one-click triple connection~

If you have any questions, please leave a message, let's discuss and learn together

----------------------Talk is cheap, show me the code----- ------------------

Guess you like

Origin blog.csdn.net/qq_34115899/article/details/131450910