Sprin中Bean的顺序使用及说明

 更新时间:2024年05月21日 11:17:44   作者:影࿐ེ  
这篇文章主要介绍了Sprin中Bean的顺序使用及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

一、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中,通过<bean id="">方式声明的bean也是按照代码的编写顺序依次加载的   
  • (5)同一类中加载顺序:Constructor >> @Autowired >> @PostConstruct >> @Bean   
  • (6)同一类中加载顺序:静态变量 / 静态代码块 >> 构造代码块 >> 构造方法(需要特别注意的是静态代码块的执行并不是优先所有的bean加载,只是在同一个类中,静态代码块优先加载) 

特别情况下,如果想手动控制部分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
@DependsOn("dependson02")
public class Dependson01 {
 
    Dependson01(){
        System.out.println("Dependson01 success");
    }
}

执行结果:

结果:

dependson02 Success 

Dependson01 success

方法5:BeanDefinitionRegistryPostProcessor接口

通过实现BeanDefinitionRegistryPostProcessor接口,在postProcessBeanDefinitionRegistry方法中通过BeanDefinitionRegistry获取到所有bean的注册信息,将bean保存到LinkedHashMap中,并从BeanDefinitionRegistry中删除,然后将保存的bean定义排序后,重新再注册到BeanDefinitionRegistry中,即可实现bean加载顺序的控制。

/**
 *配置类
 */
@Configuration
@ComponentScan(basePackages = "demo")
public class AppConfig extends WebMvcConfigurationSupport {

	/**
	 * 注册用于控制bean加载顺序的处理器
	 * @return
	 */
	@Bean
	public static DemoProcessor demoProcessor() {
		return new DemoProcessor();
	}
}

/**
 *
 */
public class DemoProcessor implements BeanDefinitionRegistryPostProcessor{

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		
	}

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		String[] beanDefinitionNames = registry.getBeanDefinitionNames();
		int index = 0;
		//保留前n个关键bean的顺序
		for(;index<beanDefinitionNames.length;index++) {
			if(AppConfig.class.getName().equals(registry.getBeanDefinition(beanDefinitionNames[index]).getBeanClassName())) {
				break;
			}
		}
		Map<String, BeanDefinition> beans = new LinkedHashMap<>(beanDefinitionNames.length-index);
		for(;index<beanDefinitionNames.length;index++) {
			BeanDefinition beanDefinition = registry.getBeanDefinition(beanDefinitionNames[index]);
			beans.put(beanDefinitionNames[index], beanDefinition);
			registry.removeBeanDefinition(beanDefinitionNames[index]);
		}
		//TODO ...排序逻辑,注意beans中可能还包含有其他spring自身定义的bean
		List<String> orderdBeanNames = new ArrayList<>();
		//将排好序的bean再次注册到容器中
		orderdBeanNames.forEach(beanName->{
			registry.registerBeanDefinition(beanName, beans.get(beanName));
		});
	}
}

注意:

执行顺序:Constructor > @Autowired > @PostConstruct

所以正常情况下,所有的bean都已经执行了构造器Constructor,也就是bean都已经存在容器中了。

也就是正常情况下,都是可以正常执行了。

Autowired为null的情况如下:

(1)该类没有托管给spring 管理

一般在类的上面添加@Component 就可以了

(2)不能new出来的实例,

例如:A a = new A();//new的对象不会交给Spring容器管理, 所以是不行的

特殊情况:@Configuration + @Bean 这种方式是可以的

二、Bean的执行顺序

注解@Order或者接口Ordered的作用是定义Spring IOC容器中Bean的执行顺序的优先级,而不是定义Bean的加载顺序,Bean的加载顺序不受@Order或Ordered接口的影响。

特别介绍

  • @PropertySource:用于注解类,告诉当前类使用什么配置文件,配置文件必须是.property或者.xml类型。
@PropertySource(value = {"classpath:config/user1.properties","classpath:config/user2.properties"})
  • @ImportResource:通过配置文件注入bean,用于注解主配置类,导入一个或多个定义bean的配置文件,配置文件必须是.xml类型。
@ImportResource(value = {"classpath:/beans.xml"})
@SpringBootApplication(scanBasePackages = {"team.seagull.client"})
public class DeployApplication {
    public static void main(String[] args) {
        SpringApplication.run(DeployApplication.class, args);
    }
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 5种Java经典创建型模式详解

    5种Java经典创建型模式详解

    这篇文章主要为大家详细介绍了5种Java经典创建型模式,感兴趣的小伙伴们可以参考一下
    2016-03-03
  • Java实现文件上传的方法总结

    Java实现文件上传的方法总结

    这篇文章主要为大家介绍了三种Java实现文件上传的方法,文中的示例代码讲解详细,对我们的学习或工作有一定的借鉴价值,感兴趣的可以了解一下
    2023-04-04
  • Java实现浪漫流星表白的示例代码

    Java实现浪漫流星表白的示例代码

    本文将利用Java语言实现浪漫流星表白,可以实现这些功能:播放音乐、自定义流星数量、飞行速度、光晕大小、流星大小,自定义表白话语,感兴趣的可以学习一下
    2022-05-05
  • idea开启mybatis控制台SQL日志打印的代码示例

    idea开启mybatis控制台SQL日志打印的代码示例

    本文主要介绍了idea开启mybatis控制台SQL日志打印的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-12-12
  • MyBatis ResultMap 的基本用法示例详解

    MyBatis ResultMap 的基本用法示例详解

    在MyBatis 中,resultMap 用于定义数据库查询结果到Java对象属性的映射关系,本文给大家介绍MyBatis ResultMap的基本用法,感兴趣的朋友一起看看吧
    2025-06-06
  • java使用JDBC动态创建数据表及SQL预处理的方法

    java使用JDBC动态创建数据表及SQL预处理的方法

    这篇文章主要介绍了java使用JDBC动态创建数据表及SQL预处理的方法,涉及JDBC操作数据库的连接、创建表、添加数据、查询等相关实现技巧,需要的朋友可以参考下
    2017-08-08
  • 详解Java 中的函数式接口

    详解Java 中的函数式接口

    这篇文章主要为大家介绍了Java中的函数式接口,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助<BR>
    2021-12-12
  • 关于Java垃圾回收开销降低的几条建议

    关于Java垃圾回收开销降低的几条建议

    垃圾回收(Garbage Collection)是Java虚拟机(JVM)垃圾回收器提供的一种用于在空闲时间不定时回收无任何对象引用的对象占据的内存空间的一种机制,下面这篇文章主要介绍了关于Java垃圾回收开销降低的几条建议,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-02-02
  • mybatis-plus实现打印完整sql语句

    mybatis-plus实现打印完整sql语句

    这篇文章主要介绍了mybatis-plus实现打印完整sql语句方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-07-07
  • SWT JFace Bookmark 制作

    SWT JFace Bookmark 制作

    SWT JFace Bookmark 制作
    2009-06-06

最新评论