GHGL项目总结-spring+mybatis多数据库源

Posted 小崔编程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GHGL项目总结-spring+mybatis多数据库源相关的知识,希望对你有一定的参考价值。

项目需求所在,项目需要去另一个数据库中读写数据。解决方案很简单,动态配置数据源 spring框架中有这样的接口来切换数据源,创建一个DynamicDataSource的类,继承AbstractRoutingDataSource并重写determineCurrentLookupKey方法,代码:
public class DynamicDataSource extends AbstractRoutingDataSource    
    @Override  
    protected Object determineCurrentLookupKey()   
        return DynamicDataSourceHolder.getDataSouce();  
      
 
然后,创建DynamicDataSourceHolder用于持有当前线程中使用的数据源标识
/**
 * 为数据源分配线程
 * @author xw
 *
 */
public class DynamicDataSourceHolder   
    public static final ThreadLocal<String> holder = new ThreadLocal<String>();  
  
    public static void putDataSource(String name)   
        holder.set(name);  
      
  
    public static String getDataSouce()   
        return holder.get();  
      
 
spring-jdbc.xml配置文件中
    <bean name="masterdataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <!-- 数据库驱动类名。 -->
        <property name="driverClassName" value="$jdbc1.driverClass" />
        <!-- 连接数据库时使用的URL。 -->
        <property name="url" value="$jdbc1.jdbcUrl" />
        <!-- 连接数据库时使用的用户名。 -->
        <property name="username" value="$jdbc1.user" />
        <!-- 连接数据库时使用的用户密码。 -->
        <property name="password" value="$jdbc1.password" />
    </bean>
    <bean name="slavedataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <!-- 数据库驱动类名。 -->
        <property name="driverClassName" value="$jdbc2.driverClass" />
        <!-- 连接数据库时使用的URL。 -->
        <property name="url" value="$jdbc2.jdbcUrl" />
        <!-- 连接数据库时使用的用户名。 -->
        <property name="username" value="$jdbc2.user" />
        <!-- 连接数据库时使用的用户密码。 -->
        <property name="password" value="$jdbc2.password" />
    </bean>
    <bean id="dataSource" class="com.djzh.core.dataSource.DynamicDataSource">
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <entry key="master" value-ref="masterdataSource" />
                <entry key="slave" value-ref="slavedataSource" />
            </map>
        </property>
    </bean>
在jdbc.properties文件中配置数据库
jdbc1.driverClass=com.mysql.jdbc.Driver
jdbc1.jdbcUrl=jdbc:mysql://192.168.26.1:3306/test1?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc1.user=1
jdbc1.password=1
jdbc2.driverClass=com.mysql.jdbc.Driver
jdbc2.jdbcUrl=jdbc:mysql://192.168.26.1/test2?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc2.user=1
jdbc2.password=1
在代码逻辑层中写 DynamicDataSourceHolder.putDataSource("master");
基本就可以动态切换数据库。
每次写逻辑时都要写这句话,所以用更方便的方式来优化一下,那就是注解 用aop拦截方法然后设置数据库源,在到层设置更加灵活, aop拦截方法类:
/**
 * Aop实现动态对数据源的参数赋值以获取不同数据源
 * @author xw
 *
 */
public class DataSourceAspect   
      
    public void before(JoinPoint point)  
      
        Object target = point.getTarget();  
        String method = point.getSignature().getName();  
  
        Class<?>[] classz = target.getClass().getInterfaces();
        //Class<?> classz = target.getClass(); 
  
        Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())  
                .getMethod().getParameterTypes();  
        try   
            Method m = classz[0].getMethod(method, parameterTypes);  
            if (m != null && m.isAnnotationPresent(DataSource.class))   
                DataSource data = m.getAnnotation(DataSource.class);  
                DynamicDataSourceHolder.putDataSource(data.value());  
              
              
         catch (Exception e)   
            e.printStackTrace();
          
      
  
spring-aop.xml配置文件:    
 <!-- 配置数据库注解aop -->  
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>  
    
    <bean id="manyDataSourceAspect" class="com.djzh.core.dataSource.DataSourceAspect" />  
    <aop:config>  
        <aop:aspect id="c" ref="manyDataSourceAspect">  
            <aop:pointcut id="tx" expression="execution(* com.djzh.core.dao.*.*(..))"/>  
            <aop:before pointcut-ref="tx" method="before"/>  
        </aop:aspect>  
    </aop:config>  

这样在dao接口方法上写上如下注解     @DataSource("master") 就可以了。
其中遇到的问题: 测试的时候遇到这样 刚开始的时候aop拦截的是service层方法,然后进行以下的测试:
    public static void main(String args[])         @SuppressWarnings("resource")         AbstractApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");         //问题所在
        TipsTransferService service= (TipsTransferServiceImpl) context.getBean("TipsTransferService");         System.out.println(service.test1());               报错信息: Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy18 cannot be cast to com.djzh.core.service.impl.TipsTransferServiceImpl
出错在上边注释的地方,这是动态代理的问题 spring有两种代理方式JDK动态代理和CGLIB动态代理

Jdk 动态代理。具体有如下四步骤:

  1. 通过实现 InvocationHandler 接口创建自己的调用处理器;
  2. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
  3. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
  4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

GCLIB代理

  cglib(Code Generation Library)是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
  cglib封装了asm,可以在运行期动态生成新的class。
  cglib用于AOP,jdk中的proxy必须基于接口,cglib却没有这个限制。

原理区别:

jdk动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP 
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换


回归代码中,出问题的是有实现接口的,所以默认使用的是jdk动态代理,

但是TipsTransferServiceImpl和 context.getBean("TipsTransferService")是同级的,都算是实现了接口TipsTransferService,所以不能用来转换。而这里

可以使用cglib动态代理,因为它的实现方式不同于jdk动态代理。

在spring-aop.xml中可以配置强制使用cglib动态代理方式。以下链接中找到

http://blog.csdn.net/xlgen157387/article/details/51316814 主要是:proxy-target-class="true" <aop:aspectj-autoproxy proxy-target-class="true" ></aop:aspectj-autoproxy>   这样写。 还有一种解决方式就是把
 TipsTransferService service= (TipsTransferServiceImpl) context.getBean("TipsTransferService");
代码中强制转换为接口即可,即
 TipsTransferService service= (TipsTransferService) context.getBean("TipsTransferService");
通过这样的实验中,学到很多。

以上是关于GHGL项目总结-spring+mybatis多数据库源的主要内容,如果未能解决你的问题,请参考以下文章

GHGL项目总结-Myeclipse

GHGL项目总结-Oracle

GHGL项目总结-CA

GHGL项目总结-CA

GHGL项目最后总结

GHGL项目最后总结