代理模式的定义:
Provide a surrogate or placeholder for another object to control access to it.(为其他对象提供一种代理以控制对这个对象的访问。)
简单说,就是设置一个中间代理来控制访问原目标对象,达到增强原对象的功能和简化访问方式的目的。
代理模式通用类图
Java实现代理模式分为两类三种,两类是静态代理
和动态代理
,动态代理又可以分为JDK动态代理
和CGLIB动态代理
。
Java实现代理模式
静态代理
静态代理比较简单,代理类需要实现和目标接口类一样的接口。
Solver静态代理类图
接口类:ISolver
publicinterfaceISolver{voidsolve();}
目标类:Solver
publicclassSolverimplementsISolver{@Overridepublicvoidsolve(){System.out.println("疯狂掉头发解决问题……");}}
代理类:SolverProxy,代理类也要实现接口,并且还要维护一个目标对象。
publicclassSolverProxyimplementsISolver{//目标对象privateISolvertarget;publicSolverProxy(ISolvertarget){this.target=target;}@Overridepublicvoidsolve(){System.out.println("请问有什么能帮到您?");target.solve();System.out.println("问题已经解决啦!");}}
客户端;Client
publicclassClient{publicstaticvoidmain(String[]args){//目标对象:程序员ISolverdeveloper=newSolver();//代理:客服小姐姐SolverProxycsProxy=newSolverProxy(developer);//目标方法:解决问题csProxy.solve();}}
运行结果
请问有什么能帮到您?疯狂掉头发解决问题……问题已经解决啦!
我们看到,通过静态代理,可以在不修改目标对象的前提下扩展目标对象的功能。
但是,它也有一些问题:
冗余:由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
维护性不佳:一旦接口增加方法,目标对象与代理对象都要进行修改。
JDK动态代理
JDK动态代理利用了JDK反射机制,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。
它主要用到了两个反射类的API:
java.lang.reflect Proxy| static Object newProxyInstance(ClassLoader loader,Class
JDK动态代理类图
接口类:ISolver
publicinterfaceISolver{voidsolve();}
目标类:Solver,目标类需要实现接口类。
publicclassSolverimplementsISolver{@Overridepublicvoidsolve(){System.out.println("疯狂掉头发解决问题……");}}
动态代理工厂:ProxyFactory,这里的动态代理工厂,不需要实现接口,直接采用反射的方式生成一个目标对象的代理对象实例。
ps:这里用了一个匿名内部类的方法,还有一种方法,动态代理类实现InvocationHandler接口,大体上类似,就不再给出例子了。
publicclassProxyFactory{//维护一个目标对象privateObjecttarget;publicProxyFactory(Objecttarget){this.target=target;}//为目标对象生成代理对象publicObjectgetProxyInstance(){returnProxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),newInvocationHandler(){@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{System.out.println("请问有什么可以帮到您?");//调用目标对象方法ObjectreturnValue=method.invoke(target,args);System.out.println("问题已经解决啦!");returnnull;}});}}
客户端:Client
客户端生成一个代理对象实例,通过代理对象调用目标对象方法的时候,就会进入invoke()方法,最后是通过反射的方式调用目标对象的方法。
publicclassClient{publicstaticvoidmain(String[]args){//目标对象:程序员ISolverdeveloper=newSolver();//代理:客服小姐姐ISolvercsProxy=(ISolver)newProxyFactory(developer).getProxyInstance();//目标方法:解决问题csProxy.solve();}}
运行结果:
请问有什么可以帮到您?疯狂掉头发解决问题……问题已经解决啦!
我们简单总结一下静态代理和动态代理的主要区别:
静态代理动态代理最主要区别
静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件
动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中
我们也观察到,JDK动态代理,目标对象必须得实现接口,也就是说它是面向接口的,假如我们不想要接口怎么办呢?
Cglib动态代理
CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成,它是通过继承来实现的。
我们来看看使用Cglib之后,我们的客服代理是什么样的:
Cglib动态代理类图
引入依赖:Cglib是第三方类库,需要引入依赖
cglibcglib3.2.5
目标类:Solver,这里目标类不用再实现接口。
publicclassSolver{publicvoidsolve(){System.out.println("疯狂掉头发解决问题……");}}
动态代理工厂:
publicclassProxyFactoryimplementsMethodInterceptor{//维护一个目标对象privateObjecttarget;publicProxyFactory(Objecttarget){this.target=target;}//为目标对象生成代理对象publicObjectgetProxyInstance(){//工具类Enhanceren=newEnhancer();//设置父类en.setSuperclass(target.getClass());//设置回调函数en.setCallback(this);//创建子类对象代理returnen.create();}@OverridepublicObjectintercept(Objectobj,Methodmethod,Object[]args,MethodProxyproxy)throwsThrowable{System.out.println("请问有什么可以帮到您?");//执行目标对象的方法ObjectreturnValue=method.invoke(target,args);System.out.println("问题已经解决啦!");returnnull;}}
客户端:Client
publicclassClient{publicstaticvoidmain(String[]args){//目标对象:程序员Solverdeveloper=newSolver();//代理:客服小姐姐SolvercsProxy=(Solver)newProxyFactory(developer).getProxyInstance();//目标方法:解决问题csProxy.solve();}}
运行结果
请问有什么可以帮到您?疯狂掉头发解决问题……问题已经解决啦!
我们可以看到Cglib动态代理和JDK动态代理最大的区别就是:
使用JDK动态代理的对象必须实现一个或多个接口
使用Cglib动态代理的对象则无需实现接口,达到代理类无侵入。
我们还需要注意:
CGLib不能对声明为final的方法进行代理,因为是通过继承父类的方式实现,如果父类是final的,那么就无法继承父类。