Java 动态代理和CGlib
参考书目:
《从零开始写Java Web框架》黄勇
1. 代理模式
代理的思想是使用一个代理类封装一个具有某个方法的类,当我们需要在外部使用这个方法的时候,我们通过调用代理类实现。这样,我们可以在代理类里面定义一些我们想要在这个方法前面或后面做的事。下面是一个简单的例子:
Java
//定义接口
public interface Hello{
void say(String name);
}
//定义实现类
public class HelloImpl implements Hello{
public void say(String name){
System.out.println("hello , " +name);
}
}
//定义代理
public class HelloProxy implements Hello{
private Hello hello;
public HelloProxy(){
this.hello = new HelloImpl();
}
public void say(String name){
before();
hello.say(name);
after();
}
public void before(){
//do something
}
public void after(){
//do something
}
}
//使用
Hello helloProxy = new HelloProxy();
helloProxy.say("captain !")
### 2.动态代理
很容易看出,我们在上一个部分中实现的 HelloProxy类只能对HelloImpl进行代理,而当我们有多个接口和实现类需要代理的时候,需要对每个接口的实现编写一个代理类。这显然是不够优雅的做法。更好的解决方法是使用“动态代理”。所谓动态代理,也就是可以动态设置它所代理的类的代理。JDK本身为我们实现动态代理提供了两个类:InvocationHandler和Proxy。InvocationHandler源码如下:
```Java```
//InvocationHandler 源码
package java.lang.reflect;
public interface InvocationHandler {
//方法调用函数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
Proxy源码较复杂,这里就不将所有代码列出来了。我们只看Proxy类的一个方法,具体实现先不管了。
Java
//根据一个类的ClassLoader, 该类实现的所有接口和一个InvocationHandler生成一个代理该类的对象(这个对象实现了该类所有的接口)
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException{
... //具体实现参照源码
}
接下来我们使用这两个类重写Hello的代理:
```Java```
public class MyHandler implements InvocationHandler{
private Object target;
public MyHandler(Object target){
this.target = target;
}
@override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
before();
Object result = method.invoke(target, args);
after();
return result;
}
...
}
// 使用
Hello hello = new HelloImpl();
MyHandler handler = new MyHandler(hello);
Hello helloProxy = (Hello)Proxy.newProxyInstance(hello.getClass().getClassLoader(),hello.getClass().getInterfaces(),handler);
helloProxy.say("Captain!");
3.CGlib动态代理
在上一部分,我们使用JDK内置的两个类来完成了一个动态代理的实现,但是这个实现仍然有很严重的限制,那就是它只能为实现了接口的类进行代理。假如我们要代理任意的类,更好的选择是使用CGLib库。CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。最流行的OR Mapping工具hibernate也使用CGLIB来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取,是采用其他机制实现的)。
通过maven引入最新版的gclib:
xml
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.1</version>
</dependency>
接下来我们使用CGlib提供的相关API来实现Hello的代理:
```Java```
public class CGLibProxy implements MethodInterceptor{
public <T> T getProxy(Class<T> cls){
return (T)Enhancer.create(cls,this);
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable{
before();
Object result = proxy.invokeSuper(obj, args);
after();
return result;
}
...
}
//使用
CGLibProxy cgLibProxy = new CGLibProxy();
Hello helloProxy = cgLibProxy.getProxy(HelloImpl.class);
helloProxy.say("Captain!");