AOP思想实际上就是:在运行时,动态地将代码切入到类的指定方法、指定位置上.也就是说,我们可以使用AOP来帮助我们在方法执行前或执行之后,做一些额外的操作,实际上,就是代理!
通过AOP我们可以在保证原有业务不变的情况下,添加额外的动作。比如我们的某些方法执行完成之后,需要打印日志,那么这个时候,我们就可以使用AOP来帮助我们完成,它可以批量地为这些方法添加动作。可以说,它相当于将我们原有的方法,在不改变源代码的基础上进行了增强处理。
相当于我们的整个业务流程,被直接斩断,并在断掉的位置添加了一个额外的操作,再连接起来,也就是在一个切点位置插入内容。它的原理实际上就是通过动态代理机制实现的,Spring底层并不是使用的JDK提供的动态代理,而是使用的第三方库实现,它能够以父类的形式代理,而不是接口。
使用SpringAOP
先导入一个依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.13</version>
</dependency>
明确实现AOP操作需要知道这些内容
1. 需要切入的类,类的哪个方法需要被切入
2. 切入之后需要执行什么动作
3. 是在方法执行前切入还是在方法执行后切入
4. 如何告诉Spring需要进行切入
找到需要切入的类
public class Student {
String name;
int age;
//分别在test方法执行前后切入
public int test(String str) {
System.out.println("我是一个测试方法:"+str);
return str.length();
}
}
定义方法执行前后我们要执行的操作
public class AopTest {
//执行之后的方法
public void after(){
System.out.println("我是执行之后");
}
//执行之前的方法
public void before(){
System.out.println("我是执行之前");
}
}
@Before
前置通知:目标方法之前执行
@After
后置通知:目标方法之后执行(始终执行)
@AfterReturning
返回通知:执行方法结束前执行(异常不执行)
@AfterThrowing
异常通知:出现异常的时候执行
@Around
环绕通知:环绕目标方法执行
将要进行AOP操作的类注册为Bean
<bean name="student" class="com.test.bean.Student"/>
<bean name="aopTest" class="com.test.aop.AopTest"/>
一个是Student类,还有一个就是包含我们要切入方法的AopTest类,注册为Bean后,他们就交给Spring进行管理,这样Spring才能帮助我们完成AOP操作
修改顶部,引入aop相关标签
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
使用`aop:config`来添加一个新的AOP配置
<aop:config>
</aop:config>
告诉Spring,我们要切入的是哪一个类的哪个或是哪些方法
<aop:pointcut id="test" expression="execution(* com.test.bean.Student.test(String))"/>
`expression`属性的`execution`填写格式如下
修饰符 包名.类名.方法名称(方法参数)
* 修饰符:public、protected、private、包括返回值类型、static等等(使用*代表任意修饰符)
* 包名:如com.test(*代表全部,比如com.*代表com包下的全部包)
* 类名:使用*也可以代表包下的所有类
* 方法名称:可以使用*代表全部方法
* 方法参数:填写对应的参数即可,比如(String, String),也可以使用*来代表任意一个参数,使用..代表所有参数。
也可以使用其他属性来进行匹配,比如`@annotation`可以用于表示标记了哪些注解的方法被切入
为此方法添加一个执行前动作和一个执行后动作
<aop:aspect ref="aopTest">
<aop:before method="before" pointcut-ref="test"/>
<aop:after-returning method="after" pointcut-ref="test"/>
</aop:aspect>
配置完成
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
Student student = context.getBean(Student.class);
student.test("lbwnb");
}
方法执行前后,分别调用了我们对应的方法。但是仅仅这样还是不能满足一些需求,在某些情况下,我们可以需求方法执行的一些参数,比如方法执行之后返回了什么,或是方法开始之前传入了什么参数等等。
这个时候,我们可以为我们切入的方法添加一个参数,通过此参数就可以快速获取切点位置的一些信息
//执行之前的方法
public void before(JoinPoint point){
System.out.println("我是执行之前");
System.out.println(point.getTarget()); //获取执行方法的对象
System.out.println(Arrays.toString(point.getArgs())); //获取传入方法的实参
}
通过添加JoinPoint作为形参,Spring会自动给我们一个实现类对象,这样我们就能获取方法的一些信息了。
环绕方法相当于完全代理了此方法,它完全将此方法包含在中间,需要我们手动调用才可以执行此方法,并且我们可以直接获取更多的参数
编写实现环绕的方法,通过`proceed`方法来执行代理的方法
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("方法开始之前");
Object value = joinPoint.proceed();
System.out.println("方法执行完成,结果为:"+value);
return value;
}
修改xml
<aop:aspect ref="aop"> <aop:pointcut id="after" expression="execution(* com.cafuc.bean.Student.say(String,int ))"/> <aop:around method="around" pointcut-ref="after"/> </aop:aspect>
使用接口实现AOP
首先我们需要将一个类实现Advice接口,只有实现此接口,才可以被通知,比如我们这里使用`MethodBeforeAdvice`表示是一个在方法执行之前的动作
public class AopTest implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("通过Advice实现AOP");
}
}
args代表的是方法执行前得到的实参列表,还有target表示执行此方法的实例对象
修改xml
<aop:config>
<aop:pointcut id="stu" expression="execution(* com.test.bean.Student.say(String))"/>
<aop:advisor advice-ref="before" pointcut-ref="stu"/>
</aop:config>
`AfterReturningAdvice`就需要实现一个方法执行之后的操作
public class AopTest implements MethodBeforeAdvice, AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("我是方法执行之后!");
}
}
AOP 领域中的特性术语
- 通知(Advice): AOP 框架中的增强处理,通知描述了切面何时执行以及如何执行增强处理,也就是我们上面编写的方法实现。
- 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出,实际上就是我们在方法执行前或是执行后需要做的内容。
- 切点(PointCut): 可以插入增强处理的连接点,可以是方法执行之前也可以方法执行之后,还可以是抛出异常之类的。
- 切面(Aspect): 切面是通知和切点的结合,我们之前在xml中定义的就是切面,包括很多信息。
- 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
- 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,我们之前都是在将我们的增强处理添加到目标对象,也就是织入