首页 > 其他 > 详细

设计模式 —— 代理模式

时间:2019-08-19 23:33:18      阅读:120      评论:0      收藏:0      [点我收藏+]

一.定义

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

UML:

技术分享图片

二.作用

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

三.分类

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

1.静态代理:

1.1 创建服务类接口:

public interface Flyable {
    void fly();
}
public interface Runnable {
    void run();
}

1.2 创建服务类实现类:

public class Animal implements Flyable, Runnable {

    @Override
    public void fly() {
        System.out.println("animal flying");
    }

    @Override
    public void run() {
        System.out.println("animal running");
    }
}

1.3 创建代理类:

public class AnimalProxy implements Flyable, Runnable {

    private Animal animal;

    public AnimalProxy(Animal animal) {
        this.animal = animal;
    }

    @Override
    public void fly() {
        System.out.println("ready: fly");
        animal.fly();
    }

    @Override
    public void run() {
        System.out.println("ready: run");
        animal.run();
    }
}

1.4 测试:

        Animal animal1 = new Animal();
        AnimalProxy animalProxy = new AnimalProxy(animal1);
        animalProxy.fly();
        animalProxy.run();    

测试结果:

ready: fly
animal flying
ready: run
animal running

静态代理总结:
1.可以做到在不修改目标对象的功能前提下,对目标功能扩展。
2.缺点: 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护。

2.JDK实现动态代理:

动态代理有以下特点:
1.代理对象,不需要实现接口
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
3.动态代理也叫做:JDK代理,接口代理

JDK中生成代理对象的API
代理类所在包:java.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )

注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:

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

2.1 编写动态处理器:

public class ProxyHandler implements InvocationHandler {

    private Animal animal;

    public ProxyHandler(Animal animal) {
        this.animal = animal;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("invoke before:" + method.getName());
        Object object = method.invoke(animal, args);
        System.out.println("invoke after:" + method.getName());
        return object;
    }
}

2.2 测试:

        Animal animal = new Animal();
        ClassLoader classLoader = animal.getClass().getClassLoader();
        Class[] interfaces = animal.getClass().getInterfaces();
        ProxyHandler proxyHandler = new ProxyHandler(animal);
        Object newProxyInstance = Proxy.newProxyInstance(classLoader, interfaces, proxyHandler);
        Flyable fly = (Flyable) newProxyInstance;
        fly.fly();
        Runnable run = (Runnable) newProxyInstance;
        run.run();    

测试结果:

invoke before:fly
animal flying
invoke after:fly
invoke before:run
animal running
invoke after:run

总结:
代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理

3.Cglib代理:

JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

3.1 创建CGLIB代理类:

public class CglibProxy implements MethodInterceptor {
        private Object target;
        public Object getInstance(final Object target) {
           this.target = target;
           Enhancer enhancer = new Enhancer();
           enhancer.setSuperclass(this.target.getClass());
           enhancer.setCallback(this);
           return enhancer.create();
        }
 
        public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
           System.out.println("before : " + method.getName());
           Object result = methodProxy.invoke(object, args);
           System.out.println("after : " + method.getName());
           return result;
        }
}

3.2 测试:

            Animal animal = new Animal();
            CglibProxy cglibProxy = new CglibProxy();
            Animal animalCglibProxy = (Animal) cglibProxy.getInstance(animal);
            animalCglibProxy.buyHosue();        

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

Cglib拓展:通过动态代理来实现方法增强、方法拦截:

将cglib动态代理思想带入Android开发

 

设计模式 —— 代理模式

原文:https://www.cnblogs.com/sqzr-001/p/11380395.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!