代理模式(jdk CGLib 实例中深入学习)

代理模式之前在前端中都运用了很多,类中将某个功能需要某个类去实现,自己做不了需要代理为你做事;

tableview delegate,代理为他提供cell 为他提供高度,为他提供cell数量,没有提供话,tableview存在,但是不够健全。

代理深入一点理解就是将某个类进行功能增强。在原本逻辑前后增加一些逻辑,而调用者无感知。代理模式属于结构型 模式,有静态代理和动态代理。

静态代理:

代理为某个特定的类进行代理服务,不够灵活,若要进行功能修改,那么代理和被代理都需要代码修改,违背的开闭原则。最后还是以例子说话,就以买票例子,普通的人需要买票,可以找到买票代理买到符合想要的票,模型设计,首先设计一个接口,获取票的接口


public interface TicketInterface {

    public void getTiket(HrTicket hrTicket);

}

票模型

public class HrTicket {

    private String name;
    private String price;

    public HrTicket(String name, String price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        this.price = price;
    }
}

买票的人

public class NormalPerson extends Person implements TicketInterface{

    private HrTicket hrTicket;


    public void getTiket(HrTicket hrTicket) {
        System.out.println("获得了某个票");
        this.hrTicket = hrTicket;
    }
}

静态代理:

创建一个人 有获得票的属性,为购票人提供符合要求的票

public class PersonProxyTicket extends Person implements TicketInterface{


    public PersonProxyTicket(NormalPerson normalPerson) {
        this.normalPerson = normalPerson;
    }

    private NormalPerson normalPerson;

    public void getTiket(HrTicket hrTicket) {
        System.out.println("查看该票是否符合客户要求");
        System.out.println("符合客户要求---发票完成");
        normalPerson.getTiket(hrTicket);

    }

}

 

动态代理:

不需要关心代理对象是谁,为代理对象动态提供代理服务,首先我们使用jdk进行操作,将原理写在代码当中

public class DyPersonProxyTicket implements InvocationHandler {

    private Object target;

    public Object getInstance(Object object) throws Exception {
        this.target = object;
        Class clazz = target.getClass();

        //1.获取被代理类,拿到代理类和接口
        //2.Proxy 通过反射机制拿到被代理类的所有方法和接口方法 进行代码重构重新生成新的代码类.class,里面重写的接口方法,
        //3.将class文件重新加载到jvm里面进行运行
        //整个过程叫字节码重组,是不是很高大上居然做了这么多的事一行代码
        //JDK 中有一个规范,在 ClassPath 下只要是$开头的 class 文件一般都是自动生成的
        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);

    }
    

    //生成的新类对象的时候之前保存了当前代理对象,在调用相同名称的方法的时候,会调用invoke方法

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        
        System.out.println("查看该票是否符合客户要求");
        System.out.println("符合客户要求---发票完成");
        //在新的类中找到了被代理对象,并且执行被代理对象的方法
        Object object = method.invoke(this.target,args);
        return object;
    }

}
//调用通过反射机制响应找到    
public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        return ma.invoke(obj, args);
    }

其实我们可以理解在运行时调用通过代码重组的方式,生成了一个全新的静态代理类。就是静态代理的升级版。

有一点需要注意的是我之前在设计类的时候没有添加接口,但是jdk是强制需要一个接口的,所以在设计的时候需要遵守整个规则。

如果用cglibe的话就不需要考虑接口的问题

采用cglib:

public class FsPersonProxyTicket implements MethodInterceptor {

    public Object getInstance(Class clazz) throws Exception{

        //和jdk 的方式其实是基本一样的,但是生成class文件,代码重构组合的时候没有采用反射机制,
        //重写了被代理类的所有方法,生成了三个class文件
        //一个是被代理类FastClass 另个是代理类FastClass(这两个是在调用的时候才被创建),还有一个编译的时候就会生成一个继承被代理类的子类

        /**代理类和被代理类各生成一个 Class,这个 Class 会为代 理类或被代理类的方法分配一个 index(int 类型)。
         * 这个 index 当做一个入参,FastClass 就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,
         * 所以调用效率比 JDK动态代理通过反射调用高
         * 内部处理逻辑很复杂
         * **/

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        System.out.println("查看该票是否符合客户要求");
        System.out.println("符合客户要求---发票完成");

        Object ob = methodProxy.invokeSuper(o,objects);
        return ob;
    }
}

最后:

1.JDK 动态代理是实现了被代理对象的接口,CGLib 是继承了被代理对象。

2.JDK 和 CGLib 都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLib 使用 ASM 框架写 Class 字节码,Cglib 代理实现更复杂,生成代理类比 JDK 效率低。
3.JDK 调用代理方法,是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用方法, CGLib 执行效率更高。

你可能感兴趣的:(java设计模式)