代理模式中代理类和被代理类提供同样的行为,比如实现同一个接口,代理类内部持有对被代理类的引用,在代理类中调用被代理类的方法。

访问者通过调用代理类的方法进而调用被代理类的方法。对调用者来说似乎没有差别,但是在代理类的方法里调用被代理类的方法前后可以增加额外的功能逻辑,这样就可以实现目标方法的增强效果。如下图的结构:

静态代理通过编写代理类的静态代码实现代理。测试代码如下:

//测试业务接口
public interface BizService{void querySkuStock();
}//测试业务接口实现类
public class BizServiceImpl implements BizService {@Overridepublic void querySkuStock(){//doSomething...}
} //静态代理类
public class BizServiceProxy implements BizService {private BizServiceImpl bizServiceImpl;public BizServiceProxy(BizServiceImpl bizServiceImpl){this.bizService= bizService;}public void querySkuStock(){log.info("querySkuStock开始...");bizServiceImpl.querySkuStock();log.info("querySkuStock结束...");}
}//测试调用
@Test
public void testBizServiceProxy(){BizServiceProxy bizServiceProxy = new BizServiceProxy(new BizServiceImpl());bizServiceProxy.querySkuStock();
}

这种静态代理的方式存在一个最大的问题就是对目标方法的调用逻辑写死在代理类方法里面,也就是每一个被代理类的方法都是不同的,需要创建同样个数的代理类才能实现对不同被代理类的调用。为了解决这个问题,动态代理应运而生,它的解决思路就是将对目标类的调用提取出来抽象成一个接口,在这个接口的实现类里面去实现具体的调用逻辑。结构如下图:

动态代理中用的比较多的两种:JDK动态代理CGLIB动态代理

JDK动态代理

使用JDK的java.lang.reflect.Proxy类的newProxyInstance方法实现的代理。用一个测试接口看下具体实现:

//测试接口
public interface JdkProxyDemoService {     void queryTrs();
}//测试接口的实现类
@Service
public class JdkProxyDemoServiceImpl implements JdkProxyDemoService {public void queryTrs() {log.info("业务方法执行...");}
}//自定义调用处理器
public class LycJdkProxyInvocationHandler implements InvocationHandler {private Object targetObj;public LycJdkProxyInvocationHandler(Object targetObj) {this.targetObj = targetObj;}//proxy 被代理对象     method 目标对象中的方法对象    args 方法对象的参数    @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("执行之前...");Object rtnObj = method.invoke(targetObj, args);System.out.println("执行之后...");return rtnObj;}
}//测试调用代理类的方法
public class LycAspectTest {public static void main(String[] args) {JdkProxyDemoService targetService = new JdkProxyDemoServiceImpl();InvocationHandler h = new LycJdkProxyInvocationHandler(targetService); Object proxyInstance = Proxy.newProxyInstance(targetService.getClass().getClassLoader(),targetService.getClass().getInterfaces(), h); JdkProxyDemoService jdkProxyDemoService = (JdkProxyDemoService) proxyInstance;jdkProxyDemoService.queryTrs();}
}

idea中通过配置VM参数可以将动态生成的代理类的字节码文件保存到本地,方便观察分析代理类的结构。

JDK8及以前版本:-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
JDK8以后版本:-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true

配置好后再次执行上面的测试代码,在项目目录下就会生成代理类的目录

可以看到JDK动态代理生成的代理类默认是继承Proxy,并实现了被代理类实现的接口,在实现的接口方法里面调用InvocationHandler的invoke方法,这就是前面说的将对目标方法的调用抽象到接口中,在接口的实现类里面定义具体的调用逻辑,这里就可以增加额外的功能,对目标方法进行增强。

从代理类的结构也可以看出来,为什么说JDK动态代理是对接口的代理,因为Java是单继承的,这个代理类默认已经继承了Proxy也就不能再继承其他类了。

JDK动态代理除了需要被代理类是接口类还存在一个问题,因为它是实现被代理类实现的接口方法,如果被代理类有个私有的方法,这种方式生成的代理类就无法对这个私有方法进行代理了,对于这些局限性CGLIB动态代理就能很好的解决。

CGLIB动态代理

对类进行代理。code generator library代码生成库,动态的生成字节码对象。同样通过一个测试类来看下具体实现:

//测试类
public class CglibProxyDemo {public void queryCost() {log.info("业务方法执行查询成本...");}
}//测试调用处理
public class LycCglibProxyInterceptor implements MethodInterceptor {// proxy:代理对象 method:目标对象中的方法 args:目标对象中的方法 methodProxy:代理对象中的代理方法对象@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("执行之前....");Object rtnObj = methodProxy.invokeSuper(proxy, args);System.out.println("执行之后....");return rtnObj;}
}//测试代理类生成及调用
public class LycAspectTest {public static void main(String[] args) {// 创建空的字节码对象Enhancer enhancer = new Enhancer();// 设置字节码对象的父类也就是目标类enhancer.setSuperclass(CglibProxyDemo.class);//创建回调对象Callback callback = new LycCglibProxyInterceptor();@1// 设置字节码对象的回调方法enhancer.setCallback(callback);// 得到代理对象CglibProxyDemo cglibProxyDemo = (CglibProxyDemo) enhancer.create();// 调用方法cglibProxyDemo.queryCost();    }
}

同样的通过编码的方式也可以将动态生成的代理类的字节码文件保存到本地,代码如下:

//保存生成的字节码文件
byte[] generate = enhancer.getStrategy().generate(enhancer);
FileOutputStream fileOutputStream = new FileOutputStream(
new File("C:\\个人文档\\learing\\lyccglibtest.class"));
fileOutputStream.write(generate);
fileOutputStream.flush();
fileOutputStream.close();

执行上面的LycAspectTest 测试代码,本地项目目录下也会生成的字节码文件。反编译观察分析它的结构:

可以看到CGLIB生成的代理类是通过创建一个新的类继承被代理类,并重写了父类的方法,在重写的方法里面调用的接口的intercept方法,这个就是前面定义的调用目标类的目标方法的具体逻辑,在这里面可以增加额外的逻辑实现对目标方法的增强。

这种通过创建子类并重写父类方法的方式就要求被代理的方法不能被final修饰,否则子类无法重写也就无法进行代理。

动态代理一个比较重要的应用就是在Spring框架中AOP基于动态代理在程序运行期间对目标方法进行增强。
在IOC阶段通过跟踪代码可以知道,对于开启切面编程或者开启事务管理的配置的项目,在初始化Bean的前后会调用BeanPostProcessor的两个方法,在初始化Bean之前调用postProcessBeforeInitialization方法解析切面类生成通知类,在初始化Bean之后调用postProcessAfterInitialization判断匹配切面通知类以及增强器,如果有匹配到就会生成代理类,代码如下:

两种代理的核心逻辑方法:

JdkDynamicAopProxy. invoke(Object proxy, Method method, Object[] args)

// 获取目标类
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 获取方法的拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {// 如果没有拦截器直接反射执行目标方法Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {// 将拦截器链组装成MethodInvocation**MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);**// 调用proceed()执行拦截器retVal = **invocation.proceed();**
}

CglibAopProxy. intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)

//获取目标类
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 获取拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {// 直接反射执行Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = invokeMethod(target, method, argsToUse, methodProxy);
}else {// 调用拦截器retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
// 返回值
retVal = processReturnType(proxy, target, method, retVal);
return retVal;

CGLIB代理里面CglibMethodInvocation.proceed()逻辑是执行super.proceed();而CglibMethodInvocation 继承ReflectiveMethodInvocation
这样看来AOP中JDK动态代理和CGLIB动态代理最终都是调用的ReflectiveMethodInvocation.proceed(),这个类的方法具体逻辑如下:

对于@Aspect切面类生成的通知类会执行if里面的代码,而@Transaction事务管理中事务增强器TransactionInterceptor会执行else里面的代理。看下事务管理如何进行事务增强的:

TransactionInterceptor类:public Object invoke(final MethodInvocation invocation) throws Throwable {Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
TransactionAspectSupport类:
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable {TransactionAttributeSource tas = getTransactionAttributeSource();final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);final PlatformTransactionManager tm = determineTransactionManager(txAttr);final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); @1 :开启事务Object retVal = null;try {retVal = invocation.proceedWithInvocation(); @2: 调用拦截器方法执行} catch (Throwable ex) {completeTransactionAfterThrowing(txInfo, ex);@3: 异常回滚处理throw ex;} finally {cleanupTransactionInfo(txInfo);@4:最终处理}commitTransactionAfterReturning(txInfo);@5:事务提交return retVal;}
}

AOP中实现增强就是生成代理类的时候定义的调用目标类的目标方法的逻辑,将通知类或者增强器封装成拦截器链,采用责任链模式首先依次调用拦截器的方法执行拦截器内部逻辑,所有拦截器都调用后最后再调用目标方法,然后再依次返回到每个拦截器最终将执行结果返回出去。

JDK动态代理和CGLIB动态代理相关推荐

  1. 什么是代理模式?代理模式有什么用?通过一个小程序分析静态代理和动态代理。自己简单实现动态代理。JDK动态代理和CGLIB动态代理的区别。

    1. 代理模式有什么用 ①功能增强,在实现目标功能的基础上,又增加了额外功能.就像生活中的中介一样,他跟两边客户会有私下的交流. ②控制访问,代理不让用户直接和目标接触.就像中间商一样,他们不会让我们 ...

  2. Java动态代理的两种实现方法:JDK动态代理和CGLIB动态代理

    Java动态代理的两种实现方法:JDK动态代理和CGLIB动态代理 代理模式 JDK动态代理 CGLIB动态代理 代理模式 代理模式是23种设计模式的一种,指一个对象A通过持有另一个对象B,可以具有B ...

  3. 利用代码分别实现jdk动态代理和cglib动态代理_面试之动态代理

    大家好!我是CSRobot,从今天开始,我将会发布一些技术文章,内容就是结合春招以来的面试所遇到的问题进行分享,首先会对知识点进行一个探讨和整理,在最后会给出一些面试题并作出解答,希望可以帮助到大家! ...

  4. JDK动态代理和CGLib动态代理简单演示

    JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例. 一.首先我们进行JDK动态代理的演示. 现在我们有一个简单的业务接口Saying,如下: package te ...

  5. JAVA 进阶篇 动态代理 JDK动态代理和CGlib动态代理

    JDK动态代理和CGlib动态代理 JDK动态代理: 利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. CGlib动态代理: 利用ASM(开源的Java ...

  6. jdk动态代理和cglib动态代理实现及区别

    代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能. 代理模式又分为:静态代理.jdk动态代 ...

  7. Java两种动态代理JDK动态代理和CGLIB动态代理

    目录 代理模式 JDK动态代理 cglib动态代理 测试 代理模式 代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式.为了对外开放协议,B往往实现了一个 ...

  8. Spring AOP之---基于JDK动态代理和CGLib动态代理的AOP实现

    AOP(面向切面编程)是OOP的有益补充,它只适合那些具有横切逻辑的应用场合,如性能监测,访问控制,事物管理,日志记录等.至于怎么理解横切逻辑,敲完实例代码也就明白了. 为什么要使用AOP,举个栗子: ...

  9. Java中动态代理的两种方式JDK动态代理和cglib动态代理以及区别

    视频功能审核通过了,可以看视频啦!记得点关注啊~ 注意:因为网络原因,视频前一两分钟可能会比较模糊,过一会儿就好了 记得点关注啊,视频里的wx二维码失效了,wx搜索:"聊5毛钱的java&q ...

  10. 【Spring6】| GoF之代理模式(JDK动态代理和CGLIB动态代理)

    目录 一:GoF之代理模式 1. 对代理模式的理解 2. 静态代理 3. 动态代理 3.1 JDK动态代理 3.2 CGLIB动态代理 一:GoF之代理模式 1. 对代理模式的理解 生活场景1:牛村的 ...

最新文章

  1. 注释参数opengl的surfaceview使用findViewById返回null问题解决
  2. centos6 安装 mantisbt-1.2.8 —— (5)Mantisbt-1.2.8在contos上的安装
  3. Java培训学习笔记分享:SpringMVC框架
  4. elasticsearch Java API 之Delete By Query API
  5. 警惕“***性社工”现象
  6. aix 查看内存,CPU 配置信息
  7. 创建sdcard.img时,提示permission dennid
  8. Nodejs的url模块方法
  9. Linux审计sudo
  10. 如何在一个元素上有多个CSS过渡?
  11. 计算机操作员(高级)理论知识考试卷,计算机操作员高级试题
  12. MATLAB中的单相与三相dq变换模块
  13. [实战]黑帽SEO的RayFile排名做法
  14. mysql端口establish_sqlserver提示The Network Adapter could not establish the con
  15. 新冠疫情全球数据可视化
  16. DGL系列之(二):使用DGL实现GCN
  17. 披荆斩棘,蜕变从来学吧这里开始
  18. 自动化1121和1122班学生链接
  19. 郑州大学计算机新媒体专业介绍,专业介绍:网络与新媒体专业
  20. springboot计算机专业毕业设计优秀级别最新题目

热门文章

  1. 数理统计复习笔记六——Pearson卡方拟合优度检验
  2. 算法分析与设计实验报告——0-1背包问题的动态规划算法实现
  3. LCD1602----LiquidCrystal库的使用2
  4. 基于飞桨实现BigGAN生成动漫图像——为艺术创作赋能
  5. 高频实验设备,高频电子线路信号发生器实验箱
  6. 通信原理及matlab仿真实验指导书,通信原理实验指导书——2015.pdf
  7. SQL Server 2019 安装教程(详细免费,自定义安装)
  8. java 汉字转拼音(解决多音字问题)
  9. android wifi检测呼吸,WiFi已经逆天了 现在能检测到你的呼吸
  10. python作函数图像_如何使用python的matplotlib模块画余切函数图像