`
wanglihu
  • 浏览: 909233 次
  • 性别: Icon_minigender_1
  • 来自: 黑龙江
社区版块
存档分类

Spring3注释装配的最佳实践

阅读更多
Spring3注释装配的最佳实践


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

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

Xml代码
<?xml version="1.0" encoding="UTF-8"?>  
<beans ‘略去声明’>  
               
    <bean id="xxDAO" class="com.data.switching.db.dao.impl.XxDAOImpl"  
        parent="sqlMapClientDAO" />  
                           
   
  
略去同样999个配置 ... ...   
使用后的情况:

Xml代码
<?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,实现类如下:

Java代码
import org.springframework.stereotype.Repository;   
...   
  
@Repository  
public class XxDAOImpl extends SqlMapClientDaoSupport implements XxDAO {   
...   
在配置文件中加入:

Xml代码
<context:annotation-config /> 
<context:component-scan base-package="com.db.dao"  
name-generator="com.seraph.bi.suite.support.core.generator.IBatisDaoBeanNameGenerator" />  
因接口名为XxDAO,而实现类名为 XxDAOImpl,引用类中的域名是接口的首字母小写名XxDAO,而容器生成的默认类名是 XxDAOImpl,所以不行,但spring预留了

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

Java代码
/**  
* 类说明: 生成iBatis的DAO的Spring注册名,规则是首字母小写,并去掉后缀名<br>  
* 创建时间: 2011-1-26 下午12:44:20<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"要如何处理呢?

Xml代码
<bean id="scRoleDAO" class="com.dao.impl.XxDAOImpl"  
        parent="sqlMapClientDAO" />  
Xml代码
<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中后注入其依赖。类代码如下:

Java代码
/**  
* 类说明: 向iBatis的DAO中注入依赖<br>  
* 创建时间: 2011-1-26 上午10:51:28<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;   
  
}  
然后加入此类的配置即可,

Xml代码
<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容器的工作原理和设计思想,而后我们可以对其实现有益且有必要的改进工作,但最终都是旨在简化配置,减少没有必要的工作量。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics