Java动态编程---动态代理
java中动态编程用到的技术有:反射(动态代理),javassist和ASM,这几种动态编程方法相比较,在性能上Javassist高于反射,但低于ASM,因为Javassist增加了一层抽象。在实现成本上Javassist和反射都很低,而ASM由于直接操作字节码,相比Javassist源码级别的api实现成本高很多。几个方法有自己的应用场景,比如Kryo使用的是ASM,追求性能的最大化。而NBeanCopyUtil采用的是Javassist,在对象拷贝的性能上也已经明显高于其他的库,并保持高易用性。实际项目中推荐先用Javassist实现原型,若在性能测试中发现Javassist成为了性能瓶颈,再考虑使用其他字节码操作方法做优化。
下面以代码的方式实现一个动态代理。
目标:
在调用持久层UserDAO将业务数据写入数据库的前后加入日志的功能。代码结构如下图所示:
User:
package dynamicproxy.model; public class User { public User(String name, int age) { super(); this.name = name; this.age = age; } private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
IUserDAO:
package dynamicproxy.dao; import dynamicproxy.model.User; public interface IUserDAO { String addUser(User user); String updateUser(User user); }
UserDAO:
package dynamicproxy.dao; import dynamicproxy.model.User; public class UserDAO implements IUserDAO { public String addUser(User user) { System.out.println("开始向数据库中写入数据..."); return String.format("添加用户[%s]成功", user.getName()); } public String updateUser(User user) { System.out.println("开始向数据库中写入数据..."); return String.format("修改用户[%s]成功", user.getName()); } }
关键的InvocationHandler类LogInterceptor:
package dynamicproxy.interceptor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class LogInterceptor implements InvocationHandler { private Object target; /* * 在执行指定方法之前调用 */ private void beforeMethod(Method method) { System.out.println(String.format("日志:用户开始执行[%s]方法...", method.getName())); } /* * 在执行指定方法之后调用 */ private void afterMethod(Method method) { System.out.println(String.format("日志:用户执行[%s]方法完成.", method.getName())); } public LogInterceptor(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { beforeMethod(method);// 加入日志处理逻辑 Object result = method.invoke(target, args);// 调用被代理对象的指定method方法 afterMethod(method);// 加入日志处理逻辑 return result; } }
测试代码:
package dynamicproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import org.junit.Test; import dynamicproxy.dao.IUserDAO; import dynamicproxy.dao.UserDAO; import dynamicproxy.interceptor.LogInterceptor; import dynamicproxy.model.User; public class UserDAOTest { @Test public void test() { IUserDAO dao = new UserDAO(); InvocationHandler logInterceptor = new LogInterceptor(dao);// InvocationHandler dao = (IUserDAO) Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(), logInterceptor); System.out.println(dao.addUser(new User("franson", 21))); System.out.println(System.lineSeparator()); System.out.println(dao.updateUser(new User("franson", 21))); } }
运行JUnit测试结果如下:
日志:用户开始执行[addUser]方法...
开始向数据库中写入数据...
日志:用户执行[addUser]方法完成.
添加用户[franson]成功
日志:用户开始执行[updateUser]方法...
开始向数据库中写入数据...
日志:用户执行[updateUser]方法完成.
修改用户[franson]成功
这样就达到了我们在不修改原有业务逻辑代码的情况下实现添加日志的功能。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话