`

Spring3注释装配的最佳实践

阅读更多

2005夏于上海,初次使用了Spring框架开发企业应用,当时还没有中文书籍,只能看Spring官方提供的Reference,甚是简陋,直到一年后人民邮电出版了第一本关于Spring技术的中文翻译书籍《Spring in action》,解决了广大人民群众的精神需求,也因此书让我认识了Manning出版社,之后一直在关注他的Action系列图书,此前将众多书籍封面整理成册,闲时品茶拿来翻阅不亦乐乎,有兴趣的同学可以雅俗共赏《Manning出版社In Action系列图书 》。此篇且谈Spring注释配置之实践。

 

关键词: Spring, Annotation, iBatis, 依赖注入(IOC), BeanNameGenerator, Inner Class, 后依赖注入

 

引言:

 

长久以来国内的众多应用都在使用Spring框架,它为我们带来的好处不言而喻。但问题是Spring2.0以下版本尚未支持注释装配,而企业应用大多分作MVC三层结构,每层Bean的配置渐渐膨胀,直到打开了XML文件,IDE不堪重负崩溃为止,情形实为惊人。后有了Convention over Configuration的软件设计范式,即“约定优于配置”,也作“约定编程”。Ruby and Rails和EJB3也都按此实现,Spring注释也基于此。

 

首先,在解答为什么要使用注释装配之前,先看看没有它时配置文件臃肿的样子,如:持久层DAO的Spring配置文件

 

 

<?xml version="1.0" encoding="UTF-8"?>
<beans ‘略去声明’>
			
	<bean id="scDbInfoDAO" class="com.data.switching.db.dao.impl.ScDbInfoDAOImpl"
		parent="sqlMapClientDAO" />
						
	<bean id="scFtpInfoDAO" class="com.data.switching.db.dao.impl.ScFtpInfoDAOImpl"
		parent="sqlMapClientDAO" />
				
	<bean id="scParmInfoDAO" class="com.data.switching.db.dao.impl.ScParmInfoDAOImpl"
		parent="sqlMapClientDAO" />
				
	<bean id="scParmTypeDAO" class="com.data.switching.db.dao.impl.ScParmTypeDAOImpl"
		parent="sqlMapClientDAO" />
				
	<bean id="scRoleDAO" class="com.data.switching.db.dao.impl.ScRoleDAOImpl"
		parent="sqlMapClientDAO" />	
			
	<bean id="scRoleMenuDAO" class="com.data.switching.db.dao.impl.ScRoleMenuDAOImpl"
		parent="sqlMapClientDAO" />	
			
	<bean id="scSiteLoadDAO" class="com.data.switching.db.dao.impl.ScSiteLoadDAOImpl"
		parent="sqlMapClientDAO" />	
			
	<bean id="scSiteStatDAO" class="com.data.switching.db.dao.impl.ScSiteStatDAOImpl"
		parent="sqlMapClientDAO" />	

略去同样999个配置 ... ... 

 

使用后的情况:

 

 

<?xml version="1.0" encoding="UTF-8"?>
<beans>
	
<context:annotation-config />

<context:component-scan base-package="com.longtop.data.switching.db.dao"
name-generator="com.seraph.bi.suite.support.core.generator.IBatisDaoBeanNameGenerator" />				
		
</beans>
 

现在大家想必都了解到为什么使用注释配置,两者之间后者很优雅,而这全在于约定优于配置。

 

解决方案:

 

改造过程是,首先在DAO的实现类中加入@Repository标签,说明这是持久层的服务。另外两层的标签@Service, @Controller,实现类如下:

 

 

import org.springframework.stereotype.Repository;
...

@Repository
public class ScDbInfoDAOImpl extends SqlMapClientDaoSupport implements ScDbInfoDAO {
...
 

在配置文件中加入:

 

 

<context:annotation-config />

<context:component-scan base-package="com.longtop.data.switching.db.dao"
name-generator="com.seraph.bi.suite.support.core.generator.IBatisDaoBeanNameGenerator" />

 

因接口名为ScDbInfoDAO,而实现类名为 ScDbInfoDAOImpl,引用类中的域名是接口的首字母小写名scDbInfoDAO ,而容器生成的默认类名是 scDbInfoDAOImpl,所以不行,但spring预留了

接口BeanNameGenerator,只要实现它我们就可以自己指定生成bean的名字,这里的实现类如下:

 

/**
 * 类说明: 生成iBatis的DAO的Spring注册名,规则是首字母小写,并去掉后缀名<br>
 * 创建时间: 2011-1-26 下午12:44:20<br>
 * 
 * @author seraph<br>
 * @email: seraph115@gmail.com<br>
 */
public class IBatisDaoBeanNameGenerator implements BeanNameGenerator {

	private static final Logger logger = Logger
			.getLogger(IBatisDaoBeanNameGenerator.class);

	private static final String DAO_IMPLEMENTS_SUFFIX = "Impl";

	public String generateBeanName(BeanDefinition paramBeanDefinition,
			BeanDefinitionRegistry paramBeanDefinitionRegistry) {
		String[] strs = paramBeanDefinition.getBeanClassName().split("\\.");
		String shortName = strs[strs.length - 1];
		shortName = StringUtils.uncapitalize(shortName);
		shortName = shortName.replace(DAO_IMPLEMENTS_SUFFIX, "");

		logger.debug("Generated a ibatis DAO bean's name: [" + shortName + "]");

		return shortName;
	}

}
 

到这里我们可以自由的指定注释类的bean名称,但对于为DAO提供dataSource和sqlMapClient的Inner Class,即parent="sqlMapClientDAO"要如何处理呢?

 

<bean id="scRoleDAO" class="com.data.switching.db.dao.impl.ScRoleDAOImpl"
		parent="sqlMapClientDAO" />

 

<bean id="sqlMapClientDAO"
	class="org.springframework.orm.ibatis.support.SqlMapClientDaoSupport"
	abstract="true">
	<property name="dataSource" ref="${jdbc.dataSource}" />
	<property name="sqlMapClient" ref="sqlMapClient" />
</bean>

<bean id="sqlMapClient" class="com.seraph.bi.suite.support.core.IncludesSqlMapClientFactoryBean">
	<property name="configLocation" value="classpath:ibatis/platform/orcl/sqlmap.xml" />
</bean>

 

为了解决此问题,我们实现了一个后置注入的类:SqlMapClientDaoInjector用来在DAO加载到context中后注入其依赖。类代码如下:

 

/**
 * 类说明: 向iBatis的DAO中注入依赖<br>
 * 创建时间: 2011-1-26 上午10:51:28<br>
 * 
 * @author seraph<br>
 * @email: seraph115@gmail.com<br>
 */
public class SqlMapClientDaoInjector implements ApplicationContextAware, InitializingBean {

	private static final Logger logger = Logger.getLogger(SqlMapClientDaoInjector.class);
	
	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		SpringContext.setApplicationContext(applicationContext);
	}

	public void afterPropertiesSet() throws Exception {
		Assert.notNull(dataSource, "Property 'dataSource' is required.");
		Assert.notNull(sqlMapClient, "Property 'sqlMapClient' is required.");
		injectDependence();
	}
	
	private void injectDependence() {
                // 获取Context上下文
		ApplicationContext ctx = SpringContext.getApplicationContext();
                // 按类型获取上下文中的对象
                Map<String, SqlMapClientDaoSupport> map = ctx.getBeansOfType(org.springframework.orm.ibatis.support.SqlMapClientDaoSupport.class, true, true);
		for (Iterator<String> i = map.keySet().iterator(); i.hasNext();) {
			try {
				String supportName = (String) i.next();
				SqlMapClientDaoSupport support = map.get(supportName);
				// 后注入依赖
				support.setSqlMapClient(sqlMapClient);
				support.setDataSource(dataSource);
			} catch (RuntimeException e) {
				logger.error("SqlMapClientDaoInjector.injectDependence()", e); 
  			}
		}
	}
	
	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	public void setSqlMapClient(SqlMapClient sqlMapClient) {
		this.sqlMapClient = sqlMapClient;
	}
	
	private DataSource dataSource;

	private SqlMapClient sqlMapClient;

}

 然后加入此类的配置即可,

	<bean id="sqlMapClientDaoInjector"
		class="com.seraph.bi.suite.support.dao.assembly.SqlMapClientDaoInjector">
		<property name="dataSource" ref="${jdbc.dataSource}" />
		<property name="sqlMapClient" ref="sqlMapClient" />
	</bean>

 

至此我们完成了Spring注释配置的改造。

 

总结下实现思路,首先是在需要自动加载的类上加入@Repository注释标签,对于需要改变默认类名生成规则的约定,编写实现BeanNameGenerator接口的类,然后对于需要抽象的内置类的配置,自实现后依赖注入的实现。针对此例我们要体会实现的思路,即了解Spring容器的工作原理和设计思想,而后我们可以对其实现有益且有必要的改进工作,但最终都是旨在简化配置,较少没有必要的工作量。

 

人的懒惰,推进了科技的发展。新年好,祝工作学习愉快!2011年Beijing

分享到:
评论
17 楼 dyllove98 2011-02-12  
Seraph115 写道
gtssgtss 写道
我们公司的配置文件用人名命名,出错了马上知道找谁......Annotation怎么实现?


对于你们公司的这种特殊需求,对于使用Annotation并无影响,给出我的解决方案,首先你们的配置文件以员工名命名,这个可以使用javadoc,如:
xxx

签上大名后就可以搜索了,同样也可以跟踪

用配置文件,我在可以看一个配置文件就可以了解所有类相关的注入情况,依赖关系直接在配置文件中修改即可,也无需修改类源代码,如果用注释的话,恐怕要进入源代码中修改吧
   
16 楼 Seraph115 2011-02-10  
仁者见仁,智者见智。注释开发是一种趋势,EJB3中在使用注释,Hibernate中也在使用注释,简化开发是最终的目的。

至于是否能快速的查找到你想要的类,也要看你的包结构设计是否合理,类名是否真实的反应出类的作用,马汀大叔的敏捷一书中也提到好的类名方法名要比代码注释更重要。

Seraph云云
15 楼 yjl6691088 2011-02-03  
个人感觉 作为coder 注解才是王道。简单 方便 高效。
但xml使用起来清晰 易懂 后期维护人员 不要那么蛋疼了。。。
14 楼 ahyyxx222 2011-02-03  
我觉得,BEAN多了以后,从XML中查找一个BEAN的配置并不比从代码中找到那个类要快,改起来一样不比改注解快,所以我认为完全可以用注解代替那堆BEAN定义。

仅有那种规则性的,牵涉到众多类的BEAN值得在XML里写
13 楼 little_shieh 2011-02-02  
注释这个东西吧确实是好东西,比如Spring事务配置方面的这些用注释就比较简单。但是其他的一些比如action的配置用xml还是好些,后续的维护比较清晰。不然就悲剧了
12 楼 skzr.org 2011-02-02  
xyqck163 写道
lz  注解方式我用过,并且我负责的一个小项目就是全注解形式的。但你有没有想过,这种方式在编码阶段是方便了,不用写配置文件了,但是如果过上两个月有个功能出现了个bug,改起来会相当麻烦。 还有就是重构的时候比较麻烦。 配置文件的好处就是bean定义整洁清楚,比较集中,容易修改排错

比较喜欢这样的形式。


呵呵,用了这个所谓的约定,确实感觉编程方便了,如果是做一次性项目,这样做确实省确了不少的时间

我现在的思路:
赞同一次性的小项目这样弄还是不错的
如果是比较庞大的项目(或者是产品有N多现场的项目),错更复杂,明确的bean定义更加整洁清晰(特别是那种多存储、有需要整合重复服务的)
11 楼 xyqck163 2011-02-02  
lz  注解方式我用过,并且我负责的一个小项目就是全注解形式的。但你有没有想过,这种方式在编码阶段是方便了,不用写配置文件了,但是如果过上两个月有个功能出现了个bug,改起来会相当麻烦。 还有就是重构的时候比较麻烦。 配置文件的好处就是bean定义整洁清楚,比较集中,容易修改排错
10 楼 Seraph115 2011-02-02  
gtssgtss 写道
flashing 写道
gtssgtss 写道
我们公司的配置文件用人名命名,出错了马上知道找谁......Annotation怎么实现?

囧,这个太有特色了,难道不测试就提交?有多大可能在配置问题上出错?
即使出错了难道没有svn,hg什么的查历史?
再说起码的实践,版本管理+CI应该有的

最后,用人名这个,我觉得简直是个joke了,太欢乐了


哈哈,的确非常囧,小作坊,必须的

我猜这么做是为了最大限度节省每日集成的时间吧,领导要求这样,我也不多嘴问


推荐持续集成,让CI不厌其烦的执行集成工作,毕竟人的耐心不如冷冰冰的电脑,同时可以单元测试,覆盖率,Checkstyle,代码质量等等
9 楼 gtssgtss 2011-02-02  
flashing 写道
gtssgtss 写道
我们公司的配置文件用人名命名,出错了马上知道找谁......Annotation怎么实现?

囧,这个太有特色了,难道不测试就提交?有多大可能在配置问题上出错?
即使出错了难道没有svn,hg什么的查历史?
再说起码的实践,版本管理+CI应该有的

最后,用人名这个,我觉得简直是个joke了,太欢乐了


哈哈,的确非常囧,小作坊,必须的

我猜这么做是为了最大限度节省每日集成的时间吧,领导要求这样,我也不多嘴问
8 楼 Seraph115 2011-02-02  
gtssgtss 写道
我们公司的配置文件用人名命名,出错了马上知道找谁......Annotation怎么实现?


对于你们公司的这种特殊需求,对于使用Annotation并无影响,给出我的解决方案,首先你们的配置文件以员工名命名,这个可以使用javadoc,如:
/** 
 * 类说明: 需求A<br> 
 * 创建时间: 2011-1-26 上午10:51:28<br> 
 *  
 * @author 员工007<br> 
 * @email: <br> 
 */ 


签上大名后就可以搜索了,同样也可以跟踪

7 楼 flashing 2011-02-02  
gtssgtss 写道
我们公司的配置文件用人名命名,出错了马上知道找谁......Annotation怎么实现?

囧,这个太有特色了,难道不测试就提交?有多大可能在配置问题上出错?
即使出错了难道没有svn,hg什么的查历史?
再说起码的实践,版本管理+CI应该有的

最后,用人名这个,我觉得简直是个joke了,太欢乐了
6 楼 IcyFenix 2011-02-01  
gtssgtss 写道
我们公司的配置文件用人名命名,出错了马上知道找谁......Annotation怎么实现?


-__________-# 离职了、换人维护了,要改名不?
5 楼 gtssgtss 2011-02-01  
我们公司的配置文件用人名命名,出错了马上知道找谁......Annotation怎么实现?
4 楼 Seraph115 2011-02-01  
evanzzy 写道
Spring还是用配置文件配置比较好,臃肿的话可以拆分为多个配置文件,不太建议使用Annotation进行注入


是可以用import的方式拆分,不过也只是解决了大XML文件编辑的问题,但目的并不在于此,而在于去除配置的不便及开发过程的繁琐问题,开发一个MVC三层的应用,要配置三层的类,而类文件本身已说明了自己的用途,何必要在配置文件中配置,还有很多同学的IDE没有类似SpringIDE的插件,这样就更麻烦了,因为你需要自己确认包名类名,不知大家对此可有体会?
3 楼 liukai 2011-02-01  
spring该用注释的还是用注释
不然就像楼主说的
注入dao层 services层多的吓死你
注解只需要2行就解决问题了
2 楼 evanzzy 2011-02-01  
Spring还是用配置文件配置比较好,臃肿的话可以拆分为多个配置文件,不太建议使用Annotation进行注入
1 楼 gtssgtss 2011-01-31  
<context:component-scan base-package="com.longtop" >
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" /> 
	</context:component-scan>

相关推荐

    spring-basic:弹簧基础

    spring Bean 自动装配(注释)@Atutowired Spring自定义事件(spring自定义事件) 春天@Profile Spring Profile XML 配置 spring-aop 初识spring aop AOP(注解) 弹簧 mvc 1.第一次Spring MVC 2.Spring MVC ...

    spring注解使用详解

    基于注释(Annotation)的配置有越来越流行的趋势,Spring 2.5 顺应这种趋势,提供了完全基于注释配置 Bean、装配 Bean 的功能,您可以使用基于注释的 Spring IoC 替换原来基于 XML 的配置

    Spring in Action(第2版)中文版

    第3章高级bean装配 3.1声明父bean和子bean 3.1.1抽象基bean类型 3.1.2抽象共同属性 3.2方法注入 3.2.1基本的方法替换 3.2.2获取器注入 3.3注入非springbean 3.4注册自定义属性编辑器 3.5使用spring的特殊...

    Spring in Action(第二版 中文高清版).part2

    第3章 高级Bean装配 3.1 声明父Bean和子Bean 3.1.1 抽象基Bean类型 3.1.2 抽象共同属性 3.2 方法注入 3.2.1 基本的方法替换 3.2.2 获取器注入 3.3 注入非Spring Bean 3.4 注册自定义属性编辑器 3.5 使用...

    Spring in Action(第二版 中文高清版).part1

    第3章 高级Bean装配 3.1 声明父Bean和子Bean 3.1.1 抽象基Bean类型 3.1.2 抽象共同属性 3.2 方法注入 3.2.1 基本的方法替换 3.2.2 获取器注入 3.3 注入非Spring Bean 3.4 注册自定义属性编辑器 3.5 使用...

    spring基础

    Spring 2.5 引入了 @Autowired 注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 @Autowired 按照类型来装配 改变如下: package com.baobaotao; import org.springframework.beans....

    annotations-api-6.0.53.jar

    spring中注释 @Resource(按名称进行装配) jar包

    springMVC详解以及注解说明 中文WORD版.rar

    基于注释(Annotation)的配置有越来越流行的趋势,Spring 2.5 顺应这种趋势,提供了完全基于注释配置 Bean、装配 Bean 的功能,您可以使用基于注释的 Spring IoC 替换原来基于 XML 的配置。本文通过实例详细讲述了 ...

    spring

    春天1,HelloSpring IOC创建对象spring1模块2,依赖注入spring2模块3,自动装配spring3模块4,注释spring4模块5,JavaConfig spring5模块

    spring-example01:自动连接兼容的Intellij自定义注释

    Spring示例Spring 4.2.0(最低Java 7语言级别) 使用兼容的Intellij IDE“友好”注释自动装配。 显示XML配置和带有合格注入的自动装配注入相同类型的不同bean,例如Supplier,并创建注释分类器以选择实现。 这些也...

    spring-kotlin-divedive:使用Spring从Java到Kotlin的旅程

    当单个构造函数自动装配时(从Spring 4.3开始),无需注释构造函数,显示2种语法 @RequestMapping别名: @GetMapping , @PostMapping等。 在IDEA中通过CTRL + F9重新加载(在Mac上为CMD + SHIFT + F9)

    EWALectureNotes:幻灯片和讲座笔记Hacettepe大学的企业Web体系结构讲座的笔记

    spring-annotation-based-autowiring:基于Spring注释的自动装配bean spring-autowiring-constructor:构造函数注入自动装配 spring-bean-definition-override:使用多个Spring配置覆盖bean定义 spring-bean-...

    springboot光影视频.zip

    这些视频课程涵盖了Spring Boot的基本概念、配置、自动装配、数据访问、安全性、RESTful API等方面的内容,适合初学者和有经验的开发者学习。这个资源包的主要特点如下:内容丰富:包含多个视频课程,涵盖了Spring ...

    Java Web程序设计教程

    3.2.2jsp注释方式 37 3.2.3jsp声明方式 38 3.2.4jsp表达式的应用 39 3.2.5jsp的脚本段 39 3.2.6jsp的编译指令 40 3.2.7jsp的动作指令 41 3.2.8jsp的内置对象 43 3.3认识servlet 46 3.3.1servlet的开发 46 ...

    java web技术开发大全(最全最新)

    JSP+Servlet+Struts+Hibernate+Spring+Ajax》内容包括Web客户端技术、JSP/Servlet技术、Struts 2(*、类型转换、输入校验、上传和下载文件、Struts 2的各种标签、对 AJAX的支持等)、Spring(Ioc容器、装配Java Bean...

    SpringFundamentals_injectionExamples

    SpringFundamentals_injectionExamples ...带有自动装配示例的 Spring 注释 对于每个项目,我们包括以下示例: 自动装配 成员变量注入(反射) 二传手注入 构造函数注入 范围示例(单例和原型) 属性文件注入

    java web开发技术大全

    JSP+Servlet+Struts+Hibernate+Spring+Ajax》内容包括Web客户端技术、JSP/Servlet技术、Struts 2(*、类型转换、输入校验、上传和下载文件、Struts 2的各种标签、对 AJAX的支持等)、Spring(Ioc容器、装配Java Bean...

Global site tag (gtag.js) - Google Analytics