AOP: 核心是在不改动源代码时,对已有代码进行增强。这个增强操作可以是业务逻辑外的其他重复出现的操作或任务。
前期准备
以Spring为框架,只需要准备一个待加强的类。
以一个用户登录的例子,这个类中有两个方法,登录和登出。现在需要做一件事,在登录前
输出当前时刻,在登出前
也输出当前时刻。
public class UserService {
public void login(){
// 增强1
System.out.println("用户登录");
}
public void logOut(){
// 增强2
System.out.println("用户登出");
}
}
增强类
这里使用了日期模板,以及使用模板输出时间。
public class TimePrint {
SimpleDateFormat matter = new SimpleDateFormat("现在时间:yyyy年MM月dd日E HH时mm分ss秒");
public void printTime(){
System.out.println(matter.format(new Date()));
}
}
注意事项:
Spring底层还是使用的是aspectj
的方式,所以一定要再加上该依赖。
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>
使用XML的方式
模板:这里的命名空间中加上了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
https://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
将类注册到容器中
待增强类 和 增强的工具类都注入到容器中
<bean id="userService" class="juejin.aopXml.bean.UserService"></bean>
<bean id="timePrint" class="juejin.aopXml.utils.TimePrint"></bean>
使用配置的方式,配置aop
固定写法:
其中,<aop:aspect id="timeAspect" ref="timePrint">
,重要的是ref
指向的是增强类。
<aop:config>
<aop:aspect id="timeAspect" ref="timePrint">
</aop:aspect>
</aop:config>
Ok,Ok。现在暂停了解一下Spring给我们提供的加强类型有哪些(简单点说就是,你希望加强的方法
在执行具体方法
的什么时候去执行)。主要有以下几个(该无序内容引自:从 0 开始深入学习 Spring - LinkedBear - 掘金小册 (juejin.cn)):
- before 前置通知:目标对象的方法调用之前触发。
- after后置通知:目标对象的方法调用之后触发。
- afterReturning 返回通知:目标对象的方法调用完成,在返回结果值之后触发。
- afterThrowing 异常通知:目标对象的方法运行中抛出 / 触发异常后触发。
- around 环绕通知:编程式控制目标对象的方法调用。
那么目标很明确,我们要做的是目标对象的方法调用之前触发,也就是在执行System.out.println("用户登录");
之前,执行System.out.println(new Date().getTime());
了解了加强类型,我们需要写的是,告诉Spring哪个方法需要被增强。先看我直接写出来,再理解其中的意思。
<aop:before method="printTime" pointcut="execution(public * juejin.aopXml.bean.UserService.* (..))"></aop:before>
- 内容都被
<aop:before></aop:before>
包围着,这就是上面说的:前置通知,目标对象的方法调用之前触发。 method="printTime"
:表示调用增强类中的printTime()
方法。pointcut=""
:表示上面的具体要给哪个类中的哪个方法进行printTime()
增强。- 这的切点表达式意思是: 方法要求是:public的,范围值任意,在
juejin.aopXml.bean
包下,在UserService
类中的任意方法,参数个数任意。
pointcut=""
,这个引号中的内容是关于
切点表达式的内容,这个具体的使用内容因篇幅原因在其他文章中介绍。
启动类
使用了线程的睡眠方式,睡眠一秒,测试两个方法是否被增强。
public class AopXmlApplication {
public static void main(String[] args) throws InterruptedException {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("juejin/aopXml/xmlAop.xml");
UserService userService = ctx.getBean(UserService.class);
userService.login();
Thread.sleep(1000);
userService.logOut();
}
}
输出:
现在时间:2022年12月04日星期日 21时23分51秒
用户登录
现在时间:2022年12月04日星期日 21时23分52秒
用户登出
附录:完整的XML-aop配置内容
<aop:config>
<aop:aspect id="timeAspect" ref="timePrint">
<aop:before method="printTime" pointcut="execution(public * juejin.aopXml.bean.UserService.* (..))"></aop:before>
</aop:aspect>
</aop:config>