JDK动态代理

动态代理详解

1 学习动态代理的目的
  动态代理技术都是在框架中使用,例如:Struts1、Struts2、Spring和Hibernate中都使用了动态代理技术。如果你不想自己写个框架,那么你基本上是用上不动态代理技术的。学习动态代理技术的目的是为了更好的理解框架内部的原理,也就是说是为了将来学习框架打基础!动态代理技术有点难度!而且明白了动态代理技术可能一时也想不到他适合在什么情况下使用它。这些东西都会在学习框架时渐渐明白。

2 运行时实现指定的接口
  想实现某个接口,你需要写一个类,然后在类名字的后面给出“implements”XXX接口。这才是实现某个接口:

public interface MyInterface {
  void fun1();
  void fun2();
}
复制代码
public class MyInterfaceImpl implements MyInterface {
  public void fun1() {
    System.out.println("fun1()");
  }
  public void fun2() {
    System.out.println("fun2()");
  }
}
复制代码

  上面的代码对我们来说没有什么新鲜感,我们要说的是动态代理技术可以通过一个方法调用就可以生成一个对指定接口的实现类对象。

Class[] cs = {MyInterface.class};
MyInterface mi = (MyInterface)Proxy.newProxyInstance(loader, cs, h);

  上面代码中,Proxy类的静态方法newProxyInstance()方法生成了一个对象,这个对象实现了cs数组中指定的接口。没错,返回值mi是MyInterface接口的实现类。你不要问这个类是哪个类,你只需要知道mi是MyInterface接口的实现类就可以了。你现在也不用去管loader和h这两个参数是什么东东,你只需要知道,Proxy类的静态方法newProxyInstance()方法返回的方法是实现了指定接口的实现类对象,甚至你都没有看见实现类的代码。
  动态代理就是在运行时生成一个类,这个类会实现你指定的一组接口,而这个类没有.java文件,是在运行时生成的,你也不用去关心它是什么类型的,你只需要知道它实现了哪些接口即可。

3 newProxyInstance()方法的参数
  Proxy类的newInstance()方法有三个参数:
    ClassLoader loader:它是类加载器类型,你不用去理睬它,你只需要知道怎么可以获得它就可以了:MyInterface.class.getClassLoader()就可以获取到ClassLoader对象,没错,只要你有一个Class对象就可以获取到ClassLoader对象;
    Class[] interfaces:指定newProxyInstance()方法返回的对象要实现哪些接口,没错,可以指定多个接口,例如上面例子只我们只指定了一个接口:Class[] cs = {MyInterface.class};
    InvocationHandler h:它是最重要的一个参数!它是一个接口!它的名字叫调用处理器!你想一想,上面例子中mi对象是MyInterface接口的实现类对象,那么它一定是可以调用fun1()和fun2()方法了,难道你不想调用一下fun1()和fun2()方法么,它会执行些什么东东呢?其实无论你调用代理对象的什么方法,它都是在调用InvocationHandler的invoke()方法!

复制代码
public static void main(String[] args) {
  Class[] cs = {MyInterface.class};
  ClassLoader loader = MyInterface.class.getClassLoader();
  InvocationHandler h = new InvocationHandler() {
    public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
      System.out.println("无论你调用代理对象的什么方法,其实都是在调用invoke()...");
      return null;
    }
  };
  MyInterface mi = (MyInterface)Proxy.newProxyInstance(loader, cs, h);
  mi.fun1();
  mi.fun2();
}
复制代码

  InvocationHandler接口只有一个方法,即invoke()方法!它是对代理对象所有方法的唯一实现。也就是说,无论你调用代理对象上的哪个方法,其实都是在调用InvocationHandler的invoke()方法。

  想象中的类:

复制代码
class X implements MyInterface {
  private InvocationHandler h;
  public X(InvocationHandler h) {
    this.h = h;
  }
  public void fun1() {
    h.invoke();
  }
  public void fun2() {
    h.invoke();
  }
}
复制代码

  注意,X类是我们用来理解代理对象与InvocationHandler之间的关系的,但它是不存在的类。是我们想象出来的!也就是说,它是用来说明,无论你调用代理对象的哪个方法,最终调用的都是调用处理器的invoke()方法。

4 InvocationHandler的invoke()方法
  InvocationHandler的invoke()方法的参数有三个:
    Object proxy:代理对象,也就是Proxy.newProxyInstance()方法返回的对象,通常我们用不上它;
    Method method:表示当前被调用方法的反射对象,例如mi.fun1(),那么method就是fun1()方法的反射对象;
    Object[] args:表示当前被调用方法的参数,当然mi.fun1()这个调用是没有参数的,所以args是一个零长数组。
  最后要说的是invoke()方法的返回值为Object类型,它表示当前被调用的方法的返回值,当然mi.fun1()方法是没有返回值的,所以invoke()返回的就必须是null了。

复制代码
public static void main(String[] args) {
  Class[] cs = {MyInterface.class};
  ClassLoader loader = MyInterface.class.getClassLoader();
  InvocationHandler h = new InvocationHandler() {
    public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
      System.out.println("当前调用的方法是:" + method.getName());
      return null;
    }
  };
  MyInterface mi = (MyInterface)Proxy.newProxyInstance(loader, cs, h);
  mi.fun1();
  mi.fun2();
}
//当前调用的方法是:fun1
//当前调用的方法是:fun2
复制代码

5 动态代理的用途
  动态代理的用途与装饰模式很相似,就是为了对某个对象进行增强。所有使用装饰者模式的案例都可以使用动态代理来替换。
  下面我们用一个例子来说明动态代理的用途!
  我们来写一个Waiter接口,它只有一个serve()方法。MyWaiter是Waiter接口的实现类:

public interface Waiter {
  public void serve();
}
public class MyWaiter implements Waiter {
  public void serve() {
    System.out.println("服务...");
  }
}

  现在我们要对MyWaiter对象进行增强,要让它在服务之前以及服务之后添加礼貌用语,即在服务之前说“您好!”,在服务之后说:“很高兴为您服务!”。

复制代码
public class MainApp1 {
  public static void main(String[] args) {
    ClassLoader loader = MainApp1.class.getClassLoader();
    Class[] cs = {Waiter.class};
    Waiter target = new MyWaiter();
    MyInvocationHandler h = new MyInvocationHandler(target);
    Waiter waiter = (Waiter)Proxy.newProxyInstance(loader, cs, h);
    waiter.serve();
  }
}
class MyInvocationHandler implements InvocationHandler {
  public Waiter target;
  public MyInvocationHandler(Waiter target) {
    this.target = target;
  }
  public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
    System.out.println("您好!");
    Object result = method.invoke(target, args);
    System.out.println("很高兴为您服务!");
    return result;
  }
}
复制代码

 实例(AOP):

复制代码
 1 import org.junit.Test;
 2 /*
 3  * 目标是让目标对象和增强都可以切换!
 4  */
 5 public class Demo3 {
 6     @Test
 7     public void fun1() {
 8         ProxyFactory factory = new ProxyFactory();//创建工厂
 9         factory.setTargetObject(new ManWaiter());//设置目标对象
10         factory.setBeforeAdvice(new BeforeAdvice() {//设置前置增强
11             public void before() {
12                 System.out.println("您好不好!");
13             }
14         });
15         
16         factory.setAfterAdvice(new AfterAdvice() {//设置后置增强
17             public void after() {
18                 System.out.println("再见不见!");
19             }
20         });
21         Waiter waiter = (Waiter)factory.createProxy();
22         waiter.shouQian();
23     }
24 }
复制代码
复制代码
 1 import java.lang.reflect.InvocationHandler;
 2 import java.lang.reflect.Method;
 3 import java.lang.reflect.Proxy;
 4 
 5 /**
 6  * 它用来生成代理对象
 7  * 它需要所有的参数
 8  * * 目标对象
 9  * * 增强
10  * @author cxf
11  */
12 /**
13  * 1. 创建代理工厂
14  * 2. 给工厂设置三样东西:
15  *   * 目标对象:setTargetObject(xxx);
16  *   * 前置增强:setBeforeAdvice(该接口的实现)
17  *   * 后置增强:setAfterAdvice(该接口的实现)
18  * 3. 调用createProxy()得到代理对象
19  *   * 执行代理对象方法时:
20  *   > 执行BeforeAdvice的before()
21  *   > 目标对象的目标方法
22  *   > 执行AfterAdvice的after()
23  * @author cxf
24  *
25  */
26 public class ProxyFactory {
27     private Object targetObject;//目标对象
28     private BeforeAdvice beforeAdvice;//前置增强
29     private AfterAdvice afterAdvice;//后置增强
30     /**
31      * 用来生成代理对象
32      * @return
33      */
34     public Object createProxy() {
35         /*
36          * 1. 给出三大参数
37          */
38         ClassLoader loader = this.getClass().getClassLoader();
39         Class[] interfaces = targetObject.getClass().getInterfaces();
40         InvocationHandler h = new InvocationHandler() {
41             public Object invoke(Object proxy, Method method, Object[] args)
42                     throws Throwable {
43                 /*
44                  * 在调用代理对象的方法时会执行这里的内容
45                  */
46                 // 执行前置增强
47                 if(beforeAdvice != null) {
48                     beforeAdvice.before();
49                 }
50                 
51                 Object result = method.invoke(targetObject, args);//执行目标对象的目标方法
52                 // 执行后置增强
53                 if(afterAdvice != null) {
54                     afterAdvice.after();
55                 }
56                 
57                 // 返回目标对象的返回值
58                 return result;
59             }
60         };
61         /*
62          * 2. 得到代理对象
63          */
64         Object proxyObject = Proxy.newProxyInstance(loader, interfaces, h);
65         return proxyObject;
66     }
67     
68     
69     public Object getTargetObject() {
70         return targetObject;
71     }
72     public void setTargetObject(Object targetObject) {
73         this.targetObject = targetObject;
74     }
75     public BeforeAdvice getBeforeAdvice() {
76         return beforeAdvice;
77     }
78     public void setBeforeAdvice(BeforeAdvice beforeAdvice) {
79         this.beforeAdvice = beforeAdvice;
80     }
81     public AfterAdvice getAfterAdvice() {
82         return afterAdvice;
83     }
84     public void setAfterAdvice(AfterAdvice afterAdvice) {
85         this.afterAdvice = afterAdvice;
86     }
87 }
复制代码
复制代码
1 public class ManWaiter implements Waiter {
2     public void serve() {
3         System.out.println("服务中...");
4     }
5     
6     public void shouQian() {
7         System.out.println("混蛋,给我钱!");
8     }
9 }
复制代码
1 // 服务员
2 public interface Waiter {
3     // 服务
4     public void serve();
5     public void shouQian();
6 }
1 public interface AfterAdvice {
2     public void after();
3 }
复制代码
1 /**
2  * 前置增强
3  * @author cxf
4  *
5  */
6 public interface BeforeAdvice {
7     public void before();
8 }
<think>好的,我现在要详细理解一下JDK动态代理。首先,用户可能已经知道代理模式的基本概念,但动态代理和静态代理有什么区别呢?动态代理是在运行时生成代理类,而静态代理需要手动编写代理类。那JDK动态代理具体是怎么实现的呢? 我记得JDK动态代理主要依赖于java.lang.reflect.Proxy类和InvocationHandler接口。Proxy类用来创建代理对象,而InvocationHandler负责处理代理对象的方法调用。用户可能需要知道具体的实现步骤,比如如何定义接口、实现InvocationHandler,以及使用Proxy.newProxyInstance方法生成代理对象。 不过,可能有些细节我记不太准,比如Proxy.newProxyInstance的参数顺序是否正确。应该是类加载器、接口数组和InvocationHandler实例。需要确认是否正确。还有,JDK动态代理只能代理接口,不能代理类,这点很重要,用户可能想知道为什么,或者有没有其他方式代理类,比如CGLIB。 另外,InvocationHandler的invoke方法参数包括代理对象、Method对象和参数数组。在实现时需要注意不要直接在invoke方法中调用代理对象的方法,否则会导致递归调用,出现栈溢出。这点需要提醒用户注意。 可能用户还关心动态代理的应用场景,比如Spring AOP中的使用,或者日志记录、事务管理、权限控制等横切关注点的处理。这时候可以举一个简单的例子,比如记录方法执行时间的日志代理。 另外,需要提到动态代理的优缺点。优点是无需手动编写代理类,减少代码冗余;缺点是不能代理类,只能代理接口,这可能会限制某些使用场景。这时候可以提到CGLIB库作为补充,它通过继承方式代理类,但在final类或方法上有局限性。 最后,是否应该比较一下JDK动态代理和CGLIB的区别?比如JDK基于接口,CGLIB基于继承,性能方面可能在不同版本中有差异,但现在可能差别不大。不过这可能超出用户当前的问题范围,可以简要提及。 总结回答的结构应该是:定义、实现步骤、关键类和接口、示例代码、应用场景、优缺点以及与其他技术的对比。需要确保示例代码正确,比如正确使用Proxy.newProxyInstance,正确实现InvocationHandler,并在invoke方法中添加增强逻辑。 可能还要检查一下术语是否正确,比如“横切关注点”是否准确,或者有没有拼写错误。另外,示例代码中的类名和方法名是否符合Java规范,比如接口和实现类的命名是否清晰,日志输出是否正确。 最后,确保语言口语化,避免过于技术化的表达,让用户容易理解。分步骤解释,逻辑清晰,让用户能够跟着步骤实现一个简单的动态代理例子。</think>关于JDK动态代理,我来分步骤为您详细解释: 1️⃣ **基本原理** JDK动态代理是Java原生支持的代理技术,通过在**运行时动态生成代理类**来实现功能增强。与需要手动编写代理类的静态代理不同,它就像"魔法师"一样在程序运行期间自动生成代理对象。 2️⃣ **核心组件** - `java.lang.reflect.Proxy`:生成代理类的工厂类 - `java.lang.reflect.InvocationHandler`:方法调用处理器(实现增强逻辑) 3️⃣ **实现步骤** 1. 定义业务接口 ```java public interface UserService { void saveUser(String username); } ``` 2. 实现目标类 ```java public class UserServiceImpl implements UserService { @Override public void saveUser(String username) { System.out.println("保存用户:" + username); } } ``` 3. 编写调用处理器 ```java public class LogHandler implements InvocationHandler { private final Object target; // 被代理对象 public LogHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("【日志】方法开始执行:" + method.getName()); long start = System.currentTimeMillis(); Object result = method.invoke(target, args); // 调用原始方法 long end = System.currentTimeMillis(); System.out.println("【日志】方法执行耗时:" + (end - start) + "ms"); return result; } } ``` 4. 生成代理对象 ```java public static void main(String[] args) { UserService target = new UserServiceImpl(); UserService proxy = (UserService) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new LogHandler(target) ); proxy.saveUser("张三"); // 通过代理调用方法 } ``` 4️⃣ **运行结果** ``` 【日志】方法开始执行:saveUser 保存用户:张三 【日志】方法执行耗时:3ms ``` 5️⃣ **特点分析** - ✅ 优势: - 无需手动编写代理类 - 可复用增强逻辑(如日志、事务等) - 符合开闭原则(对扩展开放,修改关闭) - ❌ 限制: - 只能代理接口(无法代理具体类) - 性能开销略高于直接调用(反射机制导致) 6️⃣ **应用场景** - 日志记录 - 事务管理 - 权限校验 - 性能监控 - 缓存处理 7️⃣ **与CGLIB对比** | | JDK动态代理 | CGLIB | |------------------|---------------------|---------------------| | 代理方式 | 基于接口 | 基于继承(子类化) | | 速度 | 较快(JDK8+优化) | 稍慢(需生成字节码)| | 依赖 | 无需额外库 | 需引入CGLIB库 | | 目标类限制 | 必须实现接口 | 不能是final类 | 📌 **最佳实践**:Spring框架默认优先使用JDK动态代理,当目标类没有接口时自动切换为CGLIB代理。 这种动态代理机制正是Spring AOP等框架实现面向切面编程的核心技术基础,通过它我们可以优雅地实现各种横切关注点的统一处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值