傀儡政权之代理模式

代理模式应用非常广泛,特别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乐园

傀儡政权之代理模式
上一篇:使用aop扫描不到对应的包解决办法


下一篇:Java Cglib动态代理原理源码分析