java动态代理

1 实现方式

Java 代理模式实现方式,主要有如下五种方法

1.静态代理,工程师编辑代理类代码,实现代理模式;在编译期就生成了代理类。
2.基于 JDK 实现动态代理,通过jdk提供的工具方法Proxy.newProxyInstance动态构建全新的代理类(继承Proxy类,并持有InvocationHandler接口引用 )字节码文件并实例化对象返回。(jdk动态代理是由java内部的反射机制来实例化代理对象,并代理的调用委托类方法)
3.基于CGlib 动态代理模式 基于继承被代理类生成代理子类,不用实现接口。只需要被代理类是非final 类即可。(cglib动态代理底层是借助asm字节码技术
4.基于 Aspectj 实现动态代理(修改目标类的字节,织入代理的字节,在程序编译的时候 插入动态代理的字节码,不会生成全新的Class )
5.基于 instrumentation 实现动态代理(修改目标类的字节码、类装载的时候动态拦截去修改,基于javaagent) -javaagent:spring-instrument-4.3.8.RELEASE.jar (类装载的时候 插入动态代理的字节码,不会生成全新的Class )

委托类 即指的是代理模式中的被代理对象
代理类 指的是生成的代表委托类的一个角色

2 静态代理实现

静态代理是代理类在编译期间就创建好了,不是编译器生成的代理类,而是手动创建的类。在编译时就已经将接口,被代理类,代理类等确定下来。软件设计中所指的代理一般是指静态代理,也就是在代码中显式指定的代理。

实现步骤
委托类和代理类之间的约束接口Dog
约束接口实现类Wolf,实现 Dog 接口,委托角色
代理类实现 FeederProxy,实现Dog接口,并含有一个Dog接口引用属性。 代理角色,代理Dog接口属性引用实例的行为并可以新增公共逻辑

Dog Interface

  • @Description: 静态代理类接口, 委托类和代理类都需要实现的接口规范。* 定义了一个犬科动物的两个行为接口,吃东西,奔跑。
  • 作为代理类 和委托类之间的约束接口

public interface Cat {
public String eatFood(String foodName);
public boolean running();
}
委托类 Lion

  • @Description: 狮子 实现了猫科动物接口Cat, 并实现了具体的行为。作为委托类实现
    /public class Lion implements Cat { private String name; private int runningSpeed; public String getName() { return name;
    } public void setName(String name) { this.name = name;
    } public int getRunningSpeed() { return runningSpeed;
    } public void setRunningSpeed(int runningSpeed) { this.runningSpeed = runningSpeed;
    } public Lion() {
    } @Override
    public String eatFood(String foodName) {
    String eat = this.name + " Lion eat food. foodName = " + foodName;
    System.out.println(eat); return eat;
    } @Override
    public boolean running() {
    System.out.println(this.name + " Lion is running . Speed :" + this.runningSpeed); return false;
    }
    }
    代理类角色(FeederProxy)
    package org.vincent.proxy.staticproxy;/
    *
  • @author PengRong
  • @package org.vincent.proxy.staticproxy
  • @date 2018/12/15 - 17:19
  • @ProjectName JavaAopLearning
  • @Description: 饲养员 实现Cat接口,作为静态代理类实现。代理狮子的行为。
  • 代理类中可以新增一些其他行为,在实践中主要做的是参数校验的功能。
    /public class FeederProxy implements Cat { private Cat cat; public FeederProxy(){} public FeederProxy(Cat cat) { if (cat instanceof Cat) { this.cat = cat;
    }
    } public void setCat(Cat cat) { if (cat instanceof Cat) { this.cat = cat;
    }
    } @Override
    public String eatFood(String foodName) {
    System.out.println("proxy Lion exec eatFood "); return cat.eatFood(foodName);
    } @Override
    public boolean running() {
    System.out.println(“proxy Lion exec running.”); return cat.running();
    }
    }
    静态代理类测试
    package org.vincent.proxy;import org.vincent.proxy.staticproxy.Cat;import org.vincent.proxy.staticproxy.FeederProxy;import org.vincent.proxy.staticproxy.Lion;/
    *
  • @author PengRong
  • @package org.vincent.proxy
  • @date 2018/12/15 - 18:31
  • @ProjectName JavaAopLearning
  • @Description: 静态代理类测试
    /public class staticProxyTest { public static void main(String[] args) {
    Lion lion = new Lion();
    lion.setName(“狮子 小王”);
    lion.setRunningSpeed(100); /
    *
    * new 静态代理类,静态代理类在编译前已经创建好了,和动态代理的最大区别点
    */
    Cat proxy = new FeederProxy(lion); System.out.println(Thread.currentThread().getName()+" – " + proxy.eatFood(“水牛”));
    proxy.running();
    }
    }
    静态代理很好的诠释了代理设计模式,代理模式最主要的就是有一个公共接口(Cat),一个委托类(Lion),一个代理类(FeederProxy),代理类持有委托类的实例,代为执行具体类实例方法。 上面说到,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。这里的间接性就是指客户端不直接调用实际对象的方法,客户端依赖公共接口并使用代理类。 那么我们在代理过程中就可以加上一些其他用途。 就这个例子来说在 eatFood方法调用中,代理类在调用具体实现类之前添加System.out.println("proxy Lion exec eatFood ");语句 就是添加间接性带来的收益。代理类存在的意义是为了增加一些公共的逻辑代码。

动态代理类(基于接口实现)
静态代理是代理类在代码运行前已经创建好,并生成class文件;动态代理类 是代理类在程序运行时创建的代理模式。

动态代理类的代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。 想想你有100个静态代理类,现在有一个需求,每个代理类都需要新增一个处理逻辑,你需要打开100个代理类在每个代理方法里面新增处理逻辑吗? 有或者代理类有5个方法,每个方法都需要新增一个处理逻辑, 你需要在每个方法都手动新增处理逻辑吗? 想想就挺无趣的。动态代理类帮你一键搞定。

动态代理类涉及角色
委托类和代理类实现的公共接口(Person.java)
实现公共接口的具体委托类(SoftwareEngineer.java)
InvocationHandler接口被Proxy类回调处理,一般实现 InvocationHandler 接口的类具有委托类引用,接口方法 invoke 中添加公共代码并调用委托类的接口方法。(PersonInvocationHandler.java)
JDK提供生成动态代理类的核心类Proxy ( JDK 提供的Proxy.java)
基于JDK技术 动态代理类技术核心 Proxy类和一个 InvocationHandler 接口
java的java.lang.reflect包下提供了Proxy类和一个 InvocationHandler 接口,这个类Proxy定义了生成JDK动态代理类的方法 getProxyClass(ClassLoader loader,Class… interfaces)生成动态代理类,返回class实例代表一个class文件。可以保存该 class 文件查看jdk生成的代理类文件长什么样

该生成的动态代理类继承Proxy类,(重要特性) ,并实现公共接口。

InvocationHandler这个接口 是被动态代理类回调的接口,我们所有需要增加的针对委托类的统一处理逻辑都增加到invoke 方法里面在调用委托类接口方法之前或之后 结束战斗。

案例
公共接口
package org.vincent.proxy.dynamicproxy;/**

  • Created by PengRong on 2018/12/25.
  • 创建Person 接口 用于定义 委托类和代理类之间的约束行为
    /public interface Person{ /*
    *
    • @param name 人名
    • @param dst 工作目的地
      /
      void goWorking(String name, String dst); /
      *
    • 获取名称
    • @return
      /
      String getName( ); /
      *
    • 设置名称
    • @param name
      /
      void setName(String name);
      }
      具体实现类,等下被委托,被代理的类 SoftwareEngineer.java
      package org.vincent.proxy.dynamicproxy;/
      *
  • Created by PengRong on 2018/12/25.
  • 动态代理委托类实现, 实现接口 Person。 被动态生成的代理类代理
    /public class SoftwareEngineer implements Person{ public SoftwareEngineer(){} public SoftwareEngineer(String name){ this.name=name;
    } private String name; public String getName() { return name;
    } public void setName(String name) { this.name = name;
    } @Override
    public void goWorking(String name, String dst) {
    System.out.println(“name =”+name+" , 去 “+dst +” 工作");
    }
    }
    InvocationHandler 接口实现 PersonInvocationHandler.java
    package org.vincent.proxy.dynamicproxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.util.Arrays;/
    *
  • Created by PengRong on 2018/12/25.
  • PersonInvocationHandler 类 实现InvocationHandler接口,这个类中持有一个被代理对象(委托类)的实例target。该类别JDK Proxy类回调
  • InvocationHandler 接口中有一个invoke方法,当一个代理实例的方法被调用时,代理方法将被编码并分发到 InvocationHandler接口的invoke方法执行。
    /public class PersonInvocationHandler implements InvocationHandler { /*
    • 被代理对象引用,invoke 方法里面method 需要使用这个 被代理对象
      /
      T target; public PersonInvocationHandler(T target) { this.target = target;
      } /
      *
    • @param proxy 代表动态生成的 动态代理 对象实例
    • @param method 代表被调用委托类的接口方法,和生成的代理类实例调用的接口方法是一致的,它对应的Method 实例
    • @param args 代表调用接口方法对应的Object参数数组,如果接口是无参,则为null; 对于原始数据类型返回的他的包装类型。
    • @return
    • @throws Throwable
      /
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /
      *
      • 在转调具体目标对象之前,可以执行一些功能处理
        /
        System.out.println(“被动态代理类回调执行, 代理类 proxyClass =”+proxy.getClass()+" 方法名: " + method.getName() + “方法. 方法返回类型:”+method.getReturnType()
        +" 接口方法入参数组: "+(args ==null ? “null” : Arrays.toString(args))); /
        *
      • 代理过程中插入监测方法,计算该方法耗时
        /
        MonitorUtil.start();
        Thread.sleep(1); /
        * 调用呗代理对象的真实方法,*/
        Object result = method.invoke(target, args);
        MonitorUtil.finish(method.getName()); return result;
        }
        }
        PersonInvocationHandler invoke 方法中添加的公共代码,这里简单以统计方法执行时间为逻辑
        package org.vincent.proxy.dynamicproxy;/**
  • Created by PengRong on 2018/12/25.
  • 方法用时监控类
    /public class MonitorUtil { private static ThreadLocal tl = new ThreadLocal<>(); public static void start() {
    tl.set(System.currentTimeMillis());
    } /
    *
    • 结束时打印耗时
    • @param methodName 方法名
      /
      public static void finish(String methodName) { long finishTime = System.currentTimeMillis();
      System.out.println(methodName + “方法执行耗时” + (finishTime - tl.get()) + “ms”);
      }
      }
      最后的是 怎么创建代理类
      package org.vincent.proxy.jdkdynamicProxy;import org.vincent.proxy.dynamicproxy.Person;import org.vincent.proxy.dynamicproxy.PersonInvocationHandler;import org.vincent.proxy.dynamicproxy.SoftwareEngineer;import sun.misc.ProxyGenerator;import java.io.FileOutputStream;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import java.nio.file.Path;import java.nio.file.Paths;import java.util.Arrays;import java.util.Properties;/
      *
  • 动态代理类测试
  • Created by PengRong on 2018/12/25.
    /public class JdkDynamicProxyTest { public static void main(String[] args) throws Exception { // 打开保存JDK动态代理生成的类文件
    saveGeneratedJdkProxyFiles(); /
    *
    * 第一种方法: 通过 Proxy.newProxyInstance 方法 获取代理对象
    /
    System.out.println("-------------------第一种创建代理类方法--------------"); //创建一个实例对象,这个对象是被代理的对象,委托类
    Person person = new SoftwareEngineer(“Vincent”); //创建一个与代理类相关联的InvocationHandler,每一个代理类都有一个关联的 InvocationHandler,并将代理类引用传递进去
    InvocationHandler Handler = new PersonInvocationHandler<>(person); //创建一个 代理对象 personProxy 来代理 person,创建的代理对象的每个执行方法都会被替换执行Invocation接口中的invoke方法
    Person personProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{Person.class}, Handler); /
    * 代理类信息 /
    System.out.println(“package = " + personProxy.getClass().getPackage() + " SimpleName = " + personProxy.getClass().getSimpleName() + " name =” + personProxy.getClass().getName() + " CanonicalName = " + “” + personProxy.getClass().getCanonicalName() + " 实现的接口 Interfaces = " + Arrays.toString(personProxy.getClass().getInterfaces()) + " superClass = " + personProxy.getClass().getSuperclass() + " methods =" + Arrays.toString(personProxy.getClass().getMethods())); // 通过 代理类 执行 委托类的代码逻辑
    personProxy.goWorking(personProxy.getName(), “深圳”); System.out.println("-------------------第二种创建代理类方法--------------"); /
    *
    * 动态代理对象步骤
    * 1、 创建一个与代理对象相关联的 InvocationHandler,以及真实的委托类实例
    * 2、Proxy类的getProxyClass静态方法生成一个动态代理类stuProxyClass,该类继承Proxy类,实现 Person.java接口;JDK动态代理的特点是代理类必须继承Proxy类
    * 3、通过代理类 proxyClass 获得他的带InvocationHandler 接口的构造函数 ProxyConstructor
    * 4、通过 构造函数实例 ProxyConstructor 实例化一个代理对象,并将 InvocationHandler 接口实例传递给代理类。
    /
    // 1、创建 InvocationHandler 实例并设置代理的目标类对象
    Person persontwo = new SoftwareEngineer(“Vincent”);
    InvocationHandler Handlertwo = new PersonInvocationHandler<>(persontwo); // 2 创建代理类,是一个字节码文件, 把 proxyClass 保存起来就能看到 他继承Proxy 类,实现Person接口
    Class proxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), new Class[]{Person.class}); /
    * 代理类信息 /
    System.out.println(“package = " + proxyClass.getPackage() + " SimpleName = " + proxyClass.getSimpleName() + " name =” + proxyClass.getName() + " CanonicalName = " + “” + proxyClass.getCanonicalName() + " 实现的接口 Interfaces = " + Arrays.toString(proxyClass.getInterfaces()) + " superClass = " + proxyClass.getSuperclass() + " methods =" + Arrays.toString(proxyClass.getMethods())); // 3、 通过 proxyClass 获得 一个带有InvocationHandler参数的构造器constructor
    Constructor ProxyConstructor = proxyClass.getConstructor(InvocationHandler.class); // 4、通过构造器创建一个 动态代理类 实例
    Person stuProxy = (Person) ProxyConstructor.newInstance(Handlertwo); /
    * 检测生成的类是否是代理类 /
    System.out.println("stuProxy isProxy "+Proxy.isProxyClass(stuProxy.getClass())); /
    * 获取 代理类关联的 InvocationHandler 是哪个*/
    InvocationHandler handlerObject = Proxy.getInvocationHandler(stuProxy);
    System.out.println(handlerObject.getClass().getName());
    stuProxy.goWorking(stuProxy.getName(), “广州”); // 保存代理類
    saveClass("$PersonProxy0", proxyClass.getInterfaces(), “D:/123/”);
    } /**
    • 生成代理类 class 并保持到文件中
    • @param className 生成的代理类名称
    • @param interfaces 代理类需要实现的接口
    • @param pathdir 代理类保存的目录路径,以目录分隔符结尾
      /
      public static void saveClass(String className, Class[] interfaces, String pathdir) { /
      *
      • 第一个参数是 代理类 名 。
      • 第二个参数是 代理类需要实现的接口
        /
        byte[] classFile = ProxyGenerator.generateProxyClass(className, interfaces); /
        *
      • 如果目录不存在就新建所有子目录
        /
        Path path1 = Paths.get(pathdir); if (!path1.toFile().exists()){
        path1.toFile().mkdirs();
        }
        String path = pathdir + className + “.class”; try (FileOutputStream fos = new FileOutputStream(path)) {
        fos.write(classFile);
        fos.flush();
        System.out.println(“代理类class文件写入成功”);
        } catch (Exception e) {
        System.out.println(“写文件错误”);
        }
        } /
        *
    • 设置保存Java动态代理生成的类文件。
    • @throws Exception
      */
      public static void saveGeneratedJdkProxyFiles() throws Exception {
      Field field = System.class.getDeclaredField(“props”);
      field.setAccessible(true);
      Properties props = (Properties) field.get(null);
      props.put(“sun.misc.ProxyGenerator.saveGeneratedFiles”, “true”);
      }}

你可能感兴趣的:(Java)