Java Spring-AOP中的动态代理

2017-11-10 16:17:12

AOP中有两种代理方式,分别是JDK的动态代理CGLib的动态代理

  • JDK的动态代理

Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。

目标对象必须要实现某种接口。

动态代理类(以下简称为代理类)是一个实现在创建类时在运行时指定的接口列表的类,该类具有下面描述的行为。 代理接口 是代理类实现的一个接口。 代理实例 是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序 对象,它可以实现接口 InvocationHandler。通过其中一个代理接口的代理实例上的方法调用将被指派到实例的调用处理程序的 Invoke 方法,并传递代理实例、识别调用方法的 java.lang.reflect.Method 对象以及包含参数的 Object 类型的数组。调用处理程序以适当的方式处理编码的方法调用,并且它返回的结果将作为代理实例上方法调用的结果返回。

* 常用方法

// 接口
public interface Person {
    public void add();
    public void delete();
}

// 实现类
public class Student implements Person {
    @Override
    public void add() {
        System.out.println("添加操作...");
    }

    @Override
    public void delete() {
        System.out.println("删除操作...");
    }
}

// 测试
public class Demo {
    public static void main(String[] args) {
        Student s= new Student();
        // JDK Proxy操作
        Person proxy = (Person) Proxy.newProxyInstance(s.getClass().getClassLoader(),
                s.getClass().getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("进行身份校验...");
                        Object res = method.invoke(s, args);
                        return res;
                    }
                });
        proxy.add();
    }
}
  • CGLib的动态代理
CGLIB(Code Generation Library)是一个开源项目。
是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成。
最新版本Spring已经将CGLib开发类引入spring-core-3.2.0.RELEASE.jar,所以不用导包。
CGLIB不需要接口也是可以生成代理的。
本质是通过继承的方式进行增强。
1、CGLib的原理
CGLIB动态代理的原理就是用Enhancer生成一个原有类的子类,并且设置好callback到proxy, 则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxy的intercept() 函数,如图

在intercept()函数里,除执行代理类的原因方法,在原有方法前后加入其他需要实现的过程,改变原有方法的参数值,即可以实现对原有类的代理了。这似于AOP中的around advice。

2、使用MethodInterceptor接口实现方法回调

当对代理中所有方法的调用时,都会转向MethodInterceptor类型的拦截(intercept)方法,在拦截方法中再调用底层对象相应的方法。下面我们举个例子,假设你想对目标对象的所有方法调用进行权限的检查,如果没有经过授权,就抛出一个运行时的异常。

net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用 来实现拦截(intercept)方法的调用。

MethodInterceptor接口只定义了一个方法:

public Object intercept(Object object, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;

参数Object object是被代理对象,不会出现死循环的问题。

参数java.lang.reflect.Method method是java.lang.reflect.Method类型的被拦截方法。

参数Object[] args是被被拦截方法的参数。

参数MethodProxy proxy是CGLIB提供的MethodProxy 类型的被拦截方法。

注意:

1、若原方法的参数存在基本类型,则对于第三个参数Object[] args会被转化成类的类型。如原方法的存在一个参数为int,则在intercept方法中,对应的会存在一个Integer类型的参数。

2、若原方法为final方法,则MethodInterceptor接口无法拦截该方法。

// 目标类
public class Student implements Person {
    @Override
    public void add() {
        System.out.println("添加操作...");
    }

    @Override
    public void delete() {
        System.out.println("删除操作...");
    }
}

// 实现MethodInterceptor接口的实现类
public class CGLibProxy implements MethodInterceptor {
    // 首先得获得该对象
    Student s;

    // 通过构造函数来初始化
    CGLibProxy(Student s){
        this.s = s;
    }

    // 创建代理
    public Student newProxy(){
        // 创建CGLib的核心类
        Enhancer en = new Enhancer();
        // 由于CGLib是通过继承方式实现的,所以需要进行父类的设置
        en.setSuperclass(s.getClass());
        // 设置回调函数,因为当前类实现了Callback接口的子接口MethodInterceptor
        // 所以在回调函数里传的参数是this,并且在每次调用目标对象的方法的时候,其实
        // 是在调用intercept方法
        en.setCallback(this);
        // 返回代理对象
        return (Student) en.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("进行权限校验...");
        Object res = methodProxy.invokeSuper(o, objects);
        return res;
    }
}


// 测试
public class Demo {
    public static void main(String[] args) {
        Student s= new Student();

        // CGLib的动态代理
        CGLibProxy cg = new CGLibProxy(s);
        Student p = cg.newProxy();
        p.add();
    }
}
  •  结论:Spring框架,如果类实现了接口,就使用JDK的动态代理生成代理对象,如果这个类没有实现任何接口,使用CGLIB生成代理对象

 

 

posted @ 2017-11-10 17:23  hyserendipity  阅读(396)  评论(0编辑  收藏  举报