代理设计模式

前言

代理设计模式原理:使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

静态代理和动态代理:

  • 静态代理:一个代理只能服务于一种类型的对象,代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。
  • 动态代理:动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时 根据需要动态创建目标类的代理对象。

应用场景:Spring的AOP,面向切面编程,实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术。

  • AOP 采取横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。

  • 主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。

  • 简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能

静态代理

代理模式可以在不修改被代理对象的基础上,通过代理对象进行一些功能的附加与增强。静态代理只适合业务功能固定不变的情况。

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
package cn.lrwjz.reflection;

public class StaticProxyTest {
public static void main(String[] args) {
AnTaClothFactory anta = new AnTaClothFactory(); // 创建被代理对象
proxyClothFactory proxyClothFactory = new proxyClothFactory(anta); // 创建代理对象
proxyClothFactory.produceCloth();

}
}

interface ClothFactory{
void produceCloth();
}

class proxyClothFactory implements ClothFactory{

private ClothFactory clothFactory; // 用被代理类对象实例化

public proxyClothFactory(ClothFactory clothFactory) {
this.clothFactory = clothFactory;
}

@Override
public void produceCloth() {
System.out.println("代理前所做的事情。。。。");
clothFactory.produceCloth();
System.out.println("代理完成所做的事情。。。。");
}
}

class AnTaClothFactory implements ClothFactory{

@Override
public void produceCloth() {
System.out.println("生成安踏品牌衣服!!!");
}
}

动态代理

动态代理实现步骤:

  • 使用JDK代理类java.lang.reflect.Proxy(Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类)。
  • 调用Proxy类中的newProxyInstance静态方法。
  • 实现InvocationHandle接口,该接口所在位置为:java.lang.reflect.InvocationHandler。
  • 重写接口中的**Object invoke(Object proxy, Method method, Object[] args)**方法。
  • 返回一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口。

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)throws IllegalArgumentException

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

Object invoke(Object proxy, Method method, Object[] args)

  • Ojbect proxy:表示需要代理的对象
  • Method method:表示要操作的方法
  • Object[] args:method方法所需要传入的参数(可能没有为,null.也可能有多个)
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package cn.lrwjz.reflection;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicProxyTest {
public static void main(String[] args) {
// 创建需要被代理的对象
SuperMan superMan = new SuperMan();
// 代理对象
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
String belief = proxyInstance.getBelief();
System.out.println(belief);
proxyInstance.eat("柠檬");

}
}

interface Human{
String getBelief();
void eat(String food);
}

// 被代理类
class SuperMan implements Human{

@Override
public String getBelief() {
return "I believe I can save the world!";
}

@Override
public void eat(String food) {
System.out.println("I like eat " + food);
}
}

class ProxyFactory{
// 调用此方法,返回一个代理类的对象。
public static Object getProxyInstance(Object obj){
MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
myInvocationHandler.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), myInvocationHandler);
}
}

class MyInvocationHandler implements InvocationHandler {
private Object obj; // 需要使用被代理类的对象进行赋值

public void bind(Object obj){
this.obj = obj;
}

// 当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
// 将被代理类要执行的方法a的功能就声明在invoke()中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
// obj:被代理类的对象
// args:方法的参数
Object returnValue = method.invoke(obj, args);

// 上述方法的返回值就作为当前类中的invoke()的返回值。
return returnValue;
}
}

总结

动态代理和静态代理相比较,最大的好处就是接口中声明的所有的方法都被转移到一个集中的方法中去处理,就是invocke()方法,这样在接口中声明的方法比较多的情况下我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。

动态代理只能代理接口,代理类都需要实现InvocationHandler类,实现invoke方法。该invoke方法就是调用被代理接口的所有方法时需要调用的,该invoke方法的返回值是被代理接口的一个实现类。

JDK动态代理和CGLIB动态代理的区别主要有以下几点:

  • JDK动态代理要求被代理的对象实现一个或多个接口,而CGLIB动态代理不要求被代理的对象实现接口。
  • JDK动态代理是通过实现接口来进行代理的,而CGLIB动态代理是通过继承类来进行代理的。
  • JDK动态代理是JDK自带的功能,不需要引入外部库,而CGLIB动态代理是一个第三方库,需要引入依赖。
  • JDK动态代理只能对方法进行拦截,而CGLIB动态代理可以对方法和属性进行拦截。