前言
AOP是面向切面编程,是OOP(面向对象编程)的延续。面向对象编程大家都使用过,万物皆对象嘛。而面向切面编程则是针对业务处理过程中的某一面进行提取,而不是面向整个对象来编程。
面向切面编程最大的特点就是,能够做到在不改变源码的前提下,实现对原有功能的升级与扩展,降低模块之间的耦合性,他的底层是动态代理。
一、浅提一嘴IOC
IOC、AOP是spring框架的两大核心技术,IOC是控制反转,也就是将创建对象的过程交给spring来管理。IOC创建的对象都放在spring容器中,想要使用对象就使用DI,也就是直接用依赖注入就可以使用啦~
二、AOP的核心概念
AOP总共有五大核心概念,分别是:
1、连接点(joinPoint):连接点是指程序执行过程中的一些点,比如方法调用,异常处理等。在 Spring AOP 中,仅支持方法级别的连接点。说直接一点就是,类中的所有方法,只要符合切入点条件的,能被增强的,都是连接点
2、通知(Advice):指拦截到连接点之后要执行的代码
3、切入点(PointCut):是对连接点进行拦截的条件定义
4、切面(Aspect):通知+切入点,将通知中的代码,通过切入点,动态加载到方法里面
5、目标对象(Target):真正被扩展的类的对象
可能不太好理解,我们画个图就好办了
三、使用步骤
1.导入依赖
代码如下(示例):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.构建连接点
其实就是写一点类和方法
这是一个非常简单的SpringBoot项目,简单实现了部门管理和员工管理两个功能
页面效果如下:
连接点我们一般选在service或者dao层,因为这两层是对业务、数据进行处理的层,而controller层主要是为了和前端进行交互,一般不用做增强。
连接点有几个重要的方法:
1、获取目标类名:joinPoint.getTarget().getClass().getName();
2、获取目标方法签名:joinPoint.getSignature();
3、获取目标方法名:joinPoint.getSignature().getName();
4、获取目标参数:joinPoint.getArgs();
仅在环绕通知里面还有一个特殊的:
5、执行原始方法:joinPoint.proceed();
3.编写通知
编写通知其实就是编写切面类的方法,也就是你想要增强的方法内容。这里我想将操作信息纪录下来,并在数据库中记录。因此我的切面方法就是要给数据库的字段赋值~
注意 :切面类需要打两个注解:分别是@Component、@Aspect
代码如下(仅适用于小编自己的项目,这些代码具体情况具体设计):
@Component
@Aspect
public class LogAspect {
@Autowired
private HttpServletRequest request;
@Autowired
private OperateLogMapper operateLogMapper;
@Around("@annotation(com.study1.util.Log)")
public Object addToMsg(ProceedingJoinPoint pjp) throws Throwable {
OperateLog operateLog = new OperateLog();
operateLog.setId(null);
//1、判断是谁在操作
//1.1、获取token
String token = request.getHeader("token");
//1.2、解析token
Claims claims = JwtUtils.jwtBack(token);
//获取操作人id
Integer id = (Integer) claims.get("id");
operateLog.setOperateUser(id);
//获取操作时间
operateLog.setOperateTime(LocalDateTime.now());
//获取操作的类名
String name = pjp.getTarget().getClass().getName();
operateLog.setClassName(name);
//获取操作的方法名
String name1 = pjp.getSignature().getName();
operateLog.setMethodName(name1);
//获取方法参数
Object[] args = pjp.getArgs();
operateLog.setMethodParams(Arrays.toString(args));
//方法耗时,开始时间
long start = System.currentTimeMillis();
//执行原方法获取返回值
Object proceed = pjp.proceed();
String retuenValue = JSON.toJSONString(proceed);
operateLog.setReturnValue(retuenValue);
//结束时间
long end = System.currentTimeMillis();
operateLog.setCostTime(start-end);
operateLogMapper.insert(operateLog);
return proceed;
}
}
4.编写切入点
1、切入点其实就是看你如何去定义和筛选要增强的方法,切入点表达式一共有两个:
(1)execution(“url…”):指定路径增强,url是指要增强的类的路径,当前路径下的所有方法都会被AOP拦截,进行一波加强后再还给service层。
(2)@annotation(“url…”):指定自定义注解增强,url是自定义注解的全路径,当前项目中,只要打了此自定义注解额度,都会被AOP拦截,进行一波加强后再还给service层。
我这个示例项目中,只需要将修改、删除的操作记录下来,而查询本身不需要对数据库进行修改因此就不需要增强,所以不适合用路径增强。(具体情况具体分析哈~)
2、使用自定义注解增强,就要先自定义一个注解:
代码如下(示例):
@Target({ElementType.METHOD}) //指定这个注解打在什么地方上 类/方法/接口 等等。。。
@Retention(RetentionPolicy.RUNTIME) //指定这个注解的生命周期
public @interface Log {
}
3、写好自定义注解之后,就可以引入到切面类里面了。细心的小伙伴已经发现了,在上述代码的方法上,有一个@Around注解,在里面已经写好切入点表达式了。
5.定义切面
切面类里面有一个很重要的知识点,叫通知类型。常见的通知类型一共有五种:
1、Around(环绕通知):顾名思义就是既可以在目标方法前增强,也可以在目标方法后增强的通知类型。这是功能最强大、可以调用目标方法、设置参数、获取返回值的通知种类等。
2、Before(前置通知):顾名思义就是只能在目标方法执行前增强
3、After(后置通知):顾名思义就是只能在目标方法执行后增强
4、AfterReturning(返回后通知):这个也是在目标方法执行后增强。但是与后置通知不一样的是,这个方法在遇到异常后不执行
5、AfterThrowing(异常后通知):顾名思义,只有发生异常了,才做增强
这有什么作用呢?其实它的作用就相当于你自行选择,你要增强或添加的方法,应该在目标方法执行之前执行,还是目标方法执行之后执行。增强完的方法要符合逻辑,因此才有五种分类。
在这个示例项目中,我想要增强的功能是,记录操作的人,以及操作了什么事,因为涉及到操作时间,因此要用到环绕通知。在上述代码的方法上方定义了环绕通知的注解。
测试一下:
删除五一加班部
查看五一加班部的部门id:
操作前查看部门操作表里面的记录
在前端执行删除操作:
删除完成查看部门表,加班部不存在了
在看部门操作表,14号部门已经解散了
最后根据操作时间就可以确定是谁操作了删除功能
总结
充电时刻
AOP在SpringBoot中的实际运用就是充当一个动态代理的作用,在不改变源码的前提下,动态的给它增加功能。亦或者大量的方法里面都有相同的方法,就可以用AOP进行代码提取。极大加快了我们的开发速率~