问题描述

  我们在使用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自动注入同名问题剖析的更多相关文章

  1. Quartz与Spring集成 Job如何自动注入Spring容器托管的对象

    在Spring中使用Quartz有两种方式实现:第一种是任务类继承QuartzJobBean,第二种则是在配置文件里定义任务类和要执行的方法,类和方法可以是普通类.很显然,第二种方式远比第一种方式来的 ...

  2. Spring框架学习之依赖注入

    Spring框架从2004年发布的第一个版本以来,如今已经迭代到5.x,逐渐成为JavaEE开发中必不可少的框架之一,也有人称它为Java下的第一开源平台.单从Spring的本身来说,它贯穿着整个表现 ...

  3. Spring框架使用@Autowired自动装配引发的讨论

    问题描述 有同事在开发新功能测试时,报了个错,大致就是,在使用 @Autowired 注入时,某个类有两个bean,一个叫a,一个叫b. 一般这种情况应该声明注入哪个bean,他没有声明,他不知道这个 ...

  4. spring mvc:属性无法自动注入

    在使用spring mvc 3开发一个项目模块时,遇到这样一个奇怪的问题: 前端页面发送的请求中,所有参数都无法自动注入到指定的@ModelAttribute对象中,经过检查,参数名称与接受对象的属性 ...

  5. spring boot测试类自动注入service或dao

    使用Spring Boot进行单元测试时,发现使用@Autowired注解的类无法自动注入,当使用这个类的实例的时候,报出NullPointerException,即空指针异常. Spring Boo ...

  6. Spring Boot @Autowired 没法自动注入的问题

    Application 启动类: @SpringBootApplication @EnableConfigurationProperties @ComponentScan(basePackages = ...

  7. web 工程中利用Spring的 ApplicationContextAware接口自动注入bean

    最常用的办法就是用 ClassPathXmlApplicationContext, FileSystemClassPathXmlApplicationContext, FileSystemXmlApp ...

  8. quartz整合spring框架service层对象注入为null解决方案

    Job实现类代码 package cn.itcast.quartz; import org.quartz.Job; import org.quartz.JobExecutionContext; imp ...

  9. Spring框架学习08——自动代理方式实现AOP

    在传统的基于代理类的AOP实现中,每个代理都是通过ProxyFactoryBean织入切面代理,在实际开发中,非常多的Bean每个都配置ProxyFactoryBean开发维护量巨大.解决方案:自动创 ...

随机推荐

  1. haproxy Mycat集2---KeepAlived

    KA 配两台 MASTER,BACKUP节点 安装Keepalived 1.下载安装依赖包 yum install -y wget make gcc openssl-devel popt-devel ...

  2. fping简介及使用方法

    fping命令的官网为:http://fping.org/ 历史版本下载地址:http://fping.org/dist/ 第一步:安装. wget http://fping.org/dist/fpi ...

  3. bzoj 3579: 破冰派对

    题意: 给你一个图,问你有多少个方案把他分成连个新的图.使得一个图是一个团,另外一个是独立集 一些闲话: 以前做过一次这个题..当时听说爆搜可以过,就无脑莽过去了.. 也没有思考为什么爆搜能过,或者有 ...

  4. Android开发 View的UI刷新Invalidate和postInvalidate

    Invalidate 正常刷新 /** * 使整个视图无效.如果视图可见, * {@link #onDraw(android.graphics.Canvas)} 调用此方法后将在后续的UI刷新里调用o ...

  5. drop database出现1010

    > drop database glc; ERROR (HY000): Error dropping database (can't rmdir './glc/', errno: 17) Fri ...

  6. WifiManager Wifi 管理器&&知识点

    WifiManager 主要使用的技术: SimpleWifi,MahaApp.Metro控件 一 网卡设置 1.获取所有网卡(NetWorkAdapter类) 方法A 通过API SELECT * ...

  7. Linux 添加时间

    添加在指令后面 `date +%Y%m%d%H%M`注意date和+之间一定要有空格 ps: %%    一个文字的 %  %a    当前locale 的星期名缩写(例如: 日,代表星期日)  %A ...

  8. 数据提取--JSON

    什么是数据提取? 简单的来说,数据提取就是从响应中获取我们想要的数据的过程 非结构化的数据:html等 结构化数据:json,xml等 处理方法:正则表达式.xpath 处理方法:转化为python数 ...

  9. 如何在CRichEditCtrl控件中直接读如RTF格式的文件(这个是通过流的方式来读取文件)

    如何在CRichEditCtrl控件中直接读如RTF格式的文件   Inserting an RTF string using StreamIn   ------------------------- ...

  10. mysql中geometry类型的简单使用

    mysql中geometry类型的简单使用 编写本文的目的: 让和两天前的我一样的初学者,能够更快的使用geometry类型存储空间点数据    也是为了自己加深印象,更熟练的使用geometry类型 ...