SpringAOP的简单使用

Posted tsymqwb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringAOP的简单使用相关的知识,希望对你有一定的参考价值。

# SpringAOP

## 概述

AOP的全称是Aspect Oriented Programming(面向切面编程)

OOP语言提供了类与类之间纵向的关系(继承、接口),而AOP补充了横向的关系(比如在不改变目标类中源代码的情况下给com.john.demo.dao包下所有类中以insert和update开头的方法添加事务管理)

 

### SpringAOP和AspectJ的区别

? AspectJ是一个专门主打面向切面编程的框架。 它是使用一种特殊的语言(扩展自Java语言)来编写切面代码,后缀是.aj格式,并且需要使用专门的编译器将其编译成jvm可以运行的class文件。

? SpringAOP底层也是使用了AspectJ的方案,但是在上层做了很多封装层面的工作,可以让开发人员直接使用Java代码来编写切面。并且由于使用的是标准的Java语言,所以并不需要在额外安装一个专门的编译器。但是由于开发人员直接接触的是Spring AOP,那么凡是Spring中没有实现的那些AOP功能,我们就用不了了,这种情况下只能跟产品经理撕逼或者去学习原生的AspectJ。

## AOP的术语

- 切面(Aspect)

简单来说,切面就是我们要往目标代码中插入进去的代码。

- 连接点(Join Pointer)

理论上所有可能会被切入的地方都可以称之为连接点

- 切入点(Pointcut)

选择某个连接点切入,将切面代码织入进去。这个连接点就叫做切入点。

- 织入(Weaving)

把切面代码糅合到目标代码中的过程就是织入。

- 通知(Advice)

通知决定了切面代码织入到目标代码中后,运行的时机(比如是在目标方法执行前,还是执行后)。

## 在Spring中使用AOP

### 基于XML方式使用

1. 把aop的schema引入

```xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
```

2. 创建一个切面类,并且以bean的方式配置到IOC容器中

```java
package com.lanou3g.spring;
public class MyAspect {

public void wakeup() {
System.out.println("[前置通知]我刚学习SpringAOP睡着了,刚才谁打了我一下?");
}


public void goToBed() {
System.out.println("[后置通知]SpringAOP太难了,一不小心又睡着了");
}


public void afterRetuing(Object message) {
System.out.println("[后置返回通知]方法执行已经return了,方法返回值是:" + message);
}

public void afterThrowing(Throwable ex) {
System.out.println("[后置异常通知]方法执行出现异常,异常原因:" + ex.getMessage());
}

/**
* 环绕通知
* 可以接受一个ProceedingJoinPoint参数
* 通过此参数可以获取到被切入方法的所有信息
* 还可以通过此参数来决定是否调用目标方法
*/
public void aroundAdvice(ProceedingJoinPoint joinPoint) {

// 连接点参数可以获取到被切入方法的所有信息
// 这里演示了如何获取被切入方法的名称
String targetMethodName = joinPoint.getSignature().getName();
System.out.println("[环绕通知]被切入的方法名:" + targetMethodName);

//
System.out.println("[环绕通知]即将开始新的一天, 早起的鸟儿有虫吃!");
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("[环绕通知]这真是充实的一天, 早睡早起,方能养生!");
}
}
```

```xml
<bean id="myAspect" class="com.lanou3g.spring.MyAspect" />
```

3. 使用<aop:config>标签配置aop(将切面、切入点、通知结合到一起)

1. 定义切入点表达式
2. <aop:aspect>
1. 引用外部定义的切面bean
2. 配置通知,引用切入点表达式

```xml
<aop:config>
<!-- 切入点表示匹配com.lanou3g.spring包下的所有类中所有以oneDay开头的方法,方法的参数、返回值不限 -->
<aop:pointcut id="myPointcut" expression="execution(* com.lanou3g.spring..*.oneDay*(..))" />
<aop:aspect ref="myAspect">
<!-- 无论是否出现异常,只要被切入的方法开始运行,都会触发此通知 -->
<aop:before method="wakeup" pointcut-ref="beforeOneDay" />
<!-- 无论是否出现异常,只要被切入的方法运行结束,都会触发此通知 -->
<aop:after method="goToBed" pointcut-ref="beforeOneDay" />
<!--
可以最大限度的对被切入方法附加功能,在方法执行前、后都可以通知(无论是否出现异常)
,还可以获取到被切入方法的所有信息,包括是否调用被切入的方法
-->
<aop:around method="aroundAdvice" pointcut-ref="beforeOneDay" />
<!-- 被切入的方法正常返回值以后,会触发此通知 -->
<aop:after-returning method="afterRetuing" pointcut-ref="beforeOneDay" returning="message" />
<!-- 被切入的方法抛出异常以后,会触发此通知,并且不会触发after-returning -->
<aop:after-throwing method="afterThrowing" pointcut-ref="beforeOneDay" throwing="ex" />
</aop:aspect>
</aop:config>
```

 

### 基于注解方式使用

1. 开启AOP注解支持

方式一:注解的方式

```xml
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

}
```

方式二:xml中开启

```xml
<aop:aspectj-autoproxy/>
```

2. 定义切面类

```java
/**
* 该切面用来插入起床的逻辑
*/
@Aspect
@Component //@Aspect注解没有将bean交给ioc容器管理的功能
public class MyAspect {

@Before("com.lanou3g.spring.aop.MyPointcut.allOneDayMehtod()")
public void wakeup() {
System.out.println("[前置通知]我刚学习SpringAOP睡着了,刚才谁打了我一下?");
}

@After("com.lanou3g.spring.aop.MyPointcut.allOneDayMehtod()")
public void goToBed() {
System.out.println("[后置通知]SpringAOP太难了,一不小心又睡着了");
}

@AfterReturning(value = "com.lanou3g.spring.aop.MyPointcut.allOneDayMehtod()", returning = "message")
public void afterRetuing(Object message) {
System.out.println("[后置返回通知]方法执行已经return了,方法返回值是:" + message);
}

@AfterThrowing(value = "com.lanou3g.spring.aop.MyPointcut.allOneDayMehtod()", throwing = "ex")
public void afterThrowing(Throwable ex) {
System.out.println("[后置异常通知]方法执行出现异常,异常原因:" + ex.getMessage());
}

/**
* 环绕通知
* 可以接受一个ProceedingJoinPoint参数
* 通过此参数可以获取到被切入方法的所有信息
* 还可以通过此参数来决定是否调用目标方法
*/
// @Around("com.lanou3g.spring.aop.MyPointcut.allOneDayMehtod()")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) {

// 连接点参数可以获取到被切入方法的所有信息
// 这里演示了如何获取被切入方法的名称
String targetMethodName = joinPoint.getSignature().getName();
System.out.println("[环绕通知]被切入的方法名:" + targetMethodName);

//
System.out.println("[环绕通知]即将开始新的一天, 早起的鸟儿有虫吃!");
Object ret = null;
try {
ret = joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("[环绕通知]这真是充实的一天, 早睡早起,方能养生!");
return ret;
}
}
```

> 注意:@Aspect注解没有将bean交给ioc容器管理的功能,我们需要额外添加一个@Component注解

3. 定义切入点

官方建议我们将所有的切入点统一定义到一个地方管理,在配置通知时通过引入的方式来使用。方便后期维护(一处修改,处处生效)

```java
@Component
public class MyPointcut {
// 通过@Pointcut注解定义一个切入点
@Pointcut("execution(* oneDay(..))")
public void allOneDayMehtod() {}
}
```

 

4. 在切面类中添加要切入的代码

> 参见定义切面部分

5. 在切入的代码方法上添加通知的注解

> 参见定义切面部分

以上是关于SpringAOP的简单使用的主要内容,如果未能解决你的问题,请参考以下文章

SpringAOP+注解实现简单的日志管理

@Around简单使用示例——SpringAOP增强处理

SpringAOP+注解实现简单的日志管理

SpringBoot之集成SpringAOP分析

SpringBoot之集成SpringAOP分析

SpringAOP简单入门