欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

不信你学不会Spring(4)——AOP面向切面编程

程序员文章站 2022-06-05 19:57:06
...

一、什么是AOP

之前提到过代理模式,可以在保证目标类源代码不改变的情况下增加新的功能。代理又分两种,动态代理和静态代理。

  • 静态代理一个代理类只能为同一类型的对象做代理,代理类需要实现和目标类相同的接口。
  • 动态代理可以处理多个不同类,并且将所有方法转移到调用处理器一个集中的方法中处理。

动态代理的作用:

  • 在目标类源代码不改变的情况下,增加额外功能
  • 减少重复的代码
  • 解耦和,然业务代码和其他日志、事务等非业务代码分离

动态代理有两种实现方式:

  • JDK动态代理,使用jdk中的Proxy,Method,InvocationHanderl创建代理对象。这种方式要求目标类必须实现一个接口。
  • cglib动态代理,第三方的工具库。原理是继承,通过继承目标类创建子类,子类就是代理对象。要求目标类不能是final的,方法也不能是final的。

AOP面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。


二、使用AOP

1.相关术语

  • Aspect:切面,表示要增强的功能代码。
  • JoinPoint:连接点,可以插入切面的地方。可以是每个方法前后,抛出异常等等都是练连接点。
  • ‘PointCut:切入点,根据条件选择切入到那些连接点。
  • Advice:通知,是切面中的实际逻辑,也就是在原有方法哪里执行切面(是在原有方法前执行,原有方法后执行等等)。

2.使用Aspectj流程

我们一般不使用spring自带的aop,而是使用aspect框架实现aop。实现步骤如下:

(1)添加依赖

	<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>

(2)创建目标类和它的接口
(3)创建切面类:普通类。
- 在类的上面加入@Aspect
- 在类中定义方法,方法就是要执行的切面代码。在方法上加入advice相关注解
- advice相关注解中要指定切入点表达式execution()

@Before(value = "execution(切入点表达式)")

(4)创建spring配置文件,在其中创建目标对象,切面对象。并声明aspectj框架中的自动代理生成器标签。

<!--会把spring容器中的所有的目标对象一次性都生成代理对象-->
<aop:aspectj-autoproxy>

(5)最后在需要使用目标类的地方,通过getBean取出的目标类其实是已经被代理的代理类。

3.JoinPoint参数

在切面方法的参数列表中,可以加入JoinPoint形参。该形参可以获取连接点方法的相关参数。

@Before(value = ”切入电表达式“)
public void test(JoinPoint jp){
	jp.getSignature   // 方法的定义
	jp.getSignature.getName // 方法的名字
	jp.getArgs  // 方法的参数
}

4.advice注解

(1)@Before() :前置通知注解

  • 属性:value,为切入点表达式
  • 方法形参:无
  • 位置:在目标方法之前执行
  • 特点:不会改变目标方法的执行结果

(2)@AfterReturning :后置通知

  • 属性:value,为切入点表达式;returning,自定义变量,为目标方法的返回值,要与方法形参名相同
  • 方法形参:Object类型,形参名要和注解的returning属性一致。表示目标方法的返回结果
  • 位置:在目标方法之后执行
  • 特点:能够获取目标方法的返回值,可以修改引用类型的属性值。

(3)@Around :环绕通知

  • 属性:value,切入点表达式
  • 方法形参:ProceedingJoinPoint
  • 方法需要有返回值,推荐使用Object
  • 位置:在目标方法前后都能增强
  • 特点:能修改目标方法的执行结果,控制目标方法执不执行
ProceedingJoinPoint就类似于动态代理中的method,即被代理的方法。
可以在切面方法中使用ProceedingJoinPoint方法中的proceed()表示执行目标方法。

(4)@AfterThrowing :异常通知

  • 属性:value,切入电表达式;throwing,表示目标方法抛出的异常对象。自定义名称,但是名称必须和方法形参Exception的名称相同
  • 方法形参:Exception,自定义形参名,但是必须和注解的throwing属性值相同。
  • 位置:在目标方法抛出异常执行

(5)After:最终通知

  • 返回值:无
  • 方法形参:无
  • 位置:总是会在目标方法之后执行 ,有异常也会执行

5.切入点表达式

切入点表达式是advice相关注解的value的值,它表明你写的这个切面方法要切入到那些连接点处。AspectJ切入点表达式的原型为:

execution(modifiers-pattern?(访问权限)	ret-type-pattern(返回值类型)
			declaring-type-pattern(包名类名)?name-pattern(param-pattern)(方法名(参数类型和参数个数))
			throws-pattern(抛出异常类型)?)

// 以上没加?的返回值类型和方法名(参数类型和参数个数)是必须有的,其他可以没有。

在切入点表达式中可以使用以下符号:

  • * 任意字符
  • ..用在方法参数中,表示任意多个参数;用在包名后面,表示当前包及其子包
  • +用在类名后,表示当前类及其子类;用在接口后,表示当前接口及其实现类

例如:

execution(* com.xyz.service..*.*(..))
表示service包及其子包中的任意类的任意方法。

execution(* *.service.*.*(..))
表示只有第一级service子包下所有的类中所有的方法

[email protected]

@Pointcut(value = "execution(切入点表达式)")
public void test(){
// 无需写代码
}


// 使得切入点表达式可以复用
@Before(value = "test()")
public void service(){
// 切面代码
}

7.注意

当目标类有接口时使用的是JDK的动态代理,没有接口时使用的是cglib动态代理。

<!--有接口时也使用cglib动态代理-->
<aop:aspectj-autoproxy proxy-target-class="true">