首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Spring3诠释装配的最佳实践

2012-09-06 
Spring3注释装配的最佳实践2005夏于上海,初次使用了Spring框架开发企业应用,当时还没有中文书籍,只能看Spr

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" /><bean id="scFtpInfoDAO" /><bean id="scParmInfoDAO" /><bean id="scParmTypeDAO" /><bean id="scRoleDAO" /><bean id="scRoleMenuDAO" /><bean id="scSiteLoadDAO" /><bean id="scSiteStatDAO" />略去同样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;...@Repositorypublic 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" />

?

<bean id="sqlMapClientDAO"ref="${jdbc.dataSource}" /><property name="sqlMapClient" ref="sqlMapClient" /></bean><bean id="sqlMapClient" 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);@Overridepublic 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"ref="${jdbc.dataSource}" /><property name="sqlMapClient" ref="sqlMapClient" /></bean>

?

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

?

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

?

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

<context:component-scan base-package="com.longtop" ><context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" /> </context:component-scan>/** * 类说明: 需求A<br> * 创建时间: 2011-1-26 上午10:51:28<br> * * @author 员工007<br> * @email: <br> */

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

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

用配置文件,我在可以看一个配置文件就可以了解所有类相关的注入情况,依赖关系直接在配置文件中修改即可,也无需修改类源代码,如果用注释的话,恐怕要进入源代码中修改吧
   

热点排行