程序员

Java中的两种动态代理

2018-02-12  本文已影响28人  fanyank

前言

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方法不能被继承,所以动态代理类自然就没有这个方法)。

上一篇下一篇

猜你喜欢

热点阅读