spring boot 自动装配 bean 的顺序

来源:9-2 不再随意设置数据类型,不给未来留隐患

笑看从前小菜哥

2020-11-03

首先在一哥结婚的这个日子里我来提问,说明一下,我就是故意来提问的。这都是韭菜教我来提问的。

现在这里恭喜一哥 新婚快乐,早生贵子。然后后面是我的提问。

前几天我这边在自定义 bean 的时候遇到了一个比较严重的问题,我的 baen 要求要在其中一个bean之后实例化,但是一直不能实现哪个功能,我加了 @ConditionalOnBean 这个注解之后,他直接就不生效了,就是直接不会创建这个bean,然后我有使用了 @AutoConfigureAfter ,想着在哪个bean之后在实例化,照耀没有生效,最后百度后,查到@AutoConfigureAfter 是要在 spring.factories 中声明后才有效,那一哥能不能给我讲解下这块的原理和 bean加载顺序吗。

写回答

2回答

张勤一

2020-11-09

小菜哥你好:

    其实你这里所说的,我估计应该是你自定义的 Bean 存在着依赖关系,那么,你最好(因为足够简单)使用的应该是 @DependsOn 注解。同时,这个注解是 Spring 框架提供的,更加具有通用性,而你所说的 @AutoConfigureAfter 则是 SpringBoot 提供的。

    @AutoConfigureAfter、@AutoConfigureBefore、@AutoConfigureOrder 其实现原理基本都是在 getInPriorityOrder() 方法中通过 ASCII、Sort值、before/after 来排序的,你可以看到源码实现:

public List<String> getInPriorityOrder(Collection<String> classNames) {
        final AutoConfigurationSorter.AutoConfigurationClasses classes = new AutoConfigurationSorter.AutoConfigurationClasses(this.metadataReaderFactory, this.autoConfigurationMetadata, classNames);
        List<String> orderedClassNames = new ArrayList(classNames);
        // 首先根据 ASCII 来进行排序
        Collections.sort(orderedClassNames);
        // 根据 Order
        Collections.sort(orderedClassNames, new Comparator<String>() {
            public int compare(String o1, String o2) {
                int i1 = classes.get(o1).getOrder();
                int i2 = classes.get(o2).getOrder();
                return i1 < i2 ? -1 : (i1 > i2 ? 1 : 0);
            }
        });
        // 根据 @AutoConfigureAfter @AutoConfigureBefore
        List<String> orderedClassNames = this.sortByAnnotation(classes, orderedClassNames);
        return orderedClassNames;
    }

    而 @DependsOn 就简单了,简言之就是当在构造 “BeanA”的过程中发现其标注有 @DependsOn 的话,就会先调用一遍其中指定的 beans 的构造流程:

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
	@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

        ...

	// Guarantee initialization of beans that the current bean depends on.
	String[] dependsOn = mbd.getDependsOn();
	if (dependsOn != null) {
		for (String dep : dependsOn) { // 循环所有 dependsOn 的 bean名
			if (isDependent(beanName, dep)) { // 校验一下是否存在循环所 dependsOn,循环dependsOn是不允许的
				throw new BeanCreationException(mbd.getResourceDescription(), beanName,
						"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
			}
			registerDependentBean(dep, beanName); // 注册dependsOn关系,用于上边校验循环dependsOn用
			try {
				getBean(dep); // 让被依赖的bean先走构造流程
			}
			catch (NoSuchBeanDefinitionException ex) { // 用被依赖的bean名无法构造bean需要抛出异常
				throw new BeanCreationException(mbd.getResourceDescription(), beanName,
						"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
			}
		}
	}

	...

}

    

    我是勤一,致力于将这门课程的问答区打造为 Java 知识体系知识库,Java 知识体系 BBS!共同建造、维护这门课程,我需要每一个你!

1
0

qq_电耗子_0

2020-11-03

删了吧 没救了

1
1
笑看从前小菜哥
没有用 试过了
2020-11-03
共1条回复

Java实操避坑指南 SpringBoot/MySQL/Redis错误详解

掌握业务开发中各种类型的坑,,Java web开发领域通用

466 学习 · 204 问题

查看课程