zoukankan      html  css  js  c++  java
  • 【spring】Spring3注释装配的最佳实践持久层

    http://www.iteye.com/topic/900129

    引言:

    长久以来国内的众多应用都在使用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>

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

  • 相关阅读:
    使用T4为数据库自动生成实体类
    asp.net 下OnClientClick的妙用
    使用缓存的9大误区(上)
    使用缓存的9大误区(下)
    毕业后的五年拉开大家差距的原因在哪里
    mysql字符集
    redhat网络基础配置
    mysql远程连接问题
    Hibernate hql语句修改部分字段
    JQuery使用方法总结
  • 原文地址:https://www.cnblogs.com/549294286/p/2999996.html
Copyright © 2011-2022 走看看