springboot如何通过profile加载不同bean

1:定义bean类

@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");
    }
}

以上类通过@Profle指定bean在哪个环境中生效。

2:测试类

@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:修改为dev环境

修改application.properties

spring.profiles.active=dev

测试:


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:修改为test环境

修改application.properties

spring.profiles.active=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:原理分析

看下@Profile注解源码:

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

	String[] value();

}

可以看到其组合了@Conditional条件注解,并且将ProfileCondition作为条件类,源码如下:

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;
	}

}

再来看下org.springframework.context.annotation.Condition源码:

@FunctionalInterface
public interface Condition {
    
    

	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

可以看到这是一个函数接口,只有一个方法matches该方法就是执行具体条件匹配。
我们再来看下@Conditional注解,源码如下:

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

}

@Target({ElementType.TYPE, ElementType.METHOD})可以看出,该注解可以使用在类上和方法上,本例就是使用的在方法上,如springboot中的自动配置类,很多时候就是使用在类上。
最后为了加深理解,来将java config类翻译为如下注意不是可执行代码,只是为了示意

@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:自己定义一个例子

6.1:定义java config类

@Configuration
public class ProfileTestConfiguration {
    
    

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

6.2:定义MyConditionAnnotation注解

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

    String advantage();
}

6.3:定义Condition实现类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);
    }
}

只有配置的是才为真。

6.4:使用@Import注解引入配置类

@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:测试

因为我们配置的就是@MyConditionAnnotation(advantage = "帅")应该为true,直接测试:

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='章子怡'}

修改为@MyConditionAnnotation(advantage = "丑"),测试:

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:spring5默认提供的Condition实现

在spring5中默认的提供的org.springframework.context.annotation.ConfigurationCondition,org.springframework.context.annotation.ProfileCondition,但是准确来说只有org.springframework.context.annotation.ProfileCondition,因为前者还只是一个接口,并不是具体的类,如下图:
在这里插入图片描述
可见,在spring中并没有得到太大的重视,没有被大量使用,但是在springboot中,得到了很大范围的发扬光大,并且成为springboot的核心功能自动配置的重要一环,可以简单看下在springboot中的众多的实现类:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/wang0907/article/details/113480884