Java动态代理

代理

最近在学习 Spring 框架,AOP涉及到动态代理的知识,故整理一下,了解动态代理之前,我们首先应该了解下什么是代理。

代理是基本的设计模式之一,它是你为了提供额外的或不同的操作,而插入的用来代替的"实际"对象的
对象,这些操作通常涉及与"实际"对象的通信,因此代理通常充当着中间人的角色。

当我们希望跟踪对方法的调用,或者希望度量这些调用的开销时,我们肯定不希望将这些代码合并到业务代码中,此时我们就可以使用代理来实现。下面我们通过一个示例了解下代理

代码演示1

定义一个接口

1
2
3
4
public interface Behavior {
void eat();
void say(String str);
}

接口的实现类

1
2
3
4
5
6
7
8
9
10
11
12
public class Student implements Behavior {

@Override
public void eat() {
System.out.println("我是学生,我要吃学生餐");
}

@Override
public void say(String str) {
System.out.println(str);
}
}

定义一个代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class SimpleProxy implements Behavior {

private Behavior behavior;

public SimpleProxy(Student behavior) {
this.behavior = behavior;
}

@Override
public void eat() {
System.out.println("---- 方法执行前逻辑 ----");
behavior.eat();
System.out.println("---- 方法执行后逻辑 ----");
}

@Override
public void say(String str) {
System.out.println("---- 方法执行前逻辑 ----");
behavior.say(str);
System.out.println("---- 方法执行后逻辑 ----");
}
}

测试

1
2
3
4
5
6
7
@Test
public void test(){
SimpleProxy simpleProxy = new SimpleProxy(new Student());
simpleProxy.eat();
System.out.println("\n");
simpleProxy.say("hello,teacher");
}

打印

动态代理

Java的动态代理比代理的思想更进一步。因为它可以动态的创建代理并动态的处理对所代理方法的调用在动
态代理上所做的所有调用都会被重定到单一的调用处理器上他的工作是揭示调用的类型并确定相应的对策

实现Java动态代理的过程中涉及到一个接口 InvocationHandler 以及一个类 Proxy。

接口:InvocationHandler

InvocationHandler is the interface implemented by the invocation handler 
of a proxy instance.
译:
   InvocationHandler是代理实例化对象的调用处理程序(invocation handler)实现的接口。

Each proxy instance has an associated invocation handler.When a method is 
invoked on a proxy instance, the method invocation is encoded and dispatched 
to the {@code invoke}method of its invocation handler.
译:
    每个代理实例化对象都具有一个关联的调用处理程序(invocation handler)。当代理实例化对象
    调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法

InvocationHandler 仅有一个方法invoke()

Object invoke(Object proxy,Method method,Object[] args)throws Throwable
     * @param proxy 调用方法的代理实例化对象
     * @param method 指代的是我们所要调用真实对象的某个方法的Method对象
     * @param args 指代的是调用真实对象某个方法时接受的参数

类:Proxy

Proxy provides static methods for creating dynamic proxy classes and instances,
and it is also the superclass of all dynamic proxy classes created by those 
methods.
译:
    Proxy 提供了创建动态代理类和实例化对象的方法,同时也是这些方法创建的所有动态代理类的超类

方法:newProxyInstance

public static Object newProxyInstance(ClassLoader loader,
                              Class<?>[] interfaces,
                              InvocationHandler h)
                       throws IllegalArgumentException
 该方法返回一个指定接口的动态代理实例化对象,该接口可以将方法调用指派到指定的调用处理程
 序(invocation handler)

 参数:
    loader - 定义代理类的类加载器
    interfaces - 代理类要实现的接口列表
    h - 指派方法调用的调用处理程序

代码演示2

定义一个接口

1
2
3
4
public interface Behavior {
void eat();
void say(String str);
}

接口的实现类

1
2
3
4
5
6
7
8
9
10
11
12
public class Student implements Behavior {

@Override
public void eat() {
System.out.println("我是学生,我要吃学生餐");
}

@Override
public void say(String str) {
System.out.println(str);
}
}

创建一个动态代理处理器

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
/**
* Created by lizhen on 2018/9/3 下午8:51
* 定义一个动态代理处理器,该类必须实现 InvocationHandler 接口
*/
public class DynamicProxyHandler implements InvocationHandler {

// 目标对象
private Object target;

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

/**
* @param proxy 调用方法的代理实例
* @param method 指代的是我们所要调用真实对象的某个方法的Method对象
* @param args 指代的是调用真实对象某个方法时接受的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println("------- 方法调用前操作 ------");
System.out.println("proxy:" + proxy.getClass());
System.out.println("Method:" + method);
method.invoke(target, args);// 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
System.out.println("------- 方法调用后操作 ------");

return method.invoke(target, args);
}
}

测试

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
@Test
public void test() {

// 我们要代理的真实对象
Student student = new Student();

// 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
InvocationHandler handler = new DynamicProxyHandler(student);

/*
* 实例化代理对象
* loader:定义代理类的类加载器,一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
* interfaces: student.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
* h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
*/
Behavior proxies = (Behavior) Proxy.newProxyInstance(student.getClass().getClassLoader(), student.getClass().getInterfaces(), handler);
System.out.println(proxies.getClass().getName());

proxies.eat();

System.out.println("\n");
System.out.println("=========================== 看 我 华 不 华 丽 ================================");
System.out.println("\n");

proxies.say("hello,teacher");
}

打印

代码演示3

通过内部类定义具体的InvocationHandler

定义接口、实现类同上

创建 ProxyFactory

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
public class ProxyFactory {

// 目标对象
private Object target;

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

// 给目标对象生成代理对象(内部类)
public Object getProxyInstance(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------- 方法调用前操作 ------");
System.out.println("proxy:" + proxy.getClass());
if (args != null){
for (Object obj: args) {
System.out.println("args:" + obj);
}
}
System.out.println("Method:" + method);
method.invoke(target, args);// 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
System.out.println("------- 方法调用后操作 ------");

return null;
}
}
);
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void test(){
Student student = new Student();
ProxyFactory proxyFactory = new ProxyFactory(student);
Behavior behavior = (Behavior)proxyFactory.getProxyInstance();
System.out.println("\n");
System.out.println(behavior.getClass().getName());
System.out.println("\n");

behavior.eat();

System.out.println("\n");
System.out.println("=========================== 看 我 华 不 华 丽 ================================");
System.out.println("\n");

behavior.say("hello,teacher");
}

打印

Reference

1.java的动态代理机制详解 以及文章的评论使我受益匪浅

2. Java Platform SE 8

3.《Thinking In Java》