动态代理(二)

JDK动态代理涉及java.lang.reflect包中的两个类,Proxy和InvocationHandler。Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。InvocationHandler可以实现接口定义横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起。

JDK动态代理

下面摘自:《疯狂java讲义 第四版》 p857

当程序使用反射方式为指定接口生成系列动态代理对象时,这些动态代理对象的实现类实现了一个或多个接口。动态代理对象就需要实现一个或多个接口里定义的所有方法,但问题是:系统怎么知道如何实现这些方法? 这个时候就轮到 InvocationHandler 对象登场了,当执行动态代理对象里的方法时,实际上会替换成调用 InvocationHandler 对象的 invoke 方法。

在 Java 中,动态代理类的生成主要涉及对 ClassLoader 的使用。以 CGLIB 为例,使用 CGLIB 生成动态代理,首先需要生成 Enhancer 类实例,并指定用于处理代理业务的回调类。在 Enhancer.create() 方法中,会使用 DefaultGeneratorStrategy.Generate() 方法生成动态代理类的字节码,并保存在 byte 数组中。接着使用 ReflectUtils.defineClass() 方法,通过反射,调用 ClassLoader.defineClass() 方法,将字节码装载到 ClassLoader 中,完成类的加载。最后使用 ReflectUtils.newInstance() 方法,通过反射,生成动态类的实例,并返回该实例。基本流程是根据指定的回调类生成 Class 字节码—通过 defineClass() 将字节码定义为类—使用反射机制生成该类的实例。

程序中可以采用先生成一个动态代理类,然后通过动态代理类来创建代理对象的方式生成一个动态代理对象。代码如下:

1
2
3
4
5
6
7
8
// 创建一个InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(...);
//使用Proxy生成一个动态代理类 proxyClass
Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] {Foo.class});
//获取proxyClass类中带一个InvocationHandler参数的构造器
Constructor ctor = proxyClass.getConstructor(new Class[] {InvocationHandler.class});
//调用ctor的newInstance方法来创建动态实例
Foo f = (Foo) ctor.newInstance(new Object[] {handler});

上面的代码也可以简化成如下代码:

1
2
3
4
5
// 创建一个InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(...);
//使用Proxy直接生成一个动态代理对象
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class[] {Foo.class}, handler);

下面的代码来自于《精通Spring 4.x企业应用开发实战》 P224

业务逻辑横切代码

业务逻辑实现类的代码,省去ForumService接口类和PerformanceMonitor的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class ForumServiceImpl implements ForumService {

public void removeTopic(int topicId) {
// 横切逻辑
// PerformanceMonitor.begin();
System.out.println("模拟删除Topic记录" + topicId);
try {
Thread.currentThread().sleep(20);
} catch (Exception e) {
throw new RuntimeException(e);
}
// PerformanceMonitor.end()
}

public void removeForum(int forumId) {
// PerformanceMonitor.begin();
System.out.println("模拟删除Topic记录" + forumId);
try {
Thread.currentThread().sleep(20);
} catch (Exception e) {
throw new RuntimeException(e);
}
// PerformanceMonitor.end();
}
}

横切逻辑

将业务类中性能监视横切代码移除后,放置到InvocationHandler中,代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.lang.reflect.Method;
import java.lang.reflect.InvocationHandler;
public class PerformanceHandler implements InvocationHandler {
private Object target;

public PerformanceHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
// 横切逻辑代码定义在PerformanceMonitor中
PerformanceMonitor.begin(target.getClass().getName() + "." + method.getName());
// 使用反射方法调用业务类的目标方法
Object obj = method.invoke(target, args);
// 横切逻辑
PerformanceMonitor.end();
return obj;
}
}

invoke(Object proxy, Method method, Object[] args)方法,其中,proxy是最终生成的代理实例,一般不会用到;method是被代理目标实例的某个具体方法,通过它可以发起目标实例方法的反射调用;args是被代理实例某个方法的入参,在方法反射时调用。
其次,在构造参数里通过target传入希望被代理的目标对象,在接口方法invoke(Object proxy, Method method, Object[] args)里,将目标实例传递给method.invoke()方法,并调用目标实例的方法。

proxy代表动态代理对象,method代表正在执行的方法,args代表调用目标方法是传入的实参。

下面通过Proxy结合PerformanceHandler创建ForumService接口的代理实例。

创建代理实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.lang.reflect.Proxy;

public class ForumServiceTest {
public void proxy() {
// 希望被代理的目标业务类
ForumService target = new ForumServiceImpl();

// 将目标业务类和横切代码编织到一起
PerformanceHandler handler = new PerformanceHandler(target);

// 根据编织了目标业务类逻辑和性能监视横切逻辑的
// InvocationHandler实例创建代理实例
ForumService proxy = (ForumService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterface(),
handler);
// 调用代理实例
proxy.removeForum(10);
proxy.removeTopic(1024);
}
}

Proxy.newProxyInstance() 方法的第一个入参为类加载器;第二个入参为创建代理实例所需实现的一组接口;第三个入参是整合了业务逻辑和横切逻辑的编织器对象。

JDK动态代理另一种简洁写法

以下代码来自于 Java EE 互联网轻量级框架整合开发

接口类和实现类的定义

在动态代理中必须使用接口,CGLib不需要。
下面的代码分别是简单的接口和被代理类的定义。

1
2
3
4
5
6
7
8
9
10
11
12
// 接口
public interface Service {
public void sayHello();
}

// 实现类
public class ServiceImpl implements Service {
@Override
public void sayHello() {
System.out.println("Hello world");
}
}

动态代理绑定和代理逻辑实现

要实现动态代理要两个步骤,首先,建立起代理对象和被代理对象的关系(将目标业务类和横切代码编织到一起),然后实现代理逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class JdkProxyExample implements InvocationHandler {
private Object target = null;

public Object bind(Object target) {
this.target = target;

return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().gerInterfaces(),
this);
}

public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
System.out.println("before...");

Object obj = method.invoke(target, args);

System.out.println("after...");
return obj;
}
}

测试动态代理类

1
2
3
4
5
6
7
public class JdkProxyExampleTest {
JdkProxyExample jdk = new JdkProxyExample();

Service proxy = (Service) jdk.bind(new ServiceImpl());

proxy.sayHello();
}

bind方法同时完成了两步。

CGLib动态代理

使用JDK创建代理有一个限制,即只能为接口创建代理。Proxy的接口方法中newProxyInstance(ClassLoader loder, Class[] interfaces, InvocationHandler hander),第二个入参就是需要代理实例实现的接口列表。
假如对一个简单业务表的操作也需要创建5个类(领域对象、DAO接口、DAO实现类、Service接口和Service实现类)吗?
对于没有通过接口定义业务方法的类,可以使用CGLib动态创建代理实例。

CGLib采用底层的字节码技术,可以为一个类创建子类,在子类中采用方法拦截的技术拦截父类方法的调用并顺势织入横切逻辑。

值得一提的是,由于CGLib采用动态创建子类的方式生成代理对象,所以不能对目标类中的final或private方法进行代理。

CglibProxy

下面代码可以创建,为任何类织入性能监视横切逻辑代理对象的代理创建器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CglibProxy implemets MethodInterceptor {
// 增强类对象
private Enhancer enhancer = new Enhancer();

public Object getProxy(Class cls) {
// 设置需要创建子类的类
enhancer.setSuperClass(cls);
/* 定义代理逻辑对象为当前对象,要求当前对象实现
* MethodInterceptor方法
*/
enhancer.setCallback(this);

// 通过字节码技术动态创建子类实例
return enhancer.create();
}

/*
*代理逻辑方法,拦截父类所有方法的调用
* obj 目标类的实例
* method 目标类方法的反射对象
* args 方法的动态入参
* proxy 代理类实例
* result 代理逻辑方法返回
*/
@Override
public Object intercept(Objcet obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
PerformanceMonitor.begin(obj.getClass().getName() + "." + method.getName());
// CGLib反射调用父类中的方法
Objcet result = proxy.invokeSuper(obj, args);
PerformanceMonitor.end();

return result;
}
}

用户可以通过getProxy(Class cla)方法动态创建一个动态代理类。

Cglib测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.lang.reflect.Proxy;
import org.testg.annotations.*;
public class ForumServiceTest {
@Test
public void proxy() {
CglibProxy proxy = new CglibProxy();

// 通过动态生成子类的方式创建代理类
ForumServiceImpl forumService = (ForumServiceImpl)
proxy.getProxy(ForumServiceImpl.class);

forumService.removeForum(10);
forumService.removeTopic(1024);
}
}

拦截器

拦截器接口

1
2
3
4
5
6
7
public interface Interceptor {
boolean before(Object proxy, Object target, Method method, Objcet[] args);

void around(Object proxy, Object target, Method method, Objcet[] args);

void after(Object proxy, Object target, Method method, Objcet[] args);
}

Interceptor实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyInterceptor implements Interceptor {
@Override
public boolean before(Object proxy, Object target, Method method, Objcet[] args) {
System.err.println("反射方法前逻辑");
return false;
}

@Override
public void after(Object proxy, Object target, Method method, Objcet[] args) {
System.err.println("反射方法后逻辑");
}

@Override
public boolean around(Object proxy, Object target, Method method, Objcet[] args) {
System.err.println("取代了目标实例的方法");
}

}

在JDK动态代理中使用拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class InterceptorJdkProxy implements InvocationHandler {
// 目标实例
private Objcet target;
// 拦截器全限定名
private String interceptorClass = null;

public InterceptorJdkProxy(Objcet target, String interceptorClass) {
this.target = target;
this.interceptorClass = interceptorClass;
}

public static Object bind(Object target, String interceptorClass) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InterceptorJdkProxy(target, interceptorClass));
}

public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
if (interceptorClass == null) {
return method.invoke(target, args);
}

Object result = null;

Interceptor inceptor = (Interceptor)
Class.forName(interceptorClass).newInstance();

if (interceptor.before(proxy, target, method, args)) {
result = method.invoke(target, args);
} else {
inceptor.around(proxy, target, method, args);
}

interceptor.after(proxy, target, method, args);
return result;
}
}

0%