关于JDK动态代理与Cglib代理

最近有时间学习一下SpringAOP源码,底层用到了代理,大概是这样的:

当需要被代理的类实现了接口,则使用JDK动态代理创建代理对象,增加增强操作执行目标方法

当需要被代理的类未实现接口,则使用Cglib代理创建目标类的子类,增加增强操作执行目标方法

由此可见JDK动态代理的使用条件是 被代理的类必须实现了接口。(接口是什么无关要紧,但是必须实现了接口,生成的代理类对象也是实现了此接口的类)

Cglib代理主要是通过增强字节码,生成目标代理类的子类从而实现代理。所以这里要求目标类不能被final修饰!!!

>>JDK动态代理

1.定义动态代理类 JDKDynamicProxy.java

  1. package com.dfx.study.jdkdynamic;
  2.  
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.Proxy;
  6.  
  7. /**
  8. * JDK动态代理类
  9. * @author Administrator
  10. * 被代理类需要实现一个接口
  11. */
  12. public class JDKDynamicProxy implements InvocationHandler{
  13.  
  14. Object targetObj;//代理对象
  15.  
  16. /**
  17. * 返回代理后的对象
  18. * @param obj 需要被代理的对象
  19. * @return
  20. */
  21. public Object getProxy(Object obj){
  22. this.targetObj = obj;
  23. /*
  24. * 参数解析:
  25. * obj.getClass().getClassLoader() 指定代理类的类加载器
  26. * obj.getClass().getInterfaces() 需要被代理的类所实现的接口数组
  27. * this 当前InvocationHandler对象
  28. */
  29. return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
  30. obj.getClass().getInterfaces(), this);
  31. }
  32.  
  33. /**
  34. * 调用目标对象的方法时,会通过代理对象的invoke方法 再里面执行目标对象的方法
  35. */
  36. @Override
  37. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  38.  
  39. System.out.println("JDK proxy invoke执行前");//在目标方法执行前的增强操作
  40. method.invoke(targetObj, args);//执行目标方法
  41. System.out.println("JDK proxy invoke执行后");//在目标方法执行后的增强操作
  42.  
  43. return null;
  44. }
  45.  
  46. }

2.目标类的接口定义 UserService.java

  1. package com.dfx.study.jdkdynamic;
  2. /**
  3. * 定义接口,用于测试JDK动态代理 生成代理对象
  4. * @author Administrator
  5. *
  6. */
  7. public interface UserService {
  8.  
  9. public void say();
  10. }

3.目标类定义 (实现了UserService接口) UserServiceImpl.java

  1. package com.dfx.study.jdkdynamic;
  2. /**
  3. * 接口的实现类(也就是这次用于测试的需要被代理的类)
  4. * @author Administrator
  5. *
  6. */
  7. public class UserServiceImpl implements UserService{
  8.  
  9. @Override
  10. public void say() {
  11. System.out.println("我是目标类真实输出内容:Hello JDKDynamicProxy!!!");
  12. }
  13.  
  14. }

4.测试代理类 JDKDynamicProxy.java

  1. package com.dfx.study.jdkdynamic;
  2. /**
  3. * JDK动态代理测试类
  4. * @author Administrator
  5. *
  6. */
  7. public class JDKDynamicProxyTest {
  8.  
  9. public static void main(String[] args){
  10.  
  11. JDKDynamicProxy jdkProxy = new JDKDynamicProxy();//创建实现InvocationHandler接口的对象 动态代理实现类对象
  12. UserService userService = (UserService)jdkProxy.getProxy(new UserServiceImpl());//生成代理对象
  13. userService.say();//调用代理对象的say方法
  14.  
  15. }
  16. }

5.测试结果:

  1. JDK proxy invoke执行前
  2. 我是目标类真实输出内容:Hello JDKDynamicProxy!!!
  3. JDK proxy invoke执行后

进一步思考,判断JDK动态代理是不是真的只能代理实现接口的类呢,我们去掉接口试一下,也就是修改 UserServiceImpl.java如下

  1. package com.dfx.study.jdkdynamic;
  2. /**
  3. * 接口的实现类(也就是这次用于测试的需要被代理的类)
  4. * @author Administrator
  5. *
  6. */
  7. public class UserServiceImpl {
  8. public void say() {
  9. System.out.println("我是目标类真实输出内容:Hello JDKDynamicProxy!!!");
  10. }
  11. }

更改测试类 JDKDynamicProxy.java

  1. package com.dfx.study.jdkdynamic;
  2. /**
  3. * JDK动态代理测试类
  4. * @author Administrator
  5. *
  6. */
  7. public class JDKDynamicProxyTest {
  8.  
  9. public static void main(String[] args){
  10.  
  11. JDKDynamicProxy jdkProxy = new JDKDynamicProxy();//创建实现InvocationHandler接口的对象 动态代理实现类对象
  12. //UserService userService = (UserService)jdkProxy.getProxy(new UserServiceImpl());//生成代理对象
  13. UserServiceImpl userService = (UserServiceImpl)jdkProxy.getProxy(new UserServiceImpl());//不通实现接口 生成代理对象
  14. userService.say();//调用代理对象的say方法
  15.  
  16. }
  17. }

测试结果: (会抛出异常,生成的代理类不能被强制转换为我们的目标类UserServiceImpl,所以最后生成的代理类一定是和目标类实现了同一接口的类)

  1. Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.dfx.study.jdkdynamic.UserServiceImpl
  2. at com.dfx.study.jdkdynamic.JDKDynamicProxyTest.main(JDKDynamicProxyTest.java:13)

JDK动态代理总结:

1.代理逻辑的实际处理类一定要实现InvocationHandler(拦截器)接口,在其中的invoke()方法中对目标对象的方法进行增强,接口代理对象的所有方法都会转发到invoke()方法处理

2.实际处理类中有一个Obeject类型的对象表示目标对象,所有实现了接口的要选择此种增强的方法都可以使用这个实际处理类创建代理对象

3.代理对象使通过java反射机制在运行时通过Proxy.newProxyInstance动态生成的

4.JDK动态代理的目标类(也就是需要被代理类)必须要实现了接口,通过JDK动态代理生成的代理类一定是实现了目标类的接口,且代理对象在被调用的时候只能调用接口里的方法。

5.JDK动态代理返回的代理对象只能用接口类型接收,不能用目标类接收。(这里说的比较抽象,下面用代码解释一下)

UserService userService = (UserService) jdkProxy.getProxy(new UserServiceImpl());//生成代理对象  正确

UserServiceImpl userService = (UserServiceImpl) jdkProxy.getProxy(new UserServiceImpl());//生成代理对象  错误

>>Cglib代理

1.定义代理类 CglibProxy.java

  1. package com.dfx.study.cglib;
  2.  
  3. import java.lang.reflect.Method;
  4.  
  5. import org.springframework.cglib.proxy.Enhancer;
  6. import org.springframework.cglib.proxy.MethodInterceptor;
  7. import org.springframework.cglib.proxy.MethodProxy;
  8. /**
  9. * Cglib实现代理
  10. * @author Administrator
  11. *
  12. */
  13. public class CglibProxy implements MethodInterceptor{
  14.  
  15. Enhancer enhancer = new Enhancer();
  16.  
  17. /**
  18. * 得到代理对象 (是被代理对象的子类)
  19. * @param obj
  20. * @return
  21. */
  22. public Object getProxy(Object obj){
  23. enhancer.setSuperclass(obj.getClass());//将需要被代理的类设置为代理类的父类
  24. enhancer.setCallback(this);
  25. Object object = enhancer.create();//创建被代理类的子类对象
  26. return object;
  27. }
  28.  
  29. /**
  30. * 调用目标对象的方法时,会通过代理对象的invoke方法 再里面执行目标对象的方法
  31. */
  32. @Override
  33. public Object intercept(Object obj, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
  34. //System.out.println("Cglib proxy 入参:obj="+obj);
  35. System.out.println("Cglib proxy 入参:method="+method);
  36. System.out.println("Cglib proxy 入参:params="+params);
  37. System.out.println("Cglib proxy 入参:methodProxy="+methodProxy);
  38. System.out.println("Cglib proxy invoke执行前");//在目标方法执行前的增强操作
  39.  
  40. String con = (String) methodProxy.invokeSuper(obj, params);
  41. System.out.println("methodProxy.invokeSuper(obj, params) 执行结果:"+con);
  42.  
  43. System.out.println("Cglib proxy invoke执行后");//在目标方法执行前的增强操作
  44. return null;
  45.  
  46. }
  47.  
  48. }

2.定义需要被代理的目标类 UserService.java (不需要实现其他接口)

  1. package com.dfx.study.cglib;
  2. /**
  3. * 用于Cglib代理的测试类,不需要实现接口
  4. * @author Administrator
  5. *
  6. */
  7. public class UserService {
  8.  
  9. public String say(){
  10. System.out.println("我是目标类真实输出内容:Hello CglibProxy!!!");
  11. return "Cglib";
  12. }
  13. }

3.测试Cglib代理  CglibProxyTest.java

  1. package com.dfx.study.cglib;
  2. /**
  3. * Cglib代理测试类
  4. * @author Administrator
  5. *
  6. */
  7. public class CglibProxyTest {
  8.  
  9. public static void main(String[] args){
  10. CglibProxy cglibProxy = new CglibProxy();
  11. UserService userService = (UserService)cglibProxy.getProxy(new UserService());//得到代理对象
  12. userService.say();
  13. }
  14. }

4.Cglib代理测试结果:

  1. Cglib proxy 入参:method=public java.lang.String com.dfx.study.cglib.UserService.say()
  2. Cglib proxy 入参:params=[Ljava.lang.Object;@5a10411
  3. Cglib proxy 入参:methodProxy=org.springframework.cglib.proxy.MethodProxy@2ef1e4fa
  4. Cglib proxy invoke执行前
  5. 我是目标类真实输出内容:Hello CglibProxy!!!
  6. methodProxy.invokeSuper(obj, params) 执行结果:Cglib
  7. Cglib proxy invoke执行后

进一步证明一下Cglib代理的类不能被final修饰(final修饰的类无法被继承),因为它要通过生成目标类的子类从而实现代理,接下来我们做如下修改 UserService.java

  1. package com.dfx.study.cglib;
  2. /**
  3. * 用于Cglib代理的测试类,不需要实现接口
  4. * @author Administrator
  5. * 添加final修饰类
  6. */
  7. public final class UserService {
  8.  
  9. public String say(){
  10. System.out.println("我是目标类真实输出内容:Hello CglibProxy!!!");
  11. return "Cglib";
  12. }
  13. }

 

测试结果:(抛出异常 无法从final修饰的类中生成子类信息,由此我们可得知如果想要通过Cglib成功代理,那这个类一定不能用final修饰)

  1. Exception in thread "main" java.lang.IllegalArgumentException: Cannot subclass final class class com.dfx.study.cglib.UserService
  2. at org.springframework.cglib.proxy.Enhancer.generateClass(Enhancer.java:446)
  3. at org.springframework.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
  4. at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
  5. at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
  6. at org.springframework.cglib.proxy.Enhancer.create(Enhancer.java:285)
  7. at com.dfx.study.cglib.CglibProxy.getProxy(CglibProxy.java:25)
  8. at com.dfx.study.cglib.CglibProxyTest.main(CglibProxyTest.java:11)

Cglib代理总结:

1.CGLIB动态代理不要求目标对象一定要实现接口;

2.代理逻辑的实际处理类要实现MethodInterceptor接口,在intercept()方法中对目标对象的方法进行增强

3.CGLIB通过Enhancer对象指定代理的目标对象,实现处理逻辑的对象,使用create()在运行期间动态得到代理对象;

综合总结:

1.JDK动态代理要求目标对象一定要实现接口,Cglib则不用.

2.JDK动态代理通过反射机制要动态生成代理类,生成类的过程比较高效.

3.Cglib基于继承来实现代理,代理对象实际上是目标对象的子类,它内部通过第三方类库ASM,加载目标对象类的class文件,修改字节码来生成子类,生成类的过程较低效,但生成类以后的执行很高效,可以通过将ASM生成的类进行缓存来解决生成类过程低效的问题.

 

关于JDK动态代理与Cglib代理的更多相关文章

  1. 设计模式---JDK动态代理和CGLIB代理

    Cglig代理设计模式 /*测试类*/ package cglibProxy; import org.junit.Test; public class TestCglib { @Test public ...

  2. JDK动态代理和 CGLIB 代理

    JDK动态代理和 CGLIB 代理 JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期期间创建一个接口的实现类来完成对目标对象的代理. 代码示例 接口 public interface ...

  3. JDK动态代理和CGLIB代理的区别

    一.原理区别: java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. 而cglib动态代理是利用asm开源包,对代理对象类的class文件 ...

  4. 基于Spring AOP的JDK动态代理和CGLIB代理

    一.AOP的概念  在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的 ...

  5. 动态代理:JDK动态代理和CGLIB代理的区别

    代理模式:代理类和被代理类实现共同的接口(或继承),代理类中存有指向被代理类的索引,实际执行时通过调用代理类的方法.实际执行的是被代理类的方法. 而AOP,是通过动态代理实现的. 一.简单来说: JD ...

  6. JDK动态代理和cglib代理详解

    JDK动态代理 先做一下简单的描述,通过代理之后返回的对象已并非原类所new出来的对象,而是代理对象.JDK的动态代理是基于接口的,也就是说,被代理类必须实现一个或多个接口.主要原因是JDK的代理原理 ...

  7. JDK动态代理和cglib代理

    写一个简单的测试用例,Pig实现了Shout接口 public class MyInvocation implements InvocationHandler { Object k; public M ...

  8. (转)Java动态代理与CGLib代理

    <br>public class UserDAOImpl{ <br><br>    public void save() { <br>        / ...

  9. IT忍者神龟之Java动态代理与CGLib代理

    <br>public class UserDAOImpl{ <br><br>    public void save() { <br>        / ...

  10. SpringAOP-JDK 动态代理和 CGLIB 代理

    在 Spring 中 AOP 代理使用 JDK 动态代理和 CGLIB 代理来实现,默认如果目标对象是接口,则使用 JDK 动态代理,否则使用 CGLIB 来生成代理类. 1.JDK 动态代理 那么接 ...

随机推荐

  1. LInux内核配置过程

    内核版本 linux 2.6.32.2 配置内核的过程 配置内核可以通过执行 make menuconfig 来进行,下面分析该命令的执行流程 执行该目标 %config: scripts_basic ...

  2. eclipse新建maven项目报错Could not resolve arachetype org.apache.maven.archetypes:mmaven-archetype-quickstart:1.1 from any of the configured repositories

    使用eclipse新建maven项目,按下图所示选择后,报错 报错截图 报错详细信息 Could not resolve archetype org.apache.maven.archetypes:m ...

  3. 5个点彻底搞清楚SpringBoot注解

    作者:张伯毅 一.注解(annotations)列表 @SpringBootApplication:包含了@ComponentScan.@Configuration和@EnableAutoConfig ...

  4. NodeJS3-1基础API----Path(路径)

    path 和路径有关的操作 Path(路径)  path 模块提供用于处理文件路径和目录路径的实用工具. 它可以使用以下方式访问 const path = require('path');  1. p ...

  5. 你不知道的JavaScript(上)作用域与闭包

    第一部分 作用域与闭包 第一章 作用域是什么 1.作用域 变量赋值操作会执行两个动作:首先编译器会在当前作用域中声明一个变量(如果之前没有声明过), 然后会在运行时引擎会在作用域中查找该变量,找到就会 ...

  6. 【CHRIS RICHARDSON 微服务系列】事件驱动的数据管理-5

    编者的话 |本文来自 Nginx 官方博客,是「Chris Richardson 微服务」系列的第五篇文章.第一篇文章介绍了微服务架构模式,并且讨论了使用微服务的优缺点:第二和第三篇描述了微服务架构模 ...

  7. sleep方法要求处理中断异常:InterruptedException

    package seday08.thread;/*** @author xingsir * 当一个线程调用sleep方法处于阻塞状态的过程中,这个线程的中断方法interrupt被调用时,则sleep ...

  8. 如何正确使用 Spring Cloud?【上】

    如何更快地交付软件,每周.每天甚至每个小时向用户发布新特性?如何让新员工在入职后就能部署代码?在如此快的节奏下如何保证质量?快,我们应用开发面临的主要挑战,交付越快就越能紧密地收集到用户反馈,从而更有 ...

  9. 12-Factor与云原生

    12-Factor与云原生 云原生应用 今天先到这儿,希望对技术领导力, 企业管理,系统架构设计与评估,团队管理, 项目管理, 产品管理,团队建设 有参考作用 , 您可能感兴趣的文章: 精益IT组织与 ...

  10. AbstractMethodError: abstract method "androidx.databinding.ViewDataBinding androidx.databinding.DataBinderMapper.getDataBinder(androidx.databinding.DataBindingComponent, android.view.View, int)"

    混淆导致的数据绑定库错误 问题摘要 AbstractMethodError: abstract method "androidx.databinding.ViewDataBinding an ...