图片 1

设计模式-动态代理模式

之前介绍了代理模式,大家也都了解了代理模式,不过之前介绍的代理模式是静态代理,静态代理什么意思?静态代理指的是代理类是事先定义好的,在使用代理类的时候也是明确初始化定义的代理类。但是如果有非常多的类需要代理,那么一个个那么定义静态代理类的工作量将是非常大的,其实JDK已经给我们提供了现成的动态代理类,可以不用事先定义好所有的代理类,下面我们就介绍下JDK动态代理类。

本篇文章继续介绍Java反射机制,不同的是侧重于介绍动态代理。动态代理是代理模式中的一种,是通过Java反射机制来实现的。因此本篇文章先介绍代理模式,然后介绍Java反射机制与动态代理。

JDK中动态代理主要涉及到的是两个类:java.lang.reflect.InvocationHandler和java.lang.reflect.Proxy。下面我们先定义一下Subject接口和两个功能一样的实现类用于测试。

一、代理模式
定义:给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象。
1、代理模式的理解
代理模式使用代理对象完成用户请求,屏蔽用户对真实对象的访问。现实世界的代理人被授权执行当事人的一些事宜,无需当事人出面,从第三方的角度看,似乎当事人并不存在,因为他只和代理人通信。而事实上代理人是要有当事人的授权,并且在核心问题上还需要请示当事人。在软件设计中,使用代理模式的意图也很多,比如因为安全原因需要屏蔽客户端直接访问真实对象,或者在远程调用中需要使用代理类处理远程方法调用的技术细节,也可能为了提升系统性能,对真实对象进行封装,从而达到延迟加载的目的。
2、代理模式的参与者
代理模式的角色分四种:

Subject.java 接口类

图片 1

public interface Subject { public String doSomething(String name);}

图片.png

RealSubject.java 实际提供功能的类

主题接口: Subject
是委托对象和代理对象都共同实现的接口,即代理类的所实现的行为接口。Request()
是委托对象和代理对象共同拥有的方法。
目标对象:ReaSubject 是原对象,也就是被代理的对象。
代理对象: Proxy 是代理对象,用来封装真是主题类的代理类。
客户端 :使用代理类和主题接口完成一些工作。
3、代理模式的分类

public class RealSubject implements Subject { @Override public String doSomething(String name) { System.out.println(name + " do something!"); return name + " do something!"; }}

代理的实现分为:

需要实现动态代理功能的话实现一个代理类实现InvocationHandler的接口,并实现invoke方法。

静态代理:代理类是在编译时就实现好的。也就是说 Java
编译完成后代理类是一个实际的 class 文件。
动态代理:代理类是在运行时生成的。也就是说 Java 编译完之后并没有实际的
class 文件,而是在运行时动态生成的类字节码,并加载到JVM中。
4、代理模式的实现思路

例子中实现DynamicProxyAOP类用于在Subject类前后分别实现其他功能,类似AOP切面功能,其实Spring
AOP的内部核心实现就是通过动态代理实现的。可以看到public Object
invoke(Object proxy, Method method, Object[]
args)方法有3个入参,proxy表示代理类,method表示调用的Subject主类的方法名,args表示调用代理方法的参数数组,在内部的具体实现我们通过反射方法通过method.invode来调用实际Subject方法。

1.代理对象和目标对象均实现同一个行为接口。

public class DynamicProxyAOP implements InvocationHandler { private Object subject; public DynamicProxyAOP(Object subject) { this.subject = subject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //在执行真实subject执行的方法 System.out.println("before do something"); //执行真实subject方法 Object rtn = method.invoke(subject, args); //在执行结束后再执行的方法 System.out.println("after do something"); return rtn; }}

2.代理类和目标类分别具体实现接口逻辑。

这样就定义好了动态代理的核心类,最后我们看下如何调用动态代理类。先分别定义RealSubject和RealSubject2两个类的对象,再分别实现两个DynamicProxyAOP的代理对象,DynamicProxyAOP的入参分别为这两个Subject的对象。接下来我们创建动态代理对象,通过调用Proxy的Object
newProxyInstance(ClassLoader loader,Class<?>[]
interfaces,InvocationHandler
h)来创建动态代理对象,第一个参数为实现代理类的ClassLoader,第二个参数为被代理类的接口数组,最后一个参数为继承自InvocationHandler的handler实现具体的代理功能。

3.在代理类的构造函数中实例化一个目标对象。

public class DynamicProxyMain { public static void main(String[] args){ Subject realSubject = new RealSubject(); Subject realSubject2 = new RealSubject2(); InvocationHandler handler = new DynamicProxyAOP(realSubject); InvocationHandler handler2 = new DynamicProxyAOP(realSubject2); Subject subject =  Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler); Subject subject2 =  Proxy.newProxyInstance(realSubject2.getClass().getClassLoader(), realSubject2.getClass().getInterfaces(), handler2); String rtn = subject.doSomething("tester "); String rtn2 = subject2.doSomething("tester "); }}

4.在代理类中调用目标对象的行为接口。

从打印出的日志可以发现,定义了两个不同的继承自Subject的类,但是动态代理对象是动态创建的,不需要像静态代理那样,再为每一个Subject实现类创建代理类,是不是很方便,后续大家如果要自己实现一些切面功能,就可以通过动态代理模式来实现,例如纪录日至之类的。

5.客户端想要调用目标对象的行为接口,只能通过代理类来操作。
5、静态代理模式的简单实现

before do somethingtester do something!after do somethingbefore do somethingtester do something2!after do something

public class ProxyDemo {
public static void main(String args[]){
RealSubject subject = new RealSubject();
Proxy p = new Proxy(subject);
p.request();
}
}

本文实例中的源码:

interface Subject{
void request();
}

class RealSubject implements Subject{
public void request(){
System.out.println(“request”);
}
}

class Proxy implements Subject{
private Subject subject;
public Proxy(Subject subject){
this.subject = subject;
}
public void request(){
System.out.println(“PreProcess”);
subject.request();
System.out.println(“PostProcess”);
}
}

目标对象(RealSubject
)以及代理对象(Proxy)都实现了主题接口(Subject)。在代理对象(Proxy)中,通过构造函数传入目标对象(RealSubject
),然后重写主题接口(Subject)的request()方法,在该方法中调用目标对象(RealSubject
)的request()方法,并可以添加一些额外的处理工作在目标对象(RealSubject
)的request()方法的前后。

代理模式的好处:

假如有这样的需求,要在某些模块方法调用前后加上一些统一的前后处理操作,比如在添加购物车、修改订单等操作前后统一加上登陆验证与日志记录处理,该怎样实现?首先想到最简单的就是直接修改源码,在对应模块的对应方法前后添加操作。如果模块很多,你会发现,修改源码不仅非常麻烦、难以维护,而且会使代码显得十分臃肿。

这时候就轮到代理模式上场了,它可以在被调用方法前后加上自己的操作,而不需要更改被调用类的源码,大大地降低了模块之间的耦合性,体现了极大的优势。

静态代理比较简单,上面的简单实例就是静态代理的应用方式,下面介绍本篇文章的主题:动态代理。
二、Java反射机制与动态代理

动态代理的思路和上述思路一致,下面主要讲解如何实现。
1、动态代理介绍

动态代理是指在运行时动态生成代理类。即,代理类的字节码将在运行时生成并载入当前代理的
ClassLoader。与静态处理类相比,动态类有诸多好处。

发表评论

电子邮件地址不会被公开。 必填项已用*标注