spring多数据源的动态加载

近日来数据库的压力较大,因此作了读写分离。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

相关文章
相关标签/搜索