Spring灵魂设计模式之代理

188 阅读2分钟

0 代理的神奇之处

我们知道Spring AOP 实现的灵魂在于动态代理。

1 代理模式

那么我们接下下看看普通代理模式.

image.png

从类图我们可以看到普通代理就是和真实的被代理对象实现同一个接口,然后最终执行普通代理类的目标方法。普通代理类的目标方法除了调用目标类的方法外,在方法执行的前后可以插入一些别的事情。

// 代理类
  RealSubject  realSubject;

  public static void request() {
     preRequest();  // 切入
     realSubject.request();
     afterRequest();   // 切入
  }

2 动态代理

//UserDAO接口
public interface AbstractUserDAO {
   public String  findUserById(String userId);
}
//具体UserDAO类:真实角色
public class UserDAO implements AbstractUserDAO {
   public String  findUserById(String userId) {
      if (userId.equalsIgnoreCase("张无忌")) {
         return "张无忌,男,明教教主";
      } else {
         return String.format("查询ID为%s的用户信息失败!",userId);
      }
   }
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

//自定义请求处理程序类
public class DAOLogHandler implements InvocationHandler {
   private Object object;
   private  final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
   public DAOLogHandler() {   
   }
   
   //自定义有参构造函数,用于注入一个需要提供代理的真实主题对象
   public DAOLogHandler(Object object) {
      this.object = object;
   }
   
   //实现invoke()方法,调用在真实主题类中定义的方法
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      beforeInvoke();
      Object result = method.invoke(object, args); //转发调用
      System.out.println(result);
      afterInvoke();
      return result;
   }

   //记录方法调用时间
   public void beforeInvoke(){
      System.out.println("beforeInvoke() 执行,调用时间:" + dateTimeFormatter.format(LocalDateTime.now()));
   }

   public void afterInvoke(){
      System.out.println("afterInvoke() 执行,方法调用结束!" );
   }
}

调用程序

  public static void main(String args[]) {
      AbstractUserDAO userDAO = new UserDAO();
      InvocationHandler handler = new DAOLogHandler(userDAO);
      //动态创建代理对象,用于代理一个AbstractUserDAO类型的真实主题对象
      AbstractUserDAO proxy = (AbstractUserDAO) Proxy.newProxyInstance(AbstractUserDAO.class.getClassLoader(), new Class[]{AbstractUserDAO.class}, handler);
      proxy.findUserById("张无忌"); //调用代理对象的业务方法
      System.out.println("------------------------------");
   }

CGlib 实现动态代理

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 *  CG lib动态代理演示
 */
public class CGlibDynamicProxy {
    // 实际被代理类
    public static class UserDAO  {
        public String  findUserById(String userId) {
            if (userId.equalsIgnoreCase("张无忌")) {
                System.out.println("找到档案: 张无忌,男,明教教主");
                return "找到档案: 张无忌,男,明教教主";
            } else {
                return String.format("查询ID为%s的用户信息失败!",userId);
            }
        }
    }
    // 动态代理类
    public static class ProxyMethodInterceptor implements MethodInterceptor {
        @Override
        public Object intercept(Object enhancerObj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("在调用之前,我要干点啥呢?");
            Object returnValue = methodProxy.invokeSuper(enhancerObj, args);
            System.out.println("在调用之后,我要干点啥呢?");
            return returnValue;
        }
    }
    public static void main(String[] args) {
        //这里会保存生成之后的动态代理类
//        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"D:\home");
        //目标对象
        Enhancer en = new Enhancer();
        en.setSuperclass(UserDAO.class);
        en.setCallback(new ProxyMethodInterceptor());
        UserDAO userDao = (UserDAO) en.create();
        userDao.findUserById("张无忌");
    }
}

3 JDK proxy 和CG lib的区别

  1. JDK代理只能对实现了接口的类生成代理,而不能针对类
  2. CGlib是针对类生成代理,主要是对指定的类生成一个子类,覆盖其中的方法。因为是继承,所以该类和方法不能声明成final.CGlib是第三方类库,使用的时候需要引入。
  3. JDK代理是JDK自带的类实现,不需要依赖第三方库