springboot的bean以及一些注解@Primary、@Qualifier
1、bean的加载顺序
spring容器载入bean顺序是不确定的,在一定的范围内bean的加载顺序可以控制。
spring容器载入bean虽然顺序不确定,但遵循一定的规则:
1、按照字母顺序加载(同一文件夹下按照字母数序;不同文件夹下,先按照文件夹命名的字母顺序加载)
2、不同的bean声明方式不同的加载时机,顺序总结:@ComponentScan > @Import > @Bean
这里的ComponentScan指@ComponentScan及其子注解,Bean指的是@configuration + @bean
同时需要注意的是:
(1)Component及其子注解申明的bean是按照字母顺序加载的
(2)@configuration + @bean是按照定义的顺序依次加载的
(3)@import的顺序,就是bean的加载顺序
(4)在xml中,通过
(5)同一类中加载顺序:Constructor >> @Autowired >> @PostConstruct >> @Bean
(6)同一类中加载顺序:静态变量 / 静态代码块 >> 构造代码块 >> 构造方法(需要特别注意的是静态代码块的执行并不是优先所有的bean加载,只是在同一个类中,静态代码块优先加载)
2、部分控制bean加载顺序
注解@Order或者接口Ordered的作用是定义Spring IOC容器中Bean的执行顺序的优先级,而不是定义Bean的加载顺序,Bean的加载顺序不受@Order或Ordered接口的影响。
@Order注解等并不能控制Bean的加载顺序的~~因为Spring在解析Bean的时候,根本就没有参考这个注解。另外@Configuration配置类的加载,也不会受到@Order注解的影响,它拿到配置的数组后,仅仅就是一个for循环遍历去解析。
- 特别情况下,如果想手动控制部分bean的加载顺序,有如下方法:
方法1:构造方法依赖 (推荐)
@Component
public class CDemo1 {
private String name = "cdemo 1";
public CDemo1(CDemo2 cDemo2) {
System.out.println(name);
}
}
@Component
public class CDemo2 {
private String name = "cdemo 2";
public CDemo2() {
System.out.println(name);
}
}
CDemo2在CDemo1之前被初始化。
限制:
要有注入关系,如:CDemo2通过构造方法注入到CDemo1中,若需要指定两个没有注入关系的bean之间优先级,则不太合适(比如我希望某个bean在所有其他的Bean初始化之前执行)
循环依赖问题,如过上面的CDemo2的构造方法有一个CDemo1参数,那么循环依赖产生,应用无法启动
另外一个需要注意的点是,在构造方法中,不应有复杂耗时的逻辑,会拖慢应用的启动时间
方法2:参数注入
在@Bean标注的方法上,如果你传入了参数,springboot会自动会为这个参数在spring上下文里寻找这个类型的引用。并先初始化这个类的实例。利用此特性,我们也可以控制bean的加载顺序。
以上结果,beanB先于beanA被初始化加载。
需要注意的是,springboot会按类型去寻找。如果这个类型有多个实例被注册到spring上下文,那你就需要加上@Qualifier(“Bean的名称”)来指定
方法3:@Autowired 注入到所需的服务中
跟在xml配置中写 ref差不多的功能 spring 会解析到这个会依赖springBeanManager 所以会先加载springBeanManager
@Component
public class SystemInit {
@Autowired
private SpringBeanManager springBeanManager;
@PostConstruct
public void init() {
//初始化 script job bean
GroovyBeanInit.InitScriptJob();
}
}
方法4:@DependsOn(“xxx”)
没有直接的依赖关系的,可以通过@DependsOn注解,我们可以在bean A上使用@DependsOn注解 ,告诉容器bean B应该优先被加载初始化。
不推荐的原因:这种方法是通过bean的名字(字符串)来控制顺序的,如果改了bean的类名,很可能就会忘记来改所有用到它的注解,那就问题大了。
当一个bean需要在另一个bean实例化之后再实例化时,可使用这个注解。
@Component("dependson02")
public class Dependson02 {
Dependson02(){
System.out.println(" dependson02 Success ");
}
}
@Component("dependson02")
public class Dependson02 {
Dependson02(){
System.out.println(" dependson02 Success ");
}
}
执行结果
dependson02 Success
Dependson01 success
3、@Primary和@Qualifier
在某些情况下,需要注册多个相同类型的bean。
在此示例中,有Employee类型的zhangSanEmployee()和liSiEmployee()Bean:
@Configuration
public class PrimaryConfig {
@Bean
public Employee zhangSanEmployee() {
return new Employee("张三");
}
@Bean
public Employee liSiEmployee() {
return new Employee("李四");
}
}
如果尝试运行应用程序,与@Autowired一起应用于注入。Spring会抛出NoUniqueBeanDefinitionException。
要访问相同类型的bean,常使用@Qualifier(“beanName”)注解,通过别名控制访问相同类型。
@Configuration
public class PrimaryConfig {
@Bean
@Qualifier("zhangSanEmployee")
public Employee zhangSanEmployee() {
return new Employee("张三");
}
@Bean
@Qualifier("liSiEmployee")
public Employee liSiEmployee() {
return new Employee("李四");
}
}
注入
@Resource
private Employee zhangSanEmployee;
@Resource
private Employee liSiEmployee;
(1)将@Primary和@Bean一起使用
使用
@Configuration
public class PrimaryConfig {
@Bean
public Employee zhangSanEmployee() {
return new Employee("张三");
}
@Bean
@Primary
public Employee liSiEmployee() {
return new Employee("李四");
}
}
用@Primary标记liSiEmployee()bean。 Spring将优先于zhangSanEmployee()注入liSiEmployee()bean。
@Test
public void test1() {
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext(PrimaryConfig.class);
Employee employee = context.getBean(Employee.class);
System.out.println(employee);//Employee(name=李四)
}
(2)将@Primary与@Component一起使用
public interface Manager {
String getManagerName();
}
有一个Manager接口和两个子类
@Component
public class DepartmentManager implements Manager {
@Override
public String getManagerName() {
return "Department manager";
}
}
@Component
@Primary
public class GeneralManager implements Manager {
@Override
public String getManagerName() {
return "General manager";
}
}
都覆盖Manager接口的getManagerName()。 另外,请注意,用@Primary标记了GeneralManager bean。
测试:
@Service
public class ManagerService {
@Autowired
private Manager manager;
public Manager getManager() {
return manager;
}
}
@RunWith(SpringRunner.class)
@SpringBootTest
public class PrimaryTest {
@Resource
private ApplicationContext context;
@Test
public void test2() {
ManagerService service = context.getBean(ManagerService.class);
Manager manager = service.getManager();
System.out.println(manager.getManagerName());//General manager
}
}
本文作者:spiderMan1-1
本文链接:https://www.cnblogs.com/cgy1995/p/17943370
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步