How does springboot load different beans through profile

1: Define the bean class

@Configuration
public class ProfileTestConfiguration {
    
    

    @Bean
    @Profile(value = "dev")
    public MyProfileBean devProfileBean() {
    
    
        return new MyProfileBean("dev profile");
    }

    @Bean
    @Profile(value =  "test")
    public MyProfileBean testProfileBean() {
    
    
        return new MyProfileBean("test profile");
    }
}

The above class @Profletakes effect in which environment the bean is specified.

2: Test class

@SpringBootApplication(exclude = {
    
     SpringApplicationAdminJmxAutoConfiguration.class })
@Import(ProfileTestConfiguration.class)
public class SpringbootHelloworldApplication {
    
    

    public static void main(String[] args) {
    
    
        ConfigurableApplicationContext cac = SpringApplication.run(SpringbootHelloworldApplication.class, args);
        MyProfileBean myProfileBean = cac.getBean(MyProfileBean.class);
        System.out.println(myProfileBean);
    }

}

3: Modify to dev environment

modifyapplication.properties

spring.profiles.active=dev

test:


2021-01-31 16:35:05.269  INFO 80572 --- [           main] d.d.s.SpringbootHelloworldApplication    : Started SpringbootHelloworldApplication in 3.907 seconds (JVM running for 5.498)
MyProfileBean{
    
    name='dev profile'}

4: Modify to test environment

modifyapplication.properties

spring.profiles.active=test

test:

2021-01-31 17:14:33.127  INFO 81908 --- [           main] d.d.s.SpringbootHelloworldApplication    : Started SpringbootHelloworldApplication in 5.172 seconds (JVM running for 6.953)
MyProfileBean{
    
    name='test profile'}

5: Principle analysis

Look at the @Profilesource code of the annotations:

@Target({
    
    ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
    
    

	String[] value();

}

You can see that it combines @Conditionalconditional annotations and will be ProfileConditionused as a conditional class. The source code is as follows:

class ProfileCondition implements Condition {
    
    

	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    
    
		MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
		if (attrs != null) {
    
    
		    // 判断在配置文件中配置的spring.active.profile的值是否与@Profile注解给的value相同,
		    // 相同则为true,否则为false,为true时则加载,false则不加载
			for (Object value : attrs.get("value")) {
    
    
				if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) {
    
    
					return true;
				}
			}
			return false;
		}
		return true;
	}

}

Let's look at the org.springframework.context.annotation.Conditionsource code again:

@FunctionalInterface
public interface Condition {
    
    

	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

You can see that this is a functional interface, and there is only one method matchesthat is to perform specific condition matching.
Let's look at the @Conditionalcomments again, the source code is as follows:

@Target({
    
    ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
    
    
	Class<? extends Condition>[] value();

}

It @Target({ElementType.TYPE, ElementType.METHOD})can be seen that this annotation can be used on classes and methods. This example is used on methods, such as the automatic configuration class in springboot, which is often used on classes.
Finally, in order to deepen the understanding, to translate the java config class as follows 注意不是可执行代码,只是为了示意:

@Configuration
public class ProfileTestConfiguration {
    
    
	if (spring.profiles.active=dev) {
    
    
	    @Bean
	    public MyProfileBean devProfileBean() {
    
    
	        return new MyProfileBean("dev profile");
	    }
    }

	if (spring.profiles.active=test) {
    
    
	    @Bean
	    @Profile(value =  "test")
	    public MyProfileBean testProfileBean() {
    
    
	        return new MyProfileBean("test profile");
	    }
    }
}

6: Define an example yourself

6.1: Define java config class

@Configuration
public class ProfileTestConfiguration {
    
    

    @Bean
    @MyConditionAnnotation(advantage = "帅")
    public GirlFriend giveYouOneGirlFriend() {
    
    
        return new GirlFriend("章子怡");
    }
}

6.2: Define MyConditionAnnotation annotation

@Target({
    
     ElementType.TYPE, ElementType.METHOD })
@Conditional(MyConditionalImpl.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyConditionAnnotation {
    
    

    String advantage();
}

6.3: Define the Condition implementation class MyConditionalImpl

public class MyConditionalImpl implements Condition {
    
    

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    
    
        // 获取在@MyConditionAnnotation上设置的advantage的值,如果是"帅"则
        // 如果是"丑"则为false
        MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(MyConditionAnnotation.class.getName());
        String advantage = attrs.get("advantage").get(0).toString();
        System.out.println("advantage: " + advantage);
        return "帅".equals(advantage);
    }
}

Only the configuration is true.

6.4: Use @Import annotation to introduce configuration classes

@SpringBootApplication
@Import(ProfileTestConfiguration.class)
public class SpringbootHelloWorldApplication {
    
    

    public static void main(String[] args) {
    
    
        ConfigurableApplicationContext cac = SpringApplication.run(SpringbootHelloWorldApplication.class, args);
        GirlFriend girlFriend = cac.getBean(GirlFriend.class);
        System.out.println(girlFriend);
    }

}

6.5: Testing

Because what we configure @MyConditionAnnotation(advantage = "帅")should be true, test directly:

2021-01-31 20:11:00.357  INFO 88760 --- [           main] d.d.s.SpringbootHelloWorldApplication    : The following profiles are active: test
advantage:...
d.s.SpringbootHelloWorldApplication    : Started SpringbootHelloWorldApplication in 3.967 seconds (JVM running for 5.463)
GirlFriend{
    
    name='章子怡'}

Amended to @MyConditionAnnotation(advantage = "丑")test:

2021-01-31 20:13:31.277  INFO 88855 --- [           main] d.d.s.SpringbootHelloWorldApplication    : The following profiles are active: test
advantage:...
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'dongshi.daddy.profiletest.GirlFriend' available
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1127)
	at dongshi.daddy.springboothelloworld.SpringbootHelloWorldApplication.main(SpringbootHelloWorldApplication.java:16)

7: Condition implementation provided by spring5 by default

In spring5 the default provided org.springframework.context.annotation.ConfigurationCondition, org.springframework.context.annotation.ProfileConditionbut precisely and only org.springframework.context.annotation.ProfileConditionbecause the former is just an interface, not a concrete class, as shown below:
Insert picture description here
visible, did not get much attention in the spring, it has not been widely used, but In springboot, it has been carried forward in a wide range, and has become an important part of the automatic configuration of springboot's core functions. You can simply look at the many implementation classes in springboot:
Insert picture description here

Guess you like

Origin blog.csdn.net/wang0907/article/details/113480884