定义
从Spring Framework 3.1开始,允许Bean 装配时增加前置条件判断
判断方式
@Profile
: 配置化条件装配,Spring Framework 3.1
@Conditional
:编程条件装配,Spring Framework 4.0
实现方式
- 注解方式--
@Profile
首先定义一个接口
/**
* 计算整数求和
*/
public interface CalculateService {
Integer sum(Integer... values);
}
分别用两个实现类去实现,在类上使用 `@Profile`注解进行配置
@Profile("java7")
@Service
public class Java7CalculateServiceImpl implements CalculateService {}
@Profile("java8")
@Service
public class Java8CalculateServiceImpl implements CalculateService {}
在进行服务的调用时使用 profiles 的方式进行指定
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(CalculateServiceBootStrap.class)
.web(WebApplicationType.NONE)
.profiles("java7")
.run(args);
// 这种方式如果没有指定profile,会出现异常(NoSuchBeanDefinitionException: No qualifying bean of type *** available)
CalculateService calculateService = context.getBean(CalculateService.class);
}
- 编程方式
上面描述的profile注解实现方式,从注解的定义上可以看到,他的底层就是用@Conditional
实现的,而ProfileCondition
类是通过实现Condition
接口的matches
方法,在这个方法中通过编程条件进行过滤,返回true即可对使用了Profile
注解的类进行装配
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({ProfileCondition.class})
public @interface Profile {
String[] value();
}
class ProfileCondition implements Condition {
ProfileCondition() {
}
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;
}
}
}
结合上面的这个实现下面进行手工的配置,仿造上面实现的方法:
1.新建类实现 Condition
接口的matches
方法
public class HelloWorldCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
// 这里的annotatedTypeMetadata可以获取指定类的一些属性(TestCondition是我自己新建的一个注解)
Map<String, Object> attrs = annotatedTypeMetadata.getAnnotationAttributes(TestCondition.class.getName());
// 这里是获取这个注解的属性
String name = String.valueOf(attrs.get("name"));
String value = String.valueOf(attrs.get("value"));
// 这里我直接获取了系统配置的值,用来进行判断
String propertyName = System.getProperty(name);
return propertyName.equals(value);
}
}
- 新建注解,使用
@Conditional
去关联上面的HelloWorldCondition
实现,来进行条件的判断
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({HelloWorldCondition.class})
public @interface TestCondition {
String name();
String value();
}
- 使用注解,放在需要被装配上,这里我放在了一个bean上
@Bean
// name 设置成user.name是为了配合上面的获取系统配置的user.name的值,后面的value值会和系统的name值进行判断,如果相同matches方法就会返回true,这样就能将helloWorld这个bean进行自动装配
@TestCondition(name = "user.name", value = "Administrator")
public String helloWorld() {
return "Hello, World";
}
- 验证是否装配成功
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(HelloWorldConditionBootStrap.class)
.web(WebApplicationType.NONE)
.run(args);
// 上面如果通过注解成功进行条件筛选后,容器中是可以直接获取到helloWorld这个bean的,如果能够输出helloWorld返回的值就说明装配成功了
String helloWorld = context.getBean("helloWorld", String.class);
System.out.println("helloWorld:" + helloWorld);
context.close();
}
- 假设上面的注解中使用了不匹配的value值,看看报什么错:
@Bean
@TestCondition(name = "user.name", value = "Administrator1")
public String helloWorld() {
return "Hello, World";
}
// 这里在value后面加了一个1,应该和系统默认名称不匹配的,此时启动后出现报错如下:
NoSuchBeanDefinitionException: No bean named 'helloWorld' available
此时说明没有装配需要的Bean