静态代理、jdk动态代理、cglib动态代理
一、静态代理

Subject:抽象主题角色,抽象主题类可以是抽象类,也可以是接口,是一个最普通的业务类型定义,无特殊要求。
RealSubject:具体主题角色,也叫被委托角色、被代理角色。是业务逻辑的具体执行者。
Proxy:代理主题角色,也叫委托类、代理类。它把所有抽象主题类定义的方法给具体主题角色实现,并且在具体主题角色处理完毕前后做预处理和善后工作。(最简单的比如打印日志):
public interface Count {
// 查看账户方法
public void queryCount();
// 修改账户方法
public void updateCount();
}
public class CountImpl implements Count {
@Override
public void queryCount() {
System.out.println("查看账户方法...");
}
@Override
public void updateCount() {
System.out.println("修改账户方法...");
}
}
public class CountProxy implements Count {
private CountImpl countImpl;
/**
* 覆盖默认构造器
*
* @param countImpl
*/
public CountProxy(CountImpl countImpl) {
this.countImpl = countImpl;
}
@Override
public void queryCount() {
System.out.println("事务处理之前");
// 调用委托类的方法;
countImpl.queryCount();
System.out.println("事务处理之后");
}
@Override
public void updateCount() {
System.out.println("事务处理之前");
// 调用委托类的方法;
countImpl.updateCount();
System.out.println("事务处理之后");
}
}
1.接口:代理类需要实现一个接口,这个接口和委托类的接口是一样的,这样proxy才能和委托类行为表现一致
2.方法(Method):由于接口限制,proxy类中也要有interface中的各个方法,这就造成了代码重复
二、动态代理

定义接口
public interface UserService {
public String getName(int id);
public Integer getAge(int id);
}
1.jdk动态代理
基于JDK的动态代理关键在于两个类:InvocationHandler和Proxy。
其主要实现逻辑是,由InvocationHandler定义方法执行前后的增强逻辑,由Proxy类去生成一个继承自Proxy并且实现了真实对象接口的新对象–代理对象,对该代理对象的方法调用经由InvocationHandler拦截,执行增强逻辑和调用真实对象执行业务逻辑。
接口实现类
public class UserServiceImpl implements UserService {
public UserServiceImpl() {
}
@Override
public String getName(int id) {
System.out.println("---getName---");
return "John";
}
@Override
public Integer getAge(int id) {
System.out.println("---getAge---");
return 10;
}
}
代理实现
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("getName")) {
System.out.println("+++before get name+++");
Object res = method.invoke(target, args);
System.out.println("+++after get name+++");
return res;
} else {
Object res = method.invoke(target, args);
return res;
}
}
public static void main(String[] args) {
UserService us = new UserServiceImpl();
InvocationHandler ih = new MyInvocationHandler(us);
UserService usProxy = (UserService) Proxy.newProxyInstance(us.getClass().getClassLoader(), us.getClass().getInterfaces(), ih);
System.out.println(usProxy.getName(1));
System.out.println(usProxy.getAge(1));
System.out.println(usProxy.getClass());
}
}
2.cglib动态代理
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。 通过cglib生成代理类只需要一个目标类和一个回调函数(增强逻辑)
public class UserServiceB {
public String getName(int id) {
System.out.println("---getName---");
return "John";
}
public Integer getAge(int id) {
System.out.println("---getAge---");
return 10;
}
public static void main(String[] args) throws InterruptedException, IOException {
UserServiceB us = new UserServiceB();
// 定义增强器
Enhancer en = new Enhancer();
// 定义要代理的对象
en.setSuperclass(us.getClass());
// 定义回调函数
en.setCallback(new MethodInterceptor() {
// 这里要理解intercept方法的几个参数代表的意思
// obj指的是代理类对象
// Method指的是 目标类中被拦截的方法
// args指的是 调用拦截方法所需的参数
// MethodProxy指的是用来调用目标类被拦截方法的方法,这个方法比反射更快
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("-----before-------");
methodProxy.invokeSuper(obj, args);
System.out.println("-----after--------");
return null;
}
});
// 生成代理对象
UserServiceB usb = (UserServiceB) en.create();
// 在代理对象上调用方法
usb.getName(1);
}
}
三、jdk动态代理和CGlib动态代理的区别
1、JDK动态代理
利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
2、CGLiB动态代理
利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
3、何时使用JDK还是CGLiB?
1)如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。
2)如果目标对象实现了接口,可以强制使用CGLIB实现AOP。
3)如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。
4、如何强制使用CGLIB实现AOP?
1)添加CGLIB库(aspectjrt-xxx.jar、aspectjweaver-xxx.jar、cglib-nodep-xxx.jar)
2)在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
5、JDK动态代理和CGLIB字节码生成的区别?
1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类。
2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成final,对于final类或方法,是无法继承的。
6、CGlib比JDK快?
1)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
2)在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理,总之,每一次jdk版本升级,jdk代理效率都得到提升,而CGLIB代理消息确有点跟不上步伐。
7、Spring如何选择用JDK还是CGLiB?
1)当Bean实现接口时,Spring就会用JDK的动态代理。
2)当Bean没有实现接口时,Spring使用CGlib是实现。
3)可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)。
| 类型 | 机制 | 回调方式 | 适用场景 | 效率 |
| jdk动态代理 | 委托机制,代理类和目标类都实现了同样的接口,InvocationHandler持有目标类,代理类委托InvocationHandler去调用目标类的原始方法 | 反射 | 目标类是接口类 | 效率瓶颈在反射调用稍慢 |
| cglib动态代理 | 继承机制,代理类继承了目标类并重写了目标方法,通过回调函数MethodInterceptor调用父类方法执行原始逻辑 | 通过FastClass方法索引调用 | 非接口类,非final类,非final方法 | 第一次调用因为要生成多个Class对象较JDK方式慢,多次调用因为有方法索引较反射方式快,如果方法过多switch case过多其效率还需测试 |
静态代理、jdk动态代理、cglib动态代理的更多相关文章
- 代理模式(静态代理、JDK动态代理原理分析、CGLIB动态代理)
代理模式 代理模式是设计模式之一,为一个对象提供一个替身或者占位符以控制对这个对象的访问,它给目标对象提供一个代理对象,由代理对象控制对目标对象的访问. 那么为什么要使用代理模式呢? 1.隔离,客户端 ...
- Spring事务Transactional和动态代理(二)-cglib动态代理
系列文章索引: Spring事务Transactional和动态代理(一)-JDK代理实现 Spring事务Transactional和动态代理(二)-cglib动态代理 Spring事务Transa ...
- 总结两种动态代理jdk代理和cglib代理
动态代理 上篇文章讲了什么是代理模式,为什么用代理模式,从静态代理过渡到动态代理. 这里再简单总结一下 什么是代理模式,给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原 ...
- spring的AOP动态代理--JDK代理和CGLIB代理区分以及注意事项
大家都知道AOP使用了代理模式,本文主要介绍两个代理模式怎么设置以及区别,对原文一些内容进行了引用后加入了自己的理解和更深入的阐述: 一.JDK代理和CGLIB代理的底层实现区别* JDK代理只能 ...
- Java动态代理(二)CGLIB动态代理应用
JDK自从1.3版本开始,就引入了动态代理,JDK的动态代理用起来非常简单,但是它有一个限制,就是使用动态代理的对象必须实现一个或多个接口 .如果想代理没有实现接口的类可以使用CGLIB包. CGLI ...
- Java动态代理(三)——Cglib动态代理
一.Cglib动态代理Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类,使用Cglib即使代理类没有实现任何接口也可以实现动态代理功能.而且,它的运行速度要远远快 ...
- Java之代理(jdk静态代理,jdk动态代理,cglib动态代理,aop,aspectj)
一.概念 代理是什么呢?举个例子,一个公司是卖摄像头的,但公司不直接跟用户打交道,而是通过代理商跟用户打交道.如果:公司接口中有一个卖产品的方法,那么公司需要实现这个方法,而代理商也必须实现这个方法. ...
- java的静态代理、jdk动态代理和cglib动态代理
Java的代理就是客户端不再直接和委托类打交道,而是通过一个中间层来访问,这个中间层就是代理.使用代理有两个好处,一是可以隐藏委托类的实现:二是可以实现客户与委托类之间的解耦,在不修改委托类代码的情况 ...
- 从静态代理,jdk动态代理到cglib动态代理-一文搞懂代理模式
从代理模式到动态代理 代理模式是一种理论上非常简单,但是各种地方的实现往往却非常复杂.本文将从代理模式的基本概念出发,探讨代理模式在java领域的应用与实现.读完本文你将get到以下几点: 为什么需要 ...
- Java代理(静态代理、JDK动态代理、CGLIB动态代理)
Java中代理有静态代理和动态代理.静态代理的代理关系在编译时就确定了,而动态代理的代理关系是在运行期确定的.静态代理实现简单,适合于代理类较少且确定的情况,而动态代理则给我们提供了更大的灵活性. J ...
随机推荐
- linux系统学习
一.linux系统如何配置环境变量 1.在Windows 系统下,很多软件安装都需要配置环境变量,比如 安装 jdk ,如果不配置环境变量,在非软件安装的目录下运行javac 命令,将会报告找不到文件 ...
- ominigraffle实践之画精美流程图+自动导出Xcode工程类图
背景 在平常阅读各种文章时,注意到有的文章中流程图做得特别漂亮.特别羡慕.于是去搜索了下,发现mac平台好评很高的是一款名为ominigraffle的软件.这款软件不仅可以画图,还可以导出Xcode工 ...
- 微信商户H5支付申请不通过被驳回解法,拒绝提示:网站有不实内容或不安全信息
H5支付是指商户在微信客户端外的移动端网页展示商品或服务,用户在前述页面确认使用微信支付时,商户发起本服务呼起微信客户端进行支付.主要用于触屏版的手机浏览器请求微信支付的场景.可以方便从外部浏览器唤起 ...
- Linux两台服务器mysql数据库同步
我们在做web系统部署的时候往往涉及到两台甚至多台数据库的备份,为了数据安全考虑(虽然说到底不过是一堆0 1,但是价值千金啊),所以我们还是乖乖做同步把! 1.准备两台Linux服务器(主.从) 2. ...
- 【WEB自动化测试之控件定位】基于HTML5控件的唯一控件属性定位
一.WEB控件定位是什么 要想弄懂这个问题,我们还是基于实践来学习.我们先来看一条入门级别自动化测试用例的构成. DemoCase:正确用户名和密码登录博客园,登录成功 URL: https:// ...
- C# 压缩、解压文件夹或文件(带密码)
今天梳理一下项目中用到的压缩.解压文件夹或文件的方法,发现因为需求不同,已经用了好几个不同组件.今天就好好整理记录下,别下次遇到需求又重头开始了. DotNetZip DotNetZip是一个开源的免 ...
- h5整体页面布局
只要在每个页面加入这段js他会把每个页面平均分成横向25rem,纵向47rem//页面布局 <script> (function(doc, win) { var docEl = doc.d ...
- render 强大的渲染函数
可以动态的创建节点 可以改变表格中要去换一种形式去展示的列 (未完暂定)
- Java自定义异常的用法
package day162020072701.day1601; /** * @author liuwenlong * @create 2020-07-27 09:25:44 */ @Suppress ...
- 一文带你了解Sql优化
我们后台开发人员每天都难免与数据库打交道,那么你在写sql语句的时候有注重到自己sql的效率吗?当你sql查询速度很慢的时候你有想过是你的sql语句造成的吗?看完这篇文章,我相信你会对sql优化有了一 ...