Java代理模式(Proxy)理解

序言

老师上课讲到了一些设计模式,但是由于课程较赶,时间较紧,吃不太透,于是自己课下再学习一些,加深理解,同时在编程中多多用到。

代理模式简述

首先,代理模式是一种设计模式

先给出代理模式的概念:代理模式是指通过代理对象访问目标对象,而不是直接访问目标对象,通过字面理解即可,要通过一个代理人或者说中间人来访问我们想要访问的对象。举例就像房屋中介,买房人不想自己去一个找房主询问其是否卖房,而卖房人也不想麻烦去一个个找买房人,中介就承担起这个功能,买房人(用户)通过中介(代理对象)获知在售的其意向房产的信息(如房价、面积、位置等的)。

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

何时使用:为其他对象提供一种代理以控制对这个对象的访问。

关键代码:实现与被代理类组合。

那么为什么要有代理模式呢?其好处是可以在目标对象实现的基础上,增强额外的功能操作,即拓展目标对象的功能,如添加权限进行访问控制和审计等功能,其中举添加权限这个功能说明,通过代理对象判断访问目标对象的用户是否拥有访问的权限,没有则禁止访问,实现对信息的一个保护。

同时,代理模式也是遵循了设计思想中类的单一性原则,就是每个类功能应该单一,即专一,就是说什么人干什么事,一个人尽可能负责一个工作,同时也是去尽量实现低耦合。

代理模式要着重理解代理对象和目标对象的概念,代理对象是对目标对象的扩展,并会调用目标对象。就像房屋中介调用目标对象(卖房人)获取其房产信息,但不仅限于房产信息,房屋中介还会审计其掌握的房产资源,而后挑选合适的房产给买房人。

使用场景:按职责来划分,通常有以下使用场景: 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理

代理模式的分类

代理模式主要分为三类:

1.静态代理;

2.动态代理;

3.Cglib代理。

下面分别论述。

静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。需要注意的是,代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。

我们举个买房这个例子来说明:

首先要先创建一个服务类的接口:

1
2
3
public interface HouseService{
void buyHouse();
}

然后要实现这个服务接口:

1
2
3
4
5
6
public class HouseServiceImpl implements HouseService {
@Override
public void buyHouse() {
System.out.println("我要买房");
}
}

有了服务后,我们就需要一个中间类也就是代理类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class HouseServiceProxy implements HouseService {

private HouseService houseService;

public HouseServiceProxy(final HouseService houseService) {
this.houseService = houseService;
}

@Override
public void buyHosue() {
System.out.println("攒钱买房");
System.out.println("挑房看房");
houseService.buyHosue();
System.out.println("买房装修");
System.out.println("入住");
}

}

优点:在不修改目标对象的功能前提下,可以实现对目标对象功能的扩展。

缺点:

  1. 代理对象必须提前写出(在编译期就已经知道了代理对象),如果接口层发生了变化,代理对象的代码也要进行维护。

  2. 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多,导致一旦接口增加方法,目标对象与代理对象都要维护。

如何解决静态代理中的缺点呢?答案是可以使用动态代理方式。

动态代理

 在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK再运行时为我们动态的来创建。

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

public class DynamicProxyHandler implements InvocationHandler {

private Object object;

public DynamicProxyHandler(final Object object) {
this.object = object;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("攒钱买房");
System.out.println("挑房看房");
Object result = method.invoke(object, args);
System.out.println("买房装修");
System.out.println("入住");
return result;
}

}

动态代理的特点:

1.代理对象,不需要实现接口;
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型);
3.动态代理也叫做: JDK代理,接口代理。

JDK中生成代理对象的API
代理类所在包:java.lang.reflect.Proxy

总结:代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理。

优点:JDK动态代理解决了静态代理中需要创建多个代理类的问题。

缺点:可以看出静态代理和JDK代理有一个共同的缺点,就是目标对象必须实现一个或多个接口,假如没有,则可以使用Cglib代理

Cglib代理

上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理

Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。

JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现。

Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)。

Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

步骤:创建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
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 Object getInstance(final Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}

public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("攒钱买房");
System.out.println("挑房看房");
Object result = methodProxy.invoke(object, args);
System.out.println("买房装修");
System.out.println("入住");
return result;
}
}

Cglib子类代理实现方法:

  1. 需要引入cglib的jar文件,但是因为pring的核心包中已经包括了Cglib功能,所以可以直接引入pring-core-3.2.5.jar
  2. 引入功能包后,就可以在内存中动态构建子类

以上就是代理模式的内容,仅个人拙见,若有错误,恳请批评指正。

参考资料:

【1】https://www.runoob.com/design-pattern/proxy-pattern.html

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2019-2022 1nvisble
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信