AOP简介
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP面向切面的编程是对面向对象编程的一种补充。Java提供了封装,继承,多态等概念,实现了面向对象编程。子类很多时候也会受限于父类,当我们有某个功能需要实现(常见的拦截器功能),如果不用AOP思想,我们需要大量重写代码,这无疑给我们造成了很大的困扰。但是AOP的出现提供“横向”的逻辑,将与多个对象有关的公共模块分装成一个可重用模块,并且将这个模块整合成Aspect,即切面。
AOP中的术语
名称 | 表述 |
连接点(JoinPoint) | spring允许你使用通知的地方,基本每个方法的前,后(两者都有也行),或抛出异常时都可以是连接点 |
切面(Aspect) | 切面是通知和切入点的结合 |
目标(target) | 需要被通知的对象。也就是需要加入额外代码的对象,也就是真正的业务逻辑被组织织入切面 |
通知(Advice) | 这个就是你需要实现的结果(日志、安全拦截、事物控制等) |
切入点(Pointcut) | 在多个类或这多个方法中筛选出需要横切的方法或对象 |
织入(weaving) | 将切面作用到对象 |
引入(introduction) | 允许我们向现有的类添加新方法属性。 |
代码实践
maven依赖
引入spring boot项目基础依赖之后的额外引入(基础依赖可参考:https://blog.csdn.net/m0_37798046/article/details/115108985)
<!-- aop依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
创建目标对象
创建Person接口
public interface Person {
void playGame(String name, Integer age);
}
实现接口
import org.springframework.stereotype.Component;
@Component
public class Student implements Person{
@Override
public void playGame(String name, Integer age) {
System.out.println("姓名:"+name+";年龄:"+age+";我开始玩了");
}
}
创建AOP类
package com.xiaofeixia.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class PersonAnnotationHandler {
private static final Logger logger = LoggerFactory.getLogger(PersonAnnotationHandler.class);
/**
* 定义切点
*/
@Pointcut("execution(* com.xiaofeixia.aop.Student.*(..))")
public void playGame(){
}
/**
* 前置处理
*/
@Before("playGame()")
public void startPlay(){
System.out.println("我准备开始玩了");
}
/**
* 后置处理
*/
@After("playGame()")
public void afterPlay(){
System.out.println("我玩累了");
}
/**
* 环绕通知
*/
@Around("playGame()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
//获取方法参数值数组
Object[] args = joinPoint.getArgs();
//得到其方法签名
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
//获取方法参数类型数组
Class[] paramTypeArray = methodSignature.getParameterTypes();
for(int i = 0; i < paramTypeArray.length; i++){
//参数列表参数是Integer类型,则给其赋值18
if(Integer.class.isAssignableFrom(paramTypeArray[i])){
args[i] = 18;
}
}
//动态修改其参数
//注意,如果调用joinPoint.proceed()方法,则修改的参数值不会生效,必须调用joinPoint.proceed(Object[] args)
Object result = joinPoint.proceed(args);
logger.info("响应结果为{}",result);
//如果这里不返回result,则目标对象实际返回值会被置为null
return result;
}
}
Application测试实现
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class StartApplication {
public static void main(String[] args) {
ApplicationContext app = SpringApplication.run(StartApplication.class, args);
Student student = app.getBean(Student.class);
student.playGame("小飞侠",25);
}
}
运行结果
Spring AOP注解说明
@Aspect:描述一个切面类,定义切面类的时候需要打上这个注解
@Pointcut:声明一个切入点,切入点决定了连接点关注的内容,使得我们可以控制通知什么时候执行。(作为切入点签名的方法必须返回void 类型)
Spring AOP支持在切入点表达式中使用如下的切入点指示符:
execution - 匹配方法执行的连接点,这是你将会用到的Spring的最主要的切入点指示符。
within - 限定匹配特定类型的连接点(在使用Spring AOP的时候,在匹配的类型中定义的方法的执行)。
this - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中bean reference(Spring AOP 代理)是指定类型的实例。
target - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中目标对象(被代理的应用对象)是指定类型的实例。
args - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中参数是指定类型的实例。
@target - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中正执行对象的类持有指定类型的注解。
@args - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中实际传入参数的运行时类型持有指定类型的注解。
@within - 限定匹配特定的连接点,其中连接点所在类型已指定注解(在使用Spring AOP的时候,所执行的方法所在类型已指定注解)。
@annotation - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中连接点的主题持有指定的注解。
其中execution使用最频繁,即某方法执行时进行切入。定义切入点中有一个重要的知识,即切入点表达式,我们一会在解释怎么写切入点表达式。切入点意思就是在什么时候切入什么方法,它是连接目标对象和切边的桥梁。
@Before:前置通知:在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。
@AfterReturning :后置通知:在某连接点正常完成后执行的通知,通常在一个匹配的方法返回的时候执行。
@AfterThrowing:异常通知:在方法抛出异常退出时执行的通知。
@After 最终通知。当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
@Around:环绕通知:包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。环绕通知最麻烦,也最强大,其是一个对方法的环绕,具体方法会通过代理传递到切面中去,切面中可选择执行方法与否,执行方法几次等。
环绕通知使用一个代理ProceedingJoinPoint类型的对象来管理目标对象,所以此通知的第一个参数必须是ProceedingJoinPoint类型,在通知体内,调用ProceedingJoinPoint的proceed()方法会导致后台的连接点方法执行。proceed 方法也可能会被调用并且传入一个Object[]对象-该数组中的值将被作为方法执行时的参数。
总结
AOP是我们编程中一种重要的思维,它是对面向对象编程的一种补充,相信使用Spring系列框架的小伙伴都不陌生Mybatis以及拦截器Interceptor都是使用AOP编程实现的,AOP编程可以让我们通过横面处理批量解决某些问题,让逻辑更清晰,可以让你的业务逻辑去关注自己本身的业务。
希望本次分享对大家对AOP的理解有帮助~~~