Java 中的代理:静态代理、JDK 动态代理和 Cglib 动态代理

准备工作

代理简介

代理,简单来说,就是代替原有操作者,即委托者去处理一件事。在 Java 中代理一般分为两种,静态代理和动态代理,动态代理又分为 JDK 动态代理和 Cglib 动态代理。

创建项目

创建一个简单的纯后端的 Maven 项目,在其中引入单元测试和 Cglib 相关依赖。

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
</dependencies>

注:本案例基于 JDK 1.8

静态代理

实现静态代理需要由显式地创建代理类。与动态代理不同的是,静态代理的 class 文件在编译之后,程序运行之前就已经存在了。

接口

首先定义一个公共的 Subject 接口,该接口后续需要被委托类 RealSubject 和代理类 ProxySubject 共同实现。

public interface Subject {

    void method();
}

实现类

定义一个实现类 RealSubject,实现 method 方法。

public class RealSubject implements Subject {

    @Override
    public void method() {
        System.out.println("real method implement");
    }
}

代理类

定义一个代理类 ProxySubject,实现 Subject 接口,并使用构造器方法注入 Subject 接口。

public class ProxySubject implements Subject {

    private Subject subject;

    public ProxySubject(Subject subject) {
        this.subject = subject;
    }

    @Override
    public void method() {
        before();
        subject.method();
        after();
    }

    private void before() {
        System.out.println("static proxy: before method");
    }

    private void after() {
        System.out.println("static proxy: after method");
    }
}

构造器注入 Subject 接口,便于注入指定的实现类。在实现 method 方法时,调用委托者的 method 方法:subject.method()。before 和 after 方法分别为前置和后置的增强方法。

单元测试

为静态代理创建相应的单元测试

public class ProxySubjectTest {

    @Test
    public void method() {
        Subject subject = new RealSubject();
        ProxySubject proxySubject = new ProxySubject(subject);
        proxySubject.method();
    }
}

运行该测试方法,在控制台中观察输出。

static proxy: before method
real method implement
static proxy: after method

动态代理

准备工作

接口和实现类

定义一个接口及其实现类,实现类会委托动态代理来对其实现的方法进行增强。

public interface MyInterface {

    void methodA();

    int methodB();

    int methodC(int param1, int param2);
}
public class MyInterfaceImpl implements MyInterface {

    @Override
    public void methodA() {
        System.out.println("method A");
    }

    @Override
    public int methodB() {
        System.out.println("method B");
        return 0;
    }

    @Override
    public int methodC(int param1, int param2) {
        System.out.println("method C");
        return param1 + param2;
    }
}

没有实现接口的类

定义一个没有实现接口的类。

public class MyClass {

    public void methodA() {
        System.out.println("method A");
    }

    public int methodB() {
        System.out.println("method B");
        return 0;
    }

    public int methodC(int param1, int param2) {
        System.out.println("method C");
        return param1 + param2;
    }
}

JDK 动态代理

JDK 动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用 InvocationHandlerinvoke 方法来处理。

实现

public class MyInvocationHandler implements InvocationHandler {

    private Object delegate;

    public MyInvocationHandler(Object delegate) {
        this.delegate = delegate;
    }

    public Object createJDKProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(delegate, args);
        after();
        return result;
    }

    private void before() {
        System.out.println("jdk proxy: before method");
    }

    private void after() {
        System.out.println("jdk proxy: after method");
    }

类似于静态代理,JDK 动态代理也是通过构造器注入委托类对象 delegate

创建动态代理对象

createJDKProxy 方法中,调用 ProxynewProxyInstance 方法来创建一个 JDK 动态代理对象。

首先阅读 newProxyInstance 方法的源码注释:

/**
 * Returns an instance of a proxy class for the specified interfaces
 * that dispatches method invocations to the specified invocation
 * handler.
 *
 * @param   loader the class loader to define the proxy class
 * @param   interfaces the list of interfaces for the proxy class
 *          to implement
 * @param   h the invocation handler to dispatch method invocations to
 * @return  a proxy instance with the specified invocation handler of a
 *          proxy class that is defined by the specified class loader
 *          and that implements the specified interfaces
 */

可知,newProxyInstance 的参数分别应该传入

  • 代理类的类加载器。在本案例中,代理类即自定义的 InvocationHandler 接口实现类 MyInvocationHandler,所以传参为当前类对象的类加载器即可:this.getClass().getClassLoader()
  • 代理类要实现的接口数组类对象。这里传入委托类对象实现的接口类对象数组即可:delegate.getClass().getInterfaces()
  • 分发方法调用的 invocation handler。这里传入当前代理对象即可:this
  • 返回值为一个 JDK 代理实例。

重写 invoke 方法

invoke 方法基于反射原理,当调用代理者中的接口方法时,会首先进入invoke方法中,从 Object result = method.invoke(delegate, args) 可以看出,JDK 动态代理基于反射原理调用委托者实现的接口方法,并获取其返回值。并按顺序执行前置和后置增强方法 beforeafter

单元测试

为 JDK 动态代理创建单元测试

public class JDKProxyTest {

    private MyInterface myInterfaceJDKProxy;

    @Before
    public void setUp() {
        MyInterface myInterface = new MyInterfaceImpl();
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler(myInterface);
        myInterfaceJDKProxy = (MyInterface) myInvocationHandler.createJDKProxy();
    }

    @Test
    public void methodA() {
        myInterfaceJDKProxy.methodA();
    }

    @Test
    public void methodB() {
        assertEquals(0, myInterfaceJDKProxy.methodB());
    }

    @Test
    public void methodC() {
        int param1 = 2;
        int param2 = 3;
        assertEquals(param1 + param2, myInterfaceJDKProxy.methodC(param1, param2));
    }
}

在控制台中观察输出结果

  • methodA

    jdk proxy: before method
    method A
    jdk proxy: after method
    
  • methodB

    jdk proxy: before method
    method B
    jdk proxy: after method
    
  • methodC

    jdk proxy: before method
    method C
    jdk proxy: after method
    

Cglib 动态代理

Cglib 是一种动态代理方式,底层通过 ASM 产生字节码来完成动态代理,Cglib 与 JDK 动态代理相比,除了可以代理实现接口的类也可以代理非实现接口的类,通过 FastClass 类来避免了反射的使用。对 JDK 7 以前的版本来说,JDK 8 动态代理执行效率明显要比 Cglib 动态代理类效率差,JDK 8 即以后版本对 JDK 动态代理进行了相应的优化,这种差距就不那么明显了。但是要代理不实现接口的类来说,Cglib 就是一种必要选择。

实现方式一:实现 MethodInterceptor 接口

public class MyMethodInterceptor implements MethodInterceptor {

    private Object delegate;

    public MyMethodInterceptor(Object delegate) {
        this.delegate = delegate;
    }

    public Object createCglibProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(delegate.getClass());
        enhancer.setInterfaces(delegate.getClass().getInterfaces());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        before();
        Object result = method.invoke(delegate, args);
        after();
        return result;
    }

    private void before() {
        System.out.println("cglib proxy: before method");
    }

    private void after() {
        System.out.println("cglib proxy: after method");
    }
}

首先阅读一下 setSuperclass 方法的源码

/**
 * Set the class which the generated class will extend. As a convenience,
 * if the supplied superclass is actually an interface, <code>setInterfaces</code>
 * will be called with the appropriate argument instead.
 * A non-interface argument must not be declared as final, and must have an
 * accessible constructor.
 * @param superclass class to extend or interface to implement
 * @see #setInterfaces(Class[])
 */
public void setSuperclass(Class superclass) {
    if (superclass != null && superclass.isInterface()) {
        setInterfaces(new Class[]{ superclass });
    } else if (superclass != null && superclass.equals(Object.class)) {
        // affects choice of ClassLoader
        this.superclass = null;
    } else {
        this.superclass = superclass;
    }
}

这里传入的参数 superClass 将会被生成的代理类继承,所以这里的 superClass 传入委托者(实现类)对应的 Class 对象。

再阅读一下 setInterfaces 的源码

/**
 * Set the interfaces to implement. The <code>Factory</code> interface will
 * always be implemented regardless of what is specified here.
 * @param interfaces array of interfaces to implement, or null
 * @see Factory
 */
public void setInterfaces(Class[] interfaces) {
    this.interfaces = interfaces;
}

所以 interfaces 参数传入委托者实现的接口数组即可。这里不设置 interfaces 也没有关系,因为即使 superClass 参数为接口类型的话,在 setSuperclass 方法中会进行 setInterfaces 的工作。

callbackEnhancer 中能够拦截原方法并进行增强的关键,MethodInterceptor 接口是 callback 的子接口之一,可以对委托者的非 final 方法进行拦截,所以 callback 设置为当前 MethodInterceptor 的实现类对象,即 this;如果没有显式地创建 MethodInterceptor 的实现类,也可以隐式地创建 MethodInterceptor 的匿名内部类:

enhancer.setCallback(new MethodInterceptor() {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        before();
        Object result = method.invoke(delegate, args);
        after();
        return result;
    }
});

在设置好 superclassinterfacescallback后,调用 enhancer.create() 即可获取 Cglib 动态代理对象。

单元测试

委托者没有实现接口的类的测试

public class CglibProxy4ClassTest {

    private MyClass myClass;

    @Before
    public void setUp() {
        MyClass myInterfaceImpl = new MyClass();
        MyMethodInterceptor myMethodInterceptor = new MyMethodInterceptor(myInterfaceImpl);
        myClass = (MyClass) myMethodInterceptor.createCglibProxy();
    }

    @Test
    public void methodA() {
        myClass.methodA();
    }

    @Test
    public void methodB() {
        assertEquals(0, myClass.methodB());
    }

    @Test
    public void methodC() {
        int param1 = 2;
        int param2 = 3;
        assertEquals(param1 + param2, myClass.methodC(param1, param2));
    }

}

委托者为实现了接口的类的测试

public class CglibProxy4InterfaceTest {

    private MyInterface myInterfaceCglibProxy;

    @Before
    public void setUp() {
        MyInterface myInterface = new MyInterfaceImpl();
        MyMethodInterceptor myMethodInterceptor = new MyMethodInterceptor(myInterface);
        myInterfaceCglibProxy = (MyInterface) myMethodInterceptor.createCglibProxy();
    }

    @Test
    public void methodA() {
        myInterfaceCglibProxy.methodA();
    }

    @Test
    public void methodB() {
        assertEquals(0, myInterfaceCglibProxy.methodB());
    }

    @Test
    public void methodC() {
        int param1 = 2;
        int param2 = 3;
        assertEquals(param1 + param2, myInterfaceCglibProxy.methodC(param1, param2));
    }

}

上述两个测试类的测试结果均为:

  • methodA

    cglib proxy: before method
    method A
    cglib proxy: after method
    
  • methodB

    cglib proxy: before method
    method B
    cglib proxy: after method
    
  • methodC

    cglib proxy: before method
    method C
    cglib proxy: after method
    

实现方式二:匿名内部类

方式二和方式一的原理是一样的,只是从形式上更简洁,因为使用了匿名内部类。

public class CglibProxyFactory {

    public static Object createProxy(final Object delegate) {
        return Enhancer.create(delegate.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws InvocationTargetException, IllegalAccessException {
                before();
                Object result = method.invoke(delegate, args);
                after();
                return result;
            }

            private void before() {
                System.out.println("inner cglib proxy: before method");
            }

            private void after() {
                System.out.println("inner cglib proxy: after method");
            }
        });
    }
}

createProxy 方法中直接调用了 Enhancercreate 方法,其源码如下:

/**
 * Helper method to create an intercepted object.
 * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
 * instead of this static method.
 * @param type class to extend or interface to implement
 * @param callback the callback to use for all methods
 */
public static Object create(Class type, Callback callback) {
	Enhancer e = new Enhancer();
	e.setSuperclass(type);
	e.setCallback(callback);
	return e.create();
}
  • type 参数传入的是代理对象将要继承的类或实现的接口,在本案例中即指代MyClassMyInterfaceImplMyInterface,这里传参为 delegate.getClass()

  • callback 参数为所有非 final 方法对应的回调函数,即 MethodInterceptor 的实现类或其匿名内部类。这里简化为匿名内部类:

    new MethodInterceptor() {
    	@Override
    	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws InvocationTargetException, IllegalAccessException {
    		before();
    		Object result = method.invoke(delegate, args);
    		after();
    		return result;
    	}
    
    	private void before() {
    		System.out.println("inner cglib proxy: before method");
    	}
    
    	private void after() {
    		System.out.println("inner cglib proxy: after method");
    	}
    }
    

单元测试

同方式一的单元测试,创建一个 CglibProxyFactory4ClassTestCglibProxyFactory4InterfaceTest,分别对没有实现接口的类和实现了接口的类做单元测试,测试结果也是断言通过,并且能够完成前置和后置方法中的打印。

发布了83 篇原创文章 · 获赞 333 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/qq_15329947/article/details/104133268