• Java 动态代理机制


    Java动态代理机制(参考上一篇的“基于xml文件的IOC配置案例”)

    • 问题:大家有没有考虑过一件事呢?上面讲述的基于xml文件或者注解的IOC配置中持久层操作数据库的过程中没有事务的操作(完整的事务操作:开启事务、提交事务、回滚事务、释放资源)也能成功,那这是为何?(以转账的例子思索)原因很简单,就是每一次增删改查的操作都会获取一个连接,而相互之间没有任何的影响。那么如果在转账的过程中发生异常,数据库操作还会继续执行吗?答案是发生异常之前的数据库操作均可作数,异常之后的操作皆不可作数。这就不符合事务的第一特性——原子性(要么都成功,要么都失败)

    • 为完善之前的代码,这里还需增加两个类——数据库连接类(需要和当前的线程绑定)和事务管理类

      package com.mypro.utils;
      ​
      import javax.sql.DataSource;
      import javax.xml.crypto.Data;
      import java.sql.Connection;
      ​
      /**
       * 连接工具的类,用于从数据源获取一个连接,并实现和线程的绑定
       */
      public class ConnectionUtils {
          private ThreadLocal<Connection> tl = new ThreadLocal<>();
      ​
          private DataSource dataSource;
      ​
          public void setDataSource(DataSource dataSource) {
              this.dataSource = dataSource;
          }
      ​
          /**
           * 获取当前线程上的连接
           * @return
           */
          public Connection getThreadConnection(){
              try{
                  //1、先从ThreadLocal上获取连接
                  Connection conn = tl.get();
                  //2、判断当前线程是否有连接
                  if(conn == null){
                      //3、从数据源获取一个链接,并存入ThreadLocal中
                      conn = dataSource.getConnection();
                      tl.set(conn);
                  }
                  //4、返回当前线程上的连接
                  return conn;
              }catch (Exception e){
                  throw new RuntimeException(e);
              }
          }
      ​
          /**
           * 连接与当前的线程解绑
           */
          public void removeThreadConnection(){
              tl.remove();
          }
      }
      
      
      package com.mypro.utils;
      ​
      /**
       * 和事务管理相关的工具类,开启事务、提交事务、回滚事务、释放资源
       */
      public class TransactionManagerUtils {
          private ConnectionUtils connectionUtils;
      ​
          public void setConnectionUtils(ConnectionUtils connectionUtils) {
              this.connectionUtils = connectionUtils;
          }
      ​
          /**
           * 事务开启
           */
          public void beginTransaction(){
              try{
                  connectionUtils.getThreadConnection().setAutoCommit(false);
              }catch (Exception e){
                  e.printStackTrace();
              }
          }
      ​
          /**
           * 事务提交
           */
          public void commit(){
              try{
                  connectionUtils.getThreadConnection().commit();
              }catch (Exception e){
                  e.printStackTrace();
              }
          }
      ​
          /**
           * 事务回滚
           */
          public void rollback(){
              try{
                  connectionUtils.getThreadConnection().rollback();
              }catch (Exception e){
                  e.printStackTrace();
              }
          }
      ​
          /**
           * 释放资源
           */
          public void release(){
              try{
                  //连接资源断开
                  connectionUtils.getThreadConnection().close();
                  //连接和当前线程解绑
                  connectionUtils.removeThreadConnection();
              }catch (Exception e){
                  e.printStackTrace();
              }
          }
      }

      其实写完这两个类,之前的持久层代码增加这个事务管理类的对象来操作数据库就已经可以了(另外还需添加try...catch...异常),但是在写完之后就会发现持久层的代码特别的臃肿而且大量的代码重复,一点也不利于企业项目开发,为此这里就要用到动态代理

    • 特点:字节码随用随创建,随用随加载

    • 作用:不修改源码的基础上,对原有的方法功能增强

    • 分类

      • 基于接口的动态代理

        • 涉及的类:Proxy

        • 提供者:JDK官方

        • 创建代理对象:使用Proxy类中的newProxyInstance方法

        • 创建代理对象的要求:被代理类最少实现一个接口,如果没有则不能使用。被代理对象可以是该接口的实=实现类对象引用,如 private 接口名称 对象名称;

        • newProxyInstance方法的参数:

          • ClassLoader:类加载器,用于加载代理对象字节码的,和被代理对象使用相同的类加载器。固定写法:被代理类的对象.getClass().getClassLoader()

          • Class[]:字节码数组,用于让代理对象和被代理对象有相同的方法。固定写法:被代理类的对象.getClass().getInterfaces()

          • InvocationHandler:用于提供增强的代码,它是让我们写如何代理。我们一般都是写一个该接口的实现类,通常情况下是匿名内部类,但不是必须的。此接口的实现类都是谁用谁实现

          wordsDao = (WordsDao) Proxy.newProxyInstance(wordsDao.getClass().getClassLoader(),
                          wordsDao.getClass().getInterfaces(), new InvocationHandler() {
                              /**
                               * 执行被代理对象接口的任何方法都会经过该方法
                               * @param proxy  代理对象的引用
                               * @param method  当前执行的方法
                               * @param args   当前执行方法所需的参数
                               * @return   和被代理对象方法有相同的返回值
                               * @throws Throwable
                               */
                              @Override
                              public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                  Object rtValue = null;
                                  try{
                                      transactionManagerUtils.beginTransaction();
                                      rtValue = method.invoke(wordsService, args);
                                      transactionManagerUtils.commit();
                                  }catch (Exception e){
                                      transactionManagerUtils.rollback();
                                      e.printStackTrace();
                                  }finally {
                                      transactionManagerUtils.release();
                                      return rtValue;
                                  }
                              }
                          });
      • 基于子类的动态代理

        • 涉及的类:Enhancer

        • 提供者:第三方cglib库

        • 创建代理对象:使用Enhancer类中的create方法

        • 创建代理对象的要求:被代理类不能是最终类

        • create方法的参数:

          • Class:字节码,用于指定被代理类的对象的字节码

          • Callback:用于提供增强的代码,它是让我们写如何代理。我们一般都是写一个该接口的实现类,通常情况下是匿名内部类,但不是必须的。一般写的都是该接口的子接口实现类 MethodInterceptor

          wordsDao = (WordsDao) Enhancer.create(wordsDao.getClass(), new MethodInterceptor() {
                              /**
                               * 执行被代理对象接口的任何方法都会经过该方法
                               * @param proxy  代理对象的引用
                               * @param method  当前执行的方法
                               * @param args   当前执行方法所需的参数
                               * @param methodProxy   当前执行方法的代理对象
                               * @return   和被代理对象方法有相同的返回值
                               * @throws Throwable
                               */
                              @Override
                              public Object iintercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                                  Object rtValue = null;
                                  try{
                                      transactionManagerUtils.beginTransaction();
                                      rtValue = method.invoke(wordsService, args);
                                      transactionManagerUtils.commit();
                                  }catch (Exception e){
                                      transactionManagerUtils.rollback();
                                      e.printStackTrace();
                                  }finally {
                                      transactionManagerUtils.release();
                                      return rtValue;
                                  }
                              }
                          });
  • 相关阅读:
    关于update set from,第一次碰到,汗!
    列表CheckBox全选 结合DataGrid 进行删除操作
    DataBinder.Eval的基本格式 效率 比较
    [转载](c#)数据结构与算法分析 栈与队列
    [转载]怎样设计递归算法
    Java httpclient.CloseableHttpClient跳过https证书验证
    LeetCode129求根节点到叶节点数字之和
    LeetCode117填充每个节点的下一个右侧节点指针 II
    LeetCode131分割回文串
    LeetCode130被围绕的区域
  • 原文地址:https://www.cnblogs.com/aitiknowledge/p/12706457.html
Copyright © 2020-2023  润新知