写点什么

Java 动态代理原理

作者:菜皮日记
  • 2023-09-09
    北京
  • 本文字数:3482 字

    阅读完需:约 11 分钟

代理模式

代理模式类图:

被代理类和代理类都实现同一个接口,通过在代理类对象中注入一个被代理对象,通过调用代理对象的方法,在其方法中间接调用被代理对象,从而实现代理的作用。

代理模式要点:

  • 代理和被代理类实现同一接口

  • 代理对象中持有被代理对象的实例

静态代理

静态代理就是代理模式的具体实现。首先定义一个接口

/** * 创建Person接口 * @author Gonjan */public interface Person {    //上交班费    void giveMoney();}
复制代码

接口的实现类,也是被代理类

public class Student implements Person {    private String name;    public Student(String name) {        this.name = name;    }        @Override    public void giveMoney() {       System.out.println(name + "上交班费50元");    }}
复制代码

再定义一个代理类,同样实现接口:

/** * 学生代理类,也实现了Person接口,保存一个学生实体,这样既可以代理学生产生行为 * @author Gonjan * */public class StudentsProxy implements Person{    //被代理的学生    Student stu;        public StudentsProxy(Person stu) {        // 只代理学生对象        if(stu.getClass() == Student.class) {            this.stu = (Student)stu;        }    }        //代理上交班费,调用被代理学生的上交班费行为    public void giveMoney() {        // 这里写增强代码        stu.giveMoney();    }}
复制代码

具体使用代理类:

public class StaticProxyTest {    public static void main(String[] args) {        //被代理的学生张三,他的班费上交有代理对象monitor(班长)完成        Person zhangsan = new Student("张三");                //生成代理对象,并将张三传给代理对象        Person monitor = new StudentsProxy(zhangsan);                //班长代理上交班费        monitor.giveMoney();    }}
复制代码

为什么要有动态代理?

静态代理不足在于,增强代码只能对固定的某一个类起作用,如对 Student 类增强,那么就要编写针对 Student 类的接口 Person 的实现类,在实现类中编写增强代码。

如果增强代码是统计方法执行时间,那么就需要在各种各样不同的类的接口上,重复做上述的事情:编写实现类,类中实现接口方法,编写增强代码等。

而动态代理就是解决一套增强代码,用在各种不同类之上的方式。

动态代理 JDK 实现

如果被代理类有接口,则可以是要用 JDK 的方式。JDK 提供了一组编写动态代理的基类。

Person 类不变:

public interface Person {    //上交班费    void giveMoney();
    void hello();}
复制代码

编写 java.lang.reflect.InvocationHandler 接口的实现类,在这个接口中只有一个方法需要实现,就是 invoke 方法,在这个类中编写增强代码,未来代理功能就是通过调用这里实现的代理效果。

类中的泛型 T 已经证明这个代码可以用在不同的类上。

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;
public class MyInvocationHandler<T> implements InvocationHandler {    // invocationHandler持有的被代理对象    T target;
    public MyInvocationHandler(T target) {        this.target = target;    }
    /**     * proxy:代表动态代理对象     * method:代表正在执行的方法     * args:代表调用目标方法时传入的实参     */    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("代理执行" +method.getName() + "方法");        //代理过程中插入监测方法,计算该方法耗时        MonitorUtil.start();        Object result = method.invoke(target, args);        result = method.invoke(target, args);        result = method.invoke(target, args);        MonitorUtil.finish(method.getName());        System.out.println("------");        return result;    }}
复制代码

使用 java.lang.reflect.Proxy 根据 InvocationHandler 来生成动态代理类,接着调用动态代理类:

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;
public class ProxyTest {    public static void main(String[] args) {
        Person zhangsan = new Student("张三");
        // 把Person接口传给泛型T,表明这是一个针对Person接口的代理,并将被代理对象传入其中让其持有        InvocationHandler stuHandler = new MyInvocationHandler<Person>(zhangsan);
        // 类加载器、代理需要实现的接口,可以有多个、方法调用的实际处理者        // 这一步是为了生成动态代理类的对象        // 类加载器为了能够找到指定的接口位置、需要代理的接口表示动态代理应该实现哪些接口,以及增强代码。        Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
        // stuProxy.giveMoney();
        stuProxy.hello();        stuProxy.hello();        stuProxy.hello();
        // 同一套增强代码,替换不同的泛型 T ,实现增强不同的类        Dog dog = new Dog("小白");
        MyInvocationHandler dogHandler = new MyInvocationHandler<Animal>(dog);
        Animal dogProxy = (Animal) Proxy.newProxyInstance(Animal.class.getClassLoader(), new Class<?>[]{Animal.class}, dogHandler);        dogProxy.woo();    }}
复制代码

动态代理 CGLib 实现

JDK 动态代理是基于接口的,要求被代理类需要实现接口。如果其没有接口,可以使用 CGLib 方式,这种方式基于继承。

一个具体类,也就是被代理类:

public class HelloConcrete {    public String sayHello(String str) {        return "HelloConcrete: " + str;    }}
复制代码

编写实现了 org.springframework.cglib.proxy.MethodInterceptor 接口的类,是一个方法拦截器类,即拦截被代理方法,在其中做增强:

import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;import java.util.Arrays;
class MyMethodInterceptor implements MethodInterceptor {    @Override    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {        System.out.println("拦截方法输出: You said: " + Arrays.toString(args));        return proxy.invokeSuper(obj, args);    }}

复制代码

使用 org.springframework.cglib.proxy.Enhancer 基于被代理类构造出代理对象

import org.springframework.cglib.proxy.Enhancer;
public class TestMain {    public static void main(String[] args) {        Enhancer enhancer = new Enhancer();        enhancer.setSuperclass(HelloConcrete.class);        enhancer.setCallback(new MyMethodInterceptor());
        HelloConcrete hello = (HelloConcrete)enhancer.create();        System.out.println(hello.sayHello("I love you!"));
    }}
复制代码

enhancer 对象的创建和使用跟前面 Proxy.newProxyInstance 的目的是一样的。

参考:

  • java 动态代理实现与原理详细分析:https://www.cnblogs.com/gonjan-blog/p/6685611.html

  • Java Proxy 和 CGLIB 动态代理原理:https://www.cnblogs.com/carpenterlee/p/8241042.html

用户头像

菜皮日记

关注

全干程序员 2018-08-08 加入

还未添加个人简介

评论

发布
暂无评论
Java 动态代理原理_Java_菜皮日记_InfoQ写作社区