Java动态代理模式

代理模式的定义,为其他对象提供一种代理以控制对这个对象的访问。使用代理模式创建代理对象,让代理对象控制目标对象的访问,并且可以在不改变目标对象的情况下添加一些额外的功能。代理模式分为静态代理模式和动态代理模式。静态代理维护难度大,可扩展性较差,这是因为代理的功能在编译期就已经决定,如果代理发生在运行时期,就会灵活很多。动态代理模式可以很好的解决这个问题。

动态代理有两种实现方式,JDK提供的反射机制CGLIB

JDK反射机制

采用JDK反射机制,运用JDK1.3之后提供的Proxy和InvocationHandler两个接口,来实现运行时代理功能。核心代码如下:

public class MyInvocationHandler implements InvocationHandler {  
    private Object proxyTarget;
    public Object newProxyInstance (Object proxyTarget) {
        this.proxyTarget = proxyTarget;
        return Proxy.newProxyInstance(proxyTarget.getClass().getClassLoader(), proxyTarget.getClass().getInterfaces(), this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        doBefore(method.getName());
        Object ret = method.invoke(this.proxyTarget, args);
        doAfter(method.getName());
        return ret;
    }
    private void doBefore(String methodName) {
        System.out.println(methodName + " preprocessing ...");
    }
    private void doAfter(String methodName) {
        System.out.println("after the " + methodName + " ...");
    }
}

代码的主要执行流程是,通过Proxy的static方法newProxyInstance创建一个需要被代理对象的代理,这个代理对象可以转型为被代理对象,然后通过这个代理执行被代理对象的方法时会触发invoke方法,这一部分就是反射的具体应用,可以在执行这个方法前后添加别的处理程序。 可以很明显的看到,动态代理通过反射,可以在运行期决定加载哪个类,避免了一个类对应一个代理的问题。同时,通过统一的invoke方法,统一了对原函数的处理过程,使用动态代理很大程度上减少了重复的代码,降低了维护的复杂性和成本。

CGLIB方式

采用CGLIB,采用JDK提供的动态代理有一个限制,就是被代理对象必须实现一个或多个接口,如果被代理的类没有实现接口,就可以采用CGLIB的方式。这种方式需要实现MethodInterceptor接口,然后采用Enhancer类来创建proxy,通过设置SuperClass,Callback(s),以及CallbackFilter来创建不同需求的proxy,核心代码如下:

public class MyCglibProxy implements MethodInterceptor {  
    private static final String BOSS = "boss";
    private String name;
    public MyCglibProxy(String name) {
        this.name = name;
    }
    public Object getProxy(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }
    public Object getProxyFilter(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallbacks(new Callback[]{this, NoOp.INSTANCE});
        enhancer.setCallbackFilter(new MyCallbackFilter());
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before method " + method.getName() + " execute");
        if (!this.name.equals(BOSS)) {
            System.out.println("Permission denied!!!!");
            return null;
        }
        return methodProxy.invokeSuper(o, objects);
    }
}

需要注意的是,MyCallbackFilter(回调过滤器)需要实现CallbackFilter接口,此外,CGLIB是基于ASM(小而快的字节码处理框架),所以除了要包括CGLIB的库外还需要包括ASM的库。

Shaohang Zhao

Read more posts by this author.