代理就是中介,中间人。法律上也有代理,比如代理律师之类,委托人将自己的一部分权限委托给代理者,代理者就拥有被代理者(委托人)的部分权限,并且可以以被代理人的名义来实行这些权限,此时代理者与委托人等同,当然代理人也可以在实行权限时配合自己的能力来进行,当然不能超出这个权限。Java中的代理模式类似于上面的代理,也是为一个类(委托类)创建一个代理类,来代表它来对外提供功能。如何在Java中创建一个类的代理类呢?很简单,我们需要创建一个公共接口,委托类要实现这个接口,再创建一个接口的实现类作为代理类,在这个类中的方法中可以直接调用委托类中的同名方法,外部类要进行访问时,可以使用接口指向代理类实例,调用代理类中的方法,从而间接调用委托类中的具体方法实现。
静态代理
以法律上的委托代理为例
总接口:ZiRanRen
public interface ZiRanRen {void Quanli();}
委托人:MaYun
public class MaYun implements ZiRanRen {public void eat() {System.out.println("今天吃满汉全席");}public void drink() {System.out.println("今天喝大西洋");}@Overridepublic void Quanli() {System.out.println("我赋予我的代理律师来行使这些权利,此时代理律师全权代理我处理某些事务");}}
代理律师:LvShi
public class LvShi implements ZiRanRen {@Overridepublic void Quanli() {new MaYun().Quanli();}}
测试类:Clienter
public class Clienter {public static void main(String[] args) {ZiRanRen ls = new LvShi();ls.Quanli();}}执行结果我赋予我的代理律师来行使这些权利,此时代理律师全权代理我处理某些事务可以看出,我们想对外开放某些功能,就可以将这些功能在代理类中被引用,如此一来,屏蔽了我们不想外露的功能,只将我们想开放的功能开放出来。即委托类中其实是可以有很多方法的,很多功能的,我们可以酌情对外开放,代理类犹如一道大门,将委托类与外部调用者隔绝开来,只将部分功能赋予这个大门,来代替委托类行使这个功能,哪怕最终还是要牵扯到自身(因为最终还是要调用委托类的对应方法实现)。
总结
代理模式很简单,只要记住以下关键点,简单易实现:(1)代理类与委托类实现同一接口(2)在委托类中实现功能,在代理类的方法中中引用委托类的同名方法(3)外部类调用委托类某个方法时,直接以接口指向代理类的实例,这正是代理的意义所在:屏蔽。代理模式场景描述:(1)当我们想要隐藏某个类时,可以为其提供代理类(2)当一个类需要对不同的调用者提供不同的调用权限时,可以使用代理类来实现(代理类不一定只有一个,我们可以建立多个代理类来实现,也可以在一个代理类中进行权限判断来进行不同权限的功能调用)(3)当我们要扩展某个类的某个功能时,可以使用代理模式,在代理类中进行简单扩展(只针对简单扩展,可在引用委托类的语句之前与之后进行)代理模式虽然实现了调用者与委托类之间的强耦合,但是却增加了代理类与委托类之间的强耦合(在代理类中显式调用委托类的方法),而且增加代理类之后明显会增加处理时间,拖慢处理时间。
动态代理
为了解决上面静态代理的缺陷,动态代理就产生了。动态代理有两种方式,一种是JDK实现的动态代理,一种是CGLIB实现的动态代理,其中JDK动态代理是基于接口的,CGLIB是基于类的,明显CGLIB要高效一些,因为它免去了接口的消耗与限制。
JDK动态代理
JDK动态代理是基于接口实现的,采用的是反射原理。
要使用JDK动态代理需要实现InvocationHandler接口,InvocationHandler接口是JDK专为动态代理而设计的接口,其有数个实现类,是JDK中使用动态代理来实现一些其他的功能,我们的实现类似于它们,完全可以参考其代码。InvocationHandler接口中只有一个方法invoke,这个方法用于调用具体的实现来完成功能,调用的过程需要借助于Java反射原理。
自然人接口:ZiRanRen
public interface ZiRanRen {void quanli();}
马云:MaYun
public class MaYun implements ZiRanRen {public void eat() {System.out.println("今天吃满汉全席");}public void drink() {System.out.println("今天喝大西洋");}@Overridepublic void quanli() {System.out.println("我赋予我的代理律师来行使这些权利,此时代理律师全权代理我处理某些事务");}}
InvocationHandler实例:MyInvocationHandler
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class MyInvocationHandler implements InvocationHandler{private Object target;MyInvocationHandler(){super();}MyInvocationHandler(Object target){super();this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if("quanli".equals(method.getName())){return method.invoke(target, args);}elsereturn method.invoke(target, args);}}
测试类:Clienter
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;public class Clienter {public static void main(String[] args) {//动态代理实现ZiRanRen ls = new MaYun();InvocationHandler invocationHandler = new MyInvocationHandler(ls);ZiRanRen Proxyer = (ZiRanRen)Proxy.newProxyInstance(ls.getClass().getClassLoader(), ls.getClass().getInterfaces(), invocationHandler);Proxyer.quanli();}}执行结果我赋予我的代理律师来行使这些权利,此时代理律师全权代理我处理某些事务(1)MyInvocationHandler实现类中定义的Object target中的target指的是接口的实现类,即之前所说的委托类,本段代码中指的就是MaYun类,这个从最后测试类中第7、8行可以看出,我们将MyYun类的实例作为参数来生成MyInvocationHandler实例。(2)代码编写的阶段并未显式调用MaYun类的quanli方法,但是最后却是实实在在被调用了,这时怎么回事呢?其实这正是动态代理的魅力所在,动态代理的动态二字的含义是不局限于某一个具体类,当我们实现了一个InvocationHandler接口的实例之后,就可以使用这个实例进行任意类的代理,被代理的类是在运行时才会确定(这也正是反射的能力之所在),编码阶段根本无从知晓,复用性极强。(3)InvocationHandler接口的三个参数问题:第一个参数proxy表示的是被代理的实例(此处指MaYun类的实例ls),第二个参数method代表要执行的被代理类中的方法(此处指MaYun类中的quanli方法),第三个参数表示被代理类中方法的参数列表(此处指MaYun类的quanli方法的参数列表)。如此看来,InvocationHandler的三个参数就是精确指定要执行哪个类的哪个方法,参数是***。当然在代码中还是泛指,这个精确要到程序运行时才能真正体现。(4)测试类中创建代理类实例的模式与静态代理的模式一致,表示与被代理类实现同一接口。使用Proxy代理类的静态方法newProxyInstance方法来完成代理类实例的创建,其有三个参数:第一个是实例类的加载器,第二个是实例类实现的接口,第三个是InvocationHandler接口的实例。
CGLIB动态代理
CGLIB动态代理不同于JDK提供的动态代理,它不再基于接口,而是直接基于被代理类来完成代理模式。我们可以将CGLIB看作是一个代理类的生成工具,通过编程的方式来完成一个类的创建及实例的创建,不同于我们以前直接使用Class来创建类的方式,明显复杂性提升很多,但是CGLIB将这种复杂的类生成功能屏蔽化,我们只需要借助于它所提供的API就可以完成代理类的生成和实例的创建。要使用CGLIB,需要向导入cglib的jar包,此处我们使用:cglib-nodep-2.2.2.jar。
被代理类:MaYun
public class MaYun {public void eat() {System.out.println("今天吃满汉全席");}public void drink() {System.out.println("今天喝大西洋");}public void quanli() {System.out.println("我赋予我的代理律师来行使这些权利,此时代理律师全权代理我处理某些事务");}}
方法回调器:Cgliber
import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;/*** Cglib就犹如一个代码式的类生成器,一般手动创建类的内容涉及到的东西,都需要通过Cglib提供的API来完成。* 如下代码中:设置超类和设置回调,对应于正常类编写时,编写的继承类与方法回调* 这里实现的是MethodInterceptor接口(方法拦截器),这个接口的作用就是实现方法拦截(回调)**/public class Cgliber implements MethodInterceptor{private Object target;//这个target指的就是被代理类的实例/** 生成代理实例(依据被代理类来生成代理类),Enhancer则是代理类生成工具*/public Object getInstance(Object target){this.target = target;Enhancer enhancer = new Enhancer();enhancer.setSuperclass(this.target.getClass());//设置被继承类(超类)enhancer.setCallback(this);//设置回调return enhancer.create();}/** 回调方法* intercept是拦截之意,此处是指使用代理方法proxy来调用原类(obj)中的指定方法(method),带参数(args),*/@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)throws Throwable {proxy.invokeSuper(obj, args);return null;}}
测试类:Clienter
public class Clienter {public static void main(String[] args) {/** Cgliber可以看成是一个带有类生成能力的方法回调器*/Cgliber cgliber = new Cgliber();/** 使用方法回调器的类生成功能生成代理类实例:mayunCgliber* 由于getInstance方法返回值为Object类型,并不是MaYun类的子类型,所以需要将其强转为MaYun类型,实际上mayunCgliber是代理实例*/MaYun mayunCgliber = (MaYun) cgliber.getInstance(new MaYun());mayunCgliber.quanli();}}执行结果我赋予我的代理律师来行使这些权利,此时代理律师全权代理我处理某些事务使用CGLIB的关键就在方法回调器类Cgliber中:(1)实现MethodInterceptor接口,该接口中只有一个intercept方法,这个方法的作用就是拦截和回调,拦截的意思是我们可以在具体执行方法的前、后加上其他的代码来扩展功能,回调之意为在这个方法中调用父类(被代理类)中的指定方法。(2)为了生存代理类及其实例,需要在Cgiber中增加创建代理类的代码,使用CGLIB提供的API来进行类生成,CGLIB使用Enhancer作为类生成器,使用其下的各个接口就能完成类和实例的创建。其实在测试类中我们可以发现一点,无论是JDK的动态代理还是CGLIB的动态代理,代理类与被代理类之间是存在耦合的,这与静态代理一致,这个耦合体现在方法的调用之上。
