动态切换数据源不能正常切换

在开发双数据源按需切换的时候,发现不能按要求切换数据源。环境是:struts2、spring、Hibernate。源代码如下:
数据源设置类:

 public class DataSourceSwitcher {

    public static final String DATA_SOURCE_MASTER = "master"; // 主库
    public static final String DATA_SOURCE_SLAVE = "slave"; // 从库

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();  

    /**
     * 设置数据源
     */
    public static void setDataSource(String dbName) {
        contextHolder.set(dbName);
    }

    /**
     * 获取数据源
     */
    public static String getDataSource() {
        return (String) contextHolder.get();
    }

    /**
     * 删除数据源
     */
    public static void removeDataSource() {
        contextHolder.remove();
    } 
}

动态切换类:

 public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        System.out.println("切换数据源到:" + DataSourceSwitcher.getDataSource());
        return DataSourceSwitcher.getDataSource();
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}

AOP切面类:

 @Component
@Aspect
@Order(0)
public class DataSourceAdvice {

    private static Logger logger = Logger.getLogger(DataSourceAdvice.class);

    @Before(("@annotation(com.common.DynamicDataSourceAnnotation)"))
    public void before(JoinPoint point) {
        try {
            // 获取数据源
            String dataSource = getDataSource(point);

            // 设置数据源
            logger.info("设置数据源" + dataSource);
            DataSourceSwitcher.setDataSource(dataSource);
        } catch (Exception e) {
            logger.error("设置数据源失败...");
            logger.error(StringHandleUtils.getExceptionInfo(e));
        }
    }


    @After("@annotation(com.common.DynamicDataSourceAnnotation)")   //后置通知
    public void testAfter(JoinPoint point){
        try {
            // 获取数据源
            String dataSource = getDataSource(point);

            // 若数据源不是主库,则清空
            if(!DataSourceSwitcher.DATA_SOURCE_MASTER.equals(dataSource)) {
                logger.error("删除数据源" + dataSource + "成功...");
                DataSourceSwitcher.removeDataSource();
            }
        } catch (Exception e) {
            logger.error("删除数据源失败...");
            logger.error(StringHandleUtils.getExceptionInfo(e));
        }
    }

    @SuppressWarnings("rawtypes")
    private String getDataSource(JoinPoint point) {
        String dataSource = DataSourceSwitcher.DATA_SOURCE_MASTER;
        try{
            Class<?> className = point.getTarget().getClass();
            String methodName = point.getSignature().getName();
            Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
            Method method = className.getMethod(methodName, argClass);
            if (method.isAnnotationPresent(DynamicDataSourceAnnotation.class)) {
                DynamicDataSourceAnnotation annotation = method.getAnnotation(DynamicDataSourceAnnotation.class);
                dataSource = annotation.dataSource();
            }
        }catch(Exception e) {
            logger.error("获取数据源失败...");
            logger.error(StringHandleUtils.getExceptionInfo(e));
        }

        return dataSource;
    }
}

注解类:

 @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DynamicDataSourceAnnotation {
     String dataSource() default DataSourceSwitcher.DATA_SOURCE_MASTER;
}

配置文件:

     <!-- master -->
    <bean id="master" class="com.common.DataSource">
         <property name="driver" value="${master.datasource.driverClassName}" /> 
         <property name="driverUrl" value="${master.datasource.url}" /> 
         <property name="user" value="${master.datasource.username}"/> 
         <property name="password" value="${master.datasource.password}"/> 
         <property name="alias" value="master" />
     </bean>

    <!-- slave -->
    <bean id="slave" class="com.common.DataSource">
        <property name="driver" value="${slave.datasource.driverClassName}" /> 
        <property name="driverUrl" value="${slave.datasource.url}" /> 
        <property name="user" value="${slave.datasource.username}"/> 
        <property name="password" value="${slave.datasource.password}"/> 
        <property name="alias" value="slave" />
    </bean>

    <!-- 配置动态切换数据源 -->
    <bean id="dataSource" class="com.common.DynamicDataSource">  
        <property name="defaultTargetDataSource" ref="master"></property>
        <property name="targetDataSources">  
            <map key-type="java.lang.Object">  
                <entry value-ref="master" key="master"></entry>  
                <entry value-ref="slave" key="slave"></entry>
            </map>  
        </property>  
    </bean>

    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource">
            <ref bean="dataSource" />
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>  
            </props>
        </property>
        <property name="packagesToScan" value="com.common.pojo"/>
    </bean>

    <!-- 定义事务管理器(声明式的事务)   -->
    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <aop:aspectj-autoproxy proxy-target-class="true" />
    <tx:annotation-driven transaction-manager="transactionManager" />

使用:

Action层:

 @Action(name = "common")
@Component
public class CommonAction extends ActionSupport{

    @Autowired
    private CommonService service;

    public String queryInfo() {
        List<TradeInfo> list = service.queryInfo();
        for(TradeInfo ti : list) {
            System.out.println("service.queryInfo():" + ti.getName());
        }

        System.out.println("================================");

        List<TradeInfo> list2 = service.queryInfo2();
        for(TradeInfo ti : list2) {
            System.out.println("service.queryInfo2():" + ti.getName());
        }

        return "index";
    }
}

Service层:

 @Service
@Transactional(rollbackFor = Exception.class)
public class CommonService {

    @Autowired
    private CommonDAO commonDAO;

    @SuppressWarnings("unchecked")
    @Override
    @DynamicDataSourceAnnotation(dataSource = DataSourceSwitcher.DATA_SOURCE_MASTER)
    public List<TradeInfo> queryInfo() throws SystemException {
        try {
            String sql = "FROM TradeInfo";
            return this.commonDAO.find(sql);
        } catch (Exception e) {

        }
    }

    @SuppressWarnings("unchecked")
    @Override
    @DynamicDataSourceAnnotation(dataSource = DataSourceSwitcher.DATA_SOURCE_SLAVE)
    public List<TradeInfo> queryInfo2() throws SystemException {
        try {
            String sql = "FROM TradeInfo";
            return this.commonDAO.find(sql);
        } catch (Exception e) {

        }
    }
}

Dao层:

 @Repository
public class CommonDao extends HibernateDaoSupport {

    public List find(String sql) throws SystemException {
        try {
            return getHibernateTemplate().find(sql);
        } catch (DataAccessException e) {

        } catch (HibernateException e) {

        } catch (SQLException e) {

        }

        return null;
    }
}

测试输出结果:

 设置数据源master
切换数据源到:master
删除数据源master成功...
service.queryInfo():主数据源
================================
设置数据源slave
切换数据源到:slave
service.queryInfo()2:主数据源

从输出信息可以看出数据源并没有成功切换。queryInfo()走的是主数据源;queryInfo()2走的也是主数据源。

调试结果是:第一个方法可以正常切换数据源,第一个方法之后的方法都无法正常切换,获取的连接是第一个方法的连接。

而在Action中用Main方法:

 public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    CommonAction = (CommonAction ) ctx.getBean("commonAction");
    c.queryInfo();
)

输出结果:

 设置数据源master
切换数据源到:master
切换数据源到:master
删除数据源master成功...
service.queryInfo():主数据源
================================
设置数据源slave
切换数据源到:slave
切换数据源到:slave
service.queryInfo()2:从数据源

从输出信息可以看出数据源按照预期正常切换了。

请问出现这种问题有哪些可能因素影响呢?

大哥,你知道为什么没有能够完成切换吗?想要切换数据源必须进入你的aop切面,你第一次能够成功,说明能够进入aop切面,但是你第二次,
相当于本地方法调用,这样就进入不到aop切面了,所以你不能够完成数据源切换,要解决这个问题,你必须获取到该对象的aop代理对象给你
一行代码:
CommonAction ca = (CommonAction ) AopContext.currentProxy();
然后用ca进行方法调用,这样就能够正常切换了!