准备工作
代理简介
代理,简单来说,就是代替原有操作者,即委托者去处理一件事。在 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 动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用 InvocationHandler
的 invoke
方法来处理。
实现
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
方法中,调用 Proxy
的 newProxyInstance
方法来创建一个 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 动态代理基于反射原理调用委托者实现的接口方法,并获取其返回值。并按顺序执行前置和后置增强方法 before
和 after
。
单元测试
为 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
的工作。
callback
是 Enhancer
中能够拦截原方法并进行增强的关键,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;
}
});
在设置好 superclass
、interfaces
、callback
后,调用 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
方法中直接调用了 Enhancer
的 create
方法,其源码如下:
/**
* 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
参数传入的是代理对象将要继承的类或实现的接口,在本案例中即指代MyClass
、MyInterfaceImpl
或MyInterface
,这里传参为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"); } }
单元测试
同方式一的单元测试,创建一个 CglibProxyFactory4ClassTest
和 CglibProxyFactory4InterfaceTest
,分别对没有实现接口的类和实现了接口的类做单元测试,测试结果也是断言通过,并且能够完成前置和后置方法中的打印。