SpringAOP
什么是AOP技术
AOP是Aspect OrientedProgramming的缩写,即面向切面编程,它是一种由AOP联盟提出并制定了规范的编程范式,指导开发者如何组织程序结构。Spring框架引入了AOP思想并遵循AOP联盟的规范,通过预编译方式或运行期动态代理实现程序功能的统一维护。AOP采用横向抽取机制,能够对业务逻辑的各个部分进行隔离,取代了传统纵向继承体系中存在的重复性代码,从而降低业务逻辑各部分之间的耦合度,提高程序的可重用性和开发效率。
AOP的优势是在程序运行期间,不修改源代码的情况下对已有的方法进行增强,使得减少重复的代码、提供开发的效率、维护方便
AOP的底层原理使用的是动态代理技术,运行时为目标对象生成代理对象,通过代理对象插入通知逻辑,代理方式包括JDK的动态代理技术和CGLIB代理。其中JDK 动态代理的实现过程是首先是为接口创建代理类的字节码文件,其次使用ClassLoader将字节码文件加载到JVM,然后创建代理类实例对象,执行对象的目标方法。
SpringAOP 的核心概念:
- 连接点(JoinPoint):AOP 可增强的位置
- 切入点(Pointcut):实际被增强的方法就是切入点
- 通知(Advice):实质增强的逻辑部分称为通知
- 切面(Aspect):是一个动作,将通知应用到切入点的过程
Spring的AOP技术-配置文件方式
AspectJ 是 AOP 思想的具体实现者,是一个面向切面的框架,它扩展了Java语言,且是目前最完善、最流行的 AOP 框架之一。AspectJ定义了AOP语法,AspectJ实际上是对AOP编程思想的一个实践。
Spring AOP 虽然是另一种 AOP 实现,但它借鉴了 AspectJ 的核心概念和注解(如@Aspect、@Before等),不过实现机制与 AspectJ 不同(Spring AOP 基于动态代理,而 AspectJ 基于字节码操作)。可以说,AspectJ 为 AOP 思想的落地提供了成熟的技术方案,是 AOP 领域的事实标准之一。
创建maven项目,坐标依赖
pom.xml文件里的dependencies配置,用于声明项目依赖的第三方库。每个dependency标签都定义了一个需要引入的 jar 包,包含三个核心属性:groupId(组织 / 公司标识)、artifactId(库名称)、version(版本号)。
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--AOP联盟-->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!--Spring Aspects-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--aspectj-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.3</version>
</dependency>
</dependencies>
该代码的实现内容:基础的 Spring IoC 容器和 Bean 管理(spring-context),日志记录(commons-logging + log4j),单元测试(junit + spring-test),AOP 编程(aopalliance + spring-aspects + aspectjweaver)。
spring-context 是 Spring 框架的核心上下文依赖,Spring 应用的基础,几乎所有 Spring 项目都需要引入,实现基础的 Spring IoC 容器和 Bean 管理。commons-logging 是 Apache 的通用日志接口,Spring 框架内部使用 commons-logging 作为日志接口,因此需要引入它来适配具体的日志框架,log4j 与 commons-logging 配合使用,commons-logging 定义接口,log4j提供具体实现。spring-test 是Spring 提供的测试支持库,简化 Spring 应用的单元测试和集成测试。junit 是Java 领域最常用的单元测试框架,<scope>test</scope>
表示该依赖仅在测试阶段生效。aopalliance 是 AOP 联盟定义的 AOP 规范接口,是 Spring AOP 实现的基础规范,确保 AOP 组件的通用性。spring-aspects 是 Spring 框架的 AOP 模块实现,提供基于注解的 AOP 功能。aspectjweaver 是 AspectJ 框架的织入器Weaver,负责将切面代码织入到目标对象中,Spring AOP 借鉴了 AspectJ 的切入点表达式语法,需要 aspectjweaver 来解析这些表达式。
创建被增强的类Login和定义切面类QX
在配置文件中完成aop的配置
<bean id="login" class="com.qcby.Login"/>
<bean id="verify" class="com.qcby.QX"/>
<aop:config>
<aop:aspect ref="verify">
<aop:after method="verify" pointcut="execution(* com.qcby.Login.login(..))"/>
</aop:aspect>
</aop:config>
定义了两个 Spring 管理的 Bean,id 为唯一标识,class为其对应的实现类。<aop:config>
为AOP 配置的根标签,所有 AOP 相关配置都需要放在这个标签内。<aop:aspect ref="verify">
定义一个切面,ref=“verify” 表示引用上面定义的 verify Bean 作为切面的实现类,method 表示当切点匹配的方法执行时调用切面类中的增强方法也就是通知,pointcut 为切入点,用来精准匹配程序中需要被切面增强的方法,通过切入点表达式来定义匹配规则,最常用的是 execution() 表达式,语法如下:
execution(修饰符 返回值类型 包名.类名.方法名(参数列表))
操作结果
当com.qcby.Login类的login()方法正常执行完成后,Spring 会自动调用com.qcby.QX类的verify()方法,实现了在不修改 Login 类代码的情况下,对 login () 方法进行功能增强。这种方式体现了 AOP 的核心思想:将横切关注点与业务逻辑分离,提高代码的复用性和可维护性。
通知的五种类型:
<aop:before>
前置通知,方法执行之前进行通知<aop:after>
后置通知(最终通知),方法执行之后进行通知,只要目标方法被调用,无论方法执行是否成功都会执行通知都会触发<aop:after-returning>
返回通知,方法成功执行之后执行通知,仅在目标方法正常执行并返回结果后才会执行,如果目标方法抛出异常,则不会触发该通知<aop:after-throwing>
异常通知,只有在方法执行异常的时候才会执行通知<aop:around>
环绕通知,在方法执行之前和执行之后都会执行,通知方法必须接收ProceedingJoinPoint参数并调用proceed()方法
如下是环绕通知的实现
package com.qcby;
import org.aspectj.lang.ProceedingJoinPoint;
public class QX {
// 环绕通知
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("before.............");
// 执行被增强的方法
proceedingJoinPoint.proceed();
System.out.println("after.............");
}
}
环绕通知的本质是包裹目标方法,整个目标方法的执行权完全由环绕通知控制,只有主动调用proceedingJoinPoint.proceed()调用被增强的原始方法,才会触发目标方法的执行
Spring的AOP技术-注解方式
<context:component-scan base-package="com.qcby"/>
<!--开启Aspect生成代理对象-->
<aop:aspectj-autoproxy/>
这两行配置是 Spring 框架中用于开启组件扫描和 AOP 注解支持的核心配置,作用如下:
<context:component-scan base-package="com.qcby"/>
Spring 会扫描com.qcby包及其子包中所有带有@Component(以及衍生注解@Service、@Controller、@Repository)的类,自动将它们注册为 Spring 容器中的 Bean,无需再通过bean标签手动配置。
<aop:aspectj-autoproxy/>
开启基于注解的 AOP 支持,允许 Spring 识别@Aspect等 AOP 注解并生成代理对象。当 Spring 检测到某个类被 @Aspect 标记为切面,并且定义了针对其他类的切入点时,它会为被增强的类自动创建一个代理对象。这个代理对象在执行目标方法前后,自动插入切面中定义的增强逻辑。
需要使用@Aspect注解标识一个类为切面类,并通过@Component将其纳入 Spring 容器管理,如图:
测试结果:
补充实现环绕通知的方式:
package com.qcby;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class QX {
@Around("execution(* com.qcby.Login.login(..))")
// 环绕通知
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("before.............");
// 执行被增强的方法
proceedingJoinPoint.proceed();
System.out.println("after.............");
}
}