一文读懂Java中的动态代理

从代理模式说起

回顾前文: 设计模式系列之代理模式(Proxy Pattern)

要读懂动态代理,应从代理模式说起。而实现代理模式,常见有下面两种实现:

(1) 代理类关联目标对象,实现目标对象实现的接口

public class Proxy implements Subject { // 维持一个对真实主题对象的引用 private RealSubject realSubject; public Proxy(RealSubject realSubject) { this.realSubject = realSubject; } public void preRequest() { // ... } public void postRequest() { // ... } @Override public void request() { preRequest(); // 调用真实主题对象的方法 realSubject.request(); postRequest(); } }

(2) 代理类继承目标类,重写需要代理的方法

public class Proxy extends RealSubject { public void preRequest() { // ... } public void postRequest() { // ... } @Override public void request() { preRequest(); super.request(); postRequest(); } }

如果程序运行前就在Java代码中定义好代理类(Proxy),那么这种代理方式就叫做静态代理;若代理类在程序运行时创建就叫做动态代理

如果为特定类的特定方法生成固定的代理,当然使用静态代理就能很好满足需求。

如果要为大量不同类的不同方法生成代理,使用静态代理的话就需要编写大量的代理类,且大量代码冗余,此时动态代理就应该闪亮登场了。

Java中实现动态代理常用的技术包括JDK的动态代理、CGLib等。

JDK的动态代理 快速入门

假设我们的业务系统中有对用户(UserService)和商品(ProductService)的查询(query)和删除(delete)业务逻辑,代码如下:

public interface CommonService { Object query(Long id); void delete(Long id); } public class UserService implements CommonService { @Override public Object query(Long id) { String s = "查询到用户:" + id; System.out.println(s); return s; } @Override public void delete(Long id) { System.out.println("已删除用户:" + id); } } public class ProductService implements CommonService { @Override public Object query(Long id) { String s = "查询到商品:" + id; System.out.println(s); return s; } @Override public void delete(Long id) { System.out.println("已删除商品:" + id); } }

现在想用代理模式给这些Service统一加上业务处理时间的日志(log),如果使用静态代理,那么拿上面的例子来说就要再手动写代理类,但实际的业务系统肯定远不止这2个类,那么就需要写大量的相似冗余的代码。

那么使用JDK提供的动态代理,应该如何实现呢?

(1) 编写日志处理器LogHandler,该处理器需要实现java中的InvocationHandler接口中的invoke方法

import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class LogHandler implements InvocationHandler { // 目标对象 private Object target; public LogHandler(Object target) { this.target = target; } private void preHandle() { System.out.println("开始处理请求时间: " + System.currentTimeMillis()); } private void postHandle() { System.out.println("结束处理请求时间: " + System.currentTimeMillis()); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 请求处理前 记录日志 preHandle(); // 目标对象的业务处理逻辑 Object result = method.invoke(target, args); // 请求处理完成 记录日志 postHandle(); return result; } }

(2) 生成代理对象,并测试代理是否生效

public static void main(String[] args) { /** * @see sun.misc.ProxyGenerator#saveGeneratedFiles * jdk1.8加上这样的配置(其他版本应当取找sun.misc.ProxyGenerator#saveGeneratedFiles用的是什么) * 会将运行时生成的代理Class落磁盘,方便我们查看动态代理生成的class文件。jdk1.8应该是在当前项目根目录的com/sun/proxy目录 * 注意:在main方法中加该配置 */ System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); // 原对象 CommonService userService = new UserService(); CommonService productService = new ProductService(); // 代理对象 CommonService proxyUserService = (CommonService) Proxy.newProxyInstance( CommonService.class.getClassLoader(), new Class[]{CommonService.class}, new LogHandler(userService)); CommonService proxyProductService = (CommonService) Proxy.newProxyInstance( CommonService.class.getClassLoader(), new Class[]{CommonService.class}, new LogHandler(productService)); // 测试代理是否生效 proxyUserService.query(1L); System.out.println("----------"); proxyUserService.delete(1L); System.out.println("\n"); proxyProductService.query(1L); System.out.println("----------"); proxyProductService.delete(1L); }

(3) 运行结果

开始处理请求时间: 1594528163163 查询到用户:1 结束处理请求时间: 1594528163163 ---------- 开始处理请求时间: 1594528163163 已删除用户:1 结束处理请求时间: 1594528163163 开始处理请求时间: 1594528163163 查询到商品:1 结束处理请求时间: 1594528163163 ---------- 开始处理请求时间: 1594528163163 已删除商品:1 结束处理请求时间: 1594528163163

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsxyjj.html