人的知识就好比一个圆圈,圆圈里面是已知的,圆圈外面是未知的。你知道得越多,圆圈也就越大,你不知道的也就越多。

0%

设计模式--代理模式

定义

代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。

角色

  • 抽象主题(Subject):声明真实对象和代理对象共同的接口。
  • 真实主题(Real Subject):业务逻辑的具体执行者。
  • 代理(Proxy):也叫做委托类、代理类,它负责对真实对象的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后工作。

类图

Proxy UML

实现

普通代理

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
public interface Subject {

void request();
}

public class RealSubject implements Subject {

public void request() {
System.out.println("real request");
}
}

public class Proxy implements Subject {
private Subject subject;

public Proxy(Subject subject) {
this.subject = subject;
}

public void request() {
System.out.println("before request");
subject.request();
System.out.println("after request");

}
}

jdk动态代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class SubjectInvocationHandler implements InvocationHandler {
private Subject subject;

public SubjectInvocationHandler(Subject subject) {
this.subject = subject;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before invoke");
Object result = method.invoke(subject, args);
System.out.println("after invoke");
return result;
}

public static void main(String[] args) {
Subject subject = new RealSubject();
SubjectInvocationHandler invocationHandler = new SubjectInvocationHandler(subject);
Subject proxy = (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(),
subject.getClass().getInterfaces(), invocationHandler);
proxy.request();
}
}

cglib动态代理

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

public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("before invoke");
Object result = proxy.invokeSuper(obj, args);
System.out.println("after invoke");
return result;
}

public static void main(String[] args) {
SubjectProxy subjectProxy = new SubjectProxy();

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback(subjectProxy);

Subject proxy = (RealSubject) enhancer.create();
proxy.request();
}
}

优缺点

优点

  • 能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。
  • 客户端可以针对抽象类进行编程,增加和更换代理类无需修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性。

缺点

  • 由于在客户端和真实类之间增加了代理类,因此有些类型的代理模式可能会造成请求的处理速度变慢,例如保护代理。
  • 实现代理模式需要额外的工作,而且有些代理模式的实现较为复杂,例如远程代理。

适用场景

  • 控制访问
  • 远程代理
    远程代理可以作为另一个JVM上对象的本地代表。调用代理的方法,会被代理利用网络转发到远程执行,并且结果会通过网络返回给代理,再由代理将结果转给客户。
  • 虚拟代理
    虚拟代理作为创建开销大的对象的代表。虚拟代理经常直到我们真正需要一个对象的时候才创建它。当对象在创建前和创建中时,由虚拟对象来扮演对象的替身。对象创建后,代理会将请求直接委托给对象。
  • 保护代理
  • 防火墙代理
    控制网络资源的访问,保护主题免于“坏客户”的侵害。
  • 智能引用代理
    当主题被引用时,进行额外的动作,例如计算一个对象被引用的次数。
  • 缓存代理
    为开销大的运算结果提供暂时存储;它也允许多个客户共享结果,以减少计算或网络延迟。
  • 同步代理
    在多线程的情况下为主题提供安全的访问。
  • 复杂隐藏代理
    用来隐藏一个类的复杂集合的复杂度,并进行访问控制。有时候也成为外观代理,这不难理解。复杂隐藏代理和外观模式是不一样的,因为代理控制访问,而外观模式只提供另一组接口。
  • 写入时复制代理
    用来控制对象的复制,方法是延迟对象的复制,知道客户真的需要为止。这是虚拟代理的变体。

模式应用

Spring AOP模式默认采用JDK动态代理来实现,可以配置为使用cglib动态代理。
JDK动态代理只能对实现了接口的类生成代理,而不能针对类;cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final。

Spring AOP代理类最终都是由AopProxyFactory创建的:

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
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 如果设置了代理应该执行积极的优化(默认为false),或显示设置使用cglib代理,或代理的对象没有实现接口
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
// 如果目标类是接口,或者已经是Jdk的动态代理类,则创建jdk动态代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}

/**
* Determine whether the supplied {@link AdvisedSupport} has only the
* {@link org.springframework.aop.SpringProxy} interface specified
* (or no proxy interfaces specified at all).
*/
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
}

}

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
...

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

...
}
小礼物走一走,来 Github 关注我