代理类

了解动态代理类,首先得明确代理类的概念。
已知实现了某个接口的目标类,要给目标类的某个方法加上一些额外的功能,比如异常处理、输出日志、输出方法运行时间、事务管理等等。这时候,就可以编写一个同样实现该接口的类,这个类调用目标类的相同方法,并在调用时加上这些功能代码。这个类就是代理类,相当于目标类的封装

如有接口:

interface - Book.java
1
2
3
public interface Book {
void getName();
}

其实现类:
impl - OneBook.java
1
2
3
4
5
public class OneBook implements Book {
public void getName() {
System.out.println("One Book");
}
}

创建一个代理类来调用OneBook:
class - ProxyBook.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ProxyBook implements Book {
private OneBook ob;//将OneBook实例传入
public ProxyBook(OneBook ob){
this.ob = ob;
}
public void getName() {
beforeGetName();//调用之前
ob.getName();
afterGetName();//调用之后
}
private void afterGetName() {
System.out.println("--after get name--");
}
private void beforeGetName(){
System.out.println("--before get name--");
}
}

那么,客户端就可以调用ProxyBook来间接调用OneBook了:
1
2
Book proxy = new ProxyBook(new OneBook());
proxy.getName();


由以上代码可以看出,客户端实际需要调用的是OneBook类的getName方法,现在用ProxyBook来代理调用,同样达到目的,同时还封装了其他方法,可以处理一些其他问题。 代理类的各个方法除了要调用目标类的相应方法和对外返回目标类方法的返回结果之外,还可以在代理方法的如下位置上添加一些功能代码:

  1. 在调用目标方法之前;
  2. 在调用目标方法之后;
  3. 在调用目标方法前后;
  4. 在调用目标方法的异常catch块中

AOP

Aspect Oriented Programming,面向方面编程
一个系统中可能存在多个交叉业务,如安全、事务、日志。

这些交叉业务就像一个个切面贯穿在各个模块中。如何将这些交叉业务模块化,这就是AOP。
而代理技术就很好的解决这种需求,代理是实现AOP功能的核心和关键技术

动态代理类

如果要按照上面的方法使用代理,那么目标类必须是事先已经存在的,并将其作为参数传递到代理的内部属性。要为系统中各种接口的类都增加代理功能,可想而知,那将会是类爆炸。 JVM在运行时可以动态的生成类对象,这种动态类往往被用作代理类,这就是动态代理类。但是,JVM生成的动态类必须实现一个或多个接口,所以动态代理类只能用作具有相同接口的目标类的代理。 说明:开源的CGLIB库可以动态的生成一个类的子类,类的子类当然也可以用作该类的代理,所以要为没有接口的类生成动态代理类,可以用CGLIB库。 动态代理类位于java.lang.reflect包中,ProxyInvocationHandler

InvocationHandler

代理实例的调用处理程序。 每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。 invoke(Object proxy, Method method, Object[] args)
在代理实例上处理方法调用并返回结果。

Proxy

getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类的类对象,需要一个类加载器和目标类所实现的接口的数组。 newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):获得指定接口的代理类的实例,需要类加载、目标所实现的接口数组和一个指定接口的调用处理程序 上面的例子用动态代理:
代理的调用处理程序

class - BookHandler.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class BookHandler implements InvocationHandler {
Object target;
public BookHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
beforeMethod(); //之前
Object returnVal = method.invoke(target, args);//调用目标方法
afterMethod(); //之后
return returnVal; //返回目标方法运行结果
}
private void afterMethod() {
System.out.println("--after method--");
}
private void beforeMethod() {
System.out.println("--before method--");
}
}

客户端
class - DynamicProxyBook.java
1
2
3
4
5
6
7
8
9
10
11
import java.lang.reflect.*;
public class DynamicProxyBook {
public static void main(String[] args) {
Object target = new OneBook(); // 目标类的实例
Object proxy = Proxy.newProxyInstance( // 一次性生成代理类实例
target.getClass().getClassLoader(), // 目标类的类加载器
target.getClass().getInterfaces(), // 目标类的所实现的接口数组
new BookHandler(target)); // 将目标类实例传入
((Book) proxy).getName(); //调用目标方法
}
}

通过这种方式,被代理的对象target可以在运行时动态改变,需要调用接口可以在运行时改变,代理类的控制器(BookHandler)也可以动态改变,从而实现了非常灵活的动态代理关系。