Java中的两种动态代理
前言
Java语言本身是具有动态性的,在我们平时使用中,存在两种动态代理,分别是出自官方之手的JDK动态代理,一种是出自民间的CGLib(Code Generation Library)。
JDK动态代理
JDK动态代理依赖接口,所以我们首先创建一个接口类 Hello
public interface Hello {
public void sayHello();
}
接下来就是一个接口实现类 HelloImpl
public class HelloImpl implements Hello {
public void sayHello() {
System.out.println("HelloImpl is invoking sayHello() method...");
}
}
至此,我们准备工作已经做好了,接下来看看我们还需要做哪些工作
我们需要创建一个类,这个类的作用是给我们动态创建出来的代理类调用的,暂且命名为 DynamicProxy 吧。该类需要继承自 InvocationHandler 接口,并实现 invoke() 方法。
public class DynamicProxy implements InvocationHandler {
//委托对象实例
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("sayHello")) {
before();
Object result = method.invoke(target,args);
after();
return result;
}
return null;
}
public void before() {
System.out.println("before invoke sayHello() method");
}
public void after() {
System.out.println("after invoke sayHello() method");
}
}
可以看到在 invoke() 方法中我们在方法执行前后添加了自定义的行为,我们称之为方法拦截。接下来就是书写 main() 方法了。
public class ProxyTest {
public static void main(String[] args) {
//创建实现接口的对象
Hello helloImpl = new HelloImpl();
//该类的作用就是对方法进行拦截
DynamicProxy dynamicProxy = new DynamicProxy(helloImpl);
//在运行过程中动态创建代理类
Hello helloProxy = (Hello) Proxy.newProxyInstance(helloImpl.getClass().getClassLoader(),
helloImpl.getClass().getInterfaces(),
dynamicProxy);
//通过代理类调用方法
helloProxy.sayHello();
}
}
具体的各类之间关系我们还是来看UML图吧。
JDK动态代理如上图,我们可以看到,通过JDK生成的动态代理类$HelloProxy100与委托类(实现Hello接口的HelloImpl类)是兄弟关系,当要执行动态代理类$HelloProxy100中的任一方法时,都会去调用DynamicProxy类中的invoke()方法,这样我们就对方法实现了拦截。
JDK动态代理优化
我们在 DynamicProxy 类中添加一个获取代理类的方法,以后每次通过这个 DynamicProxy 中间类来获取代理类。
@SuppressWarnings("unchecked")
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(this.target.getClass().getClassLoader(),
this.target.getClass().getInterfaces(),
this);
}
这样我们的 main() 方法中的代码量就大大减少了。
public class ProxyTest {
public static void main(String[] args) {
DynamicProxy dynamicProxy = new DynamicProxy(new HelloImpl());
Hello helloProxy = dynamicProxy.getProxy();
helloProxy.sayHello();
}
}
CGLib动态代理
与JDK动态代理相比,CGLib动态代理是不需要接口的,这里就创建一个普通类 Hi
public class Hi {
public void sayHi() {
System.out.println("Hi,my friend");
}
}
接着,创建出一个与 DynamicProxy 作用(给动态创建出的代理类调用)相似的类,创建一个叫做 CGLibProxy 的类,该类必须实现 MethodInterceptor 接口,并实现 interceptor() 方法。
public class CGLibProxy implements MethodInterceptor {
public <T> T getProxy(Class<?> cls) {
return (T) Enhancer.create(cls,this);
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
//执行委托类中的原方法
Object result = methodProxy.invokeSuper(o,objects);
after();
return result;
}
public void before() {
System.out.println("before method");
}
public void after() {
System.out.println("after method");
}
}
最后书写 main() 方法。
public class ProxyTest {
public static void main(String[] args) {
CGLibProxy cgLibProxy = new CGLibProxy();
Hi hiProxy = cgLibProxy.getProxy(Hi.class);
hiProxy.sayHi();
}
}
各类关系我们还是使用UML来表示
CGLib动态代理可以看出,生成的动态代理类 $HiProxy100 是委托类的子类,通过反编译工具查看 $HiProxy100 源代码可得知,该类重写了委托类的 sayHi() 方法,如果发现存在实现了 MethodInterceptor 接口的对象,那么就会通过这个对象调用 intercept() 方法对委托类中的每一个方法进行拦截(除了final方法,因为final方法不能被继承,所以动态代理类自然就没有这个方法)。