《Proxy系列专题》:代理模式(静态、JDK、CGLib)
《Proxy系列专题》:代理模式(静态、JDK、CGLib)使用
现象:在如今互联网时代,项目的复杂度不断的提升,有些场景下需要一定的设计优化来支撑业务的扩展,如为了不改动原始类,但需要对其做相应事件扩展,例如:日志,事物,功能增强等。
思想:想办法用一个B类代表另一个A类的功能,不改变其A类本质。
结果:代理模式出现,这只是一个思想,实现的方式有很多种,如:静态代理、JDK动态代理、CGLib动态代理 等等其他的。
使用方式
本文主要写代理模式主要的几种代码的使用,其原理会接下来的文章种输出。
主要的代理方式
- 静态代理(类似工厂)
- 动态代理
- JDK动态代理(修改字节码方式 + 接口 + 反射)
- CGLib动态代理(修改字节码ASM + 继承 + 调用父类super)
静态代理
静态代理主要过程(整了一个外套--代理类)
- 在代理类中实现目标类相同的接口,并设置接口类型变量属性
- 将目标类实例作为参数传入赋值,如构造器方式,set方式等等
- 调用时其实是代理类的方法调用了相同目标类的方法,中转一下,在调用目标类方法的前后做一些处理
优点:代理类在编译期生成,效率高。
缺点:代理类会很多,后期维护复杂。
代码逻辑
接口类UserService
public interface UserService {
String getUserName(String username);
}
接口实现类UserServiceImpl
public class UserServiceImpl implements UserService{ @Override
public String getUserName(String username) {
System.err.println(username);
return username;
}
}
代理类UserStaticProxy
public class UserStaticProxy implements UserService{ private UserService userService; public UserStaticProxy(UserService userService) {
this.userService = userService;
} @Override
public String getUserName(String username) {
System.err.println("开始------");
String userName = userService.getUserName(username);
System.err.println("结束------");
return userName;
}
}
测试类StaticProxyTest
public class StaticProxyTest {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
userService.getUserName("test");
System.err.println("_______________________________________________");
UserStaticProxy userStaticProxy = new UserStaticProxy(new UserServiceImpl());
userStaticProxy.getUserName("lisi");
}
}
JDK动态代理
问题:为什么会出现动态代理呢,静态代理哪里不好了 回答:若是又出现一个类型需要代理,需要再次写一个代理类去维护包装,或者类型中新增一个方法功能代理类也需要跟着维护,复杂过于耦合。所以就有想法如何解决这样的尴尬困境。
这时候出现了JDK动态代理,完美解决了新增类型和新增方法的一些尴尬问题。主要将静态代理中代理类涉及到目标类有关的以其他种方式体现,变得可动态。
主要流程:
- 从静态代理中对目标类方法处理事件,进行了封装,变成了InvocationHandler调用处理器作为参数形式传递,可以代理工具类内部实现,也可调用逻辑内部匿名实现
- 传入目标类实例,进行相应的处理(提取有效信息进行相应的字节码封装)
- 代理工具类生成目标类的代理类,此时代理类实例在内存中,其内部与静态代理很相似。
代码逻辑
接口类SanGuoService
public interface SanGuoService {
void warfare(String username);
}
目标类 LiuBeiServiceImpl 实现 SanGuoService
public class LiuBeiServiceImpl implements SanGuoService{
@Override
public void warfare(String username) {
System.err.println("LiuBei-warfare:"+username);
}
}
代理工具 JDKSimpleDynamicProxy 可实现 InvocationHandler 调用处理器,也可以逻辑内部匿名实现当作参数传递
主要为了生成代理类
public class JDKSimpleDynamicProxy implements InvocationHandler { private Object object;
//创建代理类实例
public <T> T getNewProxy(T t){
object = t;
return (T) Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),this);
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.err.println("------开始----");
Object invoke = method.invoke(object, args);
System.err.println("------结束----");
return invoke;
}
}
JDK动态代理测试类 JDKSimpleDynamicProxyTest
public class JDKSimpleDynamicProxyTest {
public static void main(String[] args) {
JDKSimpleDynamicProxy jdkSimpleDynamicProxy = new JDKSimpleDynamicProxy();
LiuBeiServiceImpl liuBeiService = new LiuBeiServiceImpl();
SanGuoService sanGuoService = jdkSimpleDynamicProxy.getNewProxy(liuBeiService);
sanGuoService.warfare("刘备");
}
}
CGLib动态代理
问题:有了JDK动态代理那么方便好用高扩展,为什么又要搞出一个CGLib动态代理呢。
回答:当一个类型没有接口,则不能使用JDK动态代理了,除非给硬塞一个接口,很滑稽,那怎么办呢!所有设计了一个可以对目标类直接代理的方式,直接继承,CGLib动态代理。
其实CGLib和JDK动态代理使用流程上还是很相似的,只是内部逻辑实现不一样,若封装的好是看不出来的。
主要流程:
- 首先明确需要对目标类方法处理要做的事,构造出MethodInterceptor实现,可以代理工具类内部实现,也可以外部业务逻辑自行实现。
- 将目标类实例传入代理工具类中,进行相应参数拼装,底层内部根据参数信息逻辑拼装字节码。
- 代理工具类生成目标类的代理类,此时代理类实例在内存中,代理类内部逻辑是以super形式调用父类方法
代码逻辑
目标类:XiangYuServiceImpl 没有接口类
public class XiangYuServiceImpl { public void warfare(String username) {
System.err.println("XiangYu-warfare:"+username);
}
}
代理工具类:CGLibDynamicProxy 这类的MethodInterceptor上面说过可以这里实现,也可以业务逻辑自己实现参数传入,因为内部只需要一个参数实例
public class CGLibDynamicProxy implements MethodInterceptor { public <T> T create(Class<T> classN){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(classN);
enhancer.setCallback(this);
return (T)enhancer.create();
} @Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.err.println(proxy.getClass().getName());
System.err.println("1111111");
Object invoke = methodProxy.invokeSuper(proxy, args);
System.err.println("22222222");
return invoke;
}
}
测试类:CGLibDynamicProxyTest
public class CGLibDynamicProxyTest {
public static void main(String[] args) {
CGLibDynamicProxy cgLibDynamicProxy = new CGLibDynamicProxy();
XiangYuServiceImpl xiangYuService = cgLibDynamicProxy.create(XiangYuServiceImpl.class);
xiangYuService.warfare("guanglin");
}
}
代理模式使用总结
其实这三种代理模式都是想尽办法创建一个代理类调用目标类来实现代理。
动态代理的应用:Spring AOP、RPC调用、Hibernate的懒加载 等待
代理模式 | 代理类创建时机 | 创建方式 | 代理类行为 | 原理 | 调用处理器 | 效率方面 |
性能方面 |
静态代理 | 编译时期 | 手动创建 | 实现接口 | 直接调用目标类 | 无 | 高效 | 高效 |
JDK动态代理 | 运行时 | 基于JVM对字节码的操作实现 | 实现接口 | 反射调用 | InvocationHandler | 慢 |
JDK1.6/1.7上的对比
JDK1.8上的对比
|
CGLib动态代理 | 运行时 | 基于ASM对字节码的操作 | 继承目标类 | super调用父类 | MethodInterceptor | 慢 |
《Proxy系列专题》:代理模式(静态、JDK、CGLib)的更多相关文章
- 两种代理模式(JDK和Cglib)实例
CGlib代理模式: package CGLIB; import java.lang.reflect.Method; import JDK.Test; import net.sf.cglib.prox ...
- Spring代理模式(jdk动态代理模式)
有动态代理和静态代理: 静态代理就是普通的Java继承调用方法. Spring有俩种动态代理模式:jdk动态代理模式 和 CGLIB动态代理 jdk动态代理模式: 代码实现: 房东出租房子的方法(继承 ...
- java 设计模式之单利模式以及代理模式(静态)
1:单利模式: public class Singleton { private static Singleton uniqueInstance = null; private Singleton() ...
- 代理模式(静态代理、JDK动态代理原理分析、CGLIB动态代理)
代理模式 代理模式是设计模式之一,为一个对象提供一个替身或者占位符以控制对这个对象的访问,它给目标对象提供一个代理对象,由代理对象控制对目标对象的访问. 那么为什么要使用代理模式呢? 1.隔离,客户端 ...
- 代理模式 静态代理、JDK动态代理、Cglib动态代理
1 代理模式 使用代理模式时必须让代理类和被代理类实现相同的接口: 客户端通过代理类对象来调用被代理对象方法时,代理类对象会将所有方法的调用分派到被代理对象上进行反射执行: 在分派的过程中还可以添加前 ...
- 设计模式系列之代理模式(Proxy Pattern)——对象的间接访问
说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...
- 基于jdk proxy的动态代理模式
代理模式 是spring AOP机制的实现基础,有必要学习一下. 有两种,一种是目标类有接口的, 采用JDK动态代理,一种是目标类没接口的,采用CGLIB动态代理. 先看一组代码, package c ...
- java设计模式(一)动态代理模式,JDK与CGLIB分析
-本想着这个知识点放到Spring Aop说说可能更合适一点,但因为上一篇有所提到就简单分析下,不足之处请多多评论留言,相互学习,有所提高才是关键! 什么是代理模式: 记得有本24种设计模式的书讲到代 ...
- java 动态代理模式(jdk和cglib)
package proxy.dynamicproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Met ...
随机推荐
- aix5.3安装httpd服务
1.安装gcc(1)从IBM上下载 gcc-4.0.0-1.aix5.3.ppc.rpm gcc-cplusplus-4.0.0-1.aix5.3.ppc.rpm libgcc-4.0.0-1.aix ...
- 解锁Renderbus客户端使用新技巧----快速渲染效果图篇
度娘说,效果图最基本的要求就是:应该符合事物的本身尺寸,不能为了美观而使用效果把相关模型的尺寸变动,那样的效果图不但不能起到表现设计的作用,反而成为影响设计的一个因素.可见高效渲染效果图是都是当下我们 ...
- servlet+jsp完成简单登录
将用户在注册界面中的数据填充到数据库相对应的表格中.当用户再次登录时,从数据库中拿到相应的数据查询并与页面的数据做对比,判断是否登陆成功. 需要在HTML文件中将form表单上的action属性值设置 ...
- #2020征文-开发板# 用鸿蒙开发AI应用(五)HDF 驱动补光灯
目录: 前言 硬件准备 HDF 驱动开发 总结 前言上一篇,我们在鸿蒙上运行了第一个程序,这一篇我们来编写一个驱动开启摄像头的红外补光灯,顺便熟悉一下鸿蒙上的 HDF 驱动开发. 硬件准备先查一下原理 ...
- .NET斗鱼直播弹幕客户端(2021)
.NET斗鱼直播弹幕客户端(2021) 离之前更新的两篇<.NET斗鱼直播弹幕客户端>已经有一段时间,近期有许多客户向我反馈刚好有这方面的需求,但之前的代码不能用了--但网上许多流传的No ...
- linux服务开机自启动&注册系统服务
首先先看下linux系统开机启动顺序,如下图 对,要解决Linux CentOS 和 Red Hat Linux 系统中设置服务自启动有两种方式,就是从图中圈出的两个步骤下手. 一.修改 /etc/r ...
- 深入解析vue响应式原理
摘要:本文主要通过结合vue官方文档及源码,对vue响应式原理进行深入分析. 1.定义 作为vue最独特的特性,响应式可以说是vue的灵魂了,表面上看就是数据发生变化后,对应的界面会重新渲染,那么响应 ...
- 为什么[] == false 为true
首先要讲一下js的数据类型分为: 1.基本数据类型(原始数据类型):String.Boolean.Number.null.undefined.Symbol 2.引用数据类型:Object.Array. ...
- [usaco2010 Oct]Soda Machine
题目描述 有N个人要去膜拜JZ,他们不知道JZ会出现在哪里,因此每个人有一个活动范围,只要JZ出现在这个范围内就能被膜拜, 伟大的JZ当然希望膜拜他的人越多越好,但是JZ不能分身,因此只能选择一个位置 ...
- 5V充8.4V,5V升压8.4V给电池充电的芯片电路
5V充8.4V的锂电池,需要把USB口的5V输入,升压转换成8.4V来给两串电池充电. 5V升压8.4V给锂电池充电的专门充电IC 集成了5V升压8.4V电路和充电管理电路的PL7501C 如果不需要 ...