0%

设计模式-代理模式

为某个类提供代理,以控制对这个类的访问

代理模式

问题:有时直接访问对象时带来一些问题,比如对象创建开销很大,或者某些主业务前后需要别的逻辑。

解决方案:图片的加载很慢,可以默认图片先代替。主业务需要有事务控制、权限判断、日志记录。

优点:

​ 1.职责清晰,程序员在业务类中只有增删改的具体实现,事务、权限、日志通过代理(AOP)织入业务类,事务、权限、日志的具体实现放在事务、权限、日志各自的类中。

​ 2.增加了扩展性,事务、权限、日志可作为框架引入,并且引入的框架可自由组合。

1.静态代理:

静态代理在使用时,需要定义接口,被代理对象与代理对象一起实现相同的接口。

局限性:

​ 如果同时代理多个类,依然会导致类无限扩展

​ 如果类中有多个方法,同样的逻辑需要反复实现

例子:追求者找两个同学送礼物给女神,两个同学顺序可变

Implementation

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//女神
public class Girl {
private String name;

public Girl(String name){
this.name = name;
}

public String getName() {
return name;
}
}

//送礼物 被代理对象的接口
public interface SendGift {
void sendHower();
}

//追求者
public class Persuit implements SendGift{
private Girl girl;

public Persuit(Girl girl){
this.girl = girl;
}

@Override
public void sendHower() {
System.out.println(girl.getName() + "给你花");

}
}

//学生1
public class Student1 implements SendGift{

private SendGift sendGift;

public Student1(SendGift sendGift){
this.sendGift = sendGift;
}

@Override
public void sendHower() {
System.out.println("我是同学1,我是帮忙的");

sendGift.sendHower();

System.out.println("我是同学1,我帮忙结束");
}
}

public class Student2 implements SendGift {
private SendGift sendGift;

public Student2(SendGift sendGift){
this.sendGift = sendGift;
}

@Override
public void sendHower() {
System.out.println("我是同学2,我是帮忙的");

sendGift.sendHower();

System.out.println("我是同学2,我帮忙结束");
}
}

//测试类
public class Client {
public static void main(String[] args) {
Girl girl = new Girl("zs");
Persuit persuit = new Persuit(girl);

//先学生1,后学生2
Student2 student2 = new Student2(persuit);
Student1 student1 = new Student1(student2);
student1.sendHower();

//先学生2,后学生1
Student1 student1 = new Student1(persuit);
Student2 student2 = new Student2(student1);
student2.sendHower();
}
}

测试结果:

//先学生1,后学生2
我是同学1,我是帮忙的
我是同学2,我是帮忙的
zs给你花
我是同学2,我帮忙结束
我是同学1,我帮忙结束

//先学生2,后学生1
我是同学2,我是帮忙的
我是同学1,我是帮忙的
zs给你花
我是同学1,我帮忙结束
我是同学2,我帮忙结束

测试类执行结果1的部分解释:

​ student1.sendHower();

​ System.out.println(“我是同学1,我是帮忙的”);

​ sendGift.sendHower();

/*
sendGift → student2
student2传入Student1的构造方法  public Student1(SendGift sendGift){
    this.sendGift = sendGift;
})
上边的sendGift.sendHower();即student2.sendHower();
*/

System.out.println(“我是同学2,我是帮忙的”);

再往下一样

2.动态代理

首要条件:被代理类必须实现一个接口。

应用场景:日志系统、事务提交或回退、拦截器、权限控制等。面对切面编程AOP的核心就是动态代理机制。

静态代理中每个代理类只能为一个接口服务,这样程序开发中必然会产生许多的代理类。所以我们想通过一个代理类完成全部的代理功能,那么我们就需要用动态代理.

动态代理是在运行时,通过反射机制实现动态代理,能够代理各种类型的对象

在Java中实现动态代理机制,需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy类的支持。

java.lang.reflect.InvocationHandler接口的方法

1
2
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
  • Object proxy
    被代理对象

  • Method method
    要调用的方法

  • Object[] args
    方法调用时所需要的参数

    @return 被代理接口的实现类

java.lang.reflect.Proxy类中重要的方法

1
2
3
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,
InvocationHandler h)throws IllegalArgumentException
  • ClassLoader loader
    类加载器
  • Class<?>[] interfaces
    得到被代理类全部的接口
  • InvocationHandler h
    得到InvocationHandler接口的子类的实例

Implementation

1
2
3
4
//接口主题类
public interface Subject {
public void request();
}
1
2
3
4
5
6
7
//具体主题类
public class ConcreteSubject implements Subject {
@Override
public void request() {
System.out.println("执行request方法");
}
}
1
2
3
4
5
6
//无接口的鱼类
public class Fish {
public void getWater(){
System.out.println("给我水");
}
}
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 class ProxyHandler implements InvocationHandler {

//用来代表被代理对象
private Object target;

/**
* @parm target 被动态代理类的实例
* @return 动态代理类的实例
*/
public Object newProxyInstance(Object target) {
this.target = target;

return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}

/**
* 重写InvocationHandler类的invoke方法,在被代理对象的方法中嵌入代码
*
* @param proxy 被代理对象
* @param method 要控制的被代理对象的方法
* @param args 被代理对象方法需要的参数
* @return Object 被代理接口的实现类。
*
* @see InvocationHandler#invoke(Object, Method, Object[])
* @see java.lang.reflect.Proxy
*/

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("被代理对象方法执行前");

Object ret = null;
try {
//调用目标方法
ret = method.invoke(target, args);
} catch (Exception e) {
System.out.println("调用发生异常");
throw e;
}

System.out.println("被代理对象方法执行后");

return ret;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
//客户端测试类
public class Client {
public static void main(String[] args) {
ProxyHandler handler = new ProxyHandler();
Subject subject = (Subject) handler.newProxyInstance(new ConcreteSubject());
subject.request();

ProxyHandler handler2 = new ProxyHandler();
//不能代理无接口的类Fish,Proxy中target.getClass().getInterfaces()报错
Fish fish = (Fish) handler.newProxyInstance(new ConcreteSubject());
fish.getWater();
}
}

测试的控制台显示:

1
2
3
4
5
被代理对象方法执行前
执行request方法
被代理对象方法执行后
Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to proxy.Fish
at proxy.Client.main(Client.java:10)

3.cglib代理

这里用的是spring的cglib代理,用到了spring-core.3.2.5.jar

应用场景:Spring的AOP中,如果加入容器的目标对象有实现接口,用JDK代理.如果目标对象没有实现接口,用Cglib代理.

原理:动态生成代理类继承被代理类,执行代理类的方法时拦截,在拦截方法中写调用目标的逻辑代码。

结合实例的详细流程:动态生成代理类Proxy继承被代理类UserDao,执行代理类Proxy的重写父类Proxy的方法save()时,intercept方法拦截,在intercept方法中执行UserDao的save方法,可在save方法前后添加事务等其他业务代码。

优点:被代理类不需要实现接口。

注意:cglib代理是动态生成代理类去继承被代理类,final修饰的类无法被继承,方法被final修饰,无法重写。被static修饰,重写无效。 cglib生成对象比JDK动态代理开销更大。

Implementation

1
2
3
4
5
6
//被代理类 未实现接口
public class UserDao {
public void save(){
System.out.println("保存数据");
}
}
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
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
* Cglib子类代理工厂
* 需要spring-core的jar包
*
* @see MethodInterceptor
*/

public class ProxyFactory implements MethodInterceptor {

//被代理对象
private Object target;

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

/**
*
* @return 控制被代理对象的实例
*/
public Object getProxyInstance(){
//cglib中的工具类
Enhancer en = new Enhancer();
//设置父类
en.setSuperclass(target.getClass());

en.setCallback(this);
//创建实例
return en.create();
}

/**
* MethodInterceptor 的方法
*
*/

@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("开始事务");

//执行目标对象的方法
Object returnValue = method.invoke(target, args);

System.out.println("开始事务");
return returnValue;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
/**
* 测试类
*/
public class Client {
public static void main(String[] args) {
UserDao target = new UserDao();

UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();

proxy.save();
}
}

测试结果:

1
2
3
开始事务
保存数据
开始事务