`

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源码注释中文

    3. **Web**:该模块包含 Spring MVC(模型-视图-控制器)框架,用于构建 Web 应用程序。Spring MVC 提供了一个灵活的架构,支持多种视图技术,如 JSP、FreeMarker 和 Velocity。 4. **AOP(面向切面编程)**:...

    spring 源码中文注释

    这份"spring 源码中文注释"包含了对Spring框架源码的详细解读,对于理解其工作原理、优化代码以及定制化开发具有重要意义。 首先,我们来看看Spring的核心组件——IoC容器。IoC容器是Spring的核心,它负责管理对象...

    spring源码(注释+测试版)

    这份"spring源码(注释+测试版)"提供了Spring框架的源代码,带有注释和测试用例,对于开发者深入理解Spring的工作原理非常有帮助。 1. **spring-core**:这是Spring框架的基础模块,包含了核心的工具类和资源处理...

    spring源码中英文注释

    3. **IoC容器(Inversion of Control, IOC)**:Spring的IoC容器负责管理对象的生命周期和装配。`BeanFactory`和`ApplicationContext`是IoC容器的主要接口。通过这些接口,可以初始化、配置、查找和管理bean。 4. *...

    最全中文注释版Spring4源码

    总的来说,这个"最全中文注释版Spring4源码"是一份宝贵的资源,对于想要提升Java和Spring技术的开发者而言,它提供了深入学习和实践的平台。通过系统地研究源码,我们可以提高对Spring的理解,从而更好地运用它来...

    Spring注释包

    在这个"Spring注释包"中,我们将深入探讨Spring框架如何通过注释来简化开发,提高代码的可读性和可维护性。 首先,让我们了解一下Spring中的核心注解: 1. `@Component`:这是最基础的组件注解,用于标记一个类为...

    spring源码,配套完善的注释

    3. **Bean的生命周期**:Spring中的Bean有多种生命周期状态,包括初始化、活跃、销毁等。开发者可以通过实现接口或使用注解来定制Bean的生命周期行为,如`InitializingBean`和`DisposableBean`,或者使用`@...

    spring-framework-5.0.2.RELEASE-中文注释版-终极完美版.rar

    Spring框架还支持注解驱动开发,如`@Autowired`用于自动装配依赖,`@Service`、`@Repository`和`@Controller`用于组件扫描和类型安全的注入。同时,`@Transactional`用于声明式事务管理,使得开发者可以在不编写事务...

    spring3.x注解

    在 Spring 3.x 中,提供了两种用于属性装配的注解:@Autowired 和 @Resource。 1. @Autowired @Autowired 注解用于自动装配依赖项,默认按照类型匹配的方式(byType)进行注入。可以用于成员变量、setter 方法、...

    Spring基于注释(Annotation)的配置.pdf

    ### Spring基于注释(Annotation)的配置 #### 一、`@Autowired` 注释 `@Autowired` 是Spring框架提供的一种自动装配机制,用于简化Bean的依赖注入过程。该注解可以应用于类成员变量、方法及构造函数,帮助实现按...

    spring-source中文注释版.zip

    3. **Bean**:在Spring中,一个Java对象被称为bean,它是由Spring容器管理的对象。Bean可以通过XML、注解或Java配置进行定义,并且可以被注入到其他bean中。 4. **AOP(面向切面编程)**:Spring的AOP模块提供了在...

    spring-5.0.2中文注释版

    2. **Bean容器**:Spring Bean容器是应用程序的主要入口点,负责创建、配置、管理和装配对象。`ApplicationContext`是主要的Bean容器接口,提供了一系列方法来获取Bean实例和处理应用上下文的事件。 3. **AOP(面向...

    Spring+SpringMVC+MyBatis整合工程实例 完整版源码(带注释)

    **SSM框架整合详解** SSM框架整合是指Spring、SpringMVC和MyBatis三个开源框架的集成,常用于构建企业级的...此工程的源码已带有注释,便于理解和学习,可以直接导入IDEA运行,对于初学者来说是一个很好的实践案例。

    struts2+hibernate+Spring 框架 关于注释用法说明

    3. **Spring注解**: - `@Component`: 基本的组件注解,用于标记为Spring管理的bean。 - `@Service`: 通常用于业务层,是@Component的子注解。 - `@Repository`: 用于数据访问层,处理DAO对象,也是@Component的...

    Maven+spring3.0MVC注释方式开发的Web应用

    在本项目中,我们主要探讨的是基于Maven和Spring 3.0 MVC框架,采用注解方式进行开发的一个Web应用程序,特别地,它还利用了SimpleJdbcTemplate进行数据操作。这个项目是一个基本的CRUD(创建、读取、更新、删除)...

    Spring2.5详细注解.doc

    Spring 2.5 版本引入了基于注释(Annotation)的配置,提供了完全基于注释配置 Bean、装配 Bean 的功能,以替换原来基于 XML 的配置。这种新的配置方式可以充分利用 Java 的反射机制获取类结构信息,减少配置的工作...

    spring-framework-5.2.0.RELEASE-master.zip

    通过阅读`@Autowired`、`@PostConstruct`、`@PreDestroy`等注解的实现,我们可以了解到Spring如何进行自动装配。同时,对于`BeanDefinition`、`BeanPostProcessor`等关键接口的注释,能帮助我们深入理解bean的生命...

    spring API英文版

    3. **Web**:Spring Web模块提供了在Web环境中使用Spring的功能,包括Model-View-Controller(MVC)架构的支持,使得开发RESTful API和Web应用程序变得简单。它还包括WebSocket、Servlet监听器和过滤器等Web相关的...

    关于spring boot中几种注入方法的一些个人看法

    在 Spring 2.5 引入了 @Autowired 注释,我们平常直接引用的时候很少注意这些,只是自己写好了一个方法或者 springboot 自动配置好的一个方法我们要在另一个类中去调用,这个时候,我们就会采用该注释。 2. @...

    spring-basic:弹簧基础

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

Global site tag (gtag.js) - Google Analytics