关于AllowBeanDefinitionOverriding属性设置问题

腾讯课堂某培训机构Spring源码问题集锦

看过上一章的话,应该会对AllowBeanDefinitionOverriding这个属性有一个认识。这个是Spring中关键工厂org.springframework.beans.factory.support.DefaultListableBeanFactory的一个属性,定义如下

/** Whether to allow re-registration of a different definition with the same name. */
private boolean allowBeanDefinitionOverriding = true;

并且提供了public的读写方法

/**
 * Set whether it should be allowed to override bean definitions by registering
 * a different definition with the same name, automatically replacing the former.
 * If not, an exception will be thrown. This also applies to overriding aliases.
 * <p>Default is "true".
 * @see #registerBeanDefinition
 */
public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) {
	this.allowBeanDefinitionOverriding = allowBeanDefinitionOverriding;
}

/**
 * Return whether it should be allowed to override bean definitions by registering
 * a different definition with the same name, automatically replacing the former.
 * @since 4.1.2
 */
public boolean isAllowBeanDefinitionOverriding() {
	return this.allowBeanDefinitionOverriding;
}

如果这个属性为true的话,就可以覆盖同名的bean定义,如果设置为false,那么就不允许注册同名的bean,因为Spring会抛出异常,这个属性默认为true,但是提供了public的setter方法,当然是允许用户来修改它的,那么该如何修改呢?

老师实现如下所示(这个是错的,后面会详细探讨)

package org.springwork.demo.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.stereotype.Component;

@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) registry;
        defaultListableBeanFactory.setAllowBeanDefinitionOverriding(false);
    }

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

在课堂上老师还写了实例,然后跑了一下,这个类没有报错,所以是可以的。真的是这样的吗?
我们依旧使用上一节的例子,首先定义一个类

package org.springwork.demo.service;

public class DemoService {

	private String tag;

	public void setTag(String tag) {
		this.tag = tag;
	}

	public String getTag() {
		return tag;
	}

}

接下来applicationContext.xml定义一个名称为demoService的bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">

	<bean id="demoService"
		class="org.springwork.demo.service.DemoService">
		<property name="tag" value="xml"></property>
	</bean>
</beans>

再通过java注解的方式注册一个同名的

package org.springwork.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springwork.demo.service.DemoService;

@Configuration
@ComponentScan(basePackages = {"org.springwork.demo"})
@ImportResource(value = {"classpath:applicationContext.xml"})
public class RootConfig {

    @Bean
    public DemoService demoService() {
        DemoService demoService = new DemoService();
        demoService.setTag("anno");
        return demoService;
    }

}

通过以下方式启动

package org.springwork.demo;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springwork.demo.config.RootConfig;
import org.springwork.demo.service.DemoService;

public class SpringStartMain {

	public static void main(String[] args) {
		{
			AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(RootConfig.class);
			DemoService demoService = (DemoService) applicationContext.getBean("demoService");
			System.out.println(demoService.getTag());
			applicationContext.close();
		}
	}
}

然后在对应的地方打上断点并运行,结果如下
在这里插入图片描述
很遗憾,这里还是为true,依旧会发生同名bean定义的覆盖。那么是不是对应的MyBeanDefinitionRegistryPostProcessor没有起效呢?依旧可以打上断点,发现也就进入了。
在这里插入图片描述
那么为啥这里还是发生了覆盖了呢?对应的BeanDefinitionRegistryPostProcessor没有起到应有的作用。其实这里就是**对Spring中各事件的先后顺序没有深刻的认识导致的滥用扩展接口**。上面用于修改属性的MyBeanDefinitionRegistryPostProcessor虽然有执行的,但没有起到应有的效果。因为它是在bean被注册之后才执行的。而此时bean都注册完了,你再修改这个属性有什么用呢?

在上一章当中,我们说到Spring的配置分为基于xml、基于注解和基于Java的,这三种类型bean的注册都会在下图中的标识1和标识2全部完成。
在这里插入图片描述
标识1的地方主要是基于xml的容器启动时会解析xml并注册,这里包含有基于xml和基于注解的bean定义注册,而标识2的地方是ConfigurationClassPostProcessor的入口,而ConfigurationClassPostProcessor类会用于注册基于xml、基于注解、基于Java的bean定义。这里详细分析一下ConfigurationClassPostProcessor的执行时机。
在这里插入图片描述
首先这个类实现了PriorityOrdered接口,实现如下

@Override
public int getOrder() {
	return Ordered.LOWEST_PRECEDENCE;  // within PriorityOrdered
}

也就是说它跟其他的BeanFactoryPostProcessor相比,在接口实现上就有很高的优先级,如果要超过它,必须也实现了PriorityOrdered接口。(PriorityOrdered > Ordered > 没有实现接口的)。如果实现了PriorityOrdered接口,只要保证getOrder方法返回的值比ConfigurationClassPostProcessor小就可以了。

那不妨加上对应的接口试试看。
在这里插入图片描述
那么此时是不是就可以了呢?我们会发现还是不行。因为这里有一个先有鸡还是先有蛋的问题,这个自定义的MyBeanDefinitionRegistryPostProcessor要被注册得靠ConfigurationClassPostProcessor执行,你想在ConfigurationClassPostProcessor起作用之前执行怎么可能呢?而ConfigurationClassPostProcessor起作用之后修改AllowBeanDefinitionOverriding也没有任何意义了。所以想通过BeanDefinitionRegistryPostProcessor来修改AllowBeanDefinitionOverriding这个属性根本行不通。如果说需要修改这个属性而且起作用那么必须在下图标识1之前,如果是基于xml的容器那么还得在标识2的方法里面或者方法之前。所以想通过使用BeanDefinitionRegistryPostProcessor这种后置处理器来干预是没有任何机会的。
在这里插入图片描述
那么怎样才可以修改这个属性并真的起作用呢?使用容器类不同的构造器,在调用refresh方法之前修改属性即可。
在这里插入图片描述
在《Spring源码深度解析》当中也提供了一个方式,那就是扩展容器类实现。比如

package com.example.demo;

import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @date: 2021/4/17 19:26
 * @description: 扩展ClassPathXmlApplicationContext
 * @version: 1.0
 */
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {

    @Override
    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        super.setAllowBeanDefinitionOverriding(false);
        super.customizeBeanFactory(beanFactory);
    }
}

当然还有其他的各种方式,但是这个老师的例子是不行的!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lang20150928

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值