第一篇文章,设计模式和动态代理模式

设计模式的分类

创建型模式

工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式

适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式

行为模式

策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

什么是代理模式

代理模式主要对我们方法执行之前与之后实现增强

代理模式应用场景

  1. 日志的采集
  2. 权限控制
  3. 实现aop
  4. Mybatis mapper
  5. Spring的事务
  6. 全局捕获异常
  7. Rpc远程调用接口
  8. 分布式事务原理代理数据源

代理模式实现的原理

代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色,Proxied)以及代理类角色(Proxy),如上图所示:

第一篇文章,设计模式和动态代理模式

 

 

 

抽象主题角色:可以是接口,也可以是抽象类;

委托类角色:真实主题角色,业务逻辑的具体执行者;

代理类角色:内部含有对真实对象RealSubject的引用,负责对真实主题角色的调用,并在真实主题角色处理前后做预处理和后处理。

代理模式创建方式

静态代理

静态代理需要自己人工编写代理类代码

基于接口实现方式

 

public class OrderServiceProxy  implements  OrderService{
    private OrderService orderService;

    public OrderServiceProxy(OrderService orderService) {
        this.orderService = orderService;
    }

    public String addOrder(String userName, String userPwd) {
        System.out.println("使用静态代理类打印日志开始:userName:" + userName + "," + userPwd);
        String result = orderService.addOrder(userName, userPwd);
        System.out.println("使用静态代理类打印日志结束:userName:" + userName + "," + userPwd);
        return result;
    }
}

 

 

public interface OrderService {
    /**
     * 需要被代理的方法
     * @return
     */
     String addOrder(String userName,String userPwd);
}

 

 

public class Test001 {
    public static void main(String[] args) {
        OrderService orderService = new OrderServiceProxy(new OrderServiceImpl());
        orderService.addOrder("mayikt","123456");
    }
}

基于继承的实现方式

 

public class OrderServiceProxy  extends OrderServiceImpl {
    private OrderService orderService;

    public OrderServiceProxy(OrderService orderService) {
        this.orderService = orderService;
    }

    public String addOrder(String userName, String userPwd) {
        System.out.println("使用静态代理类打印日志开始:userName:" + userName + "," + userPwd);
        String result = super.addOrder(userName, userPwd);
        System.out.println("使用静态代理类打印日志结束:userName:" + userName + "," + userPwd);
        return result;
    }
}

动态代理与静态代理的区别

 

动态代理不需要写代理类对象,通过程序自动生成,而静态代理需要我们自己写代理类对象。

 

动态代理

动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。

动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成 。

 

Jdk动态代理

JDK动态代理的一般步骤如下:

1.创建被代理的接口和类;

2.实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理;

3.调用Proxy的静态方法,创建代理类并生成相应的代理对象;

实现原理:利用拦截器机制必须实现InvocationHandler接口中的invoke方法实现对

我们的目标方法增强。

public class JdkInvocationHandler implements InvocationHandler {
    /**
     * 目标对象
     */
    private Object target;

    public JdkInvocationHandler(Object target) {
        this.target = target;
    }

    /**
     * @param proxy  使用jdk程序生成的代理类
     * @param method 目标方法
     * @param args   方法需要传递的参数
     * @return
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("使用Jdk动态代理打印日志开始" + args[0]);
        Object result = method.invoke(target, args);
        System.out.println("使用Jdk动态代理打印日志结束" + args[1]);
        return result;
    }

    /**
     * 生成代理类
     *
     * @param <T>
     * @return
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }

}

 

 

 

 

 

 

public class JdkInvocationHandler implements InvocationHandler {
    /**
     * 目标对象
     */
    private Object target;

    public JdkInvocationHandler(Object target) {
        this.target = target;
    }

    /**
     * @param proxy  使用jdk程序生成的代理类
     * @param method 目标方法
     * @param args 方法需要传递的参数
     * @return
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("使用Jdk动态代理打印日志开始" + args[0]);
        Object result = method.invoke(target, args);
        System.out.println("使用Jdk动态代理打印日志结束" + args[1]);
        return result;
    }

    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }

}

 

 

JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(new OrderServiceImpl());
OrderServiceImpl orderService = jdkInvocationHandler.getProxy();
orderService.addOrder("mayikt", "meite");

 

 

加上该代码:

  1. 获取代理的生成的class文件

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

第一篇文章,设计模式和动态代理模式

  1.  
  2. 使用反编译工具该Proxy0.class

第一篇文章,设计模式和动态代理模式

 

注意:继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。

纯手写Jdk动态代理

 

思路分析:

  1. 定义InvocationHandler类 回调方法
  2. 使用java反射技术获取接口下所有的方法,拼接 $Proxy0.java代码
  3. 在将$Proxy0.java编译成class文件,读取到内存中

public class $Proxy0 implements com.mayikt.service.OrderService {

    private MayiktJdkInvocationHandler h;
    private static Method m3;

    public $Proxy0(MayiktJdkInvocationHandler mayiktJdkInvocationHandler) {
        this.h = mayiktJdkInvocationHandler;
    }

    @Override
    public String addOrder(String ver1, String var2) {
        try {
            return (String) h.invoke(this, m3, new Object[]{ver1, var2});
        } catch (RuntimeException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }

    static {
        try {
            m3 = Class.forName("com.mayikt.service.OrderService").getMethod("addOrder", Class.forName("java.lang.String"), Class.forName("java.lang.String"));
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

 

 

public class MyJdkInvocationHandler implements MayiktJdkInvocationHandler {
    /**
     * 目标对象
     */
    private Object target;

    public MyJdkInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("使用Jdk动态代理打印日志开始" + args[0]);
        Object result = method.invoke(target, args);
        System.out.println("使用Jdk动态代理打印日志结束" + args[1]);
        return result;
    }
    public <T> T getProxy() {
        return (T) new $Proxy0(this);
    }

}

 

MyJdkInvocationHandler myJdkInvocationHandler = new MyJdkInvocationHandler(new OrderServiceImpl());
OrderService orderService = myJdkInvocationHandler.getProxy();
orderService.addOrder("mayikt", "meite");

 

public class MyProxy {
    private static String rt = "\r\t";

    public static Object newProxyInstance(JavaClassLoader javaClassLoader,
                                          Class<?> classInfo,
                                          MayiktJdkInvocationHandler h) {
        try {
            // 1.封装java代码
            Method[] methods = classInfo.getMethods();
            String proxyClass = "package com.mayikt.service.ext;" + rt
                    + "import java.lang.reflect.Method;" + rt
                    + "import com.mayikt.service.ext.MayiktJdkInvocationHandler;" + rt
                    + "import java.lang.reflect.UndeclaredThrowableException;" + rt
                    + "public class $Proxy0 implements " + classInfo.getName() + "{" + rt
                    + "MayiktJdkInvocationHandler h;" + rt
                    + "public $Proxy0(MayiktJdkInvocationHandler h)" + "{" + rt
                    + "this.h= h;" + rt + "}"
                    + getMethodString(methods, classInfo) + rt + "}";
            // 2. 将代理类源码文件写入硬盘中
            String filename = "d:/code/$Proxy0.java";
            File f = new File(filename);
            FileWriter fw = new FileWriter(f);
            fw.write(proxyClass);
            fw.flush();
            fw.close();
            // 3.编译成class文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
            Iterable units = fileMgr.getJavaFileObjects(filename);
            JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
            t.call();
            fileMgr.close();
            // 4.class文件加入到内存中
            Class proxy0Class = javaClassLoader.findClass("$Proxy0");
            //5.使用java反射机制给函数中赋值
            Constructor m = proxy0Class.getConstructor(MayiktJdkInvocationHandler.class);
            Object o = m.newInstance(h);
            return o;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }

    public static String getMethodString(Method[] methods, Class intf) {
        String proxyMe = "";

        for (Method method : methods) {
            Class<?>[] parameterTypes = method.getParameterTypes();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < parameterTypes.length; i++) {
                sb.append(parameterTypes[i].getName() + " ver" + (i+1));
                if (i < parameterTypes.length - 1) {
                    sb.append(" ,");
                }

            }
            String parameterStr = sb.toString();
            proxyMe = "public " + method.getReturnType().getName() + " " + method.getName() + " ( " + parameterStr + " ) { " +
                    "try {   Method m3 = Class.forName(\"com.mayikt.service.OrderService\").getMethod(\"addOrder\", Class.forName(\"java.lang.String\"), Class.forName(\"java.lang.String\"));" +
                    "return (String) h.invoke(this, m3, new Object[]{ver1, ver2}); } catch (RuntimeException | Error var4) {  throw var4;  } catch (Throwable var5) {   throw new UndeclaredThrowableException(var5); } " +
                    "" +
                    " } ";

        }
        return proxyMe;
    }

}

 

 

 


public class JavaClassLoader extends ClassLoader {

    private File classPathFile;

    public JavaClassLoader(){
//        String classPath=JavaClassLoader.class.getResource("").getPath();
        String classPath="D:\\code";
        this.classPathFile=new File(classPath);
    }

    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        String className= JavaClassLoader.class.getPackage().getName()+"."+name;
        if(classPathFile!=null){
          File classFile=new File(classPathFile,name.replaceAll("\\.","/")+".class");
          if(classFile.exists()){
              FileInputStream in=null;
              ByteArrayOutputStream out=null;
              try {
                  in=new FileInputStream(classFile);
                  out=new ByteArrayOutputStream();
                  byte[] buff=new byte[1024];
                  int len;
                  while ((len=in.read(buff))!=-1){
                     out.write(buff,0,len);
                  }
                  return defineClass(className,out.toByteArray(),0,out.size());
              }catch (Exception e){
                  e.printStackTrace();
              }finally {
                  if(in!=null){
                      try {
                          in.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
                  if(out!=null){
                      try {
                          out.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
        }
        return null;
    }
}

 

 

public class MyJdkInvocationHandler implements MayiktJdkInvocationHandler {
    /**
     * 目标对象
     */
    private Object target;

    public MyJdkInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("使用Jdk动态代理打印日志开始" + args[0]);
        Object result = method.invoke(target, args);
        System.out.println("使用Jdk动态代理打印日志结束" + args[1]);
        return result;
    }


    public <T> T getProxy() {
        return (T) MyProxy.newProxyInstance(new JavaClassLoader(), target.getClass().getInterfaces()[0], this);
    }
}

 

 

CGLIB动态代理

利用asm字节码技术,生成子类实现对目标方法实现增强

Maven依赖

<dependencies>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.12</version>
    </dependency>
</dependencies>

 

 

核心代码

public class CglibMethodInterceptor implements MethodInterceptor {
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("<<<<<日志收集开始...>>>>>>>");
        Object reuslt = proxy.invokeSuper(obj, args);
        System.out.println("<<<<<日志收集结束...>>>>>>>");
        return reuslt;
    }
}

 

 

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor();
Enhancer enhancer = new Enhancer();
// 设置代理类的付类
enhancer.setSuperclass(MemberServiceImpl.class);
// 设置回调对象
enhancer.setCallback(cglibMethodInterceptor);
// 创建代理对象
MemberServiceImpl orderServiceImpl = (MemberServiceImpl) enhancer.create();
orderServiceImpl.getMember();

 

 

第一篇文章,设计模式和动态代理模式

Jdk与Cglib动态代理的区别

  1. Jdk动态代理利用反射技术生成匿名的代理类走InvokeHandler回调方法实现增强,同时也是一种基于接口的方式实现代理。
  2. Cglib动态代理利用asm字节码技术生成一个子类覆盖其中的方法实现增强
  3. 在Spring中如果需要被代理的对象如果实现了接口采用Jdk动态代理,没有实现接口则使用Cglib动态代理。

文章来自每特教育蚂蚁学院