Proxy 代理模式 动态代理 CGLIB
代理的基本概念
proxy [ˈprɒksi] n. 代理服务器;代表权;代理人,代替物;委托书;
invoke [ɪnˈvəʊk] vt. 乞灵,祈求;提出或授引…以支持或证明;召鬼;借助;
invocation [ˌɪnvəˈkeɪʃn] n. 祈祷;乞求;乞灵;乞求神助;
subject [ˈsʌbdʒɪkt] n. 主题,话题;学科,科目;[哲] 主观; adj. 须服从…的;(在君主等)统治下的; v. 提供,提出;使…隶属;
- 可以隐藏委托类的实现
- 可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。
按照代理的创建时期,可以分为两种:
- 静态代理:若代理类在程序运行前就已经存在,那么这种代理方式被成为静态代理 。静态代理通常是由程序员在Java代码中定义的, 且代理类和委托类会实现同一接口或是派生自相同的父类。
- 动态代理:在程序运行时运用反射机制动态创建而成。
静态代理
- 首先创建一个接口
- 然后创建具体实现类来实现这个接口,具体实现类中需要将接口中定义的方法的业务逻辑功能实现
- 再创建一个代理类同样实现这个接口,代理类中接口的方法只要调用具体类中的对应方法即可
这样,我们在需要使用接口中的某个方法时,直接调用代理类的方法即可,而具体的实现隐藏在了底层。
/**第一步:定义一个接口*/
interface Iuser {void eat(String s);
}/**第二步:创建具体实现类,即被代理类或委托类*/
class UserImpl implements Iuser {@Overridepublic void eat(String s) {System.out.println("我要吃" + s);}
}/**第三步:创建代理类*/
class UserProxy implements Iuser {private Iuser user;//代理类通常只实现一个接口,因为代理类中持有的通常只是接口的引用,而不是某个委托类的直接引用public UserProxy() {this.user = new UserImpl();}public UserProxy(Iuser user) {this.user = user;}@Overridepublic void eat(String s) {System.out.println("静态代理前置内容");user.eat(s);//利用的就是多态的特性,也是面向接口编程的一种典型的体现System.out.println("静态代理后置内容");}
}/**静态代理访问演示*/
public class Test {public static void main(String[] args) {UserProxy proxy = new UserProxy();//或 UserProxy proxy = new UserProxy(new UserImpl())proxy.eat("苹果");}
}
- 代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法,但是因为代理类中接口的方法往往是没什么逻辑的,它通常只是调用了委托类的同名方法而已,所以这就出现了大量重复、冗余的代码。
- 如果接口中增加或修改了某个方法,除了所有委托类需要修改代码外,所有代理类也需要修改代码,增加了代码维护的复杂度。
- 代理对象只服务于一种类型的对象,即静态代理类只能为特定的接口服务,如想要为多个接口服务则需要建立多个代理类,这在大型系统中大大增加了复杂度。
动态代理
/**第一步:定义一个接口*/
interface Iuser {void eat(String s);
}/**再定义一个接口*/
interface Irun {String run(int length);
}/**第二步:创建具体实现类,即被代理类或委托类*/
class UserImpl implements Iuser, Irun {@Overridepublic void eat(String s) {System.out.println("我要吃" + s);}@Overridepublic String run(int length) {System.out.println("我跑了 " + length + " 米");return "跑步很欢乐";}
}/**第三步:定义代理实例的【调用处理器】,这是一个位于代理类与委托类之间的【中介类】,它需要实现【InvocationHandler】接口*/
class UserHandler implements InvocationHandler {private Object object;//调用处理器持有委托类的引用,但并不限定委托类必须是某一接口或某一类public UserHandler(Object object) {//这里是用Object接收的,所以可以传递任何类型的对象this.object = object;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object returnObj = method.invoke(object, args); //【核心点】通过反射执行某个类的某方法System.out.println("【方法】" + method.getName() + "【参数】" + Arrays.toString(args) + "【返回值】" + returnObj);return returnObj;}
}/**第四步:在使用时动态创建动态代理类*/
public class Test {public static void main(String[] args) {test1();System.out.println("----------------------2---------------------");test2();System.out.println("----------------------3---------------------");test3();}private static void test1() {//完全基于接口Iuser的用法Iuser user = new UserImpl();System.out.println("【委托类】" + user.getClass().getName());//UserImplSystem.out.println("【委托类实现的接口】" + Arrays.toString(user.getClass().getInterfaces()));//[interface Iuser, interface Irun]InvocationHandler handler = new UserHandler(user);Iuser proxy = (Iuser) Proxy.newProxyInstance(Iuser.class.getClassLoader(), new Class[] { Iuser.class }, handler);//可以直接强转为IuserSystem.out.println("【代理类实现的接口】" + Arrays.toString(proxy.getClass().getInterfaces()));//[interface Iuser]。因为你只指定了Iuser接口proxy.eat("苹果");}private static void test2() {//精简形式Iuser proxy = (Iuser) Proxy.newProxyInstance(Iuser.class.getClassLoader(), new Class[] { Iuser.class }, new UserHandler(new UserImpl()));System.out.println("【代理类实现的接口】" + Arrays.toString(proxy.getClass().getInterfaces()));//[interface Iuser, interface Irun]proxy.eat("香蕉你个巴拉");}private static void test3() {//完全基于委托类UserImpl,之所以采用这种方式,是因为委托类实现了多个接口,且我们需要调用不同接口中的方法Object proxy = Proxy.newProxyInstance(UserImpl.class.getClassLoader(), UserImpl.class.getInterfaces(), new UserHandler(new UserImpl()));((Iuser) proxy).eat("你妹");//可以强转为IuserString returnObj = ((Irun) proxy).run(99);//也可以强转为IrunSystem.out.println(returnObj);}
}
接口 InvocationHandler 调用处理器
java.lang.reflect.InvocationHandler
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; //在代理实例上处理方法调用并返回结果。
- proxy - 在其上调用方法的代理实例
- method - 对应于在代理实例上调用的接口方法的 Method 实例。 Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
- args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类的实例中。
- 从代理实例的方法调用返回的值。
- 如果接口方法的声明返回类型是基本类型,则此方法返回的值一定是相应基本包装对象类的实例;否则,它一定是可分配到声明返回类型的类型。
- 如果此方法返回的值为 null 并且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出 NullPointerException。否则,如果此方法返回的值与上述接口方法的声明返回类型不兼容,则代理实例上的方法调用将抛出 ClassCastException。
Proxy 动态代理类
public class java.lang.reflect.Proxy extends Object implements java.io.Serializable
基本简介
InvocationHandler handler = new MyInvocationHandler(new FooImpl());//FooImpl是实现 Foo 接口的某一委托类
Foo f = (Foo) Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class }).getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { handler });
Foo proxy = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),//定义了由哪个ClassLoader对象来对生成的代理对象进行加载new Class[] { Foo.class },//表示的是我将要给我需要代理的对象提供一组什么接口,之后我这个代理对象就会实现了这组接口handler);//表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
代理类具有的属性(properties)
- 代理类是公共的、最终的,而不是抽象的。Proxy classes are public, final, and not abstract.
//这句话我觉得是有问题的,如下:
System.out.println("【代理类的修饰符】" + Modifier.toString(proxy.getClass().getModifiers()));//final,并不是public的
- 未指定代理类的非限定名称 The unqualified name of a proxy class is unspecified。但是,以字符串 "$Proxy" 开头的类名空间应该为代理类保留。
System.out.println("【代理类】" + proxy.getClass().getName());//【$Proxy0】
- 代理类的超类为(A proxy class extends) java.lang.reflect.Proxy。
System.out.println("【代理类的超类】" + proxy.getClass().getSuperclass());//【class java.lang.reflect.Proxy】
- 代理类会按同一顺序准确地实现其创建时指定的接口。
- 如果代理类实现了非公共接口,那么它将在与该接口相同的包中定义。If a proxy class implements a non-public interface, then it will be defined in the same package as that interface. 否则,代理类的包也是未指定的 unspecified。注意,包密封 package sealing 将不阻止代理类在运行时在特定包中的成功定义,也不会阻止相同类加载器和带有特定签名的包所定义的类。
- 由于代理类将实现所有在其创建时指定的接口,所以对其 Class 对象调用 getInterfaces 将返回一个包含相同接口列表的数组(按其创建时指定的顺序),对其 Class 对象调用 getMethods 将返回一个包括这些接口中所有方法的 Method 对象的数组,并且调用 getMethod 将会在代理接口中找到期望的 as would be expected 一些方法。
System.out.println("【代理类实现的接口】" + Arrays.toString(proxy.getClass().getInterfaces()));//[interface Iuser, interface Irun]
- 如果 Proxy.isProxyClass 方法传递代理类(由 Proxy.getProxyClass 返回的类,或由 Proxy.newProxyInstance 返回的对象的类),则该方法返回 true,否则返回 false。
System.out.println("【是否是代理类】" +Proxy.isProxyClass(proxy.getClass()));//true
- 代理类的 java.security.ProtectionDomain 与由引导类加载器(如 java.lang.Object)加载的系统类相同,原因是代理类的代码由受信任的系统代码生成。此保护域通常被授予 java.security.AllPermission。
- 每个代理类都有一个可以带一个参数(接口 InvocationHandler 的实现)的公共构造方法,用于设置代理实例的调用处理程序。并非必须使用反射 API 才能访问公共构造方法,通过调用 Proxy.newInstance 方法(将调用 Proxy.getProxyClass 的操作和调用带有调用处理程序的构造方法结合在一起)也可以创建代理实例。
代理实例具有的属性
- 提供代理实例 proxy 和一个由其代理类 Foo 实现的接口,表达式 proxy instanceof Foo 将返回 true,并且 (Foo) proxy 的强制转换操作将会成功(而不抛出 ClassCastException):
//指定代理类实现的接口为:new Class[] { Iuser.class, Irun.class }
((Iuser) proxy).eat("苹果");//可以强转为Iuser
((Irun) proxy).run(99);//也可以强转为Irun
- 每个代理实例都有一个关联的调用处理程序,它会被传递到其构造方法中。静态 Proxy.getInvocationHandler 方法将返回与作为其参数传递的代理实例相关的调用处理程序。
Iuser proxy = (Iuser) Proxy.newProxyInstance(Iuser.class.getClassLoader(), new Class[] { Iuser.class }, handler);
System.out.println(Proxy.getInvocationHandler(proxy) == handler);//true
- 代理实例上的接口方法调用将按照该方法的文档描述进行编码,并被指派到调用处理程序的 Invoke 方法。
- 在代理实例上的 java.lang.Object 中声明的 hashCode、equals 或 toString 方法的调用将按照与编码和指派接口方法调用相同的方式进行编码,并被指派到调用处理程序的 invoke 方法,如上所述。传递到 invoke 的 Method 对象的声明类是 java.lang.Object。代理类不重写从 java.lang.Object 继承的代理实例的其他公共方法,所以这些方法的调用行为与其对 java.lang.Object 实例的操作一样。
在多代理接口中重复的方法
API
- protected InvocationHandler h 此代理实例的调用处理程序。
- protected Proxy(InvocationHandler h) 使用其调用处理程序的指定值从子类(通常为动态代理类)构建新的 Proxy 实例。
- static InvocationHandler getInvocationHandler(Object proxy) 返回指定代理实例的调用处理程序。
- static boolean isProxyClass(Class<?> cl) 当且仅当指定的类通过 getProxyClass 方法或 newProxyInstance 方法动态生成为代理类时,返回 true。
- static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。
- static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
- 返回值:一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口
- 参数:
- loader - 定义代理类的类加载器。定义了由哪个ClassLoader对象来对生成的代理对象进行加载
- interfaces - 代理类要实现的接口列表。表示的是我将要给我需要代理的对象提供一组什么接口,之后我这个代理对象就会实现了这组接口
- h - 指派方法调用的调用处理程序。表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
关于 newProxyInstance 方法
((Iuser) proxy).eat("苹果");
((Irun) proxy).run(99);
CGLIB代码生成库简介
Byte Code Generation Library is high level API to generate and transform JAVA byte code. It is used by AOP, testing, data access frameworks to generate dynamic proxy objects and intercept field access.
字节码生成库是用于生成和转换JAVA字节码的高级API。它被AOP,测试,数据访问框架用于生成动态代理对象并拦截字段访问。
- net.sf.cglib.core:底层字节码操作类;大部分与ASP相关。
- net.sf.cglib.transform:编译期、运行期的class文件转换类。
- net.sf.cglib.proxy:代理创建类、方法拦截类。
- net.sf.cglib.reflect:更快的反射类、C#风格的代理类。
- net.sf.cglib.util:集合排序工具类
- net.sf.cglib.beans:JavaBean相关的工具类
import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;interface LoginService {public boolean checkUser();
}class LoginServiceImpl implements LoginService {@Overridepublic boolean checkUser() {System.out.println("LoginServiceImpl checkUser");return false;}
}interface UserService {public String getUserName();
}class UserServiceImpl implements UserService {@Overridepublic String getUserName() {System.out.println("UserServiceImpl getUserName");return null;}}class CglibProxy implements MethodInterceptor {@Overridepublic Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {System.out.println("*********代理方法执行前************");Object retObj = methodProxy.invokeSuper(proxy, params);System.out.println("*********代理方法执行后************");return retObj;}//返回目标对象的代理对象 public Object newProxy(Object target) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(target.getClass());enhancer.setCallback(this);enhancer.setClassLoader(target.getClass().getClassLoader());return enhancer.create();}
}public class Test {public static void main(String[] args) {//创建目标对象 LoginService loninService = new LoginServiceImpl();UserService userService = new UserServiceImpl();CglibProxy proxy = new CglibProxy();//创建代理对象 LoginService loninService$Proxy = (LoginService) proxy.newProxy(loninService);UserService userService$Proxy = (UserService) proxy.newProxy(userService);loninService$Proxy.checkUser();userService$Proxy.getUserName();}
}
转载于:https://www.cnblogs.com/baiqiantao/p/7485704.html
Proxy 代理模式 动态代理 CGLIB相关推荐
- 【设计模式】代理模式 ( 动态代理 | 模拟 Java 虚拟机生成对应的 代理对象 类 )
文章目录 前言 一.模拟 JVM 生成对应的 代理对象 二.模拟 JVM 生成对应的 代理对象 完整流程展示 1.目标对象接口 2.被代理对象 3.调用处理程序 4.模拟 JVM 生成的代理对象类 5 ...
- 红橙Darren视频笔记 代理模式 动态代理和静态代理
红橙Darren视频笔记 代理模式 动态代理和静态代理(Android API 25) 关于代理模式我之前有过相关的介绍: https://blog.csdn.net/u011109881/artic ...
- 【设计模式】代理模式 ( 动态代理使用流程 | 创建目标对象 | 创建被代理对象 | 创建调用处理程序 | 动态创建代理对象 | 动态代理调用 )
文章目录 前言 一.静态代理的弊端 二.动态代理的优势 三.动态代理使用流程 1.目标对象接口 2.被代理对象 3.调用处理程序 4.客户端 四.动态生成 代理对象 类 的 字节码 文件数据 前言 代 ...
- 【设计模式】代理模式 ( 动态代理 )
文章目录 一.动态代理使用流程 二.动态代理代码示例 1.订单类 2.Service 接口 3.Service 实现类 4.Service 静态代理类 5.Dao 接口 6.Dao 实现类 7.Ser ...
- Java设计模式-之代理模式(动态代理)
一.简介 1.什么叫代理模式: 简而言之就是:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起 ...
- 代理模式(动态代理)
动态代理.就是不用自己写代理类,只要实现接口就行了. 动态代理,这里介绍两种:jdk and cglib 第一个jdk //一个接口类 public interface Book{void read( ...
- Spring AOP中的静态代理和动态代理的原理和实践
对于最近博主最近写博客的兴致大发,我也在思考:为什么而写博客?在互联网时代,无论你是牛人大咖,还是小白菜鸟,都有发表自己看法的权利.无论你是对的还是错的,都会在这个平台上找到答案.所以,我会尽可能去写 ...
- 代理模式——静态代理,动态代理(JDK代理和CGLib代理)
概述 由于某些原因需要给某对象提供一个代理以控制对该对象的访问. 这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介. Java中的代理按照代理类生成时机不同又分为 ...
- 代理模式 静态代理、JDK动态代理、Cglib动态代理
1 代理模式 使用代理模式时必须让代理类和被代理类实现相同的接口: 客户端通过代理类对象来调用被代理对象方法时,代理类对象会将所有方法的调用分派到被代理对象上进行反射执行: 在分派的过程中还可以添加前 ...
最新文章
- MOS2010开发基础和集几种开发模型
- 汽车与智能家居互联时代 语音控制很关键
- ReactiveCocoa的使用方法
- Python :集合推导式和字典推导式
- 的文件夹结构_Windows中你需要知道的目录结构 「第一期」
- tsm如何备份oracle原理,征文:TSM备份Oracle脚本及策略说明
- 设置 git/npm/bower/gem 镜像或代理的方法
- Java Web乱码分析及解决方式(一)——GET请求乱码
- 在Windows mobile 5.0下操作INI文件
- 开源 免费 java CMS - FreeCMS1.5-数据对象-info
- Cocos2d-x特殊节点对象(瓦片地图、粒子系统、视差滚动)概述
- python转视频格式高清_树莓派环境下使用python将h264格式的视频转为mp4
- windows bat命令启动python程序
- DSD, DFF, DSF, DST概念解析
- 时钟周期,机器周期(cpu周期),指令周期,存储周期,总线周期
- 一行JS代码实现ie浏览器升级弹窗
- POI导出读取Excel表格讲解
- 【LiteApp系列】爱奇艺小程序架构浅析
- 【Jquery-03】jq中的样式操作
- 统计中的p-value检验
热门文章
- 消息称网易取代九城拿下《魔兽》内地代理权
- “灰太狼的羊”事件惹争议,关联商标被抢注
- 一文看懂互动视频的发展、应用及创新
- 智慧树知到期末答案python_2020智慧树知到Python程序设计基础(山东联盟)期末答案...
- matlab中syms怎么替代,科学网—Matlab中的syms与conj - 孔令才的博文
- 数据安全性强制存取控制
- 【工业通讯】CAN基础内容详解(二)——物理层
- Git基础(二)本地Git仓库管理
- 5 个不常提及的 HTML 技巧
- Photoshop 批量照片转格式