没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
Java 动态代理详解
动态代理在 Java 中有着广泛的应用,比如 Spring AOP、Hibernate 数据查询、测
试框架的后端 mock、RPC 远程调用、Java 注解对象获取、日志、用户鉴权、全
局性异常处理、性能监控,甚至事务处理等。
本文主要介绍 Java 中两种常见的动态代理方式:JDK 原生动态代理和 CGLIB 动态
代理。
由于 Java 动态代理与 java 反射机制关系紧密
public interface UserService {
public void select();
public void update();
}
public class UserServiceImpl implements UserService {
public void select() {
System.out.println("查询 selectById");
}
public void update() {
System.out.println("更新 update");
}
}
我们将通过静态代理对 UserServiceImpl 进行功能增强,在调用 select 和
update 之前记录一些日志。写一个代理类 UserServiceProxy,代理类需要实现
UserService
public class UserServiceProxy implements UserService {
private UserService target; // 被代理的对象
public UserServiceProxy(UserService target) {
this.target = target;
}
public void select() {
before();
target.select(); // 这里才实际调用真实主题角色的方法
after();
}
public void update() {
before();
target.update(); // 这里才实际调用真实主题角色的方法
after();
}
private void before() { // 在执行方法之前执行
System.out.println(String.format("log start time [%s] ", new
Date()));
}
private void after() { // 在执行方法之后执行
System.out.println(String.format("log end time [%s] ", new
Date()));
}
}
客户端测试
public class Client1 {
public static void main(String[] args) {
UserService userServiceImpl = new UserServiceImpl();
UserService proxy = new UserServiceProxy(userServiceImpl);
proxy.select();
proxy.update();
}
}
输出
log start time [Thu Dec 20 14:13:25 CST 2018]
查询 selectById
log end time [Thu Dec 20 14:13:25 CST 2018]
log start time [Thu Dec 20 14:13:25 CST 2018]
更新 update
log end time [Thu Dec 20 14:13:25 CST 2018]
通过静态代理,我们达到了功能增强的目的,而且没有侵入原代码,这是静态代
理的一个优点。
静态代理的缺点
虽然静态代理实现简单,且不侵入原代码,但是,当场景稍微复杂一些的时候,
静态代理的缺点也会暴露出来。
1、 当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有
两种方式:
只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞
大
新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类
2、 当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,
不易维护。
如何改进?
当然是让代理类动态的生成啦,也就是动态代理。
为什么类可以动态的生成?
这就涉及到 Java 虚拟机的类加载机制了,推荐翻看《深入理解 Java 虚拟机》
7.3 节 类加载的过程。
Java 虚拟机类加载过程主要分为五个阶段:加载、验证、准备、解析、初始化。
其中加载阶段需要完成以下 3 件事情:
通过一个类的全限定名来获取定义此类的二进制字节流
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各
种数据访问入口
由于虚拟机规范对这 3 点要求并不具体,所以实际的实现是非常灵活的,关于第
1 点,获取类的二进制字节流(class 字节码)就有很多途径:
从 ZIP 包获取,这是 JAR、EAR、WAR 等格式的基础
从网络中获取,典型的应用是 Applet
运 行 时 计 算 生 成 , 这 种 场 景 使 用 最 多 的 是 动 态 代 理 技 术 , 在
java.lang.reflect.Proxy 类 中 , 就 是 用 了
ProxyGenerator.generateProxyClass 来为特定接口生成形式为 *$Proxy 的代
理类的二进制字节流
由其它文件生成,典型应用是 JSP,即由 JSP 文件生成对应的 Class 类
从数据库中获取等等
所以,动态代理就是想办法,根据接口或目标对象,计算出代理类的字节码,然
后再加载到 JVM 中使用。但是如何计算?如何生成?情况也许比想象的复杂得多,
我们需要借助现有的方案。
常见的字节码操作类库
这里有一些介绍:java-source.net/open-source…
Apache BCEL (Byte Code Engineering Library):是 Java classworking 广泛使用
的一种框架,它可以深入到 JVM 汇编语言进行类操作的细节。
ObjectWeb ASM:是一个 Java 字节码操作框架。它可以用于直接以二进制形式动
态生成 stub 根类或其他代理类,或者在加载时动态修改类。
CGLIB(Code Generation Library):是一个功能强大,高性能和高质量的代码生
成库,用于扩展 JAVA 类并在运行时实现接口。
Javassist:是 Java 的加载时反射系统,它是一个用于在 Java 中编辑字节码的
类库; 它使 Java 程序能够在运行时定义新类,并在 JVM 加载之前修改类文件。
...
实现动态代理的思考方向
为了让生成的代理类与目标对象(真实主题角色)保持一致性,从现在开始将介
绍以下两种最常见的方式:
剩余15页未读,继续阅读
资源评论
Andy&lin
- 粉丝: 97
- 资源: 214
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功