问题描述

  我们在使用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. JDBC_事务说明

    JDBC控制事务:1.事务:一个包含多个步骤的业务操作,如果这个业务操作被事务管理则多个步骤同时成功或同时失败2.操作: 1.开启事务 2.提交事务 3.回滚事务3.使用Connection对象来管理 ...

  2. 再次封装ajax函数,统一入口

    根据API写网页的时候,每个页面都需要ajax请求,每次都写一大堆请求,配置什么的太麻烦,于是打算封装一个ajax函数,统一调用: 开始时是使用return返回ajax,如下: function cr ...

  3. JS事件 光标聚焦事件(onfocus)当网页中的对象获得聚点时,执行onfocus调用的程序就会被执行

    光标聚焦事件(onfocus) 当网页中的对象获得聚点时,执行onfocus调用的程序就会被执行. 如下代码, 当将光标移到文本框内时,即焦点在文本框内,触发onfocus 事件,并调用函数messa ...

  4. PokerNet-poker recognition: 基于人工神经网络的扑克识别 (5)

    结果解读 结果1 结果2 结果1 void computeBCValue(cv::Mat mat_img, std::vector<bbox_t> result_vec, std::vec ...

  5. bzoj1568 Blue Mary

    题意:P:加入一条一次函数.Q:询问x位置的最大函数值. 标程: #include<bits/stdc++.h> using namespace std; ; int q,x,n; dou ...

  6. Windows 开启win32 控制台

    {     AllocConsole();     FILE *Journal = NULL;     freopen_s(&Journal, "CONOUT$", &qu ...

  7. C++——类的继承(派生)

    类的继承就是子类可以拥有父类的成员变量和成员函数 //public 修饰的成员变量 方法 在类的内部 类的外部都能使用//protected: 修饰的成员变量方法,在类的内部使用 ,在继承的子类中可用 ...

  8. (转)详解HttpURLConnection

    请求响应流程 设置连接参数的方法 setAllowUserInteraction setDoInput setDoOutput setIfModifiedSince setUseCaches setD ...

  9. .net core, docker 在vs2019开发过程中的问题以及解决办法

    .net core, docker 在vs2019开发过程中的问题以及解决办法 记录下来,帮助Ta人~ 1.vs调试,快Build完后提示Docker 端口:xxxx,xxxx,xxxx占用 解决办法 ...

  10. 命令学习_nslookup

    nslookup 域名 这是最常用最简单的用法,可以直接获得目标域名的IP地址和CNAME. 如下是A记录的返回情况 nslookup命令会采用先反向解释获得使用的DNS服务器的名称,上图中ns.gu ...