import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * 自定义的多数据源路由器. * */ public class CustomerRoutingDataSource extends AbstractRoutingDataSource { private final transient Logger log = LoggerFactory.getLogger(CustomerRoutingDataSource.class); @Override protected Object determineCurrentLookupKey() { final String dbType = CustomerDBContextHolder.getDataSourceType(); if (log.isInfoEnabled()) { log.info("获取了KEY={}的数据源", dbType); } return dbType; } }
/** * 自定义的上下文储存器 */ public class CustomerDBContextHolder { //线程安全的ThreadLocal private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static void setDataSourceType(String dbType) { Assert.notNull(dbType, "DBType cannot be null"); contextHolder.set(dbType); } public static String getDataSourceType() { String str = (String) contextHolder.get(); if (StringUtils.isBlank(str)) str = DataSource.master; return str; } public static void clearDataSourceType() { contextHolder.remove(); } }
import java.lang.annotation.*; @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented /** *注解 */ public @interface DataSource { String name() default DataSource.master; public static String master = "master"; public static String slave = "slave"; }
import java.lang.reflect.Method; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; /** *切点 */ @Aspect @Component public class DataSourceAspect { //定义切点 @Pointcut("@annotation(org.pos.manager.data.utils.spring.DataSource)") public void daoAspect() { } @Before("daoAspect()") public void doBefore(JoinPoint joinPoint){ try { CustomerDBContextHolder.setDataSourceType(getDataSource(joinPoint)); } catch (Exception e) { e.printStackTrace(); } } @SuppressWarnings("rawtypes") public static String getDataSource(JoinPoint joinPoint) throws Exception { String targetName = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); Object[] arguments = joinPoint.getArgs(); Class targetClass = Class.forName(targetName); Method[] methods = targetClass.getMethods(); String name = ""; for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { name = method.getAnnotation(DataSource.class).name(); break; } } } return name; } }
数据源配置
<bean id="dataSourceWrite" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="${master.jdbc.driverClassName}" /> <property name="jdbcUrl" value="${master.jdbc.url}" /> <property name="user" value="${master.jdbc.userDS.username}" /> <property name="password" value="${master.jdbc.userDS.password}" /> <!-- 连接池中保留的最小连接数 --> <property name="minPoolSize" value="1" /> <!-- 连接池中保留的最大连接数 --> <property name="maxPoolSize" value="200" /> <!-- 最大空闲时间,1800秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0--> <property name="maxIdleTime" value="1800" /> <!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3--> <property name="acquireIncrement" value="5" /> <!-- 最大的PreparedStatement的数量 --> <property name="maxStatements" value="1000" /> <!-- 每60秒检查所有连接池中的空闲连接。Default: 0 --> <property name="idleConnectionTestPeriod" value="60" /> <!-- 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30--> <property name="acquireRetryAttempts" value="30" /> <property name="breakAfterAcquireFailure" value="true" /> <property name="testConnectionOnCheckout" value="false" /> </bean> <bean id="dataSourceRead" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="${slave.jdbc.driverClassName}" /> <property name="jdbcUrl" value="${slave.jdbc.url}" /> <property name="user" value="${slave.jdbc.userDS.username}" /> <property name="password" value="${slave.jdbc.userDS.password}" /> <!-- 连接池中保留的最小连接数 --> <property name="minPoolSize" value="1" /> <!-- 连接池中保留的最大连接数 --> <property name="maxPoolSize" value="200" /> <!-- 最大空闲时间,1800秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0--> <property name="maxIdleTime" value="1800" /> <!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3--> <property name="acquireIncrement" value="5" /> <!-- 最大的PreparedStatement的数量 --> <property name="maxStatements" value="1000" /> <!-- 每60秒检查所有连接池中的空闲连接。Default: 0 --> <property name="idleConnectionTestPeriod" value="60" /> <!-- 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30--> <property name="acquireRetryAttempts" value="30" /> <property name="breakAfterAcquireFailure" value="true" /> <property name="testConnectionOnCheckout" value="false" /> </bean> <bean id="dataSource" class="org.pos.manager.data.utils.spring.CustomerRoutingDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry key="master" value-ref="dataSourceWrite" /> <entry key="slave" value-ref="dataSourceRead" /> </map> </property> <property name="defaultTargetDataSource" ref="dataSourceWrite"/> </bean>
applicationContext.xml中加入aop代理配置
<aop:aspectj-autoproxy proxy-target-class="true" />
最后在service层实现动态设置
@Component(value = "accountBankService") public class AccountBankServiceImpl implements AccountBankService { @Autowired @Qualifier(value = "accountBankDao") private AccountBankDao accountBankDao; @DataSource(name = DataSource.slave) @Override public List<AccountBank> getAccountBankList() throws Exception { return accountBankDao.getAccountBankList(); } }