代理模式

在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。

介绍

意图:为其他对象提供一种代理以控制对这个对象的访问。

主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

何时使用:想在访问一个类时做一些控制。

如何实现:增加中间层,实现与被代理类组合。

应用实例:

  • Windows 里面的快捷方式。

  • 猪八戒去找高翠兰结果是孙悟空变的,可以这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰的时候看不出来这个是孙悟空,所以说孙悟空是高翠兰代理类。

  • 买火车票不一定在火车站买,也可以去代售点。

  • 一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。

  • spring 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
39
40
41
42
43
44
45
46
public interface Image {
void display();
}
public class RealImage implements Image {

private String fileName;

public RealImage(String fileName){
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
private void loadFromDisk(String fileName){
System.out.println("Loading " + fileName);
}
}
public class ProxyImage implements Image{

private RealImage realImage;

private String fileName;

public ProxyImage(String fileName){
this.fileName = fileName;
}
@Override
public void display() {
if(realImage == null){
realImage = new RealImage(fileName);
}
realImage.display();
}
}
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test_10mb.jpg");//Loading test_10mb.jpg
// 图像将从磁盘加载
image.display();//Displaying test_10mb.jpg
System.out.println("");
// 图像不需要从磁盘加载
image.display();//Displaying test_10mb.jpg
}
}

JDK动态代理

  • java.lang.reflect.Proxy:生成动态代理类和对象;
  • java.lang.reflect.InvocationHandler(处理器接口):可以通过invoke方法实现对真实角色的代理访问。
  • 每次通过Proxy生成的代理类对象都要指定对应的处理器对象。
1
2
3
4
5
// InvocationHandler接口
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
  • proxy表示被代理的对象
  • method表示被代理对象的方法
  • args表示方法的参数
1
2
// Proxy类的newProxyInstance方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
  • loader表示委托类的类加载器
  • interfaces表示委托类实现的接口(这就说明委托类一定要实现了接口才能使用)
  • h表示InvocationHandler接口的子实例,也就是动态代理类的实例

接口

1
2
3
4
5
public interface Subject {
public int sellBooks();

public String speak();
}

真实对象

1
2
3
4
5
6
7
8
9
10
11
12
13
public class RealSubject implements Subject{
@Override
public int sellBooks() {
System.out.println("卖书");
return 1 ;
}

@Override
public String speak() {
System.out.println("说话");
return "张三";
}
}

处理器对象

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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
* 定义一个处理器
* @author gnehcgnaw
* @date 2018/11/5 19:26
*/
public class MyInvocationHandler implements InvocationHandler {
/**
* 因为需要处理真实角色,所以要把真实角色传进来
*/
Subject realSubject ;

public MyInvocationHandler(Subject realSubject) {
this.realSubject = realSubject;
}

/**
*
* @param proxy 代理类
* @param method 正在调用的方法
* @param args 方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("调用代理类");
if(method.getName().equals("sellBooks")){
int invoke = (int)method.invoke(realSubject, args);
System.out.println("调用的是卖书的方法");
return invoke ;
}else {
String string = (String) method.invoke(realSubject,args) ;
System.out.println("调用的是说话的方法");
return string ;
}
}
}

调用端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.lang.reflect.Proxy;

/**
* 调用类
* @author gnehcgnaw
* @date 2018/11/7 20:26
*/
public class Client {
public static void main(String[] args) {
//真实对象
Subject realSubject = new RealSubject();

MyInvocationHandler myInvocationHandler = new MyInvocationHandler(realSubject);
//代理对象
Subject proxyClass = (Subject)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Subject.class}, myInvocationHandler);

proxyClass.sellBooks();

proxyClass.speak();
}
}

优化实现

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
public class CarProxy implements InvocationHandler {
private Object target;
/**
* 绑定一个委托对象并获得一个代理类对象
* @param target [description]
* @return [description]
*/
public Object bind(Object target) {
this.target = target;
// 取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
this);
}
@Override
//这个方法并不是我们自己去调用
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("该客户是否是vip客户");
// 执行委托类的方法
Object result = method.invoke(target,args);
System.out.println("该客户买车完成");
return result;
}
}
public class TestProxy {
public static void main(String[] args) {
CarProxy cp = new CarProxy();
// 传入一个实现了该接口的实例就行
Car car = (Car)cp.bind(new CarImp1());
// Car car = (Car)cp.bind(new CarImp2());
car.buyCar();
}
}

Cglib动态代理

Cglib动态代理是针对代理的类,动态生成一个子类,然后子类覆盖代理类中的方法,如果是private或是final类修饰的方法,则不会被重写。(方法拦截技术拦截所有父类方法的调用)

CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。

CGLIB作为一个开源项目,其代码托管在github,地址为:https://github.com/cglib/cglib

需要代理的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Engineer {
// 可以被代理
public void eat() {
System.out.println("工程师正在吃饭");
}

// final 方法不会被生成的字类覆盖
public final void work() {
System.out.println("工程师正在工作");
}

// private 方法不会被生成的字类覆盖
private void play() {
System.out.println("this engineer is playing game");
}
}

Cglib代理类

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
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class CglibProxy implements MethodInterceptor {
private Object target;

public CglibProxy(Object target) {
this.target = target;
}

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("### before invocation");
Object result = method.invoke(target, objects);
System.out.println("### end invocation");
return result;
}

public static Object getProxy(Object target) {
Enhancer enhancer = new Enhancer();
// 设置需要代理的对象
enhancer.setSuperclass(target.getClass());
// 设置代理人
enhancer.setCallback(new CglibProxy(target));
return enhancer.create();
}
}

测试方法

1
2
3
4
5
6
7
8
9
10
11
import java.lang.reflect.Method;
import java.util.Arrays;

public class CglibMainTest {
public static void main(String[] args) {
// 生成 Cglib 代理类
Engineer engineerProxy = (Engineer) CglibProxy.getProxy(new Engineer());
// 调用相关方法
engineerProxy.eat();
}
}

运行结果

1
2
3
###   before invocation
工程师正在吃饭
### end invocation

优化实现

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
public class CglibProxy implements MethodInterceptor {

private Enhancer enhancer = new Enhancer();

/**
* 获得代理类
* @return
*/
public Object getProxy(Class<?> c) {
// 设置创建子类的类
enhancer.setSuperclass(c);
// 回调方法
enhancer.setCallback(this);
// 返回代理对象
return enhancer.create();
}
@Override
/**
* 拦截所有目标方法的调用
* obj 目标类的实例
* m 目标方法的反射对象
* args 方法的参数
* proxy 代理类的实例
*/
public Object intercept(Object obj, Method m, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("火车启动啦!!");
// 代理类调用父类的方法
Object result = proxy.invokeSuper(obj, args);
System.out.println("火车停止啦!!");
return result;
}
}
public class Test {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
Train t = (Train)proxy.getProxy(Train.class);
t.move();
}
}
0%