动态代理及AOP

AOP:面向切面编程。

OOP是面向对象编程,AOP是面向切面编程。

面向切面编程并不是一个新的类型的编程,只是在原来面向对象编程的基础上添加的功能,可以理解为对OOP功能加强的编程。

AOP的思想:

想在原来的类的基础上增加一些功能,不通过改变原来类来增加功能,而是重新写一个类,在这个类中实现原来类的所有功能,并在这些功能的基础上添加新的功能。

这个新的类就是代理类。

动态代理技术:

当在一个工程中需要为很多的类编写代理类时,手工编写这些类会很麻烦。

JVM提供了一个功能,可以动态的生成代理类。

也就是不用自己写程序,程序在运行时,JVM自动的生成代理类字节码。

这种JVM自动生成的类只能用作其他类的代理类,所以叫做动态代理类。

JVM生成的动态类必须实现一个或者多个接口。所以,生成的动态类只能用作具有相同接口的目标类的代理类。

Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);

上面代码生成的代理类实现Collection接口。

上面程序利用JVM提供的生成动态代理类的方法,就是调用Proxy类的静态方法getProxyClass(classLoader,name)生成代理类。

上面getProxyClass()方法接收的第一个参数是类加载器,第二个参数是接口名字,因为可以实现一个或多个接口,所有接口名参数可以有多个,所以这个方法使用了可变参数。

通过上面的程序就得到一个实现Collection接口的代理类,通过使用getMethod()方法得到这个代理类中的方法,可以看到该代理类里面包含Collection接口的所有方法。

通过上面的方法,创建一个动态类,如果要创建该动态类的对象,要用构造方法对象调用newInstance()方法进行创建。

在创建对象的时候要传一个InvocationHandler对象给newInstance()方法。

动态代理类的对象在每次执行add()方法是都要进入InvocationHandler类的invoke()方法中执行里面的代码。

这里也可以看到,我在声明ArrayList对象时使用了泛型,但是泛型却不起作用了,在这里我也不知道是为什么。

下面用张孝祥老师课程中的一张图来深入理解AOP和动态代理。

如图,客户端调用代理类$Proxy1,$Proxy1的构造方法中有一个InvocationHandler对象,该对象对应着一个InvocationHandler接口的实现类,这个实现类中有一个invoke()方法。有三个参数,分别是调用方法的对象,方法的反射对象,方法的参数。

代理类中每次执行一个方法时,都要先进入InvocationHandler接口实现类的invoke()方法中执行,该invoke()方法中实现对原方法功能的扩充,这就现实了AOP。

invoke()方法中还有一个invoke()方法,这里可不是递归,因为里面这个invoke(target , param)方法和外面的invoke(target , method , param)方法并不是一个,因为参数列表就不一样。

来看里面的invoke()方法:

method.invoke(对象,参数)

method是方法的反射对象,该对象调用invoke()方法,传递调用此方法的对象,和该方法接收的实参。

                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
{

long start = System.currentTimeMillis();
Object retVal = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println(method.getName()+"执行时间:"+(end-start));
return retVal ;
}

前面说到代理类的意义就在于在原有类的基础上增加一些功能,这些新功能可以在三个地方增加:

1. 在调用目标方法之前增加。

2. 在调用目标方法之后增加。

3. 在处理目标方法异常的catch()块中增加。

来剖析一下这段代码:

long start = System.currentTimeMillis();        //这里执行了代码,声明一个变量记录当前时间

Object retVal = method.invoke(target, args);       //这段代码最重要,意思就是执行add()方法,把结果赋给retVal。

long end = System.currentTimeMillis();         //这里执行了代码,声明一个变量记录当前时间

System.out.println(method.getName()+"执行时间:"+(end-start));    //执行了代码,计算add()方法执行的时间。

return retVal ;      //返回add()方法的执行结果。

分析至此已经可以看得出来,在原本ArrayList类的add()方法执行之前执行了记录当前时间并赋值的代码,在add()方法执行之后也执行了代码。

这就是AOP。

流程:

代理类对象调用代理类的add(param)方法,代理类的add()方法会去找InvocationHandler类的invoke()方法,同时传递三个参数。

InvocationHandler类的invoke()方法会返回给add(param)方法一个参数,在这里这个参数就是ArrayList的对象target调用add()方法的结果。即target.add(param)。

上面的方法把要添加的功能的代码直接写在InvocationHandler类的invoke()方法中,这是一种方式。还可以用另外一种方式:

就是把该方法写成通用的Object类型,而不仅仅是Collection类型。

这样的话就可以实现任一类的动态代理。

上面说的都是JVM实现动态代理的方法,下面说说另一种情况的动态代理,因为还没有称为jdk规范,暂时了解就好。

CGLIB:

如果目标类没有实现接口,但是希望生成和目标类具有相同方法列表的代理类,JVM就做不到这一点,这是就要用到一个第三方类库来实现动态代理类的生成,这个第三方类库就是CGLIB。

CGLIB可以动态生成一个类的子类。

CGLIB之所以能够生成目标类的一个动态代理类,是因为一个类的子类是可以用作这个类的代理类。因为子类继承了父类的所有方法。

仔细想想,动态代理的出现其实就是为了提高AOP的性能。

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值