怎样才能给spring的bean的xml配置文件里的bean id加上一个前缀?
定义bean的配置文件,想加一个统一的前缀,但不是所有的bean都要加,我把前缀写到配置文件里,不过xml里对应的占位符不解析。
spring-config-beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="spring-config-beans.xml"/>
</beans>
spring-config-beans.xml
<?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:jsf="http://jsf.jd.com/schema/jsf"
default-lazy-init="true" default-autowire="byName"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://jsf.jd.com/schema/jsf http://jsf.jd.com/schema/jsf/jsf.xsd">
<!-- I want to bean id will be "asdfStudentBean",now it can't work -->
<bean id="`${myprefix}`StudentBean"
class="com.example.beans.StudentBean">
<property name="name" value="${myprefix}" />
<!-- if beanid is "asdfStudentBean" then it can work, the name will be "asdf" -->
</bean>
</beans>
application.properties
myprefix=asdf
StudentBean.java
public class StudentBean {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void printName() {
System.out.println("your name is : " + this.name);
}
}
TestCode
@SpringBootTest
class Demo3ApplicationTests {
@Autowired
private ApplicationContext applicationContext;
@Test
void contextLoads(){
StudentBean bean = (StudentBean) applicationContext.getBean("asdfStudentBean");
bean.printName();
}
}
<bean id="${myprefix}StudentBean" [FAIL]
<bean id="`${myprefix}`StudentBean" [FAIL]
<bean id="@@myprefix@@StudentBean" [FAIL]
<bean id="$${myprefix}StudentBean" [FAIL]
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
起初开始看到这个问题,修改自定义
beanName
,我下意识就联想到了spring
给的扩展接口BeanNameGenerator
,但是以前没在xml
注入bean
的方式使过,确实有点心虚,试了一下,确实不行,所以才弱弱的在问题里评论了一句,看看有没有直接说使用扫描注解的方式注入可以不?可以的话,我就不再看了。结果昨天问了,今天一直没回复,想着干脆也看看,我就不信xml
注入bean
的方式没有给出修改beanName
的扩展口子,毕竟这是spring
啊,"缝合怪"大师因为
bean
实例化之前,肯定先要有BeanDefinition
的,也就是bean
的定义,bean
的元数据,所以对于BeanDefinition
的修改,我先去看了一下BeanFactoryPostProcessor
,这是常见的扩展接口,不过一看接口方法的签名坏了,这只是一个ConfigurableListableBeanFactory
用
ConfigurableListableBeanFactory
你是可以根据beanName
拿到对应的BeanDefinition
,但我们要做的事,其实不是真正要BeanDefinition
啊,我们恰好要修改beanName
,所以用BeanFactoryPostProcessor
这个扩展接口就不行了,因为使用它时BeanFactory
中就已经生成好了beanName
和BeanDefinition
的对应关系。因此我当时想着就是从调用BeanFactoryPostProcessor
的源码出发,往前看,再找其他可以扩展的接口结果在
BeanFactoryPostProcessor
的调用类PostProcessorRegistrationDelegate
的invokeBeanFactoryPostProcessors
方法中,没找几行代码,就看到了另一个扩展接口BeanDefinitionRegistryPostProcessor
这名字一看就很对味儿,看一下方法签名
这回参数是
BeanDefinitionRegistry
,好家伙,这不就是找到根儿了么。顾名思义就是Bean
定义的注册表或者注册中心啊,再看看其中接口的方法,注册一个BeanDefinition
,删除一个BeanDefinition
,这不都整全了嘛那改个
beanName
就太简单了,举个栗子看吧三行其实就完成了修改功能。虽然还没有完成题主的前缀注入问题,也算是完成一半,不过呢,嘿嘿,简单的
bean
这样做肯定可以修改beanName
,但这里面鸭还有很多未知的坑!恰好我这次demo
的xml
文件是之前用过的,自己莫名就踩上了,顺道提上几个假设
A
和B
都是bean
,而且A extends B
,若要修改A
为A_new
,那还需要再修改B
的BeanDefinition
中的parentName
为A_new
alias
别名问题假设
A
还有name
配置,也就是id
和name
同时存在,这时name
的值就是别名,若要修改A
为A_new
,则还需要修改BeanDefinitionRegistry
中的别名映射关系好了,上面这两个坑说完了,最后来说说看起来很简单,但实则不太那么容易的注入前缀问题,也就是注入
properties
问题。我开始也是很天真,想着这不是so easy
嘛,我定义的MyBeanDefinitionRegistryPostProcessor
中加个属性prefix
然后配置文件上一配
结果一执行,没错,这个
prefix
死活都是${myprefix}
,它就不给姥姥我变!幸好我试了一下其他bean
这个${myprefix}
是可以注入的,不然我就可能宕机了。还是看了一下源码,我突然就释然了。。。
BeanDefinitionRegistryPostProcessor
到底是什么时机在执行的,它不就是在BeanFactoryPostProcessor
之前执行的么,而可以实现${myprefix}
的el
表达式解析的逻辑PropertySourcesPlaceholderConfigurer
其实就是个BeanFactoryPostProcessor
的实现,因此BeanDefinitionRegistryPostProcessor
执行的时候还没有轮到PropertySourcesPlaceholderConfigurer
执行呢。也就是说
BeanDefinitionRegistryPostProcessor
的实现类可能只能有干巴巴的逻辑,不能享受到过多的spring
注入的东西了。它虽然是个bean
,但是里面基本上不可能注入其他业务上的bean
,毕竟BeanDefinitionRegistryPostProcessor
中处理的不还都是人家bean
的beanName
和BeanDefinition
嘛所以此时我本身就想来写回答了,说让到时候这个前缀就写死在代码里,不要写在配置文件里了,不过呢还是很揪心,感觉就差一点点就完美了,个人觉得不完美的答案,我宁愿还是不写。
于是我把此时的问题换成怎么在类中读取配置文件,因为常规的
spring
读取配置不行,我就想到了spring
对于资源的抽象,毕竟配置文件本质来说也是一种资源,这里就不得不提到了其抽象接口org.springframework.core.io.Resource
而我开始不是说其他
bean
注入${myprefix}
是可以的,而处理这套逻辑前肯定要先读取配置文件,进入PropertySourcesPlaceholderConfigurer
中可以找到关键抽象类PropertySource
,这是键值对的抽象基类,只要我们想办法将配置文件抽线成PropertySource
,然后就可以调用它的getProperty
方法获取配置的值了而之前说配置文件的抽象是
Resource
,所以找到他们之间的关系就可以了,我是从返回参数入手。直接搜PropertySource
的使用地方,只看方法返回的那项就几个,而且前几个都是不是创建或者不太像,后2个,从类名就基本断定了,
PropertySourceFactory
和DefaultPropertySourceFactory
,其中DefaultPropertySourceFactory
还是实现PropertySourceFactory
,看PropertySourceFactory
的方法签名虽然没有
Resource
,但是第二个参数EncodedResource
还是很像的,进去一看,果然包含了Resource
,哈哈哈不清楚怎么把
Resource
包装成EncodedResource
,那赶紧搜搜哪里调用的,然后把他们官方的调用copy
过来就可以啦,果然有一个ConfigurationClassParser
ok,结合上面的逻辑,这下就简单了,我们首先给
MyBeanDefinitionRegistryPostProcessor
中再增加一个属性resource
同时配置文件也相应配置上,再顺道删除
prefix
的注入,反正也注入不进去,再新增了一个init
方法作为初始化方法那我们的
init
方法就来帮助我们根据resource
来初始化prefix
这里有个
buildPropertySource
方法也很简单,就是copy
过来的这一下我们的配置文件的前缀在执行
MyBeanDefinitionRegistryPostProcessor
就完美注入进来啦至于在
MyBeanDefinitionRegistryPostProcessor
的实际业务逻辑中怎么控制哪些bean
要配置前缀,那这个就看题主自己的设计啦,方法很多,比如特定的都实现某个接口啊,或者类上加一些自己的某个标记注解啊,都行,反正BeanDefinition
有bean
的class
,以及还有很多其他属性。可以根据自己情况定制以上就是我的小小看法啦,如果题主你对于
spring
容器相关知识还不是很清楚,可能看我写的思路还是有点不太顺溜,你可以先根据我提供的方法试试看,以后有机会再回过头来看吧,这里面spring
里的东西很多,我自己都觉得有时候看昏了,但是他们的设计确实很棒,也就是编程思想很6,想看懂确实需要点功夫,我这次也算是帮我自己再多学点了点东西,所以大家都加油吧!φ(゜▽゜*)♪补充一下,相关代码放这github上了,方便参考
其实这个问题好办.
你自己实现一个BeanFactory,然后需要加前缀的bean都通过这个工厂来创建即可,这个时候你就可以通过value带入${}你的配置文件变量,重新定义bean name的生成规则
自定义 BeanFactoryPostProcessor 就好了,这里面可以修改bean的定义,核心代码如下:
newName和oldName自己通过规则运算一下