在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目的是为了减少代理类的数量。解决代码复用的问题。
在内存当中动态生成类的技术常见的包括:
- JDK动态代理技术:只能代理接口。
- CGLIB动态代理技术:CGLIB(Code Generation Library)是一个开源项目。是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。它既可以代理接口,又可以代理类,底层是通过继承的方式实现的。性能比JDK动态代理要好。(底层有一个小而快的字节码处理框架ASM。)
- Javassist动态代理技术:Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。
JDK动态代理
例子:
准备一个OrderService 接口和一个OrderServiceImpl实现类:
public interface OrderService {
/**
* 生成订单
*/
void generate();
/**
* 查看订单详情
*/
void detail();
/**
* 修改订单
*/
void modify();
}
public class OrderServiceImpl implements OrderService {
@Override
public void generate() {
try {
Thread.sleep(1234);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("订单已生成");
}
@Override
public void detail() {
try {
Thread.sleep(2541);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("订单信息如下:******");
}
@Override
public void modify() {
try {
Thread.sleep(1010);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("订单已修改");
}
}
在动态代理中UserServiceProxy代理类是可以动态生成的。这个类不需要写。我们直接写客户端程序即可:
public class Client {
public static void main(String[] args) {
//目标对象
OrderService target = new OrderServiceImpl();
//创建代理对象
OrderService proxyObj =(OrderService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new TimerInvocationHandler(target));
proxyObj.generate();
proxyObj.modify();
proxyObj.detail();
}
}
以上第二步创建代理对象是需要大家理解的:
OrderService orderServiceProxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), 调用处理器对象);
这行代码做了两件事:
- 第一件事:在内存中生成了代理类的字节码
- 第二件事:创建代理对象
Proxy类全名:java.lang.reflect.Proxy。这是JDK提供的一个类(所以称为JDK动态代理)。主要是通过这个类在内存中生成代理类的字节码。
其中newProxyInstance()方法有三个参数:
- 第一个参数:类加载器。在内存中生成了字节码,要想执行这个字节码,也是需要先把这个字节码加载到内存当中的。所以要指定使用哪个类加载器加载。
- 第二个参数:接口类型。代理类和目标类实现相同的接口,所以要通过这个参数告诉JDK动态代理生成的类要实现哪些接口。
- 第三个参数:调用处理器。这是一个JDK动态代理规定的接口,接口全名:java.lang.reflect.InvocationHandler。显然这是一个回调接口,也就是说调用这个接口中方法的程序已经写好了,就差这个接口的实现类了。
所以接下来我们要写一下java.lang.reflect.InvocationHandler接口的实现类,并且实现接口中的方法,该方法用来对OrderService接口中的方法进行计时,代码如下:
public class TimerInvocationHandler implements InvocationHandler {
// 目标对象
private Object target;
// 通过构造方法来传目标对象
public TimerInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//这个invoke方法什么时候执行呢?
//是在代理对象调用代理方法的时候,jdk自动调用的
// 目标执行之前增强。
long begin = System.currentTimeMillis();
// 调用目标对象的目标方法
Object value = method.invoke(target, args);
// 目标执行之后增强。
long end = System.currentTimeMillis();
System.out.println("总耗时:"+(end-begin));
return value;
}
}
InvocationHandler接口中有一个方法invoke,这个invoke方法上有三个参数:
- 第一个参数:Object proxy。代理对象。设计这个参数只是为了后期的方便,如果想在invoke方法中使用代理对象的话,尽管通过这个参数来使用。
- 第二个参数:Method method。目标方法。
- 第三个参数:Object[] args。目标方法调用时要传的参数。
我们将来肯定是要调用“目标方法”的,但要调用目标方法的话,需要“目标对象”的存在,“目标对象”从哪儿来呢?我们可以给TimerInvocationHandler提供一个构造方法,可以通过这个构造方法传过来“目标对象”。
InvocationHandler接口中的invoke()方法没看见在哪里调用呢?
注意:当你调用代理对象的代理方法的时候,注册在InvocationHandler接口中的invoke()方法会被调用。也就是上面代码第24 25 26行,这三行代码中任意一行代码执行,注册在InvocationHandler接口中的invoke()方法都会被调用。
运行结果如下:

文章介绍了动态代理在程序运行阶段如何在内存中生成代理类,包括JDK动态代理仅限于代理接口,CGLIB支持接口和类代理,以及Javassist的字节码操作。还详细讲解了JDK动态代理的使用示例和InvocationHandler接口的作用。

2434

被折叠的 条评论
为什么被折叠?



