近日来数据库的压力较大,因此作了读写分离。java
为了尽可能少的修改已有代码(数据库主从库仍是要作的),在spring的配置中增长了数据源,使用注解的方式,增长一个切面,从controller层即肯定访问的数据源。mysql
一、在主配置文件中加入下面这行spring
<import resource="spring-mysql.xml"/>
spring的配置 spring-mysql.xmlsql
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-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/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- mysql 配置 --> <!-- datasource 1 --> <bean id="masterDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 基本属性 url、user、password --> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="20"/> <property name="minIdle" value="20"/> <property name="maxActive" value="100"/> <!-- 配置获取链接等待超时的时间 --> <property name="maxWait" value="60000"/> <!-- 配置间隔多久才进行一次检测,检测须要关闭的空闲链接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="60000"/> <!-- 配置一个链接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="300000"/> <property name="validationQuery" value="SELECT 'x'"/> <property name="testWhileIdle" value="true"/> <property name="testOnBorrow" value="false"/> <property name="testOnReturn" value="false"/> <!-- 打开PSCache,而且指定每一个链接上PSCache的大小 --> <property name="poolPreparedStatements" value="true"/> <property name="maxPoolPreparedStatementPerConnectionSize" value="20"/> <!-- 配置监控统计拦截的filters --> <property name="filters" value="stat"/> </bean> <!-- datasource 2 --> <bean id="slaveDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 基本属性 url、user、password --> <property name="url" value="${readDb.url}"/> <property name="username" value="${readDb.username}"/> <property name="password" value="${readDb.password}"/> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="20"/> <property name="minIdle" value="20"/> <property name="maxActive" value="100"/> <!-- 配置获取链接等待超时的时间 --> <property name="maxWait" value="60000"/> <!-- 配置间隔多久才进行一次检测,检测须要关闭的空闲链接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="60000"/> <!-- 配置一个链接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="300000"/> <property name="validationQuery" value="SELECT 'x'"/> <property name="testWhileIdle" value="true"/> <property name="testOnBorrow" value="false"/> <property name="testOnReturn" value="false"/> <!-- 打开PSCache,而且指定每一个链接上PSCache的大小 --> <property name="poolPreparedStatements" value="true"/> <property name="maxPoolPreparedStatementPerConnectionSize" value="20"/> <!-- 配置监控统计拦截的filters --> <property name="filters" value="stat"/> </bean> <!-- 编写spring 配置文件的配置多数源映射关系 --> <bean class="hasoffer.core.persistence.dbm.osql.datasource.DynamicDataSource" id="dataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry value-ref="masterDataSource" key="master"></entry> <entry value-ref="slaveDataSource" key="slave"></entry> <!-- key --> </map> </property> <property name="defaultTargetDataSource" ref="masterDataSource"> </property> </bean> <!-- sessionFactory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="packagesToScan" value="hasoffer.core.persistence.po"/> <property name="hibernateProperties"> <value> hibernate.dialect=org.hibernate.dialect.MySQLDialect hibernate.show_sql=false hibernate.hbm2ddl.auto=none hibernate.connection.autocommit=false hibernate.jdbc.batch_size=30 </value> </property> </bean> <bean name="org.springframework.orm.hibernate4.HibernateTemplate" class="org.springframework.orm.hibernate4.HibernateTemplate"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <!-- 事务管理器配置, Hibernate单数据源事务 --> <bean id="defaultTransactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <tx:annotation-driven transaction-manager="defaultTransactionManager" proxy-target-class="true"/> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 动态选择数据源 面向切面 --> <bean id="dataSourceAspect" class="hasoffer.core.persistence.dbm.osql.datasource.DataSourceAspect"/> <aop:config> <!--<aop:pointcut id="transactionPointCut" expression="execution(* ppp.core.*.*.*(..))"/>--> <aop:pointcut id="transactionPointCut1" expression="execution(* ppp.admin.controller.*.*(..))"/> <aop:pointcut id="transactionPointCut2" expression="execution(* ppp.api.controller.*.*(..))"/> <aop:advisor pointcut-ref="transactionPointCut1" advice-ref="dataSourceAspect" order="1"/> <aop:advisor pointcut-ref="transactionPointCut2" advice-ref="dataSourceAspect" order="2"/> <!--<aop:advisor pointcut-ref="transactionPointCut" advice-ref="dataSourceExchange" order="1"/>--> </aop:config> </beans>
二、其中为了动态加载数据源,使用了数据库
DataSourceAspect 和 DynamicDataSource
两个类 ,一个是AOP切面编程,一个是返回当前数据源express
DataSourceAspect.java编程
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { // 对应 key // <entry value-ref="readDataSource" key="read"></entry> <!-- key --> // return "read"; // return "master"; return DataSourceContextHolder.getDataSourceType(); } }
DataSourceAspect.javaapi
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.aop.AfterReturningAdvice; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class DataSourceAspect implements MethodBeforeAdvice, AfterReturningAdvice { private Logger logger = LoggerFactory.getLogger(DataSourceAspect.class); @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { DataSource ds = method.getAnnotation(DataSource.class); if (ds == null) { return; } DataSourceContextHolder.clearDataSourceType(); } @Override public void before(Method method, Object[] args, Object target) throws Throwable { DataSource ds = method.getAnnotation(DataSource.class); if (ds == null) { return; } logger.debug(String.format("method : %s/%s, datasource : %s", method.getDeclaringClass().getName(), method.getName(), ds.value())); if (ds.value() == DataSourceType.Slave) { DataSourceContextHolder.setDataSourceType("slave"); } else { DataSourceContextHolder.setDataSourceType("master"); } } }
三、DataSourceContextHolder.java 线程变量session
public class DataSourceContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); /** * @param * @return String * @throws * @Description: 获取数据源类型 */ public static String getDataSourceType() { return contextHolder.get(); } /** * @param dataSourceType 数据库类型 * @return void * @throws * @Description: 设置数据源类型 */ public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } /** * @param * @return void * @throws * @Description: 清除数据源类型 */ public static void clearDataSourceType() { contextHolder.remove(); } }
四、注解类ide
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface DataSource { DataSourceType value() default DataSourceType.Master; }
public enum DataSourceType { Master, Slave }
五、在controller的方法加上注解
要求数据源使用从库
@DataSource(value = DataSourceType.Slave)
若是使用主库进行读写,则要 @DataSource(value = DataSourceType.Master)
六、最后
配置完后须要多测试,理解其中执行顺序,什么时候设置数据源,什么时候JoinPoint等。
参考:
http://blog.csdn.net/rj042/article/details/21654627