代理模式应用非常广泛,特别java领域的Spring框架,可以说把代理模式运用到极致。其中Spring的代理又分JDK动态代理和cglib动态代理。这类不打算深入讲解Spring的动态代理,而是深入讲解一下GOF 23的代理模式。
0x01:代理模式
代理模式:给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是生活中常见的中介。好比房地产中介,买卖房子时通过中介,既安全,又方便,唯一的不足就是需要交纳一笔不菲的佣金。代理模式的通用UML类图如下
- 抽象角色(Subject):通过接口或抽象类声明真实角色实现的业务方法。
- 真实角色(RealSubject):实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
- 代理角色(Proxy):实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加额外的操作来增强功能和业务能力。
抽象角色(Subject):
public interface Subject {
public void request();
}
真实角色(RealSubject):
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("真实的请求RealSubject");
}
}
代理角色(Proxy):
public class Proxy implements Subject {
private RealSubject realSubject = null;
public Proxy() {
this.realSubject = new RealSubject();
}
@Override
public void request() {
this.doBefore();
this.realSubject.request();
this.doAfter();
}
//前置处理
private void doBefore() {
System.out.println("-------do before------");
}
//后缀处理
private void doAfter() {
System.out.println("-------do after-------");
}
}
Client客户端:
public class Client {
public static void main(String[] args) {
Proxy proxy = new Proxy();
proxy.request();
}
}
通过以上的代码就实现了一个简单的代理模式,这种实现方式也叫静态代理;但是实在项目、框架中的代理模式可并没有这么简单,比这复杂多了。
静态代理的特点:
- 静态代理由开发者去生成固定的代码进行编译。需要定义接口或抽象的父类作为抽象目标类,具体目标类和代理类一起实现相同的接口或继承相同的类,然后通过调用相同的方法来调用目标对象的方法。
- 静态代理需要目标对象和代理对象实现相同的接口。可以在不修改目标对象功能的前提下,对目标功能进行扩展和增强。
- 虽然静态代理可以很好的对目标对象进行功能扩展,但是每一个真实角色都需要建立代理类,工作量较大且不易管理;而且如果接口发生改变的话,代理类也必须进行相应的修改,这时动态代理的作用就显现出来了。
0x02:动态代理
动态代理与静态代理的区别在于:动态代理类的字节码是在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理分为JDK动态代理和cglib动态代理。
- JDK动态代理是jre提供的类库,只需依赖JDK,就可以直接使用,无需依赖第三方类库。
最近蛋壳真是火的一塌糊涂,先来个抽象房东接口
public interface IFangDong {
public void hire(String area);
}
房东实现接口
public class FangDong implements IFangDong{
@Override
public void hire(String area) {
System.out.println(area);
}
}
房东一般指那些手握几套甚至几十台房子的人,房东有房子出租,但是房子太多管不过来,一般不会自己联系要租房子的人;而是交个房地产中介。创建一个类,便于理解该类的类名以Prox结尾,但是该类不是代理类,因为他并没有实现IFangDong接口,无法对外提供服务,仅仅是一个wrapper类(包装类)。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class FangDongProxy implements InvocationHandler{
// 目标类,也就是被代理对象
private Object target;
public void setTarget(Object target)
{
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
// 这里可以做增强
System.out.println("必须租给程序员~~因为他们人傻钱多");
Object result = method.invoke(target, args);
return result;
}
// 生成代理类
public Object creatProxyedObj()
{
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}
方法creatProxyedObj返回的对象才是代理类,它需要三个参数,前两个参数的意思是在同一个classloader下通过接口创建出一个对象,该对象需要一个属性,也就是第三个参数,它是一个InvocationHandler对象。上述代理的代码使用过程一般如下:
- new一个目标对象
- new一个InvocationHandler,将目标对象set进去
- 通过creatProxyedObj创建代理对象,强转为目标对象的接口类型即可使用,实际上生成的代理对象实现了目标接口。
public class Client {
public static void main(String[] args) {
IFangDong fd = new FangDong();
FangDongProxy proxy = new FangDongProxy();
proxy.setTarget(fd);
Object obj = proxy.creatProxyedObj();
IFangDong fangDong = (IFangDong)obj;
fangDong.hire("我有130平大房子出租~~");
}
}
从以上代码可以看出如果使用JDK动态代理,必须实现InvocationHandler类,然后再该类的invoke方法做增强和调用目标类的相应方法。
- cglib动态代理,需要引入第三方类库cglib-x.x.x.jar
通过继承可以继承父类所有的公开方法,然后重写这些方法;在重写时对方法进行增强,这就是cglib的核心思想。根据里氏代换原则(LSP),父类出现的地方,子类都可以出现,所以cglib实现的代理就一定可以被正常使用。
引入jar包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
实现cglib动态代理
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class FangDongCglibProxy implements MethodInterceptor {
// 根据一个类型产生代理类,此方法不要求一定放在MethodInterceptor中
public Object CreatProxyedObj(Class<?> clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 这里增强
System.out.println("FangDongCglibProxy》》》必须租给程序员~~因为他们人傻钱多");
return methodProxy.invokeSuper(obj, args);
}
}
从上面代码可以看出,cglib和jdk动态代理有所不同,它只需要一个类型clazz就可以产生一个代理对象,而JDK动态代理要求对象必须实现接口(三个参数的第二个参数),cglib则对此没有要求。
public class Client {
public static void main(String[] args) {
FangDongCglibProxy fangDongCglibProxy = new FangDongCglibProxy();
FangDong fangDong = (FangDong)fangDongCglibProxy.creatProxyedObj(FangDong.class);
fangDong.hire("我有130平大房子出租~~");
}
}
Java乐园
标签:对象,void,Object,代理,模式,傀儡政权,cglib,public From: https://blog.51cto.com/u_13538361/6377703