5、代理模式

代理模式

定义

为其余对象提供一种代理以控制对这个对象的访问。java

为何要用代理模式?

  • 中介隔离做用:在某些状况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象能够在客户类和委托对象之间起到中介的做用,其特征是代理类和委托类实现相同的接口。
  • 开闭原则,增长功能:代理类除了是客户类和委托类的中介以外,咱们还能够经过给代理类增长额外的功能来扩展委托类的功能,这样作咱们只须要修改代理类而不须要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及过后对返回结果的处理等。代理类自己并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能仍是由委托类来实现,可是能够在业务功能执行的先后加入一些公共的服务。例如咱们想给项目加入缓存、日志这些功能,咱们就可使用代理类来完成,而不必打开已经封装好的委托类。

有哪几种代理模式?

按照代理建立的时期来进行分类的话, 能够分为两种:静态代理、动态代理。静态代理是由程序员建立或特定工具自动生成源代码,在对其编译。在程序员运行以前,代理类.class文件就已经被建立了。动态代理是在程序运行时经过反射机制动态建立的。程序员

静态代理

静态代理在使用时,须要定义接口或者父类,被代理对象与代理对象一块儿实现相同的接口或者是继承相同父类。缓存

实现工具

这里假设Tom要买一套房子,而后本身没有足够的资金,因而就让他的父亲代理他来给他买下这套房子。性能

买房子接口:测试

public interface BuyHouse {
    void Buy();
}

Tom类:this

public class Tom implements BuyHouse {
    public void Buy() {
        System.out.println("Tom买到房子了...");
    }
}

Father类:设计

public class Father implements BuyHouse {
    private Tom tom;

    public Father(Tom tom){
        this.tom = tom;
    }
    
    public void Buy() {
        System.out.println("Father给Tom买了房子...");
        tom.Buy();
    }
}

静态代理的优缺点:代理

  • 优势:在不修改目标对象功能的前提下,能经过代理对象对目标功能进行扩展
  • 缺点:由于代理对象要与目标对象实现同样的接口,因此会有不少代理类;一旦接口方法增长,目标对象与代理对象都须要维护。

动态代理日志

  • 代理对象不须要实现接口,可是目标对象仍然须要实现接口,不然不能使用动态代理
  • 代理对象的生成,是利用JDK的API,动态的内从中构建代理对象

下面咱们使用JDK动态代理的方式来对上面的静态代理示例进行改写

买房子接口和Tom类都和上面的同样

建立代理类JdkProxy:

public class JdkProxy implements InvocationHandler {
    private Object target;
    //接收被代理的目标对象对象
    public JdkProxy(Object target){
        this.target = target;
    }
    //生成目标对象的代理对象
    public Object getProxyInstance(){
        Object instance = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        return instance;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("开始Jdk动态代理,来给Tom买房子");
        //执行目标对象的方法
        Object returnVal = method.invoke(target,args);
        return returnVal;
    }
}

测试类:

public class testJdkProxy {
    @Test
    public void  test(){
        BuyHouse tom = new Tom();
        JdkProxy jdkProxy = new JdkProxy(tom);
        BuyHouse proxyInstance = (BuyHouse)jdkProxy.getProxyInstance();
        proxyInstance.Buy();
    }
}

/**测试结果
开始Jdk动态代理,来给Tom买房子
Tom买到房子了...
*/

注意Proxy.newProxyInstance()方法接受三个参数:

  • ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
  • Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
  • InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法

Cglib代理

静态代理和JDK代理模式都要求目标对象实现一个接口,可是有时候目标对象只是一个单独的对象,并无实现任何对象,这个时候可使用目标对象的子类来实现代理,这就是Cglib代理。

CGLib采用了很是底层的字节码技术,其原理是经过字节码技术为一个类建立子类,并在子类中采用方法拦截的技术拦截全部父类方法的调用,顺势织入横切逻辑。但由于采用的是继承,因此不能对final修饰的类进行代理。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

实现

建立没有实现接口的Tom类:

public class Tom {
    public void Buy() {
        System.out.println("Tom买到房子了...");
    }
}

建立Cglib代理类:

public class CglibProxy implements MethodInterceptor {
    private Object target;

    public Object getProxyInstance(Object target){
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("开始Cglib代理,来给Tom买房子");
        Object returnVal = method.invoke(target, objects);
        return returnVal;
    }
}

建立测试类:

public class testCglibProxy {
    @Test
    public void test(){
        Tom proxyInstance = (Tom) new CglibProxy().getProxyInstance(new Tom());
        proxyInstance.Buy();
    }
}

/**测试结果
开始Cglib代理,来给Tom买房子
Tom买到房子了...
*/

CGLIB代理总结: CGLIB建立的动态代理对象比JDK建立的动态代理对象的性能更高,可是CGLIB建立代理对象时所花费的时间却比JDK多得多。因此对于单例的对象,由于无需频繁建立对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时因为CGLib因为是采用动态建立子类的方法,对于final修饰的方法没法进行代理。

相关文章
相关标签/搜索