@author:Jingdai
@date:2021.05.03
为目标对象提供(包装)一个代理,这个代理可以控制对目标对象的访问。
代理类和目标对象都要实现同一个接口,同时,代理对象需要持有目标对象,外界需要调用目标对象时,直接调用代理对象。同时,代理对象在调用时可以增加一些前置处理和后置处理。下面是一个例子。
接口
package proxy;
// interface
public interface Subject {
void doRequest();
}
目标对象
package proxy;
public class SubjectImpl implements Subject {
@Override
public void doRequest() {
System.out.println("Real Do Request!");
}
}
代理对象
package proxy;
public class StaticProxy implements Subject {
Subject subject;
public StaticProxy(Subject subject) {
this.subject = subject;
}
@Override
public void doRequest() {
System.out.println("Proxy pre process!");
subject.doRequest();
System.out.println("Proxy post process!");
}
}
测试类
package proxy;
public class Test {
public static void main(String[] args) {
Subject subject = new SubjectImpl();
StaticProxy staticProxy = new StaticProxy(subject);
staticProxy.doRequest();
}
}
输出
Proxy pre process!
Real Do Request!
Proxy post process!
从上面的例子可以看出,静态代理实现简单、容易理解,只要注意需要在代理对象中增加一个目标对象的属性。但是静态代理也有缺点,比如接口中的方法增加了一个,那么不光目标对象需要同时增加一个方法的实现,静态代理对象也需要增加一个方法的实现,非常繁琐。由此,就有了动态代理。
动态代理相比静态代理更加复杂一些,看下面这个图。(这里说的是JDK的动态代理,不是CGlib的动态代理)
这里的代理处理器类似于静态代理中的代理对象,它拥有目标对象,由它来完成目标对象的调用和前置、后置处理。Java通过一个InvocationHandler接口来实现一个代理处理器,这个接口中有一个invoke() 方法,它的形参method,就是指目标对象的方法,根据此来调用目标对象的方法。
定义好InvocationHandler之后,通过Proxy类的静态方法自动创建一个代理对象,然后通过这个自动创建的代理对象完成代理功能。看下面这个例子。注意这里的接口和目标对象都和静态代理一样。
接口
package proxy;
// interface
public interface Subject {
void doRequest();
}
目标对象
package proxy;
public class SubjectImpl implements Subject {
@Override
public void doRequest() {
System.out.println("Real Do Request!");
}
}
代理处理器
package proxy;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
public class ProxyHandler implements InvocationHandler{
public Subject subject;
public ProxyHandler(Subject subject) {
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws IllegalAccessException, InvocationTargetException {
System.out.println("Handler pre process!");
Object object = method.invoke(subject, args);
System.out.println("Handler post process!");
return object;
}
}
测试类
package proxy;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
public class Test {
public static void main(String[] args) {
Subject subject = new SubjectImpl();
InvocationHandler handler = new ProxyHandler(subject);
Subject dynamicProxy = (Subject) Proxy.newProxyInstance(SubjectImpl.class.getClassLoader(),
SubjectImpl.class.getInterfaces(), handler);
dynamicProxy.doRequest();
}
}
输出
Handler pre process!
Real Do Request!
Handler post process!
这里可以看出动态代理的代理对象是Java内部自动产生的,不是我们定义类去实现的,同时这些代理对象的类都是继承自Proxy类。
这里要注意一点,如果代理的目标对象中有多个方法,那么多个方法都会通过InvocationHandler的invoke()方法进行处理,那么多个方法的前置处理和后置处理就会变得一样,怎么对不同的方法进行不同的处理呢?很简单,可以通过对invoke()方法的method参数和args参数进行判断,根据不同的方法名和调用参数做出不同的处理,看下面的例子。
接口
package proxy;
// interface
public interface Subject {
void doRequest();
void doAnotherRequest();
}
目标对象
package proxy;
public class SubjectImpl implements Subject {
@Override
public void doRequest() {
System.out.println("Real Do Request!");
}
@Override
public void doAnotherRequest() {
System.out.println("Real Do Another Request!");
}
}
代理处理器
package proxy;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
public class ProxyHandler implements InvocationHandler{
public Subject subject;
public ProxyHandler(Subject subject) {
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws IllegalAccessException, InvocationTargetException {
String methodName = method.getName();
Object object = null;
if ("doRequest".equals(methodName)) {
System.out.println("Handler pre process doRequest!");
object = method.invoke(subject, args);
System.out.println("Handler post process doRequest!");
} else if ("doAnotherRequest".equals(methodName)) {
System.out.println("Handler pre process doAnotherRequest!");
object = method.invoke(subject, args);
System.out.println("Handler post process doAnotherRequest!");
}
return object;
}
}
测试类
package proxy;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
public class Test {
public static void main(String[] args) {
Subject subject = new SubjectImpl();
InvocationHandler handler = new ProxyHandler(subject);
Subject dynamicProxy = (Subject) Proxy.newProxyInstance(SubjectImpl.class.getClassLoader(),
SubjectImpl.class.getInterfaces(), handler);
dynamicProxy.doRequest();
dynamicProxy.doAnotherRequest();
}
}
输出
Handler pre process doRequest!
Real Do Request!
Handler post process doRequest!
Handler pre process doAnotherRequest!
Real Do Another Request!
Handler post process doAnotherRequest!
关于JDK动态代理原理可以参考下一篇。