Spring Framework 到 Spring Boot 怎么一步一步走向自动装配
项目环境
Spring 注解 | 场景说明 | 起始版本 |
---|---|---|
@Profile | 配置化条件装配 | 3.1 |
@Conditional | 编程条件装配 | 4.0 |
假设需求如下:
计算数组中元素的和。
新建接口 CalculateService
/**
* 计算服务
*/
public interface CalculateService {
/**
* 求和
* @param numbers
* @return
*/
Integer sum(Integer... numbers);
}
Java7 实现
/**
* Java 7 for 循环实现
*/
@Profile("Java7")
@Service
public class Java7CalulateImpl implements CalculateService {
@Override
public Integer sum(Integer... numbers) {
System.out.println("Java7 实现");
int sum = 0;
for (int i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
return sum;
}
}
Java8实现
/**
* Java 8 Stream 实现
*/
@Profile("Java8")
@Service
public class Java8CalulateImpl implements CalculateService {
@Override
public Integer sum(Integer... numbers) {
System.out.println("Java8 实现");
return Stream.of(numbers).collect(summingInt(Integer::intValue));
}
}
测试引导类
@SpringBootApplication(scanBasePackages = "com.huajie.deepinspringboot.service")
public class CalculateBootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(CalculateBootstrap.class)
.web(WebApplicationType.NONE)
.profiles("Java8")
.run(args);
CalculateService bean = context.getBean( CalculateService.class);
System.out.println(bean);
System.out.println("sum(1...10) result:"+bean.sum(1,2,3,4,5,6,7,8,9,10));
context.close();
}
}
执行结果:
com.huajie.deepinspringboot.service.Java8CalulateImpl@7882c44a
Java8 实现
sum(1...10) result:55
Spring 4 之后,@Profile 注解的实现方式发生了变化,使用 Condition 进行实现
@Profile 注解源码:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
/**
* The set of profiles for which the annotated component should be registered.
*/
String[] value();
}
org.springframework.context.annotation.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) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles((String[]) value)) {
return true;
}
}
return false;
}
return true;
}
}
@ConditionalOnProperty
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
...
OnPropertyCondition 继承了 SpringBootCondition,而 SpringBootCondition 实现了 Condition 接口,Spring Boot 对 Condition 做了一定的封装,子类只需要实现 getMatchOutcome 的抽象方法即可,完成条件的判断逻辑。
OnPropertyCondition#getMatchOutcome 源码,逻辑大致如下
@Order(Ordered.HIGHEST_PRECEDENCE + 40)
class OnPropertyCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap(
metadata.getAllAnnotationAttributes(
ConditionalOnProperty.class.getName()));
List<ConditionMessage> noMatch = new ArrayList<>();
List<ConditionMessage> match = new ArrayList<>();
for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
ConditionOutcome outcome = determineOutcome(annotationAttributes,
context.getEnvironment());
(outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage());
}
if (!noMatch.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.of(noMatch));
}
return ConditionOutcome.match(ConditionMessage.of(match));
}
我们仿照 @ConditionalOnProperty 的方式自己来实现一个
基于编程方式实现 - @ConditionalOnSystemProperty
新建 OnSystemPropertyCondition 实现 Condition 接口
public class OnSystemPropertyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ConditionalOnSystemProperty.class.getName());
//取到配置的值
String propertyName = String.valueOf(annotationAttributes.get("name"));
String propertyValue = String.valueOf(annotationAttributes.get("value"));
//系统值
String systemPropertyValue = System.getProperty(propertyName);
//是否相同
if (ObjectUtils.nullSafeEquals(propertyValue, systemPropertyValue)) {
return true;
}
return false;
}
}
自定义注解 @ConditionalOnSystemProperty
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnSystemPropertyCondition.class)
public @interface ConditionalOnSystemProperty {
/**
* Java 系统属性名称
*
* @return
*/
String name();
/**
* Java 系统属性值
*
* @return
*/
String value();
}
测试引导类
@SpringBootApplication
public class ConditionalOnSystemPropertyBootstrap {
@Bean
@ConditionalOnSystemProperty(name = "usr.name", value = "小仙")
public String helloWorld() {
return "hello,world";
}
static {
System.setProperty("usr.name", "小仙");
}
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(ConditionalOnSystemPropertyBootstrap.class)
.web(WebApplicationType.NONE)
.run(args);
String bean = context.getBean("helloWorld", String.class);
System.out.println("helloWorld bean : " + bean);
context.close();
}
}
执行结果:
helloWorld bean : hello world
如果系统属性设置为 System.setProperty("usr.name", "小x");
或者其他信息
执行结果:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'helloWorld' available
...
当前上下文中没有这个 Bean 的定义。