最近由于项目需求,需要将Sql Server 和 Mysql 两种数据库整合到一个项目,项目的用到的框架是SSM。 因此尝试了利用AOP切面来切每次执行的Servcie方法,根据Service所在的包名来实现数据源自动切换。
1.项目架构如下:
2.在com.jiefupay.database包中建立四个类:
其中 DataSourceContextHolder.java类源码如下:
package com.jiefupay.datebase; public class DataSourceContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); //设置当前Thread的数据库类型 public static void setDbType(String dbType) { contextHolder.set(dbType); } //获取当前Thread的数据库类型 public static String getDbType() { return ((String) contextHolder.get()); } public static void clearDbType() { contextHolder.remove(); } }
其中,DataSourceName.java源码如下:
package com.jiefupay.datebase; /** * 数据库名称常量类 */ public class DataSourceName { public static final String SQLSERVER_SOURCE = "sqlserver_source"; public static final String MYSQL_SOURCE = "mysqlSource"; }
其中,DynamicDataSource.java源码如下:
package com.jiefupay.datebase; import java.util.logging.Logger; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override public Logger getParentLogger() { return null; } @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDbType(); } }
其中,ChangeDBInteceptor.java作为切点的拦截类,获取每次执行的service的全限令名,源码如下:
package com.jiefupay.datebase; import org.aspectj.lang.ProceedingJoinPoint; public class ChangDBInterceptor { public Object changeDB(ProceedingJoinPoint pjp) throws Throwable { //AOP切点在Service的 包名.类名 String path=pjp.getTarget().getClass().getName(); //如果servcie的 包名.类名 包含 agency,那说明需要切换为mysql数据源 if (path.indexOf("agency")!=-1){ DataSourceContextHolder.setDbType(DataSourceName.MYSQL_SOURCE); }else{ DataSourceContextHolder.setDbType(DataSourceName.SQLSERVER_SOURCE); } return pjp.proceed(); } }
3.jdbc.properties文件如下:
mysqldriver=com.mysql.jdbc.Driver mysqlurl=jdbc:mysql://192.168.0.144:3306/mysql?useUnicode=true&characterEncoding=UTF-8 mysqlusername=myusername mysqlpassword=mypassword driver=com.microsoft.sqlserver.jdbc.SQLServerDriver url=jdbc:sqlserver://192.168.0.144:1433;databaseName=sqlserver_source username=sqlusername password=sqlpassword #定义初始连接数 initialSize=4 #定义最大连接数 maxActive=6 #定义最大空闲 maxIdle=2 #定义最小空闲 minIdle=2 #定义最长等待时间 maxWait=60000
4.spring-mybatis.xml配置文件如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"> <!-- 自动扫描 --> <context:component-scan base-package="com.jiefupay" > <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> <!-- 引入配置文件 --> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:jdbc.properties" /> </bean> <!-- 多数据源配置 --> <bean id ="sqlserver_source" class= "org.apache.commons.dbcp.BasicDataSource" > <property name ="driverClassName" value= "${driver}"></property > <property name ="url" value="${url}" ></property > <property name="username" value="${username}" /> <property name="password" value="${password}" /> <property name="initialSize" value="${initialSize}"></property> <property name="maxActive" value="${maxActive}"></property> <property name="maxIdle" value="${maxIdle}"></property> <property name="minIdle" value="${minIdle}"></property> <property name="maxWait" value="${maxWait}"></property> </bean > <bean id="mysqlSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${mysqldriver}" /> <property name="url" value="${mysqlurl}" /> <property name="username" value="${mysqlusername}" /> <property name="password" value="${mysqlpassword}" /> <property name="initialSize" value="${initialSize}"></property> <property name="maxActive" value="${maxActive}"></property> <property name="maxIdle" value="${maxIdle}"></property> <property name="minIdle" value="${minIdle}"></property> <property name="maxWait" value="${maxWait}"></property> </bean> <!-- 动态配置数据源 --> <bean id ="dataSource" class= "com.jiefupay.datebase.DynamicDataSource" > <property name ="targetDataSources"> <map key-type ="java.lang.String"> <entry value-ref ="newdata_2014" key="sqlserver_source"></entry > <entry value-ref ="mysqlSource" key="mysqlSource"></entry > </map> </property > <property name ="defaultTargetDataSource" ref= "sqlserver_source"></property > </bean > <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="mapperLocations" value="classpath:com/jiefupay/*/mapper/*.xml"></property> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.jiefupay.*.dao" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> <bean id="changeDBInterceptor" class="com.jiefupay.datebase.ChangDBInterceptor"></bean> <!-- 配置AOP切面,所有service作为切点,执行changeDBInterceptor类中的changeDB方法 --> <aop:config proxy-target-class="true"> <aop:pointcut id="txPointcut" expression="execution(* com.jiefupay..service..*Service*.*(..)) or execution(* com.jiefupay.common..*Service*.*(..))" /> <aop:aspect ref="changeDBInterceptor" order="1"> <aop:around pointcut-ref="txPointcut" method="changeDB"/> </aop:aspect> </aop:config> </beans>