ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

傀儡政权之代理模式

2021-07-05 23:53:30  阅读:120  来源: 互联网

标签:对象 void Object 代理 模式 傀儡政权 cglib public


代理模式应用非常广泛,特别java领域的Spring框架,可以说把代理模式运用到极致。其中Spring的代理又分JDK动态代理和cglib动态代理。这类不打算深入讲解Spring的动态代理,而是深入讲解一下GOF 23的代理模式。

 

0x01:代理模式

代理模式:给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是生活中常见的中介。好比房地产中介,买卖房子时通过中介,既安全,又方便,唯一的不足就是需要交纳一笔不菲的佣金。代理模式的通用UML类图如下

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

  • 抽象角色(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平大房子出租~~");
    }
}
watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

扫码二维码

获取更多精彩

Java乐园

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

标签:对象,void,Object,代理,模式,傀儡政权,cglib,public
来源: https://blog.51cto.com/u_13538361/2984971

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有