条件装配:满足@Conditional指定的条件时,则进行组件注入
@Conditional注解下面包含许多注解,如下图:
这些注解各有各的功能,这里挑几个来记录一下。
个人理解:当该注解标注的方法或类有相应的组件时,才注册为组件,否则就不注册为组件。
看一下@ConditionalOnBean的源码,可以初步了解它的用法
测试代码:
配置类
为了测试这里我把配置类中pet01方法的@Bean注解注释掉
@Configuration
public class MyConfig {
@ConditionalOnBean(name = "cat")//当容器中有名为tom的组件的时候,下面的方法才注册为组件
@Bean
public User user01(){
User user = new User("张三",19);
user.setPet(pet01());
return user;
}
//@Bean("cat")
public Pet pet01(){
return new Pet("cat");
}
}
在主程序输出测试结果:
import com.gcl.demo1helloworld.bean.Pet;
import com.gcl.demo1helloworld.bean.User;
import com.gcl.demo1helloworld.config.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;
@SpringBootApplication(scanBasePackages="com.gcl")
public class Demo1HelloworldApplication {
public static void main(String[] args) {
//返回IOC容器
ConfigurableApplicationContext context = SpringApplication.run(Demo1HelloworldApplication.class, args);
//判断容器中是否包含user01这个组件
boolean user = context.containsBean("user01");
System.out.println("组件中包含user01?"+user);
}
}
结果:
结果分析:
可以看到,此时容器中并没有user01这个组件了。
测试代码:
配置类:
/**
* @author:小关同学爱吃汉堡
* @date: 2021/4/5 17:44
*/
@Configuration
@ConditionalOnBean(name="tom")
public class MyConfig {
@Bean
public User user01(){
User user = new User("张三",19);
user.setPet(pet01());
return user;
}
@Bean("cat")
public Pet pet01(){
return new Pet("cat");
}
}
主程序测试:
import com.gcl.demo1helloworld.bean.Pet;
import com.gcl.demo1helloworld.bean.User;
import com.gcl.demo1helloworld.config.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;
@SpringBootApplication(scanBasePackages="com.gcl")
public class Demo1HelloworldApplication {
public static void main(String[] args) {
//返回IOC容器
ConfigurableApplicationContext context = SpringApplication.run(Demo1HelloworldApplication.class, args);
//检查容器中是否有下列组件
boolean config = context.containsBean("myConfig");
System.out.println("组件中包含config?"+config);
boolean user = context.containsBean("user01");
System.out.println("组件中包含user01?"+user);
boolean cat = context.containsBean("cat");
System.out.println("组件中包含cat?"+cat);
}
}
结果:
我们可以发现,在配置类本身被否决为组件的情况下,配置类下面的所有配置都不生效了
个人理解:当给定的在bean不存在时,则实例化当前Bean。
再来看一下它的源码,如下图:
@ConditionalOnMissingBean注解作用在@bean定义上,在容器加载它作用的bean时,检查容器中是否存在目标类型(ConditionalOnMissingBean注解里面的值)的bean了,如果存在这跳过原始bean的BeanDefinition加载动作。
在方法上使用时存在一些坑,让我困惑了好久
当@ConditionalOnMissingBean注解使用name属性时会有一些问题,如下:
配置类
import ch.qos.logback.core.db.DBHelper;
import com.gcl.demo1helloworld.bean.Pet;
import com.gcl.demo1helloworld.bean.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* @author:小关同学爱吃汉堡
* @date: 2021/4/5 17:44
*/
@Configuration
public class MyConfig {
@Bean
//我们在这里使用@ConditionalOnMissingBean注解
//按理说,当容器中有名为user02的组件的时候,下面的方法就不会注册为组件
@ConditionalOnMissingBean(name = "user02")
public User user01(){
User user = new User("张三",19);
user.setPet(pet01());
return user;
}
@Bean
public User user02(){
User user = new User("张三",19);
user.setPet(pet01());
return user;
}
@Bean("cat")
public Pet pet01(){
return new Pet("cat");
}
}
在主程序测试:
import com.gcl.demo1helloworld.bean.Pet;
import com.gcl.demo1helloworld.bean.User;
import com.gcl.demo1helloworld.config.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;
@SpringBootApplication(scanBasePackages="com.gcl")
public class Demo1HelloworldApplication {
public static void main(String[] args) {
//返回IOC容器
ConfigurableApplicationContext context = SpringApplication.run(Demo1HelloworldApplication.class, args);
boolean config = context.containsBean("myConfig");
System.out.println("组件中包含config?"+config);
boolean user01 = context.containsBean("user01");
System.out.println("组件中包含user01?"+user01);
boolean user02 = context.containsBean("user02");
System.out.println("组件中包含user02?"+user02);
boolean cat = context.containsBean("cat");
System.out.println("组件中包含cat?"+cat);
}
}
结果:
这结果跟我们想的不太一样啊,user01那里按理说应该是false的,这什么情况???
问过身边的大佬之后,得出一个很草的结论:
这是一个组件创建顺序的问题,因为@ConditionalOnMissingBean(name = “user02”)执行之前,容器里面还没有名叫user02这个组件…所以user01组件总会被创建。
正确的使用姿势如下:
配置类:
import ch.qos.logback.core.db.DBHelper;
import com.gcl.demo1helloworld.bean.Pet;
import com.gcl.demo1helloworld.bean.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* @author:小关同学爱吃汉堡
* @date: 2021/4/5 17:44
*/
@Configuration
public class MyConfig {
@Bean
//@ConditionalOnMissingBean(name = "user02")
//一般来说在这里使用@ConditionalOnMissingBean注解,
//当容器中有名为user02的组件的时候,下面的方法不会注册为组件
//但是,这里有一个执行顺序的问题,当执行这条语句的时候,
//下面的user02组件还未注册,所以下面的方法是一定会被注册为组件的
public User user01(){
User user = new User("张三",19);
user.setPet(pet01());
return user;
}
@Bean
@ConditionalOnMissingBean(name = "user01")
//当user01这个组件存在时,下面的方法不注册为组件
public User user02(){
User user = new User("张三",19);
user.setPet(pet01());
return user;
}
@Bean("cat")
public Pet pet01(){
return new Pet("cat");
}
}
主程序代码不变
结果:
这时输出的结果跟我们预期的一致了,user02没有被放到容器里面。
当然,如果我们使用value属性判断时,则不会出现上述的问题,如下:
配置类
import ch.qos.logback.core.db.DBHelper;
import com.gcl.demo1helloworld.bean.Pet;
import com.gcl.demo1helloworld.bean.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* @author:小关同学爱吃汉堡
* @date: 2021/4/5 17:44
*/
@Configuration
public class MyConfig {
@Bean
@ConditionalOnMissingBean(value = User.class)
public User user01(){
User user = new User("张三",19);
user.setPet(pet01());
return user;
}
@Bean
public User user02(){
User user = new User("张三",19);
user.setPet(pet01());
return user;
}
@Bean("cat")
public Pet pet01(){
return new Pet("cat");
}
}
主程序代码不变
结果:
这时候我们发现user01没有被放入到容器中
在类上使用的话,不能使用该类里面注册的组件作为判断条件,不然条件总是为真,如下:
配置类
import ch.qos.logback.core.db.DBHelper;
import com.gcl.demo1helloworld.bean.Pet;
import com.gcl.demo1helloworld.bean.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* @author:小关同学爱吃汉堡
* @date: 2021/4/5 17:44
*/
@Configuration
@ConditionalOnMissingBean(name = "cat")//条件装配
public class MyConfig {
@Bean
public User user01(){
User user = new User("张三",19);
user.setPet(pet01());
return user;
}
@Bean
public User user02(){
User user = new User("张三",19);
user.setPet(pet01());
return user;
}
@Bean("cat")
public Pet pet01(){
return new Pet("cat");
}
}