Spring标签@Aspect-实现面向方向编程(@Aspect的多数据源自动加载)——SKY
从Spring 2.0开始,可以使用基于schema及@AspectJ的方式来实现AOP。由于@Aspect是基于注解的,因此要求支持注解的5.0版本以上的JDK。
环境要求:
1. mybits的DB连接,实现动态多数据源的设置。
2. service层实现数据访问。
我们的目标是,如果用户调用Service层中任一方法,都配置其动态数据源Id。
在此先来说一下AOP:
AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
简介
前段时间写的java设计模式--代理模式,最近在看Spring Aop的时候,觉得于代理模式应该有密切的联系,于是决定了解下Spring Aop的实现原理。
说起AOP就不得不说下OOP了,OOP中引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。但是,如果我们需要为部分对象引入公共部分的时候,OOP就会引入大量重复的代码。例如:日志功能。
AOP技术利用一种称为“横切”的技术,解剖封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,这样就能减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。
下面说说@aspect实现的动态数据源配置:
1.定义一个Aspect。
必须使用@Aspect在类名之前注解。
当用户调用com.accessor.service包中任一类的任一方法,在调用前,Spring将自动执行下面的doBefore()方法,以及Doafter()方法。
这里数据源的选择是通过枚举实现的。
1 /** 2 * Created by Administrator on 2016/11/3. 3 */ 4 @Component 5 @Aspect 6 public class MultipleDataSourceAspect{ 7 private static AtomicInteger counter = new AtomicInteger(0); 8 9 @Before("execution(* com.accessor.service..*(..))") 10 public void doBefore(JoinPoint joinPoint) throws Throwable { 11 String className = joinPoint.getTarget().getClass().getName(); 12 if (className.contains("oracle")) { 13 DataSourceContextHolder.setDataSourceType(DataSourceEnum.ORACLE_READ.getValue()); 14 } else if (className.contains("sqlserver")) { 15 setMsDataSource(joinPoint.getArgs()); 16 } 17 System.out.println("------------------begin"+counter.getAndIncrement() +"-----" + DataSourceContextHolder.getDataSourceType() + "--------thread:" + Thread.currentThread().getName()) ; 18 } 19 @After("execution(* com.accessor.service..*(..))") 20 public void doAfter(JoinPoint joinPoint) throws Throwable { 21 Object[] args = joinPoint.getArgs(); 22 if (args != null && args.length > 0) { 23 if (args[0] instanceof Map) { 24 Map param = (Map) args[0]; 25 param.remove("msHisFlag"); 26 param.remove("tabNameSuffix"); 27 } 28 } 29 System.out.println("--------------------end------------------remove:" + DataSourceContextHolder.getDataSourceType()); 30 DataSourceContextHolder.clearDataSourceType(); 31 } 32 33 //设置ms数据源 34 private void setMsDataSource(Object[] args) { 35 DataSourceEnum dataSourceEnum = null; 36 if (args != null && args.length > 0) { 37 if (args[0] instanceof Map) { 38 Map param = (Map) args[0]; 39 String fundId = CommonUtil.convert(param.get("fundId"), String.class); 40 if (fundId != null) { 41 if ("1".equals(param.get("msHisFlag"))) { 42 String tabNameSuffix = DateUtil.dateToStr(new Date(), "_yyyy_MM"); 43 param.put("tabNameSuffix", tabNameSuffix); 44 dataSourceEnum = CommonUtil.getMsDataSourceEnum(fundId, true); 45 } else { 46 dataSourceEnum = CommonUtil.getMsDataSourceEnum(fundId); 47 } 48 } 49 DataSourceContextHolder.setDataSourceType(dataSourceEnum.getValue()); 50 } 51 } 52 53 if (dataSourceEnum == null) { 54 throw new RuntimeException("无法确定查询的sqlserver数据源"); 55 } 56 } 57 }
2.spring的文件配置
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:aop="http://www.springframework.org/schema/aop" 5 xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" 6 http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 8 http://www.springframework.org/schema/context 9 http://www.springframework.org/schema/context/spring-context-3.2.xsd 10 http://www.springframework.org/schema/aop 11 http://www.springframework.org/schema/aop/spring-aop-3.1.xsd"> 12 13 14 <!-- 自动扫描(自动注入) --> 15 <context:annotation-config/> 16 <context:component-scan base-package="com.eastmoney"/> 17 <aop:aspectj-autoproxy proxy-target-class="true"/> 18 19 <bean id="springContextUtil " class="com.eastmoney.accessor.util.SpringContextUtil"/> 20 21 <!-- 引入mybatis属性配置文件 --> 22 <import resource="spring-mybatis.xml"/> 23 </beans>
3.mybatis的文件配置
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:tx="http://www.springframework.org/schema/tx" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> 8 9 <!-- 动态数据源 --> 10 <bean id="dataSource" class="com.eastmoney.accessor.datasource.DynamicDataSource"> 11 <property name="dsConfigFile" value="dataSource.xml"/> 12 </bean> 13 14 <!-- mybatis配置 --> 15 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> 16 <property name="dataSource" ref="dataSource"/> 17 <property name="mapperLocations" value="classpath*:mapper/*/*.xml"/> 18 <property name="configLocation" value="classpath:/mybatis-config.xml"/> 19 </bean> 20 21 <!-- 自动创建映射器,不用单独为每个 mapper映射--> 22 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> 23 <property name="basePackage" value="com.eastmoney.accessor.mapper"/> 24 <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> 25 </bean> 26 27 <!-- 事务管理器配置,单数据源事务 --> 28 <bean id="transactionManager" 29 class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 30 <property name="dataSource" ref="dataSource"/> 31 </bean> 32 33 <tx:annotation-driven transaction-manager="transactionManager"/> 34 35 </beans>
就这么简单而强大的Spring注解配置,非常感兴趣,下次去过一遍Spring的原理和注解的使用规则,有机会在和大家分享。
笔者:SKY-yong 2016/11/18