Java 动态代理与反射

代理

代理模式的特征是代理类与委托类有相同的接口。

为某个对象提供一个代理,通过代理类这一中间层,可以有效地隐藏和保护被代理的真实对象,以控制对这个对象的访问。

代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类以及事后处理等。

根据代理类的创建时间不同可以分为静态代理和动态代理。
静态代理:通过编写源代码,然后编译,在运行前代理类的字节码文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
在运行时加上这一句可以保存动态生成的代理类的字节码文件,再反编译后即可查看类文件。

静态代理

优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。

缺点:

  1. 代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
  2. 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度
  3. 如果要按照下面例子的方法使用代理模式,那么委托类必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个委托类必须对应一个代理类,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道委托类,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。

代理接口

1
2
3
4
5
6
/**
* 代理接口
*/
public interface ISubject {
void doSomething();
}

委托类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 委托类
*/
public class RealSubject implements ISubject {
@Override
public void doSomething() {
System.out.println("Read Subject do something");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 代理类
*/
public class SubjectProxy implements ISubject {
// 代理类持有一个委托类的对象引用
private RealSubject delegate;
public SubjectProxy(RealSubject delegate) {
this.delegate = delegate;
}
@Override
public void doSomething() {
long start = System.currentTimeMillis();
// 将消息转发给委托类处理
delegate.doSomething();
long end = System.currentTimeMillis();
System.out.println("Time cost: " + (end - start) + "ms");
}
}

静态代理工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 静态代理工厂
*/
public class SubjectStaticFactory {
/**
* 客户类调用工厂方法获得代理对象
* 对客户类来说,并不知道返回的是代理类对象还是委托类对象
* @return
*/
public static ISubject getInstance(){
return new SubjectProxy(new RealSubject());
}
}

客户类

1
2
3
4
5
6
7
public class Client {
public static void main(String[] args) {
ISubject proxy = SubjectStaticFactory.getInstance();
proxy.doSomething();
}
}

JDK动态代理

动态代理的机制

动态生成的代理类本身的特点:

  1. 包:如果所代理的接口都是 public 的,那么它将被定义在顶层包(即包路径为空),如果所代理的接口中有非 public 的接口(因为接口不能被定义为 protect 或 private,所以除 public 之外就是默认的 package 访问级别),那么它将被定义在该接口所在包(假设代理了 com.ibm.developerworks 包中的某非 public 接口 A,那么新生成的代理类所在的包就是 com.ibm.developerworks),这样设计的目的是为了最大程度的保证动态代理类不会因为包管理的问题而无法被成功定义并访问。
  2. 类修饰符:该代理类具有 final 和 public 修饰符,意味着它可以被所有的类访问,但是不能被再度继承。
  3. 类名:格式是“$ProxyN”,其中 N 是一个逐一递增的阿拉伯数字,代表 Proxy 类第 N 次生成的动态代理类,值得注意的一点是,并不是每次调用 Proxy 的静态方法创建动态代理类都会使得 N 值增加,原因是如果对同一组接口(包括接口排列的顺序相同)试图重复创建动态代理类,它会很聪明地返回先前已经创建好的代理类的类对象,而不会再尝试去创建一个全新的代理类,这样可以节省不必要的代码重复生成,提高了代理类的创建效率。
  4. 代理类的继承关系:继承Proxy类,并实现所有的代理接口。

代理类总是从排在最前面的接口中获取方法对象并分派给调用处理器,而无论代理类实例是否正在以该接口(或继承于该接口的某子接口)的形式被外部引用,因为在代理类内部无法区分其当前的被引用类型。

代理类实现的代理接口的特点

  1. 要注意不能有重复的接口,以避免动态代理类代码生成时的编译错误。
  2. 这些接口对于类装载器必须可见,否则类装载器将无法链接它们,将会导致类定义失败。
  3. 需被代理的所有非 public 的接口必须在同一个包中,否则代理类生成也会失败。
  4. 接口的数目不能超过 65535,这是 JVM 设定的限制。

优缺点

优点
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。

缺点
JDK动态代理始终无法摆脱仅支持 interface 代理的桎梏,因为它的设计注定了这个遗憾。回想一下那些动态生成的代理类的继承关系图,它们已经注定有一个共同的父类叫 Proxy。Java 的继承机制注定了这些动态代理类们无法实现对 class 的动态代理,原因是多继承在 Java 中本质上就行不通。

Java API

Proxy

java.lang.reflect.Proxy是Java动态代理机制生成的所有代理类的父类,提供了一组静态方法为一组接口动态地生成代理对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
// Proxy类的静态方法
// 获取指定代理对象所关联的调用处理器
public static InvocationHandler getInvocationHandler(Object proxy)
// 获取指定类加载器和一组接口的动态代理类的类对象
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
// 判断指定类对象是否是一个动态代理类
public static boolean isProxyClass(Class<?> cl)
// 生成动态代理类的实例,传入指定的类加载器、一组接口及调用处理器
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

InvocationHandler

调用处理器接口java.lang.reflect.InvocationHandler,自定义了一个invoke方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理类对象时都要指定一个对应的调用处理器对象。

1
2
3
4
5
6
/**
* proxy: 代理类的实例
* method: 被调用的方法对象
* args: 调用参数
*/
public Object invoke(Object proxy, Method method, Object[] args)

Classloader

类加载器java.lang.Classloader,负责将字节码装在到Java虚拟机中并为其定义Class对象,然后该类才能被使用。

Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中
每次生成动态代理类对象时都需要指定一个类装载器对象。

动态代理实现步骤

具体步骤是:

  1. 实现InvocationHandler接口创建自己的调用处理器
  2. 给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类
  3. 以调用处理器类型为参数,利用反射机制得到动态代理类的构造函数
  4. 以调用处理器对象为参数,利用动态代理类的构造函数创建动态代理类对象

分步骤实现动态代理

1
2
3
4
5
6
7
8
// Step 1
InvocationHandler handler = new InvocationHandlerImpl(..);
// Step 2
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });
// Step 3
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
// Step 4
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });

完整实例

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
public class DynamicProxyTest {
// 可以通过代理工厂来屏蔽客户类具体的创建过程和返回的到底是代理类对象还是委托类对象。
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// Step 1. 实现InvocationHandler
InvocationHandler invocationHandler = new MyInvocationHandler(new RealSubject());
// Step2. 获取代理类的Class对象
Class<?> cls = Proxy.getProxyClass(DynamicProxyTest.class.getClassLoader(), ISubject.class);
// Step3. 反射得到构造函数对象
Constructor constructor = cls.getConstructor(InvocationHandler.class);
// Step4. 反射创建代理类对象
ISubject proxy = (ISubject) constructor.newInstance(invocationHandler);
proxy.doSomething();
}
static class MyInvocationHandler implements InvocationHandler{
private Object delegate;
public MyInvocationHandler(Object delegate) {
this.delegate = delegate;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
// 利用反射机制将请求分派给委托类处理。
// Method的invoke返回Object对象作为方法执行结果
// 因为示例程序没有返回值,所以这里忽略了返回值处理
method.invoke(delegate, args);
long end = System.currentTimeMillis();
System.out.println("Time cost: "+(end - start)+"ms");
return null;
}
}
}

简化的动态代理实现

1
2
3
4
// Step 1. 实现调用处理器
InvocationHandler handler = new InvocationHandlerImpl(..);
// Step2. 创建代理类实例
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, new Class[] { Interface.class }, handler );

Cglib动态代理

JDK的动态代理机制只能代理实现了接口的类,而那么没有实现接口的委托类就不能通过JDK的完成动态代理。
Cglib动态代理是继承代理,它的底层使用ASM(JAVA字节码处理框架)在内存中动态的生成被代理类的子类,重写并增强方法的功能。但因为采用的是继承,所以不能对final修饰的类进行代理。

cglib实现动态代理的步骤:

  1. 实现MethodInterceptor接口
  2. 通过Enhancer实现代理类的创建(生成一个继承了客户类的子类)

1.委托类

1
2
3
4
5
6
public class RealSubject {
public void doSomething() {
System.out.println("Real Subject: do something");
}
}

2.Cglib代理

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
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class SubjectProxy implements MethodInterceptor{
private Object target;
/**
* 创建代理对象
* @return
*/
public Object getInstance(Object target){
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回调方法,方法拦截器
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
/**
* 回调方法
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
long start = System.currentTimeMillis();
methodProxy.invokeSuper(o, args);
long end = System.currentTimeMillis();
System.out.println("Cglib proxy, time cost:" + (end - start) + "ms");
// 这里调用的doSomething无返回值,故使用return null
return null;
}
}


感谢:
http://blog.jrwang.me/2015/java-dynamic-proxy-and%20java-reflection/
http://blog.csdn.net/giserstone/article/details/17199755