Spring--4--aop

Aop的概念

Aop(Aspect-Oriented Programing):面向方面编程,是面向对象编程(oop)的一个补充,目前是一种成熟的编程模式。
OOP(Object-Oriented Programing):面向对象编程。
Aop采用横向抽取机制,将分散到各个方法的重复代码提取出来,然后在程序编译或者运行时,再将这些提取出来的代码应用到指定方法的指定位置上运行。是OOP的延伸和补充。

Aop的术语

  • Aspect(切面):封装的用于横向插入系统功能(如事务或者日志等等)的类。
  • Joinpoint(连接点):在程序执行过程中的某个阶段点。
  • Pointcut(切入点):切面与程序流程的交叉点,即那些需要处理的连接点
  • Advice(通知/增强处理):Aop框架在特定的切入点执行的增强处理,即在定义好的切入点所要执行的程序代码,可以将其理解为切面类中的方法。
  • TargetObject(目标对象):指所有被通知的对象,也被称为被增强对象。如果AOP框架采用的是动态的AOP实现,那么该对象是一个被代理的对象。
  • Proxy(代理):将通知应用到目标对象之后,被动态创建的对象。
  • Weaving(织入):将切面代码插入到目标对象之后,从而生成代理对象的过程。
    在这里插入图片描述

动态代理

1、JDK动态代理(有局限性,使用动态代理的对象必须实现一个或多个接口)
jdk默认的代理缺陷:如果目标对象没有实现任何接口,是无法给其创建代理对象的,
通过java.lang.reflect.Proxy类来实现的,我们可以通过调用Proxy类中的newProxyInstance()方法来创建代理对象,对于调用业务接口的类,Spring会默认使用JDK动态代理来实现AOP。
在这里插入图片描述
为一个类做代理:(jdk默认的)
在这里插入图片描述

使用动态代理给类做日志。(用jdk默认的)

在这里插入图片描述
2、CGLIB代理
可以根据某一类直接进行代理,无需接口

基于代理类的AOP实现

实现简单,没有强制要求目标对象一定要实现某接口。

1、Spring的一些专业术语

在这里插入图片描述

2、Spring的通知类型

Spring按照通知在目标类方法的连接点位置,可以分为5类:
在这里插入图片描述

3、Spirng基于注解AOP使用步骤

1)导包。

除了基础的几个包,要额外需要
普通版的包:

spring-aspects-4.3.6.RELEASE.jar

加强版的几个包:(即使目标对象没有实现接口,也可以实现动态代理)

com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
com.springsource.net.sf.cglib-2.2.0.jar
2)配置

a)将目标类和切面类(封装了通知方法的类)加入到ioc容器中。
利用@Service、@Component…
b)告诉那个类是切面类。
在类上在加一个@Aspect注解
c)告诉spring每一个方法都是何时何地运行。
/**
* 告诉Spring,每个方法在什么时候运行
* @Before:在目标方法之前执行 前置通知
* @After:在目标方法之后执行 后置通知
* @AfterReturning:在目标方法正常返回执行
* @AfterThrowing:在目标方法抛出异常时执行
* @Around:环绕
*/
在这里插入图片描述

d)开启基于注解的aop功能。先导入AOP名称空间。然后在配置文件中加入这个。
aop:aspectj-autoproxy</aop:aspectj-autoproxy>
在这里插入图片描述

3)使用

在这里插入图片描述

4、抽取可重用的切入点表达式

1)、随便声明一个没有返回值void类型的空方法
2)、给方法上加上@Pointcut注解

在这里插入图片描述

3)、先加入切入点表达式,其他地方直接引用即可(若所有方法都引用了,即可以实现改一处实现改所有方法处的切入点表达式)
在这里插入图片描述

5、环绕通知(@Around)

在这里插入图片描述

@Around注解标注的方法里面可以使用一个ProceedingJoinPoint.
在这里插入图片描述

6、Spring基于配置AOP的使用(基于注解/基于配置)

基于配置的
1、将目标类和切面类用标签将组件加入容器中
2、告诉Spring哪个类是切面类。
aop:config:Aop的配置
aop:aspect 标签来指定
3、在切面类中告诉spring每个方法所执行的顺序是什么。利用aop:before等等标签
在这里插入图片描述

基于注解的
1、将目标类和切面类利用注解 @Component 注解等加入容器
2、告诉Spring哪个类是切面类。在切面类上加入 @Aspect注解。
3、在切面类中告诉spring每个方法所执行的顺序是什么。利用@Before等等注解
4、在配置文件中加入自动代理申明aop:aspectj-autoproxy</aop:aspectj-autoproxy>

7、ProxyFactoryBean

ProxyFactoryBean是一个FactoryBean的实现类,FactoryBean负责实例化一个Bean,而ProxyFactoryBean负责为其他Bean创建代理实例。在Spring中,使用ProxyFactoryBean是创建AOP代理的基本方式。
在这里插入图片描述

Aop的细节

1、Aop的底层就是动态代理,在保证了Aop的类,启动后容器保存的组件是他的代理类型,而不是本类。

若被代理的类有接口,则获取到的就是接口类,不是本类,不能直接强转为本类。容器中保存的是它的代理类型。
若被代理的类没有接口,则获取到的就不是接口类,是本类,但是容器中保存的组件还是他的代理类型。

2、切入点表达式的写法。

固定写法:execution(访问权限符 返回类型 方法全类名(参数表))
例如:@Before("execution(public int com.ioc.impl.MyMathCalcultor.(int, int))")
MyMathCalcultor 类中的所有方法,
表示所有的方法
1)、匹配一个或多个字符(用 * )

@Before("execution(public int com.ioc.impl.MyMath*.*(int, int))")

这个就是以MyMath字符开头的类,其类中所有的方法。
2)、匹配任何一个参数(用 * )
若类中的函数进行了重载(仅仅函数参数类型不同)

@Before("execution(public int com.ioc.impl.MyMath*.*(int, *))")

这样写,有且有两个参数,第一个参数为int,但是第二个是任意类型
3)、匹配任意参数任意类型(用 . .)

@Before("execution(public int com.ioc.impl.MyMath*.*( . . ))")

… 表示参数的个数和类型都是任意的。
4)、匹配一层路径( 用 * )
方法全类名称为这个先是这个类的路径然后 .方法名/.* 来表示

@Before("execution(public int com.ioc. * .MyMath*.*(int, *))")

这个 * 代表了impl包,但是若impl包下面还有子包,是代表不了的。也就是说MyMath…这个类是要在impl包下的一个类。而不能是impl包下的某个包里面的一个类。
5)、匹配任意多层路径(用 … )
方法全类名称为这个先是这个类的路径然后 .方法名/.* 来表示

@Before("execution(public int com.ioc . . MyMath*.*(int, *))")

这个 . . 就可以多层路径,与第四点不同,也就是说MyMath…这个类只要在impl包下即可,无论其在impl包直属下还是在impl包下的某个包里。
6)、( 表示返回值、 不能表示访问权限符 )**

  • 若表示返回类型,而表示任意类型的返回类型
@Before("execution(public *  com.ioc . . MyMath*.*(int, *))")

表示返回值为任意类型的方法
若想标注任意访问权限符的方法,不能用 * 表示,可以直接省略访问权限符即可。

@Before("execution( *  com.ioc . . MyMath*.*(int, *))")

表示任意访问权限符的方法
7)、任意包任意类的任意方法

execution( * *( . . ) )

8)、多个表达式 (&& || ! )
&&:两个表达式同时满足
|| :两个表达式满足任意一个即可
! :只要不满足这个表达式就切入

3、通知方法的执行顺序

1)、单切面的执行顺序
在这里插入图片描述

只有普通通知存在的时候:
正常执行:
@Before(前置通知) – >> @After(后置通知) – >> @AfterReturning(返回)
异常执行:
@Before (前置通知)-- >> @After(后置通知) – >> @AfterThrowing(异常)
若环绕通知和普通通知一起存在的时候:
环绕前置–>普通前置–>目标代码执行–>环绕正常返回/出现异常–>环绕后置–>普通后置–>普通返回或者异常。
注意:若程序出现异常,环绕通知也先执行了捕捉异常处理,所以在后面普通异常的情况下,就显示程序无异常。此时应该在环绕通知捕捉异常的同时将异常抛出。
在这里插入图片描述

2)、多切面的执行顺序
若对一个方法定义了多个切面,则会先根据定义的切面类的名称String判断大小来判断谁先执行,从而判断执行的顺序。先执行的然后后出去。
在这里插入图片描述

若想要改变默认的判断执行的顺序的话,可以在切面类上加上@Order()注解来改变执行的优先级。
在这里插入图片描述

若在某个切面上加了环绕,总的顺序,先进入的后出不变。但是在哪个切面类中加了环绕就对这个切面类执行有环绕通知的顺序。

4、在通知方法的执行时,拿到通知方法的详细信息、返回值、异常

1)只需要在通知方法的参数列表上加一个JoinPoint(import org.aspectj.lang.JoinPoint;)
joinPoint : 封装了当前目标方法的详细信息
在这里插入图片描述

2)利用returning、throwing来接收返回值与异常
在这里插入图片描述

5、通知方法的注意点

spring对通知方法的要求并不严格,但是唯一要求的是通知方法的参数一定不能乱写。
通知方法上的每一个参数,spring都要知道是什么。
joinPoint:通知方法的详细信息。
其他参数:一定要指明其用处。例如:returning、throwing

Aop的应用场景

1、AOP加日志到数据库中
2、AOP做权限验证
3、AOP做安全检查
4、AOP做事务控制

猜你喜欢

转载自blog.csdn.net/weixin_42272869/article/details/112097765
今日推荐