代理模式:JAVA静态代理和动态代理的实例和实现详解
目录
- 前言
- 静态代理
- 实现简述
- 创建human接口
- 创建接口实现类
- 创建针对接口实现增强操作的代理
- 代理实现效果
- 动态代理
- 实现简述
- 要点:向上转型
- 创建YoungMan接口
- 创建两个接口实现类
- 创建动态代理实例对象
- 代理实现效果
- 要点:InvocationHandler补充
- 代理模式和修饰模式的区别
- 总结
前言 代理模式,我们这里结合JAVA的静态代理和动态代理来说明,类比Spring AOP面向切面编程:增强消息,也是代理模式。
而我们的静态代理和动态代理,与(service)接口和(serviceImpl)接口实现类有什么区别呢?静态代理的概念与其在理解上类似,可以说静态代理是实现类的增强消息。而且,静态代理针对的是所以实现接口的实现类(通过向上转型实现)。
静态代理
实现简述
本质上是一接口一代理,对该接口的所有实现类进行"增强"(额外)操作:
如下例:human接口有两个实现类:man和woman,我们需要在每个human接口被实现的时候说他是god创造的(增强操作)。那么我们针对human接口手动实现一个GodProxy静态代理类。那么我们通过这个代理执行human接口的实现对象的方法时就可以引入增强操作。
创建human接口
public interface Human {public void sex(); }
创建接口实现类
public class Man implements Human{@Overridepublic void sex() {System.out.println( "this is Man" ); }}public class Women implements Human{@Overridepublic void sex() {System.out.println( "this is Women" ); }}
创建针对接口实现增强操作的代理
public class GodProxy implements Human{Human huamnGenarator; public GodProxy(Human huamnGenarator){this.huamnGenarator = huamnGenarator; }@Overridepublic void sex() {System.out.println( "God begin to make human" ); huamnGenarator.sex(); System.out.println(" End of work "); }}
代理实现效果
public class 静态代理 {public static void main(String[] args) {GodProxy proxy_1 = new GodProxy(new Man()); GodProxy proxy_2 = new GodProxy(new Women()); proxy_1.sex(); System.out.println("\n\n"); proxy_2.sex(); }}
文章图片
动态代理 实现简述
相对与静态代理,一个代理类proxy只能针对一个接口实现操作,众所周知类可以实现多个接口,那能不能对所有的实现类的接口实现增强操作呢?可以的,我们需要通过反射的方式,通过向上转型使得传入参数为Object对象,但是Object里面的getClass方法获取Class对象,实际上是实现类的Class(由Java的向上转型特性可知)。 获取实现类对象的class对象且获得其实现的接口数组。我们可以达到这个目的:这就是动态代理:对多个接口实现静态代理。
动态代理类主要实现InvocationHandler这个接口,接口实现invoke方法来实现增强操作。并通过一个自定义的方法来创建和绑定动态代理类和入参(向上转型为obj的实现类对象),从而实现实现类对象方法的增强操作。
即我们通过动态代理传入实例类对象,在自己写的newProxyInstance方法中通过Proxy类的newProxyInstance方法代理生成一个新的实例类对象,这个新的对象不仅包含所有的入参实力类对象信息,且在通过代理类生成新的实例类对象过程中注入了invoke方法(我们实现InvocationHandler接口的核心方法)的逻辑。
这里我们有两个疑问:
1、如何实现增强的invoke方法;——通过java.lang.reflect.Proxy.newProxyInstance方法中复原向上转型的obj对象为原对象(具体实现类对象),并绑定增强的invoke方法。
2、代理对象如何复现对象方法; ——利用向上转型的复原不变性。
要点:向上转型
向上转型:子类实例赋值给父类引用。 无法调用子类拓展方法,但是她的实现确确实实存在:
Object obj = new Girl(); Girl girl = (Girl) obj; System.err.println(obj.getClass().getName()); System.err.println(obj.getClass().getInterfaces().length); girl.nickName();
输出:
src.代理模式.Girl
2
少女
创建YoungMan接口
这里还将用到Human接口,两个接口。
public interface YoungMan {public void nickName(); }
创建两个接口实现类
public class Boy implements Human,YoungMan{@Overridepublic void sex() {System.out.println( "this is Man" ); }@Overridepublic void nickName() {System.out.println( "少年" ); }}public class Girl implements Human,YoungMan{@Overridepublic void sex() {System.out.println( "this is Women" ); }@Overridepublic void nickName() {System.out.println( "少女" ); }}
创建动态代理实例对象
这里我们主要通过Proxy.newProxyInstance方法创建一个代理类,传参:
public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h)
类加载器:指定代理类由哪个classloader加载;代理类需要代理实现的接口方法;InvocationHandler对象:表示的是当动态代理对象调用方法的时候实际执行的会是该InvocationHandler对象上的invoke方法(即增强方法)。在invoke方法中通过反射方法名称去执行实际要执行的方法和增强操作。(实例说明可以看文末补充)
注意返回的这里自定义的newProxyInstance是Object。
invoke方法中的args为方法传入的参数们;
public class GodForYoungProxy implements InvocationHandler {private Object godForYoungProxy; //参数为Object设计一个向上转型public Object newProxyInstance(Object godForYoungProxy) {this.godForYoungProxy = godForYoungProxy; //this指的是GodForYoungProxy这个InvocationHandler实现类System.err.println( godForYoungProxy.getClass().getName() ); return Proxy.newProxyInstance(godForYoungProxy.getClass().getClassLoader(), godForYoungProxy.getClass().getInterfaces(), this); }@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(" God for Young begin to work ... "); proxy = method.invoke(godForYoungProxy, args); System.out.println(" End of work "); return proxy; }}
代理实现效果
public class 动态代理 {public static void main(String[] args) {GodForYoungProxy godForYoungProxy = new GodForYoungProxy(); Human human = (Human) godForYoungProxy.newProxyInstance(new Boy()); YoungMan youngMan = (YoungMan) godForYoungProxy.newProxyInstance(new Boy()); human.sex(); youngMan.nickName(); //向上转型测试//Object obj = new Girl(); //Girl girl = (Girl) obj; //System.err.println(obj.getClass().getName()); //System.err.println(obj.getClass().getInterfaces().length); //girl.nickName(); }}
文章图片
代理生成对象中的obj向上转型对象的.getClass().getName()打印:
文章图片
要点:InvocationHandler补充 结合创建动态代理实例对象目录内容补充说明如下:官网的Proxy.newProxyInstance中的入参说明
loader – the class loader to define the proxy class同样的上述代码实现效果,我们将实现的GodForYoungProxy implements InvocationHandler中的invoke方法注释一行:
interfaces – the list of interfaces for the proxy class to implement
h – the invocation handler to dispatch method invocations to =>将方法调用分派到的调用处理程序
@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(" God for Young begin to work ... "); // proxy = method.invoke(godForYoungProxy, args); System.out.println(" End of work "); return proxy; }
明显的,我们发现少了实际的sex和nickName方法的输出内容,因为没有实现对应调用方法:
文章图片
通过这样的特性,其实我们在invoke方法里面拥有很强的操作性,比如说让指定方法执行,对不同方法执行不同策略等。
代理模式和修饰模式的区别 代理模式和装饰者模式很相似,但是他们的区别在于:
1、代理模式是在类编译的时候,增强方法就已经确定的,有些动态代理不支持多层嵌套;装饰者则可以不断递归被构造装饰;
2、代理模式:强调对对象的访问控制,方法的使用都是通过反射实现;代理模式则是强调功能的增加,相当于在原基础的被修饰者上不断套娃。
总结 【代理模式:JAVA静态代理和动态代理的实例和实现详解】本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注脚本之家的更多内容!
推荐阅读
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 事件代理
- Java|Java OpenCV图像处理之SIFT角点检测详解
- java中如何实现重建二叉树
- 数组常用方法一
- 【Hadoop踩雷】Mac下安装Hadoop3以及Java版本问题
- Java|Java基础——数组
- RxJava|RxJava 在Android项目中的使用(一)
- java之static、static|java之static、static final、final的区别与应用
- Java基础-高级特性-枚举实现状态机