Spring框架使用ByName自动注入同名问题剖析
问题描述
我们在使用spring框架进行项目开发的时候,为了配置Bean的方便经常会使用到Spring当中的Autosire机制,Autowire根据注入规则的不同又可以分为==ByName==和==ByType==这两种机制(两者的用法和区别可以参考Spring@Autowire官方文档)。但大家在使用Autowire当中==ByName==机制的时候有没有思考过这样一个问题,当我们配置了两个name属性相同的Bean,Spring在自动注入的时候会采取怎样处理方式?会覆盖?还是抛出异常?还是其他的方式?
实例验证
要得到上面的问题的答案,最简答的方案就是写实列进行验证咯~我们先在Spring中配置两个同名的Bean,配置信息如下:
<bean name="test1" class="com.yanxiao.Test1">
<property name="age" value="20" />
</bean>
<bean name="test1" class="com.yanxiao.Test1">
<property name="age" value="21" />
</bean>
接下来运行程序加载该配置文件,会发现Spring可以正常启动,于是乎我们之前关于抛出异常的假设是错误的。在程序当中获得获得自动注入的名称为test1的Bean对象打印其age属性,发现输出的结果是21,说明==对于同名的BeanSpring会采取覆盖的策略,后面配置的Bean会覆盖掉前面配置的Bean对象。==
看到这里我们最初的问题已经得到了解答,但这个问题难到不值得我们继续思考下去吗?Spring这种强制的覆盖措施真的合理吗,有些情况下由于开发人员的疏忽在没有意识的情况下导致了同名现象的产生,直接覆盖所导致的一些错误会为开发人员的排查错误带来很大的难度?
那么如何解决这个问题呢?靠开发人员的自律?不定义重复名称的bean?我觉得这个是非常不靠谱的,因为项目依赖可能比较复杂,开发人员不尽相同.所以我认为只有通过在程序中引入一种报错机制才能解决这个问题。
Spring既然作为一款优秀的开源框架,其内部除了覆盖之外有没有提供其他的如报错机制等方案供我们选择呢?
源码剖析
带着上面的问题,我们对验证程序进行单步跟踪,从源码的角度了解Spring内部的处理机制是怎样的。
当我们跟踪当Bean注册阶段,执行DefaultListableBeanFactory类的registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
方法时,发现有一个==allowBeanDefinitionOverriding==属性。
synchronized (this.beanDefinitionMap) {
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
if (!this.allowBeanDefinitionOverriding) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
}
else {
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
观察这段代码的逻辑我们可以发现,Spring框架中自动注入遇到同名Bean时其实提供了不同的方案供我们选择,一种是我们之前已经验证过的覆盖措施,通过观看源码我们发现:其实==采用覆盖方案的话如果我们的项目中有日志打印支持的话,Spring也会提供info级别的日志信息。==另一种便是抛出异常的方式。
项目实践
通过上面的源码,我们知道了Spring框架提供了一种开发人员必须解决id或者name重复的问题后才能成功启动容器的自动注入处理方法,这个方案在有些保证项目高可靠的情况下还是十分有用的,那么该怎样选取这种同名抛异常的处理机制呢?
问题的关键就在于我们应该如何改变allowBeanDefinitionOverriding属性的值,通过查阅文档了解到两种方案供大家参考:
方案一
1、自己写一个继承ContextLoaderListener的listener,比如SpringContextLoaderListener,然后重写方法customizeContext,如
public class SpringContextLoaderListener extends ContextLoaderListener {
@Override
protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {
super.customizeContext(servletContext, applicationContext);
XmlWebApplicationContext context = (XmlWebApplicationContext) applicationContext;
context.setAllowBeanDefinitionOverriding(false); //在这里将XmlWebApplicationContext属性allowBeanDefinitionOverriding设置为false,这个属性的值最终
//会传递给DefaultListableBeanFactory类的allowBeanDefinitionOverriding属性
}
}
2、在web.xml使用自定义的listener,配置如下:
<listener>
<listener-class>com.zyr.web.spring.SpringContextLoaderListener</listener-class>
</listener>
这样,在项目启动时,不同配置文件中如果有同名id或者name的bean,直接抛异常,容器停止启动,到达我们的预期效果。
其实我们的目的仅仅是更改一个属性值,方案一这种自己写一个listener的方法还是有点麻烦。下面还有一种相较更为简便的方案
方案二
1、创建一个实现接口ApplicationContextInitializer的类,如SpringApplicationContextInitializer,代码如下:
public class SpringApplicationContextInitializer implements ApplicationContextInitializer<XmlWebApplicationContext> {
public void initialize(XmlWebApplicationContext applicationContext) {
applicationContext.setAllowBeanDefinitionOverriding(false);//在这里将XmlWebApplicationContext属性allowBeanDefinitionOverriding设置为false,这个属
//性的值最终会传递给DefaultListableBeanFactory类的allowBeanDefinitionOverriding属性
}
}
2、在web.xml文件中添加配置
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.zyr.web.spring.SpringApplicationContextInitializer</param-value>
</context-param>
- 顶
- 0
- 踩
- 0
Spring框架使用ByName自动注入同名问题剖析的更多相关文章
- Quartz与Spring集成 Job如何自动注入Spring容器托管的对象
在Spring中使用Quartz有两种方式实现:第一种是任务类继承QuartzJobBean,第二种则是在配置文件里定义任务类和要执行的方法,类和方法可以是普通类.很显然,第二种方式远比第一种方式来的 ...
- Spring框架学习之依赖注入
Spring框架从2004年发布的第一个版本以来,如今已经迭代到5.x,逐渐成为JavaEE开发中必不可少的框架之一,也有人称它为Java下的第一开源平台.单从Spring的本身来说,它贯穿着整个表现 ...
- Spring框架使用@Autowired自动装配引发的讨论
问题描述 有同事在开发新功能测试时,报了个错,大致就是,在使用 @Autowired 注入时,某个类有两个bean,一个叫a,一个叫b. 一般这种情况应该声明注入哪个bean,他没有声明,他不知道这个 ...
- spring mvc:属性无法自动注入
在使用spring mvc 3开发一个项目模块时,遇到这样一个奇怪的问题: 前端页面发送的请求中,所有参数都无法自动注入到指定的@ModelAttribute对象中,经过检查,参数名称与接受对象的属性 ...
- spring boot测试类自动注入service或dao
使用Spring Boot进行单元测试时,发现使用@Autowired注解的类无法自动注入,当使用这个类的实例的时候,报出NullPointerException,即空指针异常. Spring Boo ...
- Spring Boot @Autowired 没法自动注入的问题
Application 启动类: @SpringBootApplication @EnableConfigurationProperties @ComponentScan(basePackages = ...
- web 工程中利用Spring的 ApplicationContextAware接口自动注入bean
最常用的办法就是用 ClassPathXmlApplicationContext, FileSystemClassPathXmlApplicationContext, FileSystemXmlApp ...
- quartz整合spring框架service层对象注入为null解决方案
Job实现类代码 package cn.itcast.quartz; import org.quartz.Job; import org.quartz.JobExecutionContext; imp ...
- Spring框架学习08——自动代理方式实现AOP
在传统的基于代理类的AOP实现中,每个代理都是通过ProxyFactoryBean织入切面代理,在实际开发中,非常多的Bean每个都配置ProxyFactoryBean开发维护量巨大.解决方案:自动创 ...
随机推荐
- leetcode 题型 数据结构 解法 分类总结
第2章 线性表 2.1 数组 2.1.1 Remove Duplicates from Sorted Array 2.1.2 Remove Duplicates from Sorted Array I ...
- Tomcat调优详解
前言 在这里告诫一下那些感觉自己啥都会的朋友们,其实你会的可能只是皮毛,不要感觉这个东西以前已经做过了,就不想去做了 其实你还远没有达到精通的地步,遇到以前做过的东西,也要用心的再去做一遍,你可能会从 ...
- 常见的arp欺骗
三.常见ARP欺骗形式 1.假冒ARP reply包(单播) XXX,I have IP YYY and my MAC is ZZZ! 2.假冒ARP reply包(广播) Hello everyon ...
- redis config
网络上抄袭过来的 然后顺序自己做点笔记 http://chembo.iteye.com/blog/2054021 这里有各个key 详细的描述 # redis 配置文件示例 # 当你需要为某个配置项指 ...
- Python连接数据库流行用到的第三方库
Python连接数据库流行用到的第三方库: mysqldb:只支持Python2.x mysqlclient : mysqldb的衍生版本,完全兼容mysqldb,同时支持Python3.x,安装较复 ...
- soj114 中位数
题意:给你一个长度为2n-1的数组A,设Bi是A的1~2i-1的中位数.问打乱A,有多少种不同的B序列? 标程: #include<bits/stdc++.h> using namespa ...
- 使用ajax怎么请求跨域资源
1.ajax中添加“xhrFields”和“crossDomain”,如: $.ajax({ url: url, data: data, type: "post", xhrFiel ...
- LUOGU P4042 [AHOI2014/JSOI2014]骑士游戏 (spfa+dp)
传送门 解题思路 首先设\(f[x]\)表示消灭\(x\)的最小花费,那么转移方程就是 \(f[x]=min(f[x],\sum f[son[x]] +s[x])\),如果这个转移是一个有向无环图,那 ...
- [JZOJ 5788] 餐馆
思路: 考虑树形dp. 我们设\(dp[i][j][0/1]\)表示在\(i\)为根的子树中花费\(j\)单位时间,最终回到/不必回到\(i\)的最大收益. 转移三种: \(dp[x][j][0] = ...
- Socket.EndReceive 方法 (IAsyncResult)
.NET Framework (current version) 其他版本 .NET Framework 4 .NET Framework 3.5 .NET Framework 3.0 . ...