代理模式

代理模式

代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.

1.静态代理

1.1 案例:汽车行驶功能中增加行驶时间记录功能。

接口

public interface Moveable {
    void move();
}

Car类

import java.util.Random;

public class Car implements Moveable {
    @Override
    public void move() {
        try {
            System.out.println("汽车行驶中...");
            Thread.sleep(new Random().nextInt(1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

现在需要记录汽车行驶时间,根据开放封闭原则和面向对象思想,继承Car类 或者 实现Movealbe接口

1.2 继承方式

类Car2继承Car并实现功能扩展

public class Car2 extends Car {

    @Override
    public void move() {
        long startTime = System.currentTimeMillis();
        System.out.println("Car2汽车开始行驶...");
        super.move();
        long endTime = System.currentTimeMillis();
        System.out.println("Car2汽车结束行驶...");
        System.out.println("Car2行驶时间:" + (endTime - startTime) + "毫秒");
    }
}

测试结果:

1.3 聚合方式(实现接口)

类Car3实现Moveable接口并聚合Car类实现功能扩展

public class Car3 implements Moveable{
    private Car car;

    public Car3(Car car) {
        this.car = car;
    }

    @Override
    public void move() {
        long startTime = System.currentTimeMillis();
        System.out.println("Car3汽车开始行驶...");
        car.move();
        long endTime = System.currentTimeMillis();
        System.out.println("Car3汽车结束行驶...");
        System.out.println("Car3行驶时间:" + (endTime - startTime) + "毫秒");
    }
}

测试结果:

测试类

public class Main {
    public static void main(String[] args) {
        //继承方式
//        Moveable m = new Car2();
//        m.move();

        // 接口方式
//        Moveable m = new Car3(new Car());
//        m.move();

    }
}

静态代理总结:
  a. 可以做到在不修改目标对象的功能前提下,对目标功能扩展.
  b. 缺点:

    因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.

2.动态代理

2.1 动态代理特点

  2.1.1 代理对象,不需要实现接口
  2.1.2 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
  2.1.3 动态代理也叫做:JDK代理,接口代理

2.2 JDK中生成代理对象的API

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

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

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

  ClassLoader loader:指定当前目标对象使用类加载器
Class<?>[] interfaces:目标对象实现的接口的类型
InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
2.2.2 Interface InvocationHandler
该接口作为参数传入Class Proxy的静态方法中,该接口中仅定义了一个方法
public Object invoke(Object obj, Method method, Obiect[] args)

  Object obj:指代理类

  Method method:被代理的方法

  args:参数数组

2.3 代码实现

TimeHandler类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class TimeHandler implements InvocationHandler {
    private Object target;

    public TimeHandler(Object obj) {
        this.target = obj;
    }

    /**
     *
     * @param proxy 被代理类
     * @param method  被代理对象
     * @param args  方法参数
     * @return Object
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long startTime = System.currentTimeMillis();
        System.out.println("汽车开始行驶...");
        method.invoke(target);
        long endTime = System.currentTimeMillis();
        System.out.println("汽车结束行驶...");
        System.out.println("行驶时间:" + (endTime - startTime) + "毫秒");
        return null;
    }
}

Main

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        //JDK动态代理
        Car car = new Car();
        InvocationHandler h = new TimeHandler(car);
        Class<?> cls = car.getClass();
        /**
         * 参数:类加载器1,实现接口2,InvocationHandler3
         */
        Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);

        m.move();

    }
}

动态代理总结:

  a. 代理类不需要实现接口,被代理类必须实现接口(参数2)

  b. 使用动态代理时,必须实现InvocationHandler接口(事务处理器)

3.cglib代理

静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但当一个类没有实现任何接口呢,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理

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

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

Cglib子类代理实现方法:
  1.需要引入cglib和asm的jar文件
  2.引入功能包后,就可以在内存中动态构建子类
  3.代理的类不能为final,否则报错
  4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

代码示例:

public class Train {
    public void move(){
        System.out.println("火车行驶中...");
    }
}
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxy implements MethodInterceptor {

    private Enhancer enhancer = new Enhancer();

    /**
     * 获取到代理类
     * @param clazz
     * @return
     */
    public Object getProxy(Class clazz){
        // 设置创建子类的类,即为那个类创建代理类
        enhancer.setSuperclass(clazz);
        //设置回调函数
        enhancer.setCallback(this);
        //创建代理实例并返回
        return enhancer.create();
    }

    /**
     * 拦截所有目标类方法的调用
     * @param o 目标类的实例
     * @param method  目标方法的反射对象
     * @param objects  方法的参数
     * @param methodProxy  代理类的实例
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("计时开始...");
        //代理类调用父类的方法,cglib是继承方式
        methodProxy.invokeSuper(o,objects);
        System.out.println("计时结束...");
        return null;
    }
}
public class Main {
    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
        Train t = (Train) cglibProxy.getProxy(Train.class);
        t.move();
    }
}

4. Spring AOP

在Spring的AOP编程中,如果加入容器的目标对象有实现接口,用JDK代理,如果目标对象没有实现接口,用Cglib代理

 

posted @ 2019-05-23 11:35  Star灬木子李  阅读(154)  评论(0编辑  收藏  举报