代理模式的定义与特点

代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
现实中的房屋中介就是代理模式的体现,租房着找中介住房,房东找中介代理租房。

  • 优点
    • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
    • 代理对象可以扩展目标对象的功能;
    • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性
  • 缺点
    • 代理模式会造成系统设计中类的数量增加
    • 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
    • 增加了系统的复杂度;

代理模式的结构与实现

代理模式的结构比较简单,主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问,下面来分析其基本结构和实现方法。
代理模式的角色如下:

  • 抽象角色:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  • 真实角色:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
  • 代理角色:提供了与真实角色相同的接口,其内部含有对真实角色的引用,它可以访问、控制或扩展真实角色的功能。
  • 客户角色:调用代理角色。

在代码中,一般代理会被理解为代码增强,实际上就是在原代码逻辑前后增加一些代码逻辑,而使调用者无感知。

分类

静态代理

静态代理是实实在在存在对应的代理类,跟被代理类有强耦合性。

代码实现

  • 抽象角色
1
2
3
4
5
//抽象房东
public interface AbstractHouseOwner {
//租房
public void rent();
}
  • 真实角色
1
2
3
4
5
6
7
//实际房东
public class HouseOwner implements AbstractHouseOwner {
@Override
public void rent() {
System.out.println("房东出租房屋!");
}
}
  • 代理角色
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//中介
public class Proxy implements AbstractHouseOwner {
private HouseOwner houseOwner;

public Proxy() {
}

public Proxy(HouseOwner houseOwner) {
this.houseOwner = houseOwner;
}

@Override
//帮房东出租房屋
public void rent() {
System.out.println("我是中介!");
houseOwner.rent();
System.out.println("想租吗?走带你看房签合同!");
}
}
  • 客户角色
1
2
3
4
5
6
7
//客户
public class Static_proxyTest {
public static void main(String[] args) {
Proxy proxy = new Proxy(new HouseOwner());
proxy.rent();
}
}

运行结果如下

我是中介!
房东出租房屋!
想租吗?走带你看房签合同!

动态代理

动态代理是在代码运行过程中,根据代理模板或代理工厂动态创建出对应被代理类的代理类。
动态代理类的实现方式有很多

  • 基于接口: jdk
  • 基于类 : gclib
  • java字节码: javassist

jdk动态代理

需要了解两个类

  • Proxy: 代理
  • InvocationHandler: 调用处理程序

代码实现

  • 抽象角色
1
2
3
4
5
6
7
//抽象房东
public interface AbstractHouseOwner {
//租房
public void rent();
//收钱
public void collectMoney();
}
  • 真实角色
1
2
3
4
5
6
7
8
9
10
11
12
//实际房东
public class HouseOwner implements AbstractHouseOwner {
@Override
public void rent() {
System.out.println("房东要出租房屋!");
}

@Override
public void collectMoney() {
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
//中介生成器,无业务能力,业务可以由拓展实现
public class ProxyInvocationHandler implements InvocationHandler {

//被代理的接口
private Object obj;
private ProxyExpand objE;

public ProxyInvocationHandler(Object obj) {
this.obj = obj;
}

public ProxyInvocationHandler(Object obj,ProxyExpand objE) {
this.obj = obj;
this.objE = objE;
}

//生成得到代理对象
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), obj.getClass().getInterfaces(),this);
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(objE != null){
objE.before();
}
Object result = method.invoke(obj,args);
if(objE != null){
objE.after();
}
return result;
}
}
1
2
3
4
5
6
//拓展接口
public interface ProxyExpand {
public void before();
public void after();
}

1
2
3
4
5
6
7
8
9
10
11
12
//拓展能力实现
public class ProxyExpandImpl implements ProxyExpand{
@Override
public void before() {
System.out.println("我是中介");
}

@Override
public void after() {
System.out.println("想租吗?走带你看房签合同!");
}
}
  • 客户角色
1
2
3
4
5
6
7
//客户
public class Dynamic_proxyText {
public static void main(String[] args) {
//代理角色
((AbstractHouseOwner)new ProxyInvocationHandler(new HouseOwner(),new ProxyExpandImpl()).getProxy()).rent();
}
}

运行结果如下

我是中介!
房东出租房屋!
想租吗?走带你看房签合同!

总结

虽然相对于静态代理,动态代理减少了开发任务和耦合性,但是能生成代理的类必须实现接口,无接口的类,是无法动态代理的。

cglib动态代理

代码实现

跟jdk代理一样,只有生成代理角色的方式不同

  • 代理角色
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
//中介生成器,无业务能力,业务可以由拓展实现
public class ProxyMethodInterceptor implements MethodInterceptor {
//被代理的接口
private Object obj;
private ProxyExpand objE;

public ProxyMethodInterceptor(Object obj) {
this.obj = obj;
}

public ProxyMethodInterceptor(Object obj,ProxyExpand objE) {
this.obj = obj;
this.objE = objE;
}

public Object getProxy(){
//1.代理对象生成器(工厂思想)
Enhancer enhancer = new Enhancer();
//2.在增强器上设置两个属性
//设置要生成代理对象的目标对象:生成的目标对象类型的子类型
enhancer.setSuperclass(obj.getClass());
//设置回调方法
enhancer.setCallback(this);
//3.创建获取对象
return enhancer.create();
}

@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if(objE != null){
objE.before();
}
Object result = method.invoke(obj,args);
if(objE != null){
objE.after();
}
return result;
}
}
  • 客户
1
2
3
4
5
6
//客户
public class Dynamic_proxyText {
public static void main(String[] args) {
((HouseOwner)new ProxyMethodInterceptor(new HouseOwner(),new ProxyExpandImpl()).getProxy()).rent();
}
}

运行结果如下

我是中介!
房东出租房屋!
想租吗?走带你看房签合同!

总结

cglib创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是cglib创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用cglib合适,反之使用jdk方式要更为合适一些。同时由于cglib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。