在对Spring Container管理的bean进行配置时,有一个很好用的功能就是自动注入,可以根据不同规则对bean所依赖的bean进行自动set,相信最常用的就是设置default-autowire="byName"这种方式啦;这样我们在进行配置时就可以减少很多的xml代码、当我们在Java代码中新增或减少bean的注入时就无需再对xml配置进行同步变更(针对采取xml配置的方式,若采用Annotation的方式则不存在该问题),从而提高了编码效率;

一般情况下采取这种自动注入的方式MS也没啥问题,但是在一些特殊情况下可能会导致莫名其妙的问题,从而要花费大量的时间去排错;在最近的一个项目中就被我遇上了;问题大概是这样的:以前的应用基于Oracle单数据源进行开发,Spring配置采取byName自动注入;但由于数据量剧增导致Oracle服务器不堪重负,小型机扩容成本太高,于是我们决定将原有应用改造成基于Mysql的按用户水平划分的分布式DB结构;这样的话,就需要修改DAO层关于datasource相关的一些配置,在调整过程中,问题就产生了;

原有与datasource相关的大致配置如下:
<beans default-autowire="byName"> <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="cache"> <value>false</value> </property> <property name="proxyInterface"> <value>javax.sql.DataSource</value> </property> <property name="jndiName"> <value>java:OracleDS</value> </property> </bean> <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="configLocation"> <value>/bean/ibatis/sqlmap-config.xml</value> </property> </bean> <bean id="daoDefinition" abstract="true"> <property name="sqlMapClient" ref="sqlMapClient"/> <property name="dataSource" ref="dataSource"/> </bean> </beans>
所有的DAO配置中都设置parent="daoDefinition",并在Java实现中extends SqlMapClientDaoSupport;

应用改造时,我们采用了基于Spring的AbstractRoutingDataSource来实现动态数据源的加载,但由于应用中只有部分表的数据能进行分库,所以改造后的系统中将会存在两种类型的数据源,其一仍然为原Oracle主库,通过JNDI访问、其二为新增的Mysql分库,通过动态ds访问;

由于AbstractRoutingDataSource从当前线程中获取绑定的用户信息,而在同一次用户的操作中,可能会存在既访问原来的Oracle主库、又访问Mysql分库的情况,所以动态ds中需要兼容这种情况,并且在每次DB操作完成之后需要清空当前线程中绑定的变量,这样主要是为了避免所有的DAO层都进行变动,但在哪里自动完成当前线程绑定变量的清空操作呢?当然是在SqlMapClientTemplate中,因为所有的DAO操作都是通过这个template封装接口进行访问的,所以需要继承该类并对所有的method进行扩展;新的配置如下:
<beans default-autowire="byName"> <bean id="mixedDdbDatasource" class="com.alisoft.eshop.ddb.datasource.MixedDdbDatasource"> <property name="ddbMetaService" ref="ddbMetaService" /> <!-- Oracle数据源的JNDI名称、默认值为java:jdbc/ds --> <!-- <property name="oracleJndiDsName" value="java:OracleDS" /> --> </bean> <bean id="ddbSqlMapClientTemplate" class="com.alisoft.eshop.ddb.dao.support.DdbSqlMapClientTemplate"> <property name="sqlMapClient" ref="sqlMapClient"/> <property name="dataSource" ref="mixedDdbDatasource"/> </bean> <bean id="ddbDaoDefinition" abstract="true"> <property name="sqlMapClientTemplate" ref="ddbSqlMapClientTemplate"/> <property name="ddbMetaService" ref="ddbMetaService"/> </bean> </beans>
涉及到访问Mysql分库的DAO均设置parent="ddbDaoDefinition",并在Java实现中extends DdbSqlMapClientDaoSupport;

这样的配置MS没有问题,template中已经明确设置采用动态ds,DAO实现类中也已明确继承新的template类!后来经跟踪代码发现:还是Spring的自动注入惹的祸
原因如下:

SqlMapClientDaoSupport中可以自动注入3个bean:datasource、sqlMapClient、sqlMapClientTemplate;而这3个bean的注入是有先后顺序的,其实datasource、sqlMapClient都是设置到sqlMapClientTemplate中的!问题就出现在这里了;
在我们的配置中,parent中显示设置了sqlMapClientTemplate,而没有设置其他两个属性;按照Spring自动注入的默认规则,首先会将显示set的bean注入,此时的sqlMapClientTemplate是被设置了动态ds的正确的bean,但是显示set注入完成之后,Spring还会根据是否自动注入扫描bean里面的set方法,完成非显示bean的注入,此时问题就出现了,Spring根据默认datasource这个name找到了原有的基于JNDI的那个bean进行注入了,于是sqlMapClientTemplate中的datasource就被覆盖了,也即原来正确的动态ds被替换回了原有的基于JNDI的Oracle主库的datasource!

原因找到之后问题就好解决了,方法很多,比如:

  1. 在ddbDaoDefinition配置中增加显示设置<property name="dataSource" ref="mixedDdbDatasource"/>;
    注意:为保险起见,这个显示datasource的set放在sqlMapClientTemplate之后,因为Spring会按照设置的先后顺序进行注入,若放在sqlMapClientTemplate之前,则会被后者覆盖掉,具体依赖于被注入bean内set方法的实现;
  2. 去掉DAO配置文件中的自动注入;其实DAO层一般不会依赖于其他的bean,都是被依赖的,所以这个自动注入完全没有必要,反而给应用带来隐患;

网店版重生系列:都是Spring配置中自动注入惹的祸相关推荐

  1. 网店版重生系列:多数据源单sqlMapClient导致NullPointerException问题跟踪

    从前面的<网店版重生系列:都是Spring配置中自动注入惹的祸>中我们可以看出一些有关datasource.sqlMapClientTemplate.sqlMapClient的相关配置信息 ...

  2. 网店版重生系列:因为webwork.configuration.xml.reload遭遇Web应用性能测试瓶颈

    网店版重生项目中,因为我们要将最主要的核心数据由Oracle迁移到分布式Mysql中:虽然说业务逻辑不进行任何改动,只是将数据源由单一的Oracle改造成基于Mysql的动态数据源,但为了保险起见,我 ...

  3. 网店版重生系列:回头看Jboss配置项CatchSystemOut、Java2ClassLoadingCompliance、UseJBossWebLoader

    最近一直忙于做一个纯技术驱动的项目:网店重生!就是要把网店版改造成为基于Mysql的分布式应用:由于在过去的1年半的时间里面,网店版都没有怎么发展,只是一些例行维护:此次要这么大规模重构的确是一个不小 ...

  4. 网店版重生系列:Linux下Jboss启动、关闭、端口配置等常见问题FAQ

    单台Linux服务器中如何部署多个独立应用,即多个应用不能run在一个jboss实例中?换言之,这个问题也可以这样描述:在单台Linux服务器中服务启动多个Jboss实例? 默认情况下,jboss启动 ...

  5. 为什么我不建议你这么干?教育部说打电竞、开网店、自媒体都属于就业的背后……...

    loonggg 读完需要 5 分钟 速读仅需 2 分钟 大家好,我是你们的校长. 最近教育部明晰高校毕业生就业统计指标:打电竞.开网店.自媒体都属于就业了,这几天持续上了热搜. 在知乎上很多人都在问: ...

  6. spring配置中加载properties文件方法

    首先,遇到一个问题,spring配置中加载properties文件配置如下: <context:property-placeholder ignore-unresolvable="tr ...

  7. spring配置中id和name属性的区别

    可能大家在网上都应该搜索过在 Spring 配置中 id 和 name 属性的区别,可能你会搜索到有一大堆的区别,不过在我这里可能不一样了. 我这里 Spring 的版本为 3.2.4,区别不是很大, ...

  8. Spring 拦截器和过滤器中自动注入为 null 的原因及解决方案

    起因 开发过程中在过滤器(filter)中注入Bean出现空指针异常,通过查找资料了解空指针的原因,特此记录. 问题分析 由于其他bean在service,controller层注入一点问题也没有,开 ...

  9. 解决Spring+Quartz无法自动注入bean问题

    问题 我们有时需要执行一些定时任务(如数据批处理),比较常用的技术框架有Spring + Quartz中.无奈此方式有个问题:Spring Bean无法自动注入. 环境:Spring3.2.2 + Q ...

最新文章

  1. BIOS不识别linux,linux – 在BIOS中启用VT但KVM无法检测到
  2. 维基链(WICC)当前币值应该还远远没有达到它本身应有的高度
  3. 项目执行maven update时,报:Preference node org.eclipse.wst.validation
  4. OpenGL学习笔记以及其它学习思考
  5. 前端传值后端接收不到_解决vue get请求传参后端接收不到参数值(java sptingboot)
  6. 如何设置jinternalframe无边框_word文档美化技巧:加个边框提升版面颜值
  7. MySQL无法重启问题解决Warning: World-writable config file '/etc/my.cnf' is ignored
  8. (46)VHDL实现4位桶性形移位器
  9. python布尔运算可以比较浮点数吗_Python3 基础之:令人困惑的浮点数运算
  10. java面向对象的特征封装_Java总结-Java面向对象的基本特征之一:封装性
  11. Replace Record with Data Class
  12. vivo手机通用的官方售后解锁工具包_一加全系列手机一键解锁BootLoader超详细图文刷机教程...
  13. 联想服务器怎么备份系统软件,联想电脑管家备份电脑驱动程序教程
  14. 聊求职:写简历的大原则与小技巧
  15. 宇宙简史|生物学家也要了解的物理
  16. 京东Deco 智能代码体验版正式上线啦,快来体验设计稿一键生成代码~
  17. 统计|如何理解估计量的三条评价标准
  18. 基于matlab的颜色识别与提取_机器视觉综合实训有得
  19. ESP8266-Arduino编程实例-MMA7455L加速计驱动
  20. 21天学会Java之(Java SE第十三篇):网络编程、TCP/UDP通信

热门文章

  1. 你用什么方法做副业赚钱?
  2. 安装MSDE2000进度条不动
  3. 在团队中如何带领新手
  4. 前端工程师的 浏览器兼容性自查工具——caniuse
  5. 关于无限小数的问题————编程语言描述
  6. 前端js多个小数相加出现无限循环小数的解决办法
  7. 赵大超的学习周志(十)
  8. animate用法 js原生_animate动画、原生JS实现轮播图
  9. PowerToys Windows 工具集
  10. 江苏大学2020计算机考研,江苏大学2020年硕士研究生复试时间及主要安排