Java笔记:代理模式
静态代理
静态代理的实现需要一个接口以及两个共同实现该接口的类,为示以区分,描述如下:
Subject接口(公共接口):两个类都要继承该接口,可使公共接口引用来引用代理类对象形成向上造型。
RealSubject类(真正实现者):继承Subject接口,作为接口方法的主要实现者。
Proxy类(代理类):继承Subject接口,同时以组合的形式包含一个RealSubject类对象,需要调用RealSubject的方法时调用Proxy类的同名方法,由Proxy类的方法再调用RealSubject类方法,实现业务的处理。同时代理类也可在同名方法中RealSubject方法执行前后添加一些自己的方法增强。
Subject接口代码:
public interface Subject {
void sayHello();
}
RealSubject类代码:
public class RealSubject implements Subject {
@Override
public void sayHello() {
System.out.println("Hello!");
}
}
Proxy代理类代码与测试代码:
public class MyProxy implements Subject {
private RealSubject realSubject;
public MyProxy(RealSubject realSubject){
this.realSubject = realSubject;
}
@Override
public void sayHello() {
System.out.println("前置增强");
realSubject.sayHello();
System.out.println("后置增强");
}
public static void main(String[] args) {
MyProxy myProxy = new MyProxy(new RealSubject());
myProxy.sayHello();
}
}
由以上代码可见,代理类在不改变实际操作类对象方法代码的基础上,对实际操作类对象进行了封装与方法上的增强,可以使对象的方法得到扩展,在想要使用真正实现类进行操作时,调用代理类的同名方法,由代理类调用真正实现类的方法(向上造型)完成目的。
静态代理仍还有一些缺陷,每一个RealProject需要被代理时,都需要创建出来一个代理类,而代理类仅仅是调用被代理类的一些方法,此时就会出现代码的赘余,使用两个类、一个公共接口才能实现代理模式。而jdk使用了动态代理的方法来避免手写代理类情况的出现。
动态代理
代理类在程序运行时创建的代理模式称为动态代理。在静态代理中,代理类是需要我们手动实现提前写好的真正的类,而动态代理的代理类是根据我们编写的代码动态生成的,代理类可由jdk隐式创建。由jdk实现的动态代理需要两个核心部件:InvocationHandler接口和Proxy类。动态代理简单实现部件如下:
动态代理使用的类
生成代理类的工厂类:
public class DynamicProxyHolder implements InvocationHandler {
//由动态代理对象所持有的被增强的对象 业务逻辑的具体实现者
private Object target;
//拦截器对象 方法增强的具体实现者
private Interceptor interceptor;
//传入目标对象和拦截器 使用拦截器增强方法 生成动态代理对象
public static Object getDynamicProxyHolder(Object target, Interceptor interceptor){
DynamicProxyHolder holder = new DynamicProxyHolder();
holder.target = target;
holder.interceptor = interceptor;
Object proxyInstance = Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), holder);
return proxyInstance;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//异常标志
boolean exceptionFlag = false;
Invocation invocation = new Invocation(args, method, target);
Object returnObject = null;
try {
if (interceptor.before()){
//是否执行环绕方法由拦截器的before方法执行结果决定
returnObject = this.interceptor.around(invocation);
} else {
//不进行环绕增强 直接执行主逻辑
returnObject = method.invoke(target, args);
}
} catch (Exception e){
//接收抛出异常 将异常标志设置为真
exceptionFlag = true;
}
interceptor.after();
if (exceptionFlag){
//产生异常 执行拦截器中发生异常情况
interceptor.afterThrowing();
} else {
//方法执行过程中未产生异常 执行拦截器中方法正常完成情况
interceptor.afterReturning();
}
return returnObject;
}
}
可以看到,该类的实际作用有两个,其一是产生该类的实例对象作为真实实现类被增强对象和增强该对象方法的拦截器对象的持有者,另一个作用是作为工厂类,调用其静态方法生成动态代理对象。
该类含有两个方法:
静态方法getDynamicProxyHolder(Object target, Interceptor interceptor),工厂方法,生成该类的一个实例对象并接受真实实现类和增强方法拦截器,并调用类Proxy的静态方法newProxyInstance来生成一个代理对象并返回。该静态方法含有三个参数:ClassLoader loader, Class<?>[] interfaces, InvocationHandler h,分别为类加载器、真实实现类所实现的接口以及实现了InvocationHandler接口的对象。前两个参数一般直接从真正实现类对象中获取,最后一个参数则由生成代理类DynamicProxyHolder的实例对象提供,以便在调用动态代理对象方法时能调用DynamicProxyHolder实例对象的invoke方法。
成员方法invoke(Object proxy, Method method, Object[] args),实现InvocationHandler接口后需重写的方法。协调配合真正实现类和增强方法拦截器调用两者方法实现对真正实现类的代理与方法的增强。
其中巧妙的几点在于:
1、提前设置异常标记位,由于真正实现类方法只能正常结束或抛出异常,所以在执行真实实现类方法的逻辑外加了try-catch块,抛出异常将异常标记位置为真,最后执行出现异常的逻辑,否则执行方法完成的逻辑。
2、由拦截器对象interceptor的before方法执行结果(返回值为布尔类型)决定是否进行环绕增强,若返回为真,调用interceptor的around方法,向其中传入提前依靠真实实现类对象、当前执行方法method和方法参数args[]生成的局部变量invocation,在拦截器的环绕增强中调用invocation的proceed()方法(最终仍使用method.invoke()方法依靠反射执行方法);否则不选择交由拦截器执行,直接通过method.invoke()方法执行当前真实实现类对象所执行的方法。
该方法需要使用到两个类的对象:Invocation方法调用类和Interceptor拦截器对象,代码如下:
Invocation类:
public class Invocation {
private Object[] params;
private Method method;
private Object target;
public Invocation(Object[] params, Method method, Object target) {
this.params = params;
this.method = method;
this.target = target;
}
//反射方法
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, params);
}
}
此类更像一个方法的保存类,接收执行方法和参数,使用被调用方法method.invoke()方法以反射的形式调用方法,完成真实实现类的方法调用。
拦截器Interceptor接口:
public interface Interceptor {
boolean before();
void after();
Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException;
void afterReturning();
void afterThrowing();
boolean useAround();
}
拦截器真正实现:
public class MyIntercepter implements Interceptor{
@Override
public boolean before() {
System.out.println("before ...");
return true;
}
@Override
public void after() {
System.out.println("after ...");
}
@Override
public Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
System.out.println("around before ...");
Object o = invocation.proceed();
System.out.println("around after ...");
return o;
}
@Override
public void afterReturning() {
System.out.println("after returning ...");
}
@Override
public void afterThrowing() {
System.out.println("after throwing ...");
}
@Override
public boolean useAround() {
return true;
}
}
拦截器内规定了方法增强的一系列动作,由代理类进行调用实现方法的增强。包含前置、后置、环绕、返回、异常通知。环绕通知时仍接收Invocation对象,调用其proceed()方法,底层method.invoke()方法完成方法调用与环绕增强。
动态代理的使用
在使用时必须将生成的代理对象强转为真实实现类实现的接口类型,而非真实实现类,否则报错:
Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to proxyStyle.RealSubject
at proxyStyle.dynamicProxy.DynamicProxyHolderTest.main(DynamicProxyHolder.java:102)
使用向上造型,接口类引用引用真实实现类对象也不行,测试类代码如下,接口与真实实现类使用之前静态代理的代码:
class DynamicProxyHolderTest{
public static void main(String[] args) {
// 在使用时必须将生成的代理对象声明为接口类型对象,而非真正实现类的对象
// 否则报错:Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to proxyStyle.RealSubject
// at proxyStyle.dynamicProxy.DynamicProxyHolderTest.main(DynamicProxyHolder.java:102)
// 使用向上造型 接口类引用引用真正实现类对象也不行
Subject proxyHolder = (Subject) DynamicProxyHolder.
getDynamicProxyHolder(new RealSubject(), new MyIntercepter());
proxyHolder.sayHello();
}
}
对象proxyHolder在执行真实实现类的sayHello()方法基础上,还通过拦截器完成了对该方法的增强,我们在代码中也没有创建真实的代理类,从而使用jdk提供的Proxy类、InvocationHandler接口完成了动态代理。关键:
1、Proxy生成动态代理对象的静态方法Proxy.newProxyInstance()。
2、InvocationHandler接口实现类对invoke()方法的重写中,对原真实实现类方法和拦截器方法的配合逻辑。
3、使用Invocation类保存方法,并调用方法method.invoke()根据真实实现类对象和入参以反射形式完成方法执行。
补充:
也可以仅将DynamicProxyHolder类作为真实实现类和拦截器的持有者,在需要使用动态代理对象时先将DynamicProxyHolder类对象与真实实现类对象和拦截器对象绑定,再传入Proxy类中的工厂方法即可。
本篇博客为《深入浅出Spring Boot 2.x》杨开震 著 4.1 约定编程 笔记。查了许多实现动态代理的例子,都没有该节细致且贴合逻辑,且例子中夹杂了方法织入概念,故作参考。