作者微信 bishe2022

代码功能演示视频在页面下方,请先观看;如需定制开发,联系页面右侧客服
spring动态数据源配置以及以及利用AOP自动设置

Custom Tab

这个问题其实网上有很多的解决办法。但是我在借鉴的时候,还是碰到了很多问题,有很多地方不明白。最后经过综合参考几篇博文,自己测试实验,终于把问题解决了。在这里记录下来,避免以后我或者大家再遇到这样的问题。

我主要参考的文章有:

1、Spring(AbstractRoutingDataSource)实现动态数据源切换    这篇文章讲的比较详细,但是最后那个dataSourceExchange类没有提供,导致我刚开始不明白这个是干什么用的,在不加这个类的情况下,使用事务的时候切换数据源会不起作用,因为在函数内手动设置数据源时,这时已经晚了。因为事务已经创建,事务的数据源已经获取了。此时该函数还没有执行,所以会导致事务获取的是默认的数据源。所以需要在事务获取数据源之间切换设置数据源。这个就是dataSourceExchange类的作用。它是一个Aspect类,用来实现在函数执行前和执行后注入。

2、Spring 配置多个数据源,并实现动态切换    这篇文章里提供了dataSourceExchange类。即里面的DataSourceAspect类。

3、 spring 动态数据源切换实例 


经过综合这三篇文章,最终形成了自己的解决方法。

DynamicDataSource类代码如下:

[java]
public class DynamicDataSource extends AbstractRoutingDataSource {    
    @Override    
    protected Object determineCurrentLookupKey() {    
        return DataSourceContextHolder.getDataSource();    
    }    
}

DataSourceContextHolder类代码如下:

[java] 
public class DataSourceContextHolder {    
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();    
    /**  
     * @Description: 设置数据源类型  
     * @param dataSource  数据源名称 
     * @return void  
     * @throws  
     */    
    public static void setDataSource(String dataSource) {    
        contextHolder.set(dataSource);    
    }    
        
    /**  
     * @Description: 获取数据源名称  
     * @param   
     * @return String  
     * @throws  
     */    
    public static String getDataSource() {    
        return contextHolder.get();    
    }    
        
    /**  
     * @Description: 清除数据源名称 
     * @param   
     * @return void  
     * @throws  
     */    
    public static void clearDataSource() {    
        contextHolder.remove();    
    }    
}

DataSourceExchange类代码如下:

[java] 
public class DataSourceExchange implements MethodBeforeAdvice,AfterReturningAdvice     
{    
    
    @Override    
    public void afterReturning(Object returnValue, Method method,    
            Object[] args, Object target) throws Throwable {    
        DataSourceContextHolder.clearDataSource();    
    }    
    
    @Override    
    public void before(Method method, Object[] args, Object target)    
            throws Throwable {    
        //这里DataSource是自定义的注解,不是java里的DataSource接口  
        if (method.isAnnotationPresent(DataSource.class))     
        {    
            DataSource datasource = method.getAnnotation(DataSource.class);    
            DataSourceContextHolder.setDataSourceType(datasource.name());    
        }    
        else    
        {    
  //target是被织入增强处理的目标对象,通过获取getDataSourceName函数来获取target的数据源名称  
            DataSourceContextHolder.setDataSource(  
target.getClass.getMethod("getDataSourceName").invoke(target).toString());    
        }    
            
    }    
}

从这段代码可以看出,织入方式支持两种,一种是注解方式,一种就是配置方式。对于注解方式,下面提供DataSource自定义注解类,在每个需要更改数据源的函数上面加上DataSource注解即可。第二种方式是配置方式,对于这种方式,从代码中可以看出,被织入的函数所在的类需要提供一个getDataSourceName的函数,该函数返回该类需要的数据源名称。这里可以根据自己实际的情况进行更改。因为我的每个Service类都是相同的数据源,所以我在每个service类中提供了一个getDataSourceName的函数。


DataSource注解代码如下:

[java] 
@Target({ElementType.METHOD, ElementType.TYPE})  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
public @interface DataSource {  
    String value() default "defaultSource";  
}

数据源配置的相关片段如下,我的系统中是spring和hibernate集成使用的。

[html] 
<?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:jee="http://www.springframework.org/schema/jee"  
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd  
    http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd  
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"  
    default-lazy-init="false">  
  
    <!-- Note: different database must match differnet context base file, for example: Oracle-context-base.xml -->  
      
    <!-- ========================= GENERAL DEFINITIONS ========================= -->  
    <!-- Configurer that replaces ${...} placeholders with values from properties files -->  
    <!-- (in this case, mail and JDBC related properties) -->  
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
        <property name="order" value="1" />  
        <property name="ignoreUnresolvablePlaceholders" value="true" />  
        <property name="locations">  
            <list>  
                <value>classpath*:resources/param/cetc-*.properties</value>  
            </list>  
        </property>  
    </bean>  
      
    <!-- Datasource for JDBC or JNDI, switch by value of jndi.enabled -->  
    <bean id="defaultSource" init-method="init" destroy-method="close"  
        class="com.cetc.datamc.kernel.core.datasource.BasicDataSourceFactoryBean">  
        <property name="dataSourceClass" value="org.apache.commons.dbcp.BasicDataSource" />  
        <property name="configProperties">  
            <props>  
                <prop key="driverClassName">${jdbc.driverClassName}</prop>  
                <prop key="url">${jdbc.url}</prop>  
                <prop key="username">${jdbc.username}</prop>  
                <prop key="password">${jdbc.password}</prop>  
                <prop key="jndiName">${jndi.name}</prop>  
            </props>  
        </property>  
        <property name="jndiEnabled">  
            <value>${jndi.enabled}</value>  
        </property>  
    </bean>  
  
    <bean id="jcsjSource" init-method="init" destroy-method="close"  
        class="com.cetc.datamc.kernel.core.datasource.BasicDataSourceFactoryBean">  
        <property name="dataSourceClass" value="org.apache.commons.dbcp.BasicDataSource" />  
        <property name="configProperties">  
            <props>  
                <prop key="driverClassName">${jdbc.driverClassName}</prop>  
                <prop key="url">${jdbc.url}</prop>  
                <prop key="username">${jdbc.username}</prop>  
                <prop key="password">${jdbc.password}</prop>  
                <prop key="jndiName">${jndi.name}</prop>  
            </props>  
        </property>  
        <property name="jndiEnabled">  
            <value>${jndi.enabled}</value>  
        </property>  
    </bean>  
      
    <!-- 配置多数据源映射关系 -->  
    <bean id="dataSource" class="com.cetc.datamc.kernel.core.datasource.DynamicDataSource">  
        <property name="targetDataSources">  
            <map key-type="java.lang.String">  
               <entry key="defaultSource" value-ref="defaultSource"></entry>  
                <entry key="jcsjSource" value-ref="jcsjSource"></entry>  
            </map>  
        </property>  
    <!-- 默认目标数据源为你主库数据源 -->  
        <property name="defaultTargetDataSource" ref="defaultSource"/>  
    </bean>  
  
     <bean id="dataSourceExchange" class="com.cetc.datamc.kernel.core.datasource.DataSourceExchange"/>  
  
     <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
        <property name="dataSource" ref="dataSource" />  
          
        <property name="hibernateProperties">  
            <props>  
                <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>  
                <prop key="hibernate.query.factory_class">org.hibernate.hql.ast.ASTQueryTranslatorFactory</prop>  
                <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>  
                <prop key="hibernate.jdbc.batch_size">50</prop>  
                <!-- <prop key="hibernate.jdbc.fetch_size">100</prop> --><!--由于oracle驱动的bug导致memory leak-->  
                <prop key="hibernate.generate_statistics">true</prop>  
                <prop key="hibernate.show_sql">true</prop>  
            </props>  
        </property>  
        <property name="lobHandler">  
            <ref bean="oracleLobHandler" />  
        </property>  
    </bean>  
      
    <bean id="txManagerHibernate"  
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
        <property name="sessionFactory">  
            <ref local="sessionFactory" />  
        </property>  
    </bean>  
</beans>

相关的aop配置片段如下:

[html] 
<?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:tx="http://www.springframework.org/schema/tx"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">  
             
    <!--FEXME 移至core工程-->  
    <!--   
      Spring 事务:  
        1、如果配置了HibernateTransactionManager,就不用DataSourceTransactionManager了  
        2、所有需要管理事务的类必须通过spring context管理。直接new的对象不行。  
        3、所有需要事务管理的类必须是interface,因为代理  
     -->  
  
  
    <tx:advice id="txAdviceHibernate" transaction-manager="txManagerHibernate">  
        <tx:attributes>  
            <tx:method name="add*" propagation="REQUIRED" rollback-for="Exception" no-rollback-for="" />  
            <tx:method name="adjust*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="assign*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="batch*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="cancel*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="change*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="create*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="execute*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="export*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="import*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="insert*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="go*" propagation="NOT_SUPPORTED" read-only="true" />  
            <tx:method name="modify*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="notify*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="recover*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="release*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="remove*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="rename*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="reset*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="run*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="save*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="set*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="subscribe*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="swap*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="trigger*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="update*" propagation="REQUIRED" rollback-for="Exception" />  
            <tx:method name="all*" propagation="NOT_SUPPORTED" read-only="true" />  
            <tx:method name="count*" propagation="NOT_SUPPORTED" read-only="true" />  
            <tx:method name="find*" propagation="NOT_SUPPORTED" read-only="true" />  
            <tx:method name="get*" propagation="NOT_SUPPORTED" read-only="true" />  
            <tx:method name="list*" propagation="NOT_SUPPORTED" read-only="true" />  
            <tx:method name="load*" propagation="NOT_SUPPORTED" read-only="true" />  
            <tx:method name="page*" propagation="NOT_SUPPORTED" read-only="true" />  
            <tx:method name="query*" propagation="NOT_SUPPORTED" read-only="true" />  
        </tx:attributes>  
    </tx:advice>  
  
    <!--此处应想办法实现动态加载-->  
    <aop:config>  
      <aop:pointcut id="service" expression="execution(* com.cetc.datamc.app.collect.bs.*.*(..)) or execution(* com.cetc.datamc.app.mdms.bs.*.*(..))" />  
        <aop:advisor pointcut-ref="service" advice-ref="txAdviceHibernate" order="2" />  
        <aop:advisor pointcut-ref="service" advice-ref="dataSourceExchange" order="1" />  
    </aop:config>  
      
</beans>

这样所有相关的配置基本上就完整了,可以完全灵活的实现动态数据源切换了



转载自:http://sdn.net/wei83523408/article/details/50776010





Home