《SpringBoot源码分析》ConditionalOn

由示例引出本文的主角

首先新建两个Pojo,分别是People和Company

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
java复制代码
/**
* @description: People
* @Author MRyan
* @Date 2020/12/5 14:20
* @Version 1.0
*/
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class People {
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private Integer age;
/**
* 所在公司
*/
private Company city;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
java复制代码
/**
* @description: Company
* @Author MRyan
* @Date 2020/12/5 14:20
* @Version 1.0
*/
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Company {
/**
* 公司名称
*/
private String companyName;
/**
* 公司所在城市
*/
private String companyCity;
}

有了这两个Pojo我们可以开始搞事情了
定义一个ConditionText类,将Company作为bean注入IOC容器中并返回对象,并同样创建People作为bean依赖Company。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
java复制代码/**
* @description: 《SpringBoot源码分析》ConditionText
* @Author MRyan
* @Date 2020/12/5 14:21
* @Version 1.0
*/
@Configuration
public class ConditionText {

@Bean
public Company loadCompany() {
Company company = new Company();
company.setCompanyName("Google");
return company;
}


@Bean
public People people(Company company) {
company.setCompanyCity("USA");
return new People("MRyan", 21, company);
}

}

然后我们在测试类中注入People并输出people信息

1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码@Slf4j
@SpringBootTest
class DemoApplicationTests {

@Autowired(required = false)
private People people;

@Test
void contextLoads() {
System.out.println("people = " + people);
}

}

在这里插入图片描述
发现正常输出没有毛病,也符合实际开发的需求。
那么问题来了,如果上面的Company没有注入成功,会出现什么事情
(将Company注释掉模拟没有注入成功的场景)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
java复制代码

/**
* @description: 《SpringBoot源码分析》ConditionText
* @Author MRyan
* @Date 2020/12/5 14:21
* @Version 1.0
*/
@Configuration
public class ConditionText {

/* @Bean
public Company loadCompany() {
Company company = new Company();
company.setCompanyName("Google");
return company;
}*/


@Bean
public People people(Company company) {
company.setCompanyCity("USA");
return new People("MRyan", 21, company);
}

}

在这里插入图片描述
在这里插入图片描述
启动直接空指针爆红了,这显然不是我们想要的结果,我们是要当Company已经注入成功那么实例化People,如果没有注入成功那么不实例化People。
那么我们该怎么做呢?
本文的重点来了:
@ConditionalOnBean注解的作用

将上述测试代码修改如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
java复制代码/**
* @description: 《SpringBoot源码分析》ConditionText
* @Author MRyan
* @Date 2020/12/5 14:21
* @Version 1.0
*/
@Configuration
public class ConditionText {

/* @Bean
public Company loadCompany() {
Company company = new Company();
company.setCompanyName("Google");
return company;
}*/

/***
*这里加了ConditionalOnBean注解,就代表如果Company存在才实例化people
*/
@ConditionalOnBean(name = "Company")
@Bean
public People people(Company company) {
company.setCompanyCity("USA");
return new People("MRyan", 21, company);
}

}

运行测试,发现这次没爆红,而且People如我们所愿没有实例化
在这里插入图片描述
ConditionalOnBean的作用是什么,它是怎么实现的呢?

注解ConditionalOnBean是什么

源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
java复制代码
/**
* {@link Conditional @Conditional} that only matches when beans meeting all the specified
* requirements are already contained in the {@link BeanFactory}. All the requirements
* must be met for the condition to match, but they do not have to be met by the same
* bean.
* <p>
* When placed on a {@code @Bean} method, the bean class defaults to the return type of
* the factory method:
*
* <pre class="code">
* &#064;Configuration
* public class MyAutoConfiguration {
*
* &#064;ConditionalOnBean
* &#064;Bean
* public MyService myService() {
* ...
* }
*
* }</pre>
* <p>
* In the sample above the condition will match if a bean of type {@code MyService} is
* already contained in the {@link BeanFactory}.
* <p>
* The condition can only match the bean definitions that have been processed by the
* application context so far and, as such, it is strongly recommended to use this
* condition on auto-configuration classes only. If a candidate bean may be created by
* another auto-configuration, make sure that the one using this condition runs after.
*
* @author Phillip Webb
* @since 1.0.0
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
//当给定的在bean存在时,则实例化当前Bean
public @interface ConditionalOnBean {

/**
* The class types of beans that should be checked. The condition matches when beans
* of all classes specified are contained in the {@link BeanFactory}.
* @return the class types of beans to check
*/
//需要作为条件的类的Class对象数组
Class<?>[] value() default {};

/**
* The class type names of beans that should be checked. The condition matches when
* beans of all classes specified are contained in the {@link BeanFactory}.
* @return the class type names of beans to check
*/
//需要作为条件的类的Name,Class.getName()
String[] type() default {};

/**
* The annotation type decorating a bean that should be checked. The condition matches
* when all of the annotations specified are defined on beans in the
* {@link BeanFactory}.
* @return the class-level annotation types to check
*/
//(用指定注解修饰的bean)条件所需的注解类
Class<? extends Annotation>[] annotation() default {};

/**
* The names of beans to check. The condition matches when all of the bean names
* specified are contained in the {@link BeanFactory}.
* @return the names of beans to check
*/
//spring容器中bean的名字
String[] name() default {};

/**
* Strategy to decide if the application context hierarchy (parent contexts) should be
* considered.
* @return the search strategy
*/
//搜索容器层级,当前容器,父容器
SearchStrategy search() default SearchStrategy.ALL;

/**
* Additional classes that may contain the specified bean types within their generic
* parameters. For example, an annotation declaring {@code value=Name.class} and
* {@code parameterizedContainer=NameRegistration.class} would detect both
* {@code Name} and {@code NameRegistration<Name>}.
* @return the container types
* @since 2.1.0
*/
//可能在其泛型参数中包含指定bean类型的其他类
Class<?>[] parameterizedContainer() default {};

}

其中我们看@Conditional(OnBeanCondition.class)是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件的才给容器注册Bean(有关于这个注解会另起一篇文章分析)

而@ConditionalOnBean作用是当给定的在bean存在时,则实例化当前Bean
需要注意配合上@Autowired(required = false)使用 required=false 的意思就是允许当前的Bean对象为null。

其实类似@ConditionalOnBean有很多注解
在这里插入图片描述
例如:

1
2
3
4
less复制代码@ConditionalOnBean         //	当给定的在bean存在时,则实例化当前Bean
@ConditionalOnMissingBean // 当给定的在bean不存在时,则实例化当前Bean
@ConditionalOnClass // 当给定的类名在类路径上存在,则实例化当前Bean
@ConditionalOnMissingClass // 当给定的类名在类路径上不存在,则实例化当前Bean

原理大致相同。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%