看过上一章的话,应该会对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);
}
}
当然还有其他的各种方式,但是这个老师的例子是不行的!