代理模式

定义

给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用

意义

通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性

实现

静态代理

静态代理由业务实现类/业务代理类两部分组成.

  • 业务实现类负责实现主要的业务方法.
  • 业务代理类负责对调用的业务方法作拦截/过滤/等预处理.

在需要调用业务时,不是直接通过业务实现类来调用的,而是通过业务代理类的同名方法来调用被处理过的业务方法

代码实现

  1. 定义业务接口
1
2
3
public interface IBookStore{
void addBook();
}
  1. 定义业务实现类
1
2
3
4
5
6
7
public class BookStoreImpl implements IBookStore{
@Override
public void addBook(){
// dosomething
System.out.println("add a new book.");
}
}
  1. 定义业务代理类
1
2
3
4
5
6
7
8
9
10
11
12
public class BookStoreProxy implements IBookStore {
private BookStoreImpl bookStoreImpl;
public BookStoreProxy(BookStoreImpl bookStoreImpl){
this.bookStoreImpl = bookStoreImpl;
}
@Override
public void addBook(){
// do more things before
bookStoreImpl.addBook();
// do more things after
}
}
  1. 调用
1
2
3
4
5
public static void main(String[] args) {
IBookStore bookstore = new BookStoreImpl();
BookStoreProxy proxy = new BookStoreProxy(bookstore);
proxy.addBook();
}

缺点

静态代理的缺点很明显: 一个代理类只能对一个业务接口的实现类进行包装, 如果有多个业务接口的话就要定义很多实现类和代理类才行. 而且如果代理类对业务方法的预处理/调用后操作都是一样的(比如: 调用前输出提示、调用后自动关闭连接),则多个代理类就会有很多重复代码.
这时我们可以定义这样一个代理类, 它能代理所有实现类的方法调用: 根据传进来的业务实现类和方法名进行具体调用. - 这就是动态代理.

JDK 动态代理

JDK 动态代理所用到的代理类在程序调用到代理类对象时才由 JVM 真正创建,JVM 根据传进来的"业务实现类对象"以及"方法名",动态地创建了一个代理类的 class 文件并被字节码引擎执行,然后通过该代理类对象进行方法调用.
我们需要做的只需指定代理类的预处理/调用后操作即可.

代码实现

  1. 定义业务接口 (同上)
  2. 定义业务实现类 (同上)
  3. 创建动态代理类 实现 调用管理接口 InvocationHandler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 注意 这里假设是实现 book 业务相关的代理,表示相关的处理逻辑 不是作为 bookstore 专属的代理
public class BookProxy implements InvocationHandler {
// 被代理对象
private Object target;

// 绑定对象 返回代理对象
public Object bind(Object target) {
// 参数: ClassLoader loader 加载 被代理类的 类加载器
// 参数: Class<?>[] interfaces 被代理类 要实现的接口列表
return new Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), this);
}

// 包装调用方法:进行预处理/调用后处理
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
// do more things before
result = method.invoke(target, args);
// do more things after
return result;
}
}
  1. 调用
1
2
3
4
5
6
public static void main(String[] args) {
BookStoreImpl bookStoreImpl = new BookStoreImpl();
BookProxy proxy = new BookProxy();
IBookStore bookStore = (IBookStore) proxy.bind(bookStoreImpl);
bookStore.addBook();
}

缺点

JDK 动态代理的代理对象在创建时需要使用业务实现类所实现的接口作为参数.
如果业务实现类是没有实现接口而是直接定义业务方法的话, 就无法使用 JDK 动态代理了.
而且 如果业务实现类中新增了接口中没有的方法, 这些方法是无法被代理的(因为无法被调用)

cglib 动态代理

cglib 是针对类来实现代理的, 原理是对指定的业务类生成一个子类, 并覆盖其中的业务方法来实现代理. 因为采用的是继承, 所以不能对final修饰的类进行代理.

代码实现

  1. 定义业务类 无需实现接口
1
2
3
4
5
6
7
8
public class BookStoreImpl2 {
public void addBook() {
System.out.println("BookStoreImpl2.addBook ...");
}
public void delBook(){
System.out.println("BookStoreImpl2.delBook ...");
}
}
  1. 创建代理类 实现 MethodInterceptor 方法代理接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class BookCglib implements MethodInterceptor {
private Object target; // 业务类对象,供代理方法中进行真正的业务方法调用

// 相当于JDK动态代理中的绑定
public Object getInstance(Object target) {
this.target = target; // 给业务对象赋值
Enhancer enhancer = new Enhancer(); // 创建加强器,用来创建动态代理类
enhancer.setSuperclass(this.target.getClass()); // 为加强器指定要代理的业务类
// 设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept() 方法进行拦截
enhancer.setCallback(this);
// 创建动态代理类对象并返回
return enhancer.create();
}

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// do more things before
methodProxy.invokeSuper(o, objects); // 调用业务类(父类)的方法
// do more things after
return null;
}
}
  1. 调用
1
2
3
4
5
6
7
public static void main(String[] args) {
BookStoreImpl2 bookStore = new BookStoreImpl2();
BookCglib cglib = new BookCglib();
BookStoreImpl2 bookStoreCglib = (BookStoreImpl2)cglib.getInstance(bookStore);
bookStoreCglib.addBook();
bookStoreCglib.delBook();
}

缺点

因为 cglib 动态代理的实现原理是生成子类, 所以对于 final 修饰的方法无法代理.