设计模式-代理模式(07)

 结构型模式

  大概学习完了五种建立型模式,分别是单例模式,工厂方法模式,抽象方法模式,建造者模式,原型模式.下面要学习的是七种结构型模式,结构型模式(Structural Pattern)描述如何将类或者对象结合在一块儿造成更大的结构.结构型模式的目的是经过组合类或对象产生更大的结构以适应更高层次的逻辑需求,它包括:代理模式,装饰模式,适配器模式,组合模式,桥梁模式,外观模式,享元模式。html

代理模式

定义

  代理模式(Proxy Pattern)也叫委托模式,是一种使用率很是高的模式。英文原话是:Provide a surrogate or placeholder for another object to control access to it.意思是:为其余对象提供一种代理以控制对这个对象的访问java

  代理模式是一项基本的设计技巧,许多模式如状态模式、策略模式、访问者模式本质上也采用了代理模式。git

角色分类

  1.抽象主题(Subject)角色:该角色是真实主题和代理主题的共同接口,以便在任何可使用真实主题的地方均可以使用代理主题。github

  2.代理主题(Proxy Subject)角色:也叫为委托类、代理类,该角色负责控制类对真实主题的引用,负责在须要的时候建立或删除真实主题对象,而且在真实主题角色处理完毕先后作预处理和蔼后处理工做。spring

  3.真实主题(Real Subject)角色:该角色也叫被委托角色或被代理角色,是业务逻辑的具体执行者。编程

/**
 * 主题角色 
 * 真实和代理的共同接口,能够在任何使用真实主题的地方使用代理主题.
 */
public interface Subject {
    //定义一个请求方法
    public void request();
}

/**
 * 真实主题角色
 * 被代理角色,是业务逻辑的执行者 
 */
public class RealSubject implements Subject{
    @Override
    public void request() {
        //业务逻辑处理
        System.out.println("真实主题角色处理中...");
    }
}

/**
 * 代理主题角色
 * 在须要的时候建立或删除真实对象,而且在真实主题角色处理完毕先后作预处理和蔼后处理工做.
 */
public class ProxySubject implements Subject{
    private Subject subject;
    
    //实例化的时候把须要代理的对象传进来
    public ProxySubject(Subject subject) {
        this.subject = subject;
    }

    //代理后的方法执行内容
    @Override
    public void request() {
        this.beforeRequest();
        subject.request();
        this.afterRequest();
    }
    
    //请求前的操做
    public void beforeRequest(){
        //预处理
        System.out.println(subject.getClass()+"被代理了,在它执行以前要执行预处理方法");
    }
    //请求后的操做
    public void afterRequest(){
        //善后处理
        System.out.println(subject.getClass()+"被代理了,在它执行以后要执行善后方法");
    }
}

//调用
public class ProxyDemo {
    public static void main(String[] args) {
        Subject subject = new RealSubject();
        System.out.println("未代理前执行request请求:------------------------");
        subject.request();
        System.out.println("代理后执行request请求:--------------------------");
        subject = new ProxySubject(subject);
        subject.request();
    }
}

一个代理主题类能够代理多个真实主题,具体代理哪一个真实主题由高层的应用模块决定,代理的真实主题能够经过代理类的构造函数传递。框架

代理模式的优势 

  1.职责清晰,真实的角色只须要实现实际的业务逻辑,而不用关心其余非本职的事务,附带的结果就是编程简洁清晰。dom

  2.高扩展性,具体主题角色随需求不一样可能有不少种,但只要实现了接口,代理类就彻底能够在不作任何修改的状况下代理各类真实主题角色。ide

  3.智能化,代理类能够在运行时才肯定须要代理的真实主题,这是一种强大的功能。函数

代理模式的使用场景

  代理模式的应用很是普遍。大到一个系统框架、企业平台,小到事务处理、代码片断,随处可见代理模式的使用。例如,Java RMI的远程调用就是一种代理模式的应用,如今流行的AOP也能够经过代理模式实现.

其余代理模式

上面的代理模式叫作静态代理模式,动态地理能够经过继承和聚合实现,静态代理能够作到在不修改目标对象的功能前提下,对目标功能扩展。可是由于静态代理代理对象须要与目标对象实现同样的接口,因此会有不少代理类,类太多.同时,一旦接口增长方法,目标对象与代理对象都要维护.要解决这个问题可使用动态代理模式

  动态代理模式

  1.代理对象,不须要实现接口
  2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(须要咱们指定建立代理对象/目标对象实现的接口的类型)
  3.动态代理也叫作:JDK代理,接口代理.

java.lang.reflect.Proxy

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

  ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的  clazz.getClassLoader()

  Class<?>[] interfaces:被代理对象实现的接口的类型,使用泛型方式确认类型  clazz.getInterfaces()

  InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法做为参数传入

 

/**
 * 车类接口
 */
public interface Moveable {
    public void run();
}

/**
 * 车类接口实现类
 */
public class Car implements Moveable {
    @Override
    public void run() {
        try {
            Thread.sleep(new Random().nextInt(3*1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("car is running...");
    }
}

/**
 * 代理以后的处理方法-处理时间记录
 */
public class TimeInvocationHandler implements InvocationHandler {
    private Object object;
    public TimeInvocationHandler(Object object) {
        this.object = object;
    }
    /* 
     * 用时代理
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("开始计算时间");
        long startTime = System.currentTimeMillis();
        method.invoke(object, args);
        long endTime = System.currentTimeMillis();
        System.out.println("计算时间完毕,共用时"+(endTime-startTime)+"毫秒");
        return null;
    }

}

/**
 * 代理后的处理方法-日志记录
 */
public class LogInvocationHandler implements InvocationHandler {
    private Object object;
    public LogInvocationHandler(Object object) {
        this.object = object;
    }
    /* 
     * 日志代理
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("日志开始记录"+method.getName());
        method.invoke(object, args);
        System.out.println("日志记录完毕");
        return null;
    }
}

        //运行
    public static void main(String[] args) {
        Car car = new Car();
        Class<?> clazz = car.getClass();
        //日志代理
        InvocationHandler logHandler = new LogInvocationHandler(car);
        Moveable mv = (Moveable)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), logHandler);
        //用时代理
        InvocationHandler timeHandler = new TimeInvocationHandler(mv);
        Moveable mv2 = (Moveable)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), timeHandler);
        //运行run方法
        mv2.run();
    }

 

运行结果:

/**
 * 建立动态代理对象
 * 动态代理不须要实现接口,可是须要指定接口类型
 */
public class DynamicProxy {
    private Object target;
    public DynamicProxy(Object target) {
        this.target = target;
    }
    
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(RealSubject.class.getClassLoader(), RealSubject.class.getInterfaces(), 
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args)
                            throws Throwable {
                        beforeRequest();
                        Object returnValue = method.invoke(target, args);  //目标对象方法
                        afterRequest();
                        return returnValue;
                    }
                });
    }
    
    //请求前的操做
    public void beforeRequest(){
        //预处理
        System.out.println(target.getClass()+"被动态代理了,在它执行以前要执行动态代理加入的预处理方法");
    }
    //请求后的操做
    public void afterRequest(){
        //善后处理
        System.out.println(target.getClass()+"被动态代理了,在它执行以后要执行动态代理加入的善后方法");
    }
}

public class DynamicProxyDemo {
    public static void main(String[] args) {
        Subject subject = new RealSubject();
        System.out.println(subject.toString()+"执行方法subject.request()结果为");
        subject.request();
        System.out.println("--------------------------------------");
        subject = (Subject) new DynamicProxy(subject).getProxyInstance();
        System.out.println("代理类"+subject.toString()+"执行方法request()方法结果为");
        subject.request();
    }
}

执行后发现结果居然是这个

这是由于System.out.println("代理类"+subject.toString()+"执行方法request()方法结果为");这行执行了toString()方法,toString()方法一样被代理了.这里有个疑问就是代理类的地址和被代理类的地址同样,执行request()方法结果却不同,若是哪位牛人知道的话,欢迎评论.

  Cglib代理

  静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,可是有时候目标对象只是一个单独的对象,并无实现任何的接口,这个时候就可使用以目标对象子类的方式类实现代理,这种方法就叫作:Cglib代理  

Cglib代理,也叫做子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

  1. JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,若是想代理没有实现接口的类,就可使用Cglib实现.
  2. Cglib是一个强大的高性能的代码生成包,它能够在运行期扩展java类与实现java接口.它普遍的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
  3. Cglib包的底层是经过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,由于它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.

实现

1.引入cglib的jar文件,Spring的核心包中已经包括了Cglib功能,因此直接引入spring-core-4.2.3.RELEASE.jar便可.
2.引入功能包后,就能够在内存中动态构建子类
3.代理的类不能为final,不然报错
4.目标对象的方法若是为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

public class Train {
    public void move(){
        System.out.println("火车正在行驶...");
    }
}

public class CglibProxy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();
    
    public Object getProxy(Class clazz){
        //设置父类
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        //建立子类
        return enhancer.create();
    }
    
    /**
     * 拦截全部目标类方法的调用
     * obj  目标类的实例
     * m   目标方法的反射对象
     * args  方法的参数
     * proxy 代理类的实例
     */
    @Override
    public Object intercept(Object obj, Method m, Object[] args,
            MethodProxy proxy) throws Throwable {
        System.out.println("开始记录到日志...");
        proxy.invokeSuper(obj, args);
        System.out.println("日志记录完毕.");
        return null;
    }

}

    /**
     * 测试
     */
    public static void main(String[] args) {
        CglibProxy proxy = new CglibProxy();
        Train t = (Train)proxy.getProxy(Train.class);
        t.move();
    }

运行结果:

public class ProxyFactory implements MethodInterceptor {
    //维护目标对象
    private Object target;
    public ProxyFactory(Object target) {
        this.target = target;
    }
    //给目标对象建立一个代理对象
    public Object getProxyInstance(){
        //1.工具类
        Enhancer enhancer = new Enhancer();
        //2.设置父类
        enhancer.setSuperclass(target.getClass());
        //3.设置调用函数
        enhancer.setCallback(this);
        //4.建立子类(代理对象)
        return enhancer.create();
    }
    @Override
    public Object intercept(Object obj, Method method, Object[] args,
            MethodProxy proxy) throws Throwable {
        beforeRequest();
        //执行目标对象的方法
        Object returnvalue = method.invoke(target, args);
        afterRequest();
        return returnvalue;
    }
    
    //请求前的操做
    public void beforeRequest(){
        //预处理
        System.out.println(target.getClass()+"被代理了,在它执行以前要执行预处理方法");
    }
    //请求后的操做
    public void afterRequest(){
        //善后处理
        System.out.println(target.getClass()+"被代理了,在它执行以后要执行善后方法");
    }
}

/*
 * 目标对象
 * 没有实现任何接口
 */
public class RealSubject {
    public void request() {
        System.out.println("----被代理对象请求方法!----");
    }
}

public class CglibDemo {
    public static void main(String[] args) {
        RealSubject target = new RealSubject();
        RealSubject proxy = (RealSubject) new ProxyFactory(target).getProxyInstance();
        proxy.request();
    }
}

源码

参考文章:Java的三种代理模式

相关文章
相关标签/搜索