使用spring aop+自定义注解实现动态使用DataSource

Spring AOP的两种实现方式:JDK动态代理和CGLIB动态代理
1、JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
2、CGLIB动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP。
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换。

AOP用来封装横切关注点,具体可以在下面的场景中使用:
Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging  调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence  持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务

参考文章:
Spring AOP 实现原理 http://blog.csdn.net/moreevan/article/details/11977115
aop,spring aop,aspectj区别与联系  http://blog.csdn.net/pingnanlee/article/details/38845955
实例简述Spring AOP之对AspectJ语法的支持  http://blog.csdn.net/kkdelta/article/details/5515882
Spring之AOP AspectJ切入点语法详解 http://jinnianshilongnian.iteye.com/blog/1415606
Java之代理(jdk静态代理,jdk动态代理,cglib动态代理,aop,aspectj) http://blog.csdn.net/centre10/article/details/6847828
使用Spring进行面向切面编程(AOP) http://www.blogjava.net/supercrsky/articles/174368.html


aop配置:

<aop:config>
	<aop:aspect ref="dataSourceAutoSwitchAdvice">
		<aop:pointcut id="daoQueryPointcut" expression="execution(* cn.vobile.dao..*.*(..)) and @annotation(query)"/>
		<aop:around method="queryData" pointcut-ref="daoQueryPointcut"/>
	</aop:aspect>
</aop:config>

<bean id="dataSourceAutoSwitchAdvice" class="cn.vobile.common.datasource.aop.DataSourceAutoSwitchAdvice"/>

自定义注解--query:

@Target({ METHOD })
@Retention(RUNTIME)
public @interface Query {

    DataSourceType[] dataSourceType() default DataSourceType.MASTER;

    QueryStrategy queryStrategy() default QueryStrategy.ONE;

}

自定义注解相关文档:
自定义注解  http://www.cnblogs.com/xing901022/p/3966799.html
Java自定义注解 http://blog.csdn.net/yixiaogang109/article/details/7328466
自定义注解入门 http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html

Advice类--DataSourceAutoSwitchAdvice:

public class DataSourceAutoSwitchAdvice {

    public Object queryData(ProceedingJoinPoint joinPoint, Query query) throws Throwable {
        try {
            List<Object> resultList = new ArrayList<Object>();
            for (DataSourceType dataSourceType : query.dataSourceType()) {
                DataSourceTypeHolder.switchTo(dataSourceType);
                Object result = joinPoint.proceed();
                if (null == result) {
                    continue;
                }

                if (query.queryStrategy() == QueryStrategy.ONE) {
                    return result;
                } else {
                    resultList.add(result);
                }
            }

            return mergeResult(resultList);
        } finally {
            DataSourceTypeHolder.clear();
        }
    }

    @SuppressWarnings("rawtypes")
    private Object mergeResult(List<Object> resultList) {
        if (CollectionUtils.isEmpty(resultList)) {
            return null;
        }

        if (resultList.size() == 1) {
            return resultList.get(0);
        }

        Object mergedResult = null;
        Object firstResult = resultList.get(0);
        if (firstResult instanceof Collection) {
            for (Object result : resultList) {
                mergedResult = CollectionUtils.union((Collection) mergedResult, (Collection) result);
            }
            return mergedResult;
        }

        throw new DataSourceAutoSwitchException("Can't merge result: unsupported class=" + firstResult.getClass());
    }

}

辅助类DataSourceTypeHolder和DataSourceType:

public abstract class DataSourceTypeHolder {
    private static ThreadLocal<DataSourceType> dataSourceTypeHolder = new ThreadLocal<DataSourceType>();

    public static void switchTo(DataSourceType dataSourceType) {
        dataSourceTypeHolder.set(dataSourceType);
    }

    public static DataSourceType getDataSourceType() {
        return dataSourceTypeHolder.get();
    }

    public static void clear() {
        dataSourceTypeHolder.remove();
    }

}


public enum DataSourceType {

    MASTER("master", "master db"),

    SLAVE("slave", "slave db"),

    ARCHIVE("archive", "archive db");

    /** The enumeration's code. */
    private String code;

    /** The enumeration's description. */
    private String desc;

    /**
     * @param code the enumeration's code
     * @param desc the enumeration's description
     */
    private DataSourceType(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    /**
     * Get an enumeration by it's code.
     * 
     * @param code the enumeration's code
     * @return {@link DataSourceType}
     */
    public static DataSourceType getEnumByCode(String code) {
        if (StringUtils.isBlank(code)) {
            return null;
        }

        for (DataSourceType ele : values()) {
            if (StringUtils.equals(code, ele.getCode())) {
                return ele;
            }
        }

        return null;
    }

    /**
     * @return the code
     */
    public String getCode() {
        return code;
    }

    /**
     * @return the desc
     */
    public String getDesc() {
        return desc;
    }

}

定义实现的dao类:

<bean id="posterBlacklistDao" class="cn.vobile.dao.posterblacklist.PosterBlacklistDaoImpl" parent="dataSourceAutoSwitchSqlMapClientDaoSupport"/>
<bean id="dataSourceAutoSwitchSqlMapClientDaoSupport" class="cn.vobile.common.datasource.dao.DataSourceAutoSwitchSqlMapClientDaoSupport" abstract="true">
		<property name="sqlMapClientTemplateMap">
			<map>
				<entry key="master" value-ref="masterSqlMapClientTemplate" />
				<entry key="slave" value-ref="slaveSqlMapClientTemplate" />
				<entry key="archive" value-ref="archiveSqlMapClientTemplate" />
			</map>
		</property>
	</bean>

DataSourceAutoSwitchSqlMapClientDaoSupport类代码如下:

public abstract class DataSourceAutoSwitchSqlMapClientDaoSupport {
    private DataSourceType                          defaultDataSourceType   = DataSourceType.MASTER;
    private final Map<String, SqlMapClientTemplate> sqlMapClientTemplateMap = new HashMap<String, SqlMapClientTemplate>();

    public SqlMapClientTemplate getSqlMapClientTemplate() {
        DataSourceType dataSourceType = DataSourceTypeHolder.getDataSourceType();
        if (null == dataSourceType) {
            dataSourceType = defaultDataSourceType;
        }

        SqlMapClientTemplate sqlMapClientTemplate = sqlMapClientTemplateMap.get(dataSourceType.getCode());
        Assert.notNull(sqlMapClientTemplate, "SqlMapClientTemplate does not exist: dataSourceType=" + dataSourceType.getCode());

        return sqlMapClientTemplate;
    }

    /**
     * @param defaultDataSourceType the defaultDataSourceType to set
     */
    public void setDefaultDataSourceType(String defaultDataSourceType) {
        DataSourceType dataSourceType = DataSourceType.getEnumByCode(defaultDataSourceType);
        Assert.notNull(dataSourceType, "defaultDataSourceType must not be null");
        this.defaultDataSourceType = dataSourceType;
    }

    /**
     * @param sqlMapClientTemplateMap the sqlMapClientTemplateMap to set
     */
    public void setSqlMapClientTemplateMap(Map<String, SqlMapClientTemplate> sqlMapClientTemplateMap) {
        Assert.notEmpty(sqlMapClientTemplateMap, "sqlMapClientTemplateMap must not be empty");
        this.sqlMapClientTemplateMap.putAll(sqlMapClientTemplateMap);
    }

    public void setSqlMapClient(Map<String, SqlMapClient> sqlMapClientMap) {
        Assert.notEmpty(sqlMapClientMap, "sqlMapClientMap must not be empty");
        for (Entry<String, SqlMapClient> entry : sqlMapClientMap.entrySet()) {
            if (!this.sqlMapClientTemplateMap.containsKey(entry.getKey())) {
                this.sqlMapClientTemplateMap.put(entry.getKey(), new SqlMapClientTemplate(entry.getValue()));
            }
        }
    }

}

实现类PosterBlacklistDaoImpl中的方法如下:

@Query(dataSourceType = DataSourceType.SLAVE)
@SuppressWarnings("unchecked")
@Override
public List<PosterBlacklistStatistic> getPosterBlacklistStatisticsFromSlaveDB(int companyId) {
return getSqlMapClientTemplate().queryForList("PosterBlacklist.getPosterBlacklistStatistics", companyId);
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值