代理模式

设计模式-----代理模式

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

    说明:例如我想租房子,虽然我可以去统计有哪些房子正在出租,每个房子是什么样的户型,房子的价格等。。。,但我就想租个房子,只想选择租或不租,不想去了解房源信息等一系列事,于是就有了租房中介,他会给你收集好这些信息,你只用选择租或不租就行了。

    代理模式包含如下角色:

        ISubject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口。

        RealSubject:真实主题角色,是实现抽象主题接口的类。

        Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。

    二、静态代理和动态代理

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

1)静态代理

/**
 * 抽象主题角色
 * @author rdb
 *
 */
public interface Sleep {
    void sleep();
}
 
/**
 * 真实主题角色
 * @author rdb
 *
 */
public class SleepImpl implements Sleep {
    @Override
    public void sleep() {
        System.out.println("熟睡中");
    }
}
 
/**
 * 代理角色
 * @author rdb
 *
 */
public class SleepProxy implements Sleep {
    private Sleep sleep;
    public SleepProxy(Sleep sleep) {
        this.sleep = sleep;
    }  
    @Override
    public void sleep() {   
        System.out.println("睡觉前要刷牙");
        sleep.sleep();
        System.out.println("睡醒后要吃早饭");
    }
}
 
/**
 * 测试代码
 * @author rdb
 *
 */
public class ProxyTest {
     
    public static void main(String[] args) {
        Sleep sleep = new SleepImpl();
        Sleep sleepProxy = new SleepProxy(sleep);
        sleepProxy.sleep();
    }
}
 
结果:睡觉前要刷牙
      熟睡中
      睡醒后要吃早饭

2)动态代理(JDK)

静态代理有一个明显的缺点:一个主题类和一个代理类一一对应,所以我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。  在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK在运行时为我们动态的来创建。   

/**
 * 抽象主题角色
 * @author rdb
 *
 */
public interface Sleep {
    void sleep();
}
 
/**
 * 真实主题角色
 * @author rdb
 *
 */
public class SleepImpl implements Sleep {
    @Override
    public void sleep() {
        System.out.println("熟睡中");
    }
}
 
/**
 * 动态处理器
 * @author rdb
 *
 */
public class DynamicProxyHandler implements InvocationHandler {   
    private Object obj ;
    public  DynamicProxyHandler(final Object obj) {       
        this.obj = obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("睡觉前要刷牙");
        Object result = method.invoke(obj, args);
        System.out.println("睡醒后要吃早饭");
        return null;
    }
}
/**
 * 测试代码
 * @author rdb
 *
 */
public class ProxyTest {
    public static void main(String[] args) {              
        Sleep sleep = new SleepImpl();
        DynamicProxyHandler dph = new DynamicProxyHandler(sleep);
        Sleep sleepProxy = (Sleep) Proxy.newProxyInstance(sleep.getClass().getClassLoader(),
                sleep.getClass().getInterfaces(), dph);
        sleepProxy.sleep();
    }
}
 
结果:睡觉前要刷牙
      熟睡中
      睡醒后要吃早饭

注意:Proxy.newProxyInstance()方法需要3个参数:类加载器(要进行代理的类)、被代理类实现的接口,动态处理器   

JDK动态代理的一般步骤:(1)创建动态处理器实现InvocationHandler接口,实现invoke()方法

                                        (2)创建被代理的类及接口

                                        (3)调用Proxy的静态方法,创建代理类

                                        (4)通过代理类调用方法

JDK生成的代理类本身就继承了Proxy类,Java只允许单继承,所以JDK的动态代理不能完成继承式动态代理(只能完成接口的动态代理,不能实现类的动态代理),但是我们可以用CGLIB的方式实现继承式的动态代理

3)动态代理(CGLIB)

//具体主题
public class Sleep1 {
    public void sleep() {
        System.out.println("熟睡中");
    }
}
 
/**
 * 
 * @author rdb
 *
 */
public class CGLibProxy implements MethodInterceptor {
 
    private Object obj;
     
    //创建代理类
    public Object getInstance(Object obj) {
        this.obj = obj;//主题对象
        Enhancer enhancer = new Enhancer();//创建加强器,用来创建动态代理类
        enhancer.setSuperclass(this.obj.getClass());
        //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
        enhancer.setCallback(this);
        //返回代理类
        return enhancer.create();
    }
     
    //回调方法实现
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)
     throws Throwable {
        System.out.println("睡觉前要刷牙");
        Object result = methodProxy.invokeSuper(obj, args);
        System.out.println("睡醒后要吃早饭");
        return result;
    }
 
}
 
/**
 * 测试代码
 * @author rdb
 *
 */
public class ProxyTest {
     
    public static void main(String[] args) {
        Sleep1 sleep = new Sleep1();
        CGLibProxy proxy = new CGLibProxy();
        Sleep1 s = (Sleep1) proxy.getInstance(sleep);
        s.sleep();
    }
}
 
结果:睡觉前要刷牙
      熟睡中
      睡醒后要吃早饭

总结:静态代理虽然能完成代理功能,但我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。

           JDK动态代理很美很强大,但由于java的单继承,只能实现接口的动态代理,不能实现类的动态代理

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

原文地址:https://www.cnblogs.com/jnba/p/10677763.html