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
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的值,如果是"帅"则为true,
// 如果是"丑"则为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中的众多实现类: