- 示 例
- 引用计数
- 适用范围
- 程序设计
- 组成角色
- 抽象角色、代理角色、真实角色
- 中文名
- 代理模式
- 性 质
- 一种设计模式
- 外文名
- Proxy Pattern
-
2021-12-16 17:15:47
定义
代理模式又叫委托模式,是为某个对象提供一个代理对象,并且由代理对象控制对原对象的访问。代理模式通俗来讲就是我们生活中常见的中介。
我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原真实对象的前提下,提供额外的功能操作,扩展真实对象的功能。比如说在真实对象的某个方法执行前后你可以增加一些自定义的操作。类型
结构型
UML图
角色
抽象角色(Subject):通过接口或抽象类声明真实角色实现的业务方法。
代理角色(Proxy):实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
真实角色(RealSubject):实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。示例
静态代理
静态代理中,我们对目标对象的每个方法的增强都是手动完成的,非常不灵活(比如接口一旦新增加方法,目标对象和代理对象都要进行修改)且麻烦(需要对每个目标类都单独写一个代理类)。从 JVM 层面来说, 静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。
静态代理实现步骤:
- 定义一个接口及其实现类;
- 创建一个代理类同样实现这个接口
- 将真实目标对象注入进代理类,然后在代理类的对应方法调用真实目标类中的对应方法。这样的话,我们就可以通过代理类屏蔽对真实目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情。是不是感觉和spring AOP 面向切面编程很像。
我们每次在Controller层中实现向数据库insert数据,一般都是经过Service层、Dao层,再到数据库,这里我们将Service层进行代理。
Order类,实体类。
public class Order { private Object orderInfo; private Integer userId; public Object getOrderInfo() { return orderInfo; } public void setOrderInfo(Object orderInfo) { this.orderInfo = orderInfo; } public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } }
Dao层接口
public interface IOrderDao { int insert(Order order); }
Dao层实现类
public class OrderDaoImpl implements IOrderDao { public int insert(Order order) { System.out.println("Dao层添加Order成功"); return 1; } }
Service接口
public interface IOrderService { int saveOrder(Order order); }
Service实现类
public class OrderServiceImpl implements IOrderService { private IOrderDao iOrderDao; public int saveOrder(Order order) { // 使用Spring注解会自己注入,这里就自己直接new iOrderDao = new OrderDaoImpl(); System.out.println("Service层调用Dao层添加Order"); return iOrderDao.insert(order); } }
OrderServiceStaticProxy类(Service层的静态代理类)
public class OrderServiceStaticProxy { private IOrderService iOrderService; public int saveOrder(Order order){ //方法增强,比如分库、校验、安全等处理(前置方法) beforeMethod(order); iOrderService = new OrderServiceImpl(); int result = iOrderService.saveOrder(order); //方法增强,比如释放资源等处理(后置方法) afterMethod(); return result; } private void beforeMethod(Order order){ int userId = order.getUserId(); //分库 int dbRouter = userId % 2; System.out.println("静态代理分配到【db"+dbRouter+"】处理数据"); //todo 设置dataSource; DataSourceContextHolder.setDBType("db"+String.valueOf(dbRouter)); System.out.println("开始执行代理任务"); System.out.println("静态代理 before code"); } private void afterMethod(){ System.out.println("静态代理 after code"); System.out.println("执行完毕"); } }
Test(应用层)
public class Test { public static void main(String[] args) { Order order = new Order(); order.setUserId(2); OrderServiceStaticProxy orderServiceStaticProxy = new OrderServiceStaticProxy(); orderServiceStaticProxy.saveOrder(order); } }
输出结果
动态代理
相比于静态代理来说,动态代理更加灵活。我们不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,通过动态代理类我们可以完成全部的代理功能。
静态代理是在代码编译后就已经确定被代理的对象了。
而动态代理是在代码运行时,通过反射机制在运行时动态生成类字节码并加载到JVM中的,也就是说,在运行过程中才确立要代理的对象。这样能够代理各种类型的对象。Spring AOP、RPC 框架的实现都依赖了动态代理,动态代理在框架中几乎是必用的一门技术。学会了动态代理之后,对于我们理解和学习各种框架的原理也非常有帮助。
对于Java 来说,动态代理的实现方式有很多种,比如 JDK 动态代理、CGLIB 动态代理等等
JDK动态代理简单介绍
JDK动态代理,java.lang.reflect.InvocationHandler接口和java.lang.reflect.Proxy类是核心。
Proxy 类中使用频率最高的方法是:newProxyInstance() ,这个方法主要用来生成一个代理对象。
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
- loader :类加载器,用于加载代理对象。
- interfaces : 被代理类实现的一些接口;
- h : 实现了 InvocationHandler 接口的对象;
要实现动态代理的话,还必须需要实现InvocationHandler 来自定义处理逻辑。 当我们的动态代理对象调用一个方法时,这个方法的调用就会被转发到实现InvocationHandler 接口类的 invoke 方法来调用。
public interface InvocationHandler { /** * 当你使用代理对象调用方法的时候实际会调用到这个方法 */ /** * proxy :动态生成的代理类 * method : 与代理类对象调用的方法相对应 * args : 当前 method 方法的参数 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
总结起来就是:我们通过Proxy 类的 newProxyInstance() 创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler 接口的类的 invoke()方法。 你可以在 invoke() 方法中自定义处理逻辑,比如在代理方法执行前后做什么其他的事情
JDK 动态代理类使用步骤
- 定义一个接口及其实现类;
- 实现 InvocationHandler 接口 并重写invoke方法,在 invoke
方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑; - 通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[]
interfaces,InvocationHandler h) 方法创建代理对象
示例
OrderServiceDynamicProxy类(Service层的JDK动态代理类)。
public class OrderServiceDynamicProxy implements InvocationHandler { // 代理类中的真实对象 private Object target; public OrderServiceDynamicProxy(Object target) { this.target = target; } public Object bind(){ Class cls = target.getClass(); //主要通过Proxy.newProxyInstance()方法获取某个类的代理对象 return Proxy.newProxyInstance(cls.getClassLoader(), //目标类的类加载 cls.getInterfaces(), // 被代理类实现的一些接口; this); // 实现了 InvocationHandler 接口的代理对象 } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object argObject = args[0]; // 执行代理业务方法之前,我们可以添加前置操作 beforeMethod(argObject); Object object = method.invoke(target,args); //同理,可以添加后置操作 afterMethod(); return object; } private void beforeMethod(Object obj){ int userId = 0; if(obj instanceof Order){ Order order = (Order)obj; userId = order.getUserId(); } int dbRouter = userId % 2; System.out.println("动态代理分配到【db"+dbRouter+"】处理数据"); //todo 设置dataSource; DataSourceContextHolder.setDBType("db"+String.valueOf(dbRouter)); System.out.println("开始执行代理任务"); System.out.println("动态代理 before code"); } private void afterMethod(){ System.out.println("动态代理 after code"); System.out.println("执行完毕"); } }
invoke() 方法: 当我们的动态代理对象调用原生方法的时候,最终实际上调用到的是 invoke() 方法,然后 invoke() 方法代替我们去调用了被代理对象的原生方法。
Test
public class Test { public static void main(String[] args) { Order order = new Order(); order.setUserId(1); IOrderService orderServiceDynamicProxy = (IOrderService) new OrderServiceDynamicProxy(new OrderServiceImpl()).bind(); orderServiceDynamicProxy.saveOrder(order); } }
输出
CGLib动态代理
CGLib简单介绍
JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,可以使用 CGLib动态代理
CGLIB (opens new window)(Code Generation Library)是一个基于ASM (opens new window)的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理。其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。
很多知名的开源框架都使用到了CGLIB (opens new window), 例如 Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。
在 CGLIB 动态代理机制中 MethodInterceptor 接口和 Enhancer 类是核心。
需要我们自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法。public interface MethodInterceptor extends Callback { // 拦截被代理类中的方法 Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable; }
obj :被代理的对象(需要增强的对象)
method :被拦截的方法(需要增强的方法)
args :方法入参
proxy :用于调用原始方法我们可以通过 Enhancer类来动态获取被代理类,当代理类调用方法的时候,实际调用的是 MethodInterceptor 中的 intercept 方法。
CGLIB 动态代理类使用步骤
- 定义一个类;
- 实现 MethodInterceptor 接口并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK动态代理中的 invoke 方法类似;
- 通过 Enhancer 类的 create()创建代理类;
不同于 JDK 动态代理不需要额外的依赖。CGLIB (opens new window)(Code Generation Library) 实际是属于一个开源项目,如果你要使用它的话,需要添加相关依赖。我这里添加的依赖是
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.10</version> </dependency> </dependencies>
示例
OrderServiceCGLibDynamicProxy类(Service层的CGLib动态代理类)。
import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class OrderServiceCGLibDynamicProxy implements MethodInterceptor { public OrderServiceImpl bind(){ Enhancer enhancer = new Enhancer();//生成代理对象 enhancer.setSuperclass(OrderServiceImpl.class);//设置对谁进行代理 enhancer.setCallback(this);//代理要做什么,设置拦截器 OrderServiceImpl orderService = (OrderServiceImpl) enhancer.create();//创建代理对象 return orderService; } /** * @param o 代理对象(增强的对象) * @param method 被拦截的方法(需要增强的方法) * @param objects 方法入参 * @param methodProxy 用于调用原始方法 */ @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { beforeMethod(objects[0]); Object object = methodProxy.invokeSuper(o,objects); afterMethod(); return object; } private void beforeMethod(Object object){ if(object instanceof Order){ Order order = (Order)object; int userId = order.getUserId(); int dbRouter = userId % 2; System.out.println("CGLib动态代理分配到 【db"+dbRouter+"】处理数据"); //TODO 分库操作 } //TODO 其他类型处理 System.out.println("开始执行代理任务"); System.out.println("CGLib动态代理 before code"); } private void afterMethod(){ System.out.println("CGLib动态代理 after code"); System.out.println("执行完毕"); } }
Test
public class Test { public static void main(String[] args) { Order order = new Order(); order.setUserId(1); OrderServiceCGLibDynamicProxy orderServiceCGLibDynamicProxy = new OrderServiceCGLibDynamicProxy(); IOrderService iOrderService = orderServiceCGLibDynamicProxy.bind(); iOrderService.saveOrder(order); } }
输出
JDK 动态代理和 CGLIB 动态代理对比
- JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。 另外, CGLIB动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。
- 就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显
代理模式总结
优点
- 实现了访问者与访问对象之间的解耦。
- 代理模式在应用层与对象之间起到中介作用,保护了对对象的访问。
- 职责清晰(单一职责):真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,代理模式可以在代理过程中增加逻辑,如Spring框架的AOP。
缺点
- 增加代理会使程序请求处理变慢。
- 类的数量变多,系统更加复杂。
应用场景
- 远程代理(Remote Proxy)
为一个位于不同的地址空间的对象提供一个本地的代理对象。这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又叫做大使(Ambassador) - 虚拟代理(Virtual Proxy)
根据需要创建开销很大的对象。如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。 - 保护代理(Protection Proxy)
控制对原始对象的访问。保护代理用于对象应该有不同的访问权限的时候。 - 智能指引(Smart Reference)
取代了简单的指针,它在访问对象时执行一些附加操作。 - Copy-on-Write代理
它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆 - 引用计数(reference counting)指针对象。
当一个复杂对象的多份副本须存在时,代理模式可以结合享元模式以减少存储器用量。典型作法是创建一个复杂对象及多个代理者,每个代理者会引用到原本的复杂对象。而作用在代理者的运算会转送到原本对象。一旦所有的代理者都不存在时,复杂对象会被移除。
References:
- https://blog.csdn.net/weixin_34007906/article/details/92172234
- https://baike.baidu.com/item/%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F/8374046?fr=aladdin
- https://javaguide.cn/java/basis/%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F%E8%AF%A6%E8%A7%A3/#
- https://blog.csdn.net/qq_37960603/article/details/104101825
- https://coding.imooc.com/class/270.html?utm_term=%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F&utm_campaign=SEM&utm_medium=33&_channel_track_key=V4q7e28K&utm_source=szjineng5&bd_vid=11577451146916505204
- https://www.cnblogs.com/jy107600/p/8657217.html
- https://www.jianshu.com/p/0021bc657203
(写博客主要是对自己学习的归纳整理,资料大部分来源于书籍和网络资料,整理不易,但是难免有不足之处,如有错误,请大家评论区批评指正。同时感谢广大博主和广大作者辛苦整理出来的资源。)
更多相关内容 -
设计模式-代理模式(静态代理、动态代理、cglib代理)
2021-03-20 16:29:40代理模式(Proxy Pattern)是一种结构性模式。代理模式为一个对象提供了一个替身,以控制对这个对象的访问。即通过代理对象访问目标目标对象,可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能...代理模式
代理模式(Proxy Pattern)是一种结构性模式。代理模式为一个对象提供了一个替身,以控制对这个对象的访问。即通过代理对象访问目标目标对象,可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
被代理的对象可以是远程对象、创建开销答得对象或需要安全控制得对象。代理模式主要有三种形式,分别是静态代理、动态代理(也称JDK代理、接口代理)和cglib代理(在内存动态创建对象而不需要实现接口,也可属于动态代理得范畴)
类图:
静态代理
静态代理是定义父类或者接口,然后被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类。代理对象与目标对象实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。
- 优点:可不修改目标对象的功能,通过代理对象对目标功能扩展。
- 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,一旦接口增加方法,目标对象与代理对象都要维护。
比如老师代课的案例:
- ITeacherDao:接口
- TeacherDao:目标对象,实现接口ITeacherDao
- TeacherDAOProxy:代理对象,也实现ITeacherDao接口,并且聚合ITeacherDao属性,通过构造器传参设置值,调用的时候通过调用代理对象的方法来调用目标对象。
代码
- 接口
public interface ITeacherDao { void teach(); // 授课的方法 }
- 被代理对象
public class TeacherDao implements ITeacherDao { @Override public void teach() { System.out.println("一键三连"); } }
- 代理对象
public class TeacherDaoProxy implements ITeacherDao { private ITeacherDao target; //通过接口聚合目标对象 public TeacherDaoProxy(ITeacherDao target) { this.target = target; } @Override public void teach() { //重写接口 System.out.println("静态代理开始"); target.teach(); System.out.println("静态代理结束"); } }
- 测试
public class Client { public static void main(String[] args) { //创建被代理对象 TeacherDao teacherDao = new TeacherDao(); //创建代理对象, 同时将被代理对象传递给代理对象 TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao); //通过代理对象,调用到被代理对象的方法 teacherDaoProxy.teach(); } } /*运行结果: 静态代理开始 一键三连 静态代理结束 */
动态代理
动态代理也叫JDK代理、接口代理。它使代理对象不需要实现接口(但目标对象要实现接口),代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象。
即使用JDK包
java.lang.reflect.Proxy
中的newProxyInstance
方法来动态的创建目标对象(被代理对象),该方法需要如下接收三个参数:- ClassLoader loader
指定当前目标对象使用的类加载器 - Class<?>[] interfaces
目标对象实现的接口类型,使用泛型方法确认类型 - InvocationHandler h
事情处理,执行目标对象的方法时,会触发事情处理器方法,把当前执行的目标对象方法作为参数传入
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
类图:
核心是
getProxyInstacne()
- 根据传入的对象TeacherDao目标对象
- 利用返回机制,返回一个代理对象
- 然后通过代理对象,调用目标对象方法
代码:
- 接口
public interface ITeacherDao { void teach(); void tesst(String name); }
- 目标对象
public class TeacherDao implements ITeacherDao { @Override public void teach() { System.out.println("一键三连"); } @Override public void tesst(String name) { System.out.println("传参测试:" + name); } }
(
插播反爬信息)博主CSDN地址:https://wzlodq.blog.csdn.net/- 代理对象
public class ProxyFactory { //维护一个目标对象 , Object private Object target; //构造器 , 对target 进行初始化 public ProxyFactory(Object target) { this.target = target; } //动态生成一个代理对象 public Object getProxyInstance() { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { //匿名类重写invoke方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("动态代理开始"); Object returnVal = method.invoke(target, args);//反射机制调用目标对象的方法 System.out.println("动态代理结束"); return returnVal; } }); } }
- 测试
public class Client { public static void main(String[] args) { //创建目标对象 ITeacherDao target = new TeacherDao(); //创建代理对象 ITeacherDao proxyInstance = (ITeacherDao)new ProxyFactory(target).getProxyInstance(); //内存中动态生成了代理对象 System.out.println(proxyInstance.getClass()); //通过代理对象,调用目标对象的方法 proxyInstance.teach(); proxyInstance.tesst("一键三连"); } } /*运行结果: 动态代理开始 一键三连 动态代理结束 动态代理开始 传参测试:一键三连 动态代理结束 */
cglib代理
Cglib代理也叫作子类代理,它使目标对象不需要实现接口,是在内存中构建一个子类对象从而实现对目标对象功能扩展,有的也将Cglib代理归属到动态代理。
Cglib是一个高性能的代码生成包,它可以在运行期扩展java类与实现java接口。被许多AOP的框架使用(如Spring AOP)。Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类。
特别注意:代理的类不能为final,否则报错
java.lang.IllegalArgumentException
,如果目标对象的方法如果为final或static,那么就不会被拦截(即不会执行目标对象额外的业务方法)。添加cglib包:
下载以下jar包,复制到项目中,然后右键add to library
。
https://github.com/wuzelong/CSDNBLOG(顺便求个star)
不一样的是代理对象实现了
MethodInterceptor
接口,重写了intercept()
方法实现对被代理对象(目标对象)的方法掉调用。代码:
- 目标对象
public class TeacherDao { public String teach() { System.out.println("一键三连"); return "好"; } }
- 代理对象
public class ProxyFactory implements MethodInterceptor { //目标对象 private Object target; public ProxyFactory(Object target) { this.target = target; } //返回target对象的代理对象 public Object getProxyInstance() { //创建一个工具类 Enhancer enhancer = new Enhancer(); //设置父类 enhancer.setSuperclass(target.getClass()); //设置回调函数 enhancer.setCallback(this); //创建子类对象,即代理对象 return enhancer.create(); } //重写intercept方法,会调用目标对象的方法 @Override public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable { System.out.println("Cglib代理开始"); Object returnVal = method.invoke(target, args); System.out.println("Cglib代理结束"); return returnVal; } }
- 测试
public class Client { public static void main(String[] args) { //创建目标对象 TeacherDao target = new TeacherDao(); //获取到代理对象,并且将目标对象传递给代理对象 TeacherDao proxyInstance = (TeacherDao)new ProxyFactory(target).getProxyInstance(); //执行代理对象的方法,触发intecept 方法,从而实现对目标对象的调用 proxyInstance.teach(); System.out.println(res); } } /*运行结果: Cglib代理开始 一键三连 Cglib代理结束 好 */
应用
- 防火墙代理
内网通过代理穿透防火墙,实现对公网的访问。 - 缓存代理
请求资源时先到缓存代理中取,如果取不到再到数据库或公网取,然后更新缓存(如Redis)。 - 远程代理
通过网络和真正的远程对象沟通信息(如远程连接服务器)。 - 同步代理
用在多线程编程中,完成多线程间同步工作。
原创不易,请勿转载(
本不富裕的访问量雪上加霜)
博主首页:https://wzlodq.blog.csdn.net/
微信公众号:吾仄lo咚锵
如果文章对你有帮助,记得一键三连❤ -
代理模式(Proxy)
2022-01-03 21:50:38代理模式一、基本介绍二、分类1.静态代理优缺点2.动态代理(1)JDK代理(2)Cglib代理 一、基本介绍 代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是:可以...一、基本介绍
- 代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
- 被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象
- 代理模式有不同的形式, 主要有静态代理和动态代理, 动态代理分为JDK代理和 Cglib代理 (可以在内存动态的创建对象,而不需要实现接口)。
- 代理模式示意图
二、分类
1.静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类
应用实例
具体要求- 定义一个接口:ITeacherDao
- 目标对象TeacherDAO实现接口ITeacherDAO
- 使用静态代理方式,就需要在代理对象TeacherDAOProxy中也实现ITeacherDAO
- 调用的时候通过调用代理对象的方法来调用目标对象.
- 特别提醒:代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。
思路分析图解(类图)
ITeacherDao类
//接口 public interface ITeacherDao { void teach(); // 授课的方法 }
Client类
public class Client { public static void main(String[] args) { //创建目标对象(被代理对象) TeacherDao teacherDao = new TeacherDao(); //创建代理对象, 同时将被代理对象传递给代理对象 TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao); //通过代理对象,调用到被代理对象的方法 //即:执行的是代理对象的方法,代理对象再去调用目标对象的方法 teacherDaoProxy.teach(); } }
TeacherDao类
public class TeacherDao implements ITeacherDao { @Override public void teach() { // TODO Auto-generated method stub System.out.println(" 老师授课中 。。。。。"); } }
TeacherDaoProxy类
//代理对象,静态代理 public class TeacherDaoProxy implements ITeacherDao{ private ITeacherDao target; // 目标对象,通过接口来聚合 //构造器 public TeacherDaoProxy(ITeacherDao target) { this.target = target; } @Override public void teach() { // TODO Auto-generated method stub System.out.println("开始代理 完成某些操作。。。。。 ");//方法 target.teach(); System.out.println("提交。。。。。");//方法 } }
优缺点
静态代理优缺点
- 优点:在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展
- 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类
- 一旦接口增加方法,目标对象与代理对象都要维护
2.动态代理
- 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
- 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
- 动态代理也叫做:JDK代理、接口代理
(1)JDK代理
- 代理类所在包:java.lang.reflect.Proxy
- JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
3)思路图解
4)代码实现
ITeacherDao类
//接口 public interface ITeacherDao { void teach(); // 授课方法 void sayHello(String name); }
ProxyFactory类
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyFactory { //维护一个目标对象 , Object private Object target; //构造器 , 对target 进行初始化 public ProxyFactory(Object target) { this.target = target; } //给目标对象 生成一个代理对象 public Object getProxyInstance() { //说明 /* * public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) //1. ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定 //2. Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型 //3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行的目标对象方法作为参数传入 */ return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("JDK代理开始~~"); //反射机制调用目标对象的方法 Object returnVal = method.invoke(target, args); System.out.println("JDK代理提交"); return returnVal; } }); } }
TeacherDao类
public class TeacherDao implements ITeacherDao { @Override public void teach() { // TODO Auto-generated method stub System.out.println(" 老师授课中.... "); } @Override public void sayHello(String name) { // TODO Auto-generated method stub System.out.println("hello " + name); } }
Client类
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub //创建目标对象 ITeacherDao target = new TeacherDao(); //给目标对象,创建代理对象, 可以转成 ITeacherDao ITeacherDao proxyInstance = (ITeacherDao)new ProxyFactory(target).getProxyInstance(); // proxyInstance=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象 System.out.println("proxyInstance=" + proxyInstance.getClass()); //通过代理对象,调用目标对象的方法 //proxyInstance.teach(); proxyInstance.sayHello(" tom "); } }
(2)Cglib代理
Cglib代理模式的基本介绍
- 静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理-这就是Cglib代理
- Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib代理归属到动态代理。
- Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截
- 在AOP编程中如何选择代理模式:
- 目标对象需要实现接口,用JDK代理
- 目标对象不需要实现接口,用Cglib代理
- Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类
Cglib代理模式实现步骤 - 需要引入cglib的jar文件
- 在内存中动态构建子类,注意代理的类不能为final,否则报错
java.lang.IllegalArgumentException: - 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
4)应用实例要求
思路图解(类图)
代码实例
ProxyFactory类import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class ProxyFactory implements MethodInterceptor { //维护一个目标对象 private Object target; //构造器,传入一个被代理的对象 public ProxyFactory(Object target) { this.target = target; } //返回一个代理对象: 是 target 对象的代理对象 public Object getProxyInstance() { //1. 创建一个工具类 Enhancer enhancer = new Enhancer(); //2. 设置父类 enhancer.setSuperclass(target.getClass()); //3. 设置回调函数 enhancer.setCallback(this); //4. 创建子类对象,即代理对象 return enhancer.create(); } //重写 intercept 方法,会调用目标对象的方法 @Override public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable { // TODO Auto-generated method stub System.out.println("Cglib代理模式 ~~ 开始"); Object returnVal = method.invoke(target, args); System.out.println("Cglib代理模式 ~~ 提交"); return returnVal; } }
TeacherDao类
public class TeacherDao { public String teach() { System.out.println(" 老师授课中 , 我是cglib代理,不需要实现接口 "); return "hello"; } }
Client类
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub //创建目标对象 TeacherDao target = new TeacherDao(); //获取到代理对象,并且将目标对象传递给代理对象 TeacherDao proxyInstance = (TeacherDao)new ProxyFactory(target).getProxyInstance(); //执行代理对象的方法,触发intecept 方法,从而实现 对目标对象的调用 String res = proxyInstance.teach(); System.out.println("res=" + res); } }
-
代理模式
2019-02-28 14:44:48大家都知道代理模式深入咱们都业务代码中,(就是代理类把接口都实现类引入进来,然后加以封装,再由其他类使用代理类)应用场景太多了,但是大多都是静态代理,如果真都实现一个动态代理我认为还是一个比较都事情,...前言
代理模式让我评价就是一个笑面虎,结构超简单,实现比较复杂。大家都知道代理模式深入咱们都业务代码中,(就是代理类把接口都实现类引入进来,然后加以封装,再由其他类使用代理类)应用场景太多了,但是大多都是静态代理,如果真都实现一个动态代理我认为还是一个比较都事情,本文对动态代理这块写对也是比较简单,
参照文献
- https://blog.csdn.net/qq_34178598/article/details/78630934
- https://blog.csdn.net/maoyuanming0806/article/details/80186248
- https://blog.csdn.net/yhl_jxy/article/details/80633194
如果大家想深入学习动态代理模式强烈建议去多看看其他人写多资料,我这个博客顶多算是一个入门介绍。
文献阅读提示:并不是他们的demo在你的本地都能跑通,同一个地方综合大家的说法理解成自己的,这样效果更好。
正文
首先介绍一下代理模式的种类,分为静态代理、jdk动态代理、cglib动态代理。
什么是代理模式?
代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。举例说明,就是一个人或者一个机构,代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之前起到中介的作用。
用绘图方式举例
房屋中介这个角色就是代理模式的核心,中介可以做很多事,房屋加价,改造房屋等等,在代码中就是对被代理对象或方法的处理等等。
下面集合代码来讲解,由于本人能力不足,只能给出代理模式的大概思想,没有结合实际开发生产的场景讲解。
静态代理
静态代理是最好理解的,涉及到一个接口、一个接口实现类、还有一个修饰这个接口实现类的代理类,分别对应着租房的动作,房东,中介三者的关系。
由于很简单,三个类的代码都贴到类一起,大家一看便知。
public interface IRenting { //租房 Integer letOut(); } public class Landlady implements IRenting { //目标类,也可以说房东 public Integer letOut() { System.out.println("房东同意租房,租金100元"); return 100; } } public class Proxy implements IRenting { //代理类,可以看作中介 public Integer letOut() { Landlady landlady = new Landlady(); Integer integer = landlady.letOut(); //现实中,代理类要处理很复杂的业务,甚至是调用其他系统操作一些事。 System.out.println("中介把房东的房子出租给你并从中渔利100元"); return integer + 100; } }
下面来模拟调用者,也就是租客是怎么找中介租房的。
public class StaticProxyTest { public static void main(String[] args) { Proxy proxy = new Proxy(); Integer integer = proxy.letOut(); System.out.println("你一共话了" + integer + "元成功租房,全程没有见过房东,中介把你们隔离开"); } }
调用方只需要把代理类注入到本类中就可以使用了。对于调用者来说,你只知道中介可以租给房子,至于他怎么操作的对你是隔离的。
JDK动态代理
jdk代理模式虽然是动态代理,但是也算比较好理解,只要你的电脑有jdk就可以玩。
其实动态代理和静态代理的思想是不变的,动态代理和静态代理的区别就是,动态代理不用我们去手编写代理类,在运行时,动态的在内存中生产代理类。
JDK动态代理的API:
在java.lang.reflect包中有一个代理类。
- java.lang.reflect.Proxy
我们分别使用三个包就可以完成JDK动态代理。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
方便大家看到动态代理的输出过程,我讲被代理对象多写一个方法,思想还是一样的,接口、实现类、代理工厂三者。
public interface TargetInterface { //被代理的两个方法 void method1(); int method2(Integer i); } public class Target implements TargetInterface { public void method1() { System.out.println("被代理的method1 running ...相当于做了一些列的业务"); } public int method2(Integer i) { System.out.println("被代理的method2 running ...并对参数做了处理"); return i + 10; } }
唯一有区别的地方就是这里,代理类。
代理类的作用是,无论被代理的类是什么返回值类型,什么参数类型,都可以通过我去调用到,对客户端实现隔离。
实际情况代理类需要做的事情远比我demo要复杂的多。public class ProxyFactory { public static <T> Object getProxy(final T t) { //返回一个代理对象 Object object = Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() { //invoke()方法是因为new InvocationHandler()而重写的。 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // proxy就是目标对象,method就是调用目标对象中方法,args就是调用目标对象中方法的参数。 //比如说:代理对象.method1(),这时proxy就是目标类,method1就是method,args就是method1方法参数。 System.out.println("执行方法前..."); //执行了invoke方法就相当于把Target(目标类)的所有方法都交给代理类去调用,这里都t是类,args是参数,可以有很多参数。 Object invoke = method.invoke(t, args); System.out.println("执行方法后..."); return invoke; } }); return object; } }
使用的时候,返回类型是一个接口,然后使用工厂类调用即可。
public class JDKProxyTest { public static void main(String[] args) { Target target = new Target(); TargetInterface proxy = (TargetInterface)ProxyFactory.getProxy(target); proxy.method1(); System.out.println("-------------------------"); int i = proxy.method2(100); System.out.println(i); } }
输出结果
注意:JDK的Proxy方式实现的动态代理 目标对象必须有接口 没有接口不能实现jdk版动态代理!
下面来说一下这个方法Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h);
返回值:Object就是代理对象
参数:
loader:代表与目标对象相同的类加载器-------目标对象.getClass().getClassLoader()
interfaces:代表与目标对象实现的所有的接口字节码对象数组----数组因为目标类可以有多个接口
h:具体的代理的操作,InvocationHandler接口
Cglib动态代理
第三方代理技术--Cglib代理。
可以对任何类生成代理,代理的原理是可以对目标对象接口实现代理,也可以进行继承代理。
需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入spring-core包,由于我依赖了SpringBoot所以这点不用担心。
跟jdk动态代理区别,第一点是可以省略目标接口。 第二点代理工厂使用的是 org.springframework.cglib.proxy.Enhancer; 来帮助我们生成代理对象的,千万不用用net 的包,可能导致错误。
代码实现:
需要依赖springboot
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.0.0.RELEASE</version> </dependency> </dependencies>
目标类,被代理对象
public class Target { public void method1() { System.out.println("被代理的method1 running ...相当于做了一些列的业务"); } public int method2(Integer i) { System.out.println("被代理的method2 running ...并对参数做了处理"); return i + 10; } }
代理类
public class CglibFactory implements MethodInterceptor { public Object getProxy(Class clazz) { Enhancer enhancer = new Enhancer(); //帮我们生成代理对象 //设置需要创建子类的类 enhancer.setSuperclass(clazz); //设置要代理的目标类,就是当前类,所以this enhancer.setCallback(this); //通过字节码技术动态创建子类实例 return enhancer.create(); } public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("执行方法前dosomething。。。"); Object invoke = methodProxy.invokeSuper(o, objects); System.out.println("执行方法后dosomething。。。"); return invoke; } }
让我们测试一下吧
public class CglibProxyTest { public static void main(String[] args) { CglibFactory cglibFactory=new CglibFactory(); Target proxy= (Target) cglibFactory.getProxy(Target.class); proxy.method1(); System.out.println(proxy.method2(100)); } }
输出结果
以上三种代理模式已经讲完,代码结构如下,索要加微信:15524579896
总结:
静态代理需要自己手动编写代理类和目标方法。
JDK动态代理就不需要自己手动实现代理类和目标方法,但动态代理的目标类要必须实现接口!
Cglib 代理的目标类可以实现接口也可以不实现,因为可以使用继承子类的方式代理。
-
【Java萌新】面试常问设计模式——代理模式
2021-06-07 19:42:32代理模式分为静态代理,和动态代理:动态代理的代理类是动态生成的 , 静态代理的代理类是我们提前写好的逻辑。 Java 中实现动态代理的方式有 2 种: JDK 动态代理 CGLIB 动态代理 1、静态代理 静态代理角色分析... -
代理模式和装饰者模式的区别
2022-02-10 16:33:07装饰者模式和代理模式的区别 前言 这个问题其实是在面试字节的时候,面试官提出来的问题。刚听到这个问题的时候一脸懵,这不是两种设计模式吗?根本没什么关联呀!这是要问啥,后来仔细回想了装饰者模式和代理模式,... -
java设计模式--工厂模式、静态代理模式、动态代理模式
2020-08-15 14:32:35工厂模式 代码实现: 步骤一:创建一个接口 Car.java package FactoryMode; public interface Car { public void run(); } 步骤二:创建实现类 BaoMaCar.java package FactoryMode; public class BaoMaCar ... -
轻松学,Java 中的代理模式及动态代理
2017-06-29 22:08:55前几天我写了《秒懂,Java 注解 (Annotation)你可以这样学》,因为注解其实算反射技术中的一部分,然后我想了一下,反射技术中还有个常见的概念就是动态代理,于是索性再写一篇关于动态代理的博文好了。... -
Unity常用设计模式之代理模式
2021-12-17 12:56:58什么是代理模式 在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。它为其他对象提供一种代理以控制对这个对象的访问, 在直接访问对象时带来的问题,比如说:要访问的... -
代理模式的使用总结
2020-04-20 14:14:37一、代理模式 二、静态代理 (一)静态代理 (二)静态代理简单实现 三、动态代理 (一)动态代理 (二)动态代理简单实现 四、动态代理原理分析 五、InvocationHandler接口和Proxy类详解 六、JDK动态代理... -
Java设计模式-代理模式
2021-08-06 11:02:40Java设计模式-代理模式 什么是代理模式? 在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。 在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。 ... -
三种代理模式详解
2020-10-25 14:30:11文章目录二、代理模式(Proxy Pattern)1、常见的几种代理模式:2、静态代理3、JDK动态代理4、CGLib代理5、CGLib和JDK动态代理的区别 二、代理模式(Proxy Pattern) 根据B站狂神视频整理:... -
详解设计模式之 - 代理模式
2019-07-01 15:16:53什么是代理模式?代理模式有什么作用? 代理模式 为真实对象提供一个代理,从而控制对真实对象的访问 代理模式的作用 通过代理,控制对对象的访问! 可以详细控制访问某个(某类)对象的方法,在调用这个方法前... -
设计模式——结构型模式之代理模式和适配器模式(类比+图解,从无到有,一文看懂几种模式的区别)
2021-12-25 15:38:26文章目录设计模式前言一、 代理模式的定义二、代理模式类图和一个设计实例三、适配器模式定义四、装饰者、代理、适配器的区别 前言 代理模式属于结构型模式中最为简单的模式之一,易于理解的同时又妙用无穷,能够... -
Mybatis 代理模式
2022-04-17 16:48:401. 代理方式获取Mapper -> getMapper() 思考一个问题,通常的Mapper接口我们都没有实现的方法却可以使用,是为什么呢?答案很简单动态代理。 开始之前介绍一下MyBatis初始化时对接口的处理:MapperRegistry是... -
搞懂MyBatis中设计模式源码——代理模式
2020-05-19 21:31:00无代理不框架,无反射不框架; 万字长文,助你理解MyBatis中动态代理源码 -
面试官问:代理模式和装饰者模式有啥区别
2020-07-18 21:40:17今天面试聊到了模式。 面试官问:装饰者模式了解吗,讲一下? 我:blabla... 面试官:那代理模式呢?...代理模式是一项基本的设计技巧,许多其他的模式,如状态模式、策略模式、访问者模式本质上也... -
常用设计模式总结(单例模式,工厂模式,代理模式)
2019-05-16 12:53:24常用设计模式总结(未完待续) 单例模式 单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型... -
Java代理模式详解
2020-06-18 14:29:11一、什么是代理模式 代理模式为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用, 其特征是代理类与... -
【C++设计模式】Proxy 代理模式详解
2019-04-26 04:09:56代理模式概述 、代理模式结构与实现 、代理模式的应用实例:模拟登录系统 、代理模式的适用场景 、代理模式优点和缺点 -
代理模式——远程代理(一)
2019-03-19 14:33:20代理模式定义 为另一个对象提供一个替身或占位符以控制对这个对象的访问。使用代理模式创建代表对象,让代表对象控制对某对象的访问,被代理的对象可是远程的对象、创建开销大的对象或需要安全控制的对象。 代理分... -
深入理解代理模式:静态代理与JDK动态代理
2018-03-01 00:22:11代理模式为其他对象提供了一种代理以控制对这个对象的访问,具体实现包括两大类:静态代理和动态代理。Java动态代理机制的出现使得Java开发人员只需要简单地指定一组接口及委托类对象便能动态地获得代理类,并且其... -
面试官问 Spring AOP 中两种代理模式的区别,我懵逼了
2020-06-29 20:30:58代理模式是一种结构性设计模式。为对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象,并允许在将请求提交给对象前后进行一些处理。 被代理的对象可以是远程对象、创建开销大的对象或需要安全... -
组合模式、代理模式(静态代理、动态代理)原理及实例
2022-02-03 17:33:57组合模式 基本介绍 组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系。 组合模式依据树形结构来组合对象,用来表示部分以及整体...