设计模式系列:代理模式

概念

为另外一个对象提供一个替身或者占位符以控制对这个对象的访问。

实现

静态代理

  • 类图:

图片

Car:

1
2
3
4
5
6
7
8
9
package com.littlehui.design.proxy.statics;

/**
* Created by littlehui on 2018/1/16.
*/
public interface Car {

public void run();
}

Bus:

1
2
3
4
5
6
7
8
9
10
11
package com.littlehui.design.proxy.statics;

/**
* Created by littlehui on 2018/1/16.
*/
public class Bus implements Car {

public void run() {
System.out.println("bus run");
}
}

BusProxy:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.littlehui.design.proxy.statics;

/**
* Created by littlehui on 2018/1/16.
*/
public class BusProxy implements Car {

public void run() {
System.out.println("car proxy");
Car bus = new Bus();
bus.run();
}
}

Client:

1
2
3
4
5
6
7
8
9
10
11
12
package com.littlehui.design.proxy.statics;

/**
* Created by littlehui on 2018/1/16.
*/
public class Client {

public static void main(String[] args) {
Car bus = new BusProxy();
bus.run();
}
}
  • car为接口
  • Bus实现car的run方法
  • BusProxy负责控制Bus访问方法。

动态代理

  • 类图:

图片

动态代理是JDK支持的一种方式 实现例子如下:

Car

1
2
3
4
5
6
7
8
package com.littlehui.design.proxy.dymatic;

/**
* Created by littlehui on 2018/1/16.
*/
public interface Car {
public void run();
}

Bus:

1
2
3
4
5
6
7
8
9
10
package com.littlehui.design.proxy.dymatic;

/**
* Created by littlehui on 2018/1/16.
*/
public class Bus implements Car {
public void run() {
System.out.println("car run");
}
}

BusProxyFactory:

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
package com.littlehui.design.proxy.dymatic;

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

/**
* Created by littlehui on 2018/1/16.
*/
public class BusProxyFactory {

private Object target;

public BusProxyFactory(Object car) {
this.target = car;
}

public Object getNewInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始事务");
//执行目标对象方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务");
return returnValue;
}
}
);
}
}

Client:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.littlehui.design.proxy.dymatic;


/**
* Created by littlehui on 2018/1/16.
*/
public class Client {

public static void main(String[] args) {
Car bus = new Bus();
Car newBus = (Car) new BusProxyFactory(bus).getNewInstance();
System.out.println(newBus.getClass());
newBus.run();
}
}

CGLIB动态代理,引入包spring-core-XX.jar
BusProxyFactoryCglib

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
package com.littlehui.design.proxy.dymatic;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
* Created by littlehui on 2018/1/16.
*/
public class BusProxyFactoryCglib 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();
}

//CGLIB的特有方式,不指定 具体对象,只指定类
public Object getInstanceByClass(Class targetClass) {
//创建加强器,用来创建动态代理类
Enhancer enhancer = new Enhancer();
//为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
enhancer.setSuperclass(targetClass);
//设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
enhancer.setCallback(this);
// 创建动态代理类对象并返回
return enhancer.create();
}

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("预处理");
methodProxy.invokeSuper(o, objects); //调用业务类(父类中)的方法
System.out.println("调用后操作");
return null;
}
}

Client:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.littlehui.design.proxy.dymatic;


/**
* Created by littlehui on 2018/1/16.
*/
public class Client {

public static void main(String[] args) {
Car bus = new Bus();
Car newBus = (Car) new BusProxyFactory(bus).getNewInstance();
System.out.println(newBus.getClass());
newBus.run();
System.out.println("-----------cglib------------");
Car cglibBus = (Car)new BusProxyFactoryCglib().getInstanceByClass(Bus.class);
cglibBus.run();
System.out.println(cglibBus.getClass());
}
}

result:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/bin/java -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:62751,suspend=y,server=n -Dfile.encoding=UTF-8 -classpath "/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/lib/tools.jar:/Users/littlehui/WorkSpaces/Home/pattern/proxy/target/classes:/Users/littlehui/software/repository/org/springframework/spring-core/5.0.0.RC3/spring-core-5.0.0.RC3.jar:/Users/littlehui/software/repository/org/springframework/spring-jcl/5.0.0.RC3/spring-jcl-5.0.0.RC3.jar:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar" com.littlehui.design.proxy.dymatic.Client
Connected to the target VM, address: '127.0.0.1:62751', transport: 'socket'
class com.sun.proxy.$Proxy0
开始事务
car run
提交事务
-----------cglib------------
预处理
Disconnected from the target VM, address: '127.0.0.1:62751', transport: 'socket'
car run
调用后操作
class com.littlehui.design.proxy.dymatic.Bus$$EnhancerByCGLIB$$1e8a65c7

Process finished with exit code 0

Cglib生成的动态代理类是业务类的子类,重写业务方法进行代理。
可以看到CGLIB调用的 class是 class com.littlehui.design.proxy.dymatic.Bus 这个在Spring类的装配和其他涉及获取Class的地方相当有用。

  • car为接口
  • Bus实现Car的run方法
  • Bus对象的访问交给了BusProxyFactory控制。
  • BusProxyFactory执行中动态地加入了run方法执行前后的标识。

场景

  • Spring AOP
  1. 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
  2. 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
  3. 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

总结

代理和装饰模式看起来很相似,但是深入理解,本质上是有区别的 主要的区别是:
使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。也就是那句话:代理模式可以控制被代理的对象
可以控制被代理对象的访问,而装饰模式是被装饰对象的增强。不体现在控制上。