Java动态代理

代理模式

代理模式是常见的设计模式,Java我们通常通过new一个对象然后调用其对应的方法来访问我们需要的服务。代理模式则是通过创建代理类(proxy)的方式来访问服务,代理类通常会持有一个委托类对象,代理类不会自己实现真正服务,而是通过调用委托类对象的相关方法,来提供服务,所以其实我们调用的还是委托类的服务,但是中间隔了一个代理类。这么做是有好处的,我们可以在访问服务之前或者之后加一下我们需要的操作。例如Spring的面向切面编程,我们可以在切入点之前执行一些操作,切入点之后执行一些操作。这个切入点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。

静态代理

在说动态代理之前先说一下静态代理,静态代理是已经写好的,直接使用即可,下面来看个demo

我们通过一个模拟租客租房的demo来演示,首先我们需要创建一个接口,来模拟租客租房,文件名是Person.java,有一个rentHouse方法

package Proxy;

public interface Person {
    public void rentHouse();
}

创建租客Renter类,实现Person接口

package Proxy;

public class Renter implements Person{
    @Override
    public void rentHouse() {
        System.out.println("租客租房成功");
    }
}

然后创建一个代理类RenterProxy实现Person接口,并且有个租客类的对象

package Proxy;

public class RenterProxy implements Person{
    private Renter rt;

    public RenterProxy(Renter rt) {
        this.rt = rt;
    }

    @Override
    public void rentHouse() {
        System.out.println("中介找房东租房,转租给租客");
        rt.rentHouse();
        System.out.println("中介给租客钥匙,租客入住");
    }
}
image-20220414194416580

动态代理

在静态代理中,代理类是已经写好的,动态代理是在运行时根据我们的逻辑动态生成的,动态代理相较于静态代理的优势在于可以很方便的对代理类的所有函数进行统一管理,如果我们想在每个代理方法前都加一个方法,如果代理方法很多,我们需要在每个代理方法都要写一遍,很麻烦。而动态代理则不需要。

在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。

接口和委托类就不重新写了,直接使用上面的即可,我们接下来需要创建一个RenterInvocationHandler类,实现InvocationHandler接口,他有个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法,然后通过反射在invoke方法中执行代理类的方法。

RenterInvocationHandler.java

package Proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class RenterInvocationHandler <T> implements InvocationHandler {
    private T rt;

    public RenterInvocationHandler(T rt) {
        this.rt = rt;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("租客和中介交流");
        Object result=method.invoke(rt,args);
        return result;
    }
}

RenterProxy.java

package Proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class RenterProxy {
    public static void main(String[] args) {
        //创建委托类的对象
        Person renter=new Renter();
        //创建RenterInvocationHandler对象
        InvocationHandler invocationHandler=new RenterInvocationHandler<Person>(renter);
        //创建代理对象
        Person renterProxy=(Person) Proxy.newProxyInstance(Person.class.getClassLoader(),new Class<?>[]{Person.class},invocationHandler);
        renterProxy.rentHouse();
    }
}

执行结果如下

image-20220414200418348

我个人理解就是通过newProxyInstance创建的动态代理对象,需要传入ClassLoader,接口的Class类数组以及InvocationHandler,获取了动态代理对象以后,调用对应动态代理对象的方法,都会去调用传入的InvocationHandler的invoke方法,如果有理解错误的地方请师傅们指点