介绍静态代理

代理模式在不改变原始类(或者叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。
下面我们通过代码来解释以下这句话。
具体的实现代码如下所示:

  1. /**
  2. * 原始类(或者称为被代理类)
  3. * UserController 类只负责业务功能
  4. */
  5. public class UserController implements IUserController {
  6. public String login() {
  7. // 业务逻辑
  8. return "login() 的返回值";
  9. }
  10. public String register() {
  11. // 业务逻辑
  12. return "register() 的返回值";
  13. }
  14. }
  1. /**
  2. * 代理类
  3. * UserControllerProxy 负责在业务代码执行前后附加其他逻辑代码,并通过委托的方式调用原始类来执行业务代码
  4. */
  5. public class UserControllerProxy implements IUserController {
  6. IUserController userController;
  7. public UserControllerProxy(IUserController userController) {
  8. this.userController = userController;
  9. }
  10. @Override
  11. public String login() {
  12. System.out.println("在 login() 业务代码执行前,附加的其他逻辑代码");
  13. String login = userController.login();
  14. System.out.println("在 login() 业务代码执行后,附加的其他逻辑代码");
  15. return login;
  16. }
  17. @Override
  18. public String register() {
  19. System.out.println("在 register() 业务代码执行前,附加的其他逻辑代码");
  20. String register = userController.register();
  21. System.out.println("在 register() 业务代码执行后,附加的其他逻辑代码");
  22. return register;
  23. }
  24. }
  1. public interface IUserController {
  2. public String login();
  3. public String register();
  4. }
  1. public static void main(String[] args) {
  2. UserControllerProxy userControllerProxy = new UserControllerProxy(new UserController());
  3. userControllerProxy.login();
  4. userControllerProxy.register();
  5. /*
  6. 执行结果打印如下:
  7. 在 login() 业务代码执行前,附加的其他逻辑代码
  8. 在 login() 业务代码执行后,附加的其他逻辑代码
  9. 在 register() 业务代码执行前,附加的其他逻辑代码
  10. 在 register() 业务代码执行后,附加的其他逻辑代码
  11. */
  12. }

代理类 UserControllerProxy 和原始类 UserController 实现相同的接口 IUserController。

  • UserController 类只负责业务功能。
  • 代理类 UserControllerProxy 负责在业务代码执行前后附加其他逻辑代码,并通过委托的方式调用原始类来执行业务代码。

上面的代码在不改变原始类(UserControler 类)代码的情况下,通过引入代理类(UserControllerProxy 代理类),来给原始类附加功能。
下面解释一下委托的意思,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。
上面的代码中,两个对象(UserControler 的实例和 UserControllerProxy 的实例)参与处理同一个请求,接受请求的对象(UserControllerProxy 的实例)将请求委托给另一个对象(UserControler 的实例)来处理。


上面代码这种实现代理模式的前提是,原始类已经实现了某接口,或者原始类没有定义接口,但是我们有权修改原始类让它实现某接口。
但是,如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的(比如它来自一个第三方的类库),我们也没办法直接修改原始类,给它重新定义一个接口。在这种情况下,我们该如何实现代理模式呢?
在这种情况下,我们可以让代理类继承原始类,具体的实现代码如下所示:

  1. /**
  2. * 原始类(或者称为被代理类)
  3. * UserController 类只负责业务功能
  4. */
  5. public class UserController {
  6. public String login() {
  7. // 业务逻辑
  8. return "login() 的返回值";
  9. }
  10. public String register() {
  11. // 业务逻辑
  12. return "register() 的返回值";
  13. }
  14. }
  1. /**
  2. * 代理类
  3. * UserControllerProxy 负责在业务代码执行前后附加其他逻辑代码,并通过委托的方式调用原始类来执行业务代码
  4. */
  5. public class UserControllerProxy extends UserController {
  6. @Override
  7. public String login() {
  8. System.out.println("在 login() 业务代码执行前,附加的其他逻辑代码");
  9. String login = super.login();
  10. System.out.println("在 login() 业务代码执行后,附加的其他逻辑代码");
  11. return login;
  12. }
  13. @Override
  14. public String register() {
  15. System.out.println("在 register() 业务代码执行前,附加的其他逻辑代码");
  16. String register = super.register();
  17. System.out.println("在 register() 业务代码执行后,附加的其他逻辑代码");
  18. return register;
  19. }
  20. }
  1. public static void main(String[] args) {
  2. UserControllerProxy userControllerProxy = new UserControllerProxy();
  3. userControllerProxy.login();
  4. userControllerProxy.register();
  5. /*
  6. 执行结果打印如下:
  7. 在 login() 业务代码执行前,附加的其他逻辑代码
  8. 在 login() 业务代码执行后,附加的其他逻辑代码
  9. 在 register() 业务代码执行前,附加的其他逻辑代码
  10. 在 register() 业务代码执行后,附加的其他逻辑代码
  11. */
  12. }

介绍动态代理

刚刚的代码实现实际上存在一定的问题:

  • 一方面,我们需要在代理类中,将原始类的所有的方法,都重新实现一遍,并且为每个方法都附加相似的代码逻辑(附加功能)。
  • 另一方面,如果要添加附加功能的类有不止一个,我们需要针对每个类都创建一个代理类。

简单来说就是,静态代理模式可能存在太多重复代码的问题。那这个问题怎么解决呢?
我们可以使用动态代理来解决这个问题。


动态代理就是,我们不事先为原始类创建代理类,而是在程序运行的过程中,动态的为原始类创建代理类。
在 Java 中,动态代理有很多种实现方式。其中两种最常见的实现方式是:

  • JDK 自身提供的动态代理;
  • cglib 动态代理。

JDK 自身提供的动态代理,主要利用了 Java 的反射机制。
cglib 动态代理,利用传说中更高性能的字节码操作机制。
下面我们逐一介绍 JDK 自身提供的动态代理 和 cglib 动态代理的实现。

JDK 动态代理

我们用 JDK 自身提供的动态代理来实现上面的功能,具体的代码实现如下所示:

  1. /**
  2. * 原始类(或者称为被代理类)
  3. * UserController 类只负责业务功能
  4. */
  5. public class UserController implements IUserController {
  6. public String login() {
  7. // 业务逻辑
  8. return "login() 的返回值";
  9. }
  10. public String register() {
  11. // 业务逻辑
  12. return "register() 的返回值";
  13. }
  14. }
  1. public class ProxyFactory {
  2. public Object newProxyInstance(Object proxied) {
  3. ClassLoader classLoader = proxied.getClass().getClassLoader();
  4. Class<?>[] interfaces = proxied.getClass().getInterfaces();
  5. myInvocationHandler myInvocationHandler = new myInvocationHandler(proxied);
  6. Object proxyInstance = Proxy.newProxyInstance(classLoader, interfaces, myInvocationHandler);
  7. return proxyInstance;
  8. }
  9. private class myInvocationHandler implements InvocationHandler {
  10. private Object proxied;
  11. public myInvocationHandler(Object proxied) {
  12. this.proxied = proxied;
  13. }
  14. @Override
  15. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  16. System.out.println("在业务代码执行前,附加的其他逻辑代码" + method.getName());
  17. Object result = method.invoke(proxied, args);
  18. System.out.println("在业务代码执行后,附加的其他逻辑代码" + method.getName());
  19. return result;
  20. }
  21. }
  22. }
  1. public interface IUserController {
  2. public String login();
  3. public String register();
  4. }
  1. public static void main(String[] args) {
  2. ProxyFactory proxyFactory = new ProxyFactory();
  3. IUserController userControllerProxy = (IUserController) proxyFactory.newProxyInstance(new UserController());
  4. String login = userControllerProxy.login();
  5. String register = userControllerProxy.register();
  6. System.out.println(login);
  7. System.out.println(register);
  8. /*
  9. 执行结果打印如下:
  10. 在业务代码执行前,附加的其他逻辑代码login
  11. 在业务代码执行前,附加的其他逻辑代码login
  12. 在业务代码执行前,附加的其他逻辑代码register
  13. 在业务代码执行前,附加的其他逻辑代码register
  14. login() 的返回值
  15. register() 的返回值
  16. */
  17. }

上面的 JDK 自身提供的动态代理的代码,其中最主要的就是 Proxy.newProxyInstance(classLoader, interfaces, myInvocationHandler);这个方法,下面介绍一下这个方法定义的参数:

  • ClassLoader loader:定义代理类的类加载器
  • Class<?>[] interfaces:代理类要实现的接口列表
  • InvocationHandler h:将方法调用分派给的调用处理程序

其中 InvocationHandler 类的 invoke() 方法的第一个参数 proxy 暗藏玄机,期待一起探索。


JDK 自身提供的动态代理的实现原理就是,代理类实例实现了指定的接口,当我们调用代理类实例的任意一个方法(接口中定义的方法)时,都会被分派到我们定义的调用处理程序中,也就是 InvocationHandler 实例的 invoke() 方法,在 invoke() 方法中通过反射将请求委托给原始类对象。

cglib 动态代理

上面介绍的 JDK 自身提供的动态代理仍然有局限性,因为要想创建原始类的代理类,那么原始类必须实现接口。如果原始类没有实现接口,那么 JDK 自身提供的动态代理时无法使用的。
那这个问题怎么解决呢?我们可以使用 cglib 动态代理解决这个问题。


cglib 动态代理采取的是创建原始类(被代理类)的子类的方式。
具体的代码实现如下所示:

因为是子类化,我们可以达到近似使用被调用者本身的效果。在 Spring 编程中,框架通常会处理这种情况,当然我们也可以显式指定。
Spring AOP 支持两种模式的动态代理

如利用传说中更高性能的字节码操作机制,类似 ASM、cglib(基于 ASM)、Javassist 等。
JDK 动态代理在设计和实现上与 cglib 等方式有什么不同,进而如何取舍?

动态代理解决的问题,及应用场景

动态代理解决了什么问题,它的应用场景是什么?
日志、用户鉴权、全局性异常处理、性能监控,甚至事务处理

参考资料

第6讲 | 动态代理是基于什么原理?
48 | 代理模式:代理在RPC、缓存、监控等场景中的应用