AOP简介
为了快速理解 AOP,本节先展示一个 AOP 的小案例,使读者可以快速了解 AOP 在 Spring Boot 中的使用。然后,再介绍其常用概念,为后续章节做一些铺垫。
AOP小案例
在开始一个 Demo 之前,需要知道使用哪些依赖,pom.xml 中需要添加以下依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
为了方便重现程序,先展示一下程序结构,这样对 AOP 的演示程序更加一目了然。我们新建一个 aop 包,主要用于存放 AOP 的知识点实例;新建 aspect 包,用于切面程序;新建 config 包,用于加载配置文件;新建 pojo 包,用于存放简单类;新建 test 包,用于程序测试。具体如图5.1所示。

首先,需要写一个方法,其方法是用于实现日志输出功能。虽然比较简单,但依旧需要使用接口来实现。在 pojo 包中新建 LogPrint 接口,代码如下所示。
package com.springBoot.aop.pojo;
/**
* @Description 用于打印的接口
*/
public interface LogPrint {
//打印功能
public void doPrint();
}
然后,需要对接口进行实现,在 pojo 包下新建 impl 包,实现类为 myLogPrint,代码如下所示。
package com.springBoot.aop.pojo.impl;
//在这个地方,需要使用@Component进行扫描
@Component("myLogPrint")
public class MyLogPrint implements LogPrint {
@Override
public void doPrint() {
System.out.println("log print");
}
}
在上面的代码中,需要用到 @Component,因为在后面测试时,思路是引入 Bean,然后调用 doPrint 方法。为了说明在实现日志输出 doPrint 功能时使用了切面技术,前后也会出现执行程序。新建 aspect 包,并新建 LogAspect 类,代码如下所示。
package com.springBoot.aop.aspect;
@Aspect
@Component
public class LogAscpect {
private Logger logger=LoggerFactory.getLogger(LogAscpect. class);
//
@Before("execution( * com.springBoot.aop.pojo.impl.MyLogPrint. doPrint(..))")
public void before(){
System.out.println("before log");
}
@After("execution( * com.springBoot.aop.pojo.impl.MyLogPrint. doPrint(..))")
public void after(){
System.out.println("after log");
}
@AfterReturning("execution( * com.springBoot.aop.pojo.impl. MyLogPrint.doPrint(..))")
public void afterReturning(){
System.out.println("afterReturning log");
}
@AfterThrowing("execution( * com.springBoot.aop.pojo.impl. MyLogPrint.doPrint(..))")
public void afterThrowing(){
System.out.println("afterThrowing log");
}
}
对于上面的代码,同样需要用到 @Component 注解,因为在使用切面技术时,需要将这个类加载到配置文件,如果没有注解,会看不到上面代码的执行。对于注解 @Before、@After、@AfterReturning、@AfterThrowing,先不详细说明,看完执行结果后再介绍。对于 @Aspect 注解,其用于让 Spring 知道这是一个切面程序。
然后,需要在 config 下面新建配置类,将 Bean 注入程序中,代码如下所示。
package com.springBoot.aop.config;
@Configuration
@ComponentScan(basePackages="com.springBoot.aop.*")
public class MySpringBootConfig {
}
在 IOC 中就有这一段代码,唯一不同的地方是扫描的包发生变化,只扫描 aop 包下的类,因为这里的测试程序集中于 AOP。
最后,需要进行测试。网上的很多实例是通过浏览器访问后台的方式访问切面程序的,这里依旧沿用写测试类调用方法。代码如下所示。
package com.springBoot.aop.test;
@SpringBootApplication
public class AopTestDemo {
private static final Logger log=LoggerFactory.getLogger(IocTestDemo. class);
public static void main(String[] args) {
ApplicationContext context=new AnnotationConfigApplicationC ontext(MySpringBootConfig.class);
MyLogPrint myLogPrint=context.getBean(MyLogPrint.class);
myLogPrint.doPrint();
}
}
上面代码,因为是运行 Spring Boot 程序,所以需要引用注解 @SpringBootApplication。如果不引入这个注解,程序只是简单的测试类,不会加载 Spring Boot 中的默认配置项,会造成执行 doPrint 方法时,程序不进入切面程序。实例程序写好后,查看控制台的执行效果。
22:40:18.096[main]DEBUGorg.springframework.beans.factory.support. DefaultListableBeanFactory - Returning cached instance of singleton bean 'myLogPrint'
22:40:18.098[main]DEBUGorg.springframework.beans.factory.support. DefaultListableBeanFactory - Returning cached instance of singleton bean 'logAscpect'
before log
log print
after log
afterReturning log
通过加粗字体 “log print”,可以知道 doPrint 方法已经被顺利执行。但在这之前与之后,都有信息被输出,通过对比会发现这里的信息来源于 LogAspect 类下被 @Before、@After、@AfterReturning 注解过的方法。
注解中字符串 “execution( *com.springBoot.aop.pojo.impl.MyLogPrint.doPrint(..))”
是一个切入点,这是正则表达式,用来说明什么情况下启用 AOP;execution 是切点 Ascpect 常用的切点函数,第一个 “*” 代表返回任意类型;后面是方法,对于 “..” 则表示任意参数。
-
@Before:在切点(doPrint)之前执行的方法。
-
@After:在切点(doPrint)之后执行的方法。
-
@AfterReturning:退出切面会执行的方法。
AOP术语
在上面的实例中,我们只是讲解了 AOP 可以增强 Bean 的原有方法,及如何简单使用 AOP。在前面实例中,很难深入描述,为了后面章节内容的学习,我们正式开始介绍 AOP 的术语。
连接点(joinPoint):在 Spring 中支持方法类型的连接点,所以,连接点就是指拦截的方法,例如实例中的 doPrint 方法。
切点(pointCut):一系列连接点的集合,对于一个切面而言,执行的不仅只是一个类的某个方法,也许是某个类的多个方法,或者多个类的某一个方法,那么具体是哪一个连接点?我们可以根据正则表达式或者指示器规则进行定义,从而找到连接点,例如,我们在实例中使用的正则表达式,但是这里的正则表达式有些简单,只指定了类的具体方法。
通知(Advice):在连接点处,AOP执行的动作,在AOP中,主要有5种通知,前置通知(BeforeAdvice)、后置通知(AfterAdvice)、返回通知(AfterReturningAdvice)、异常通知(AfterThrowingAdvice)和环绕通知(AroundAdvice),关于这几种通知,在后文会仔细讲解。
-
引入(introduction):在被通知的类中引入字段与方法。
-
目标对象(target):被代理的对象,或者说包含连接点的对象,例如MyLogPrint 类。
-
切面(aspect):可以定义各类通知、切点和引入的内容,具体可以根据图5.2进行理解。
-
织入(weave):把切面连接到应用程序上,并生成一个被通知的对象。
主要的术语已描述,我们可以借助图形来理解上面的术语。图5.2是一个 AOP 流程图,主要分为左右两个部分,这样就能直观地理解。
