本文分类:
Java

代理就是将操作委托给另一个对象实现,代理可以隐藏委托类(通常称为target)的实现,使客户端与委托类解耦,并在不改变委托类代码的情况下增加额外的操作。代理分为静态代理和动态代理,静态代理是程序运行前代理类就已经存在,代理类由程序员自己实现,而动态代理则是程序在运行过程中动态创建代理类。本文介绍 Java 动态代理,包括 JDK 动态代理和 CGLIB 动态代理。

JDK动态代理

JDK使用 java.lang.reflect 包下的 Proxy 和 InvocationHandler 来支持动态代理,其原理是让 target 类和代理类实现同一接口,代理类持有 target 对象来达到方法拦截的目的。JDK 动态代理使用 Java 反射API来创建代理类,target类必须有接口,所有生成的代理类都是 java.lang.reflect.Proxy 的子类,因为 Java 不允许多重继承,所以 JDK 动态代理不能代理普通的类。

public interface DemoService {
    String foo(String param);
}

public class DemoServiceImpl implements DemoService {
    @Override
    public String foo(String param) {
        return new StringBuffer(param).reverse().toString();
    }
}

public class LogHandler implements InvocationHandler {
    private final Logger LOGGER = Logger.getLogger(LogHandler.class.getCanonicalName());
    private DemoService target;

    public LogHandler(DemoService target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        LOGGER.logp(Level.INFO, proxy.getClass().getCanonicalName(), method.getName(), "Before Call", args);
        Object result = method.invoke(target, args);
        LOGGER.logp(Level.INFO, proxy.getClass().getCanonicalName(), method.getName(), "After Call", args);
        return result;
    }
}

public class JdkDynamicProxyDemo {
    public static void main(String[] args) {
        DemoService demoService = (DemoService) Proxy.newProxyInstance(
                JdkDynamicProxyDemo.class.getClassLoader(),
                new Class[]{DemoService.class},
                new LogHandler(new DemoServiceImpl()));
        System.out.println(demoService.foo("www.hiwzc.com"));
        System.out.println(demoService.getClass());
        System.out.println(demoService.getClass().getSuperclass());
        System.out.println(Arrays.toString(demoService.getClass().getInterfaces()));
    }
}

运行结果

moc.czwih.www
class com.sun.proxy.$Proxy0
class java.lang.reflect.Proxy
[interface com.hiwzc.demo.dynamic.proxy.jdk.DemoService]

CGLIB动态代理

CGLIB 是一个功能强大、高性能、高质量的代码生成库,用于在运行期扩展Java类和实现Java接口。与 JDK 动态代理不同的是,CGLIB 使用 ASM 字节码操作框架,在运行时动态创建 target 类的子类,所以能够实现对类的动态代理。

public class Demo {
    public String foo(String param) {
        return param == null ? null : param.toUpperCase();
    }

    public String bar(String param) {
        return param == null ? null : new StringBuffer(param).reverse().toString();
    }
}

public class CglibDynamicProxyDemo {
    public static void main(String[] args) {
        callbackDemo();
        System.out.println();
        filterDemo();
    }

    private static void callbackDemo() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Demo.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects,
                                        MethodProxy methodProxy) throws Throwable {
                System.out.println("Before " + method.getName());
                Object result = methodProxy.invokeSuper(o, objects);
                System.out.println("After " + method.getName());
                return result;
            }
        });

        Demo demo = (Demo) enhancer.create();
        System.out.println(demo.foo("www.hiwzc.com"));
        System.out.println(demo.bar("Hello World"));
        System.out.println(demo.toString());

        System.out.println(demo.getClass());
        System.out.println(demo.getClass().getSuperclass());
        System.out.println(Arrays.toString(demo.getClass().getInterfaces()));
    }

    private static void filterDemo() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Demo.class);

        Callback demoCallback = new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects,
                                        MethodProxy methodProxy) throws Throwable {
                System.out.println("Before " + method.getName());
                Object result = methodProxy.invokeSuper(o, objects);
                System.out.println("After " + method.getName());
                return result;
            }
        };
        Callback noOp = NoOp.INSTANCE;
        Callback fixedValue = new FixedValue() {
            @Override
            public Object loadObject() throws Exception {
                return "Fix Value";
            }
        };
        Callback[] callbacks = new Callback[]{demoCallback, noOp, fixedValue};

        enhancer.setCallbacks(callbacks);
        enhancer.setCallbackFilter(new CallbackFilter() {
            @Override
            public int accept(Method method) {
                switch (method.getName()) {
                    case "foo":
                        return 0;
                    case "bar":
                        return 1;
                    default:
                        return 2;
                }
            }
        });

        Demo demo = (Demo) enhancer.create();
        System.out.println(demo.foo("www.hiwzc.com"));
        System.out.println(demo.bar("Hello World"));
        System.out.println(demo.toString());
    }
}

运行结果为

Before foo
After foo
WWW.HIWZC.COM
Before bar
After bar
dlroW olleH
Before toString
Before hashCode
After hashCode
After toString
com.hiwzc.demo.dynamic.proxy.cglib.Demo$$EnhancerByCGLIB$$671629fe@56ac3a89
class com.hiwzc.demo.dynamic.proxy.cglib.Demo$$EnhancerByCGLIB$$671629fe
class com.hiwzc.demo.dynamic.proxy.cglib.Demo
[interface net.sf.cglib.proxy.Factory]

Before foo
After foo
WWW.HIWZC.COM
dlroW olleH
Fix Value

总结

下面总结一下JDK动态代理和CGLIB动态代理的区别:

JDK 动态代理 CGLIB 动态代理
只能对接口进行代理 能够代理普通类
使用Java反射API 使用ASM框架直接对字节码进行操作
代理类的父类是java.lang.reflect.Proxy 代理类的父类是Target类
生成代理类比较高效 执行代理类比较高效
本文来自 [时光记 - 王智超的个人空间](www.hiwzc.com),转载请注明出处。