细说Spring——AOP详解(AOP概览)_啦啦啦的博客-CSDN博客_aop
附上aspectj的文档地址
spring aspectj中文文档
10.2. @AspectJ support - Spring 中文文档
Spring AOP 之二:Pointcut注解表达式 - trayvon的个人空间 - OSCHINA - 中文开源技术交流社区
首先什么是AOP;
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
也就是说AOP编程是通过不删改原有代码的情况下对代码功能进行增强的一种编程方式。
AOP的应用场景包括:
日志记录
异常处理
权限验证
缓存处理
事务处理
数据持久化
效率检查
内容分发
简单聊聊AOP的原理:
在很多场景中,我们有很多类有类似的功能,或者说是完全一样,比如说是日志等。那么我们通常的做法是将日志函数写在各自的类方法里边,但这样就会导致代码繁琐的问题。进阶的方法是我们可以写一个公共类,然后将类似的函数写在公共类里,再有其他类去调用这个公共类。这个方法看起来好多了,但是我一旦要修改这个公共类,我就要在很多地方去改对这个公共类的引用。这样显然是行不通的,所以我们就引入了AOP这个概念。它能够根据切点动态的去增强代码的功能,至于怎么增强,下面再聊。
AOP相关的术语:
aspect:切面,切面有切点和通知组成,即包括横切逻辑的定义也包括连接点的定义。
pointcut:切点,每个类都拥有多个连接点,可以理解是连接点的集合,在程序应用中就是你要增强功能的类或方法(确认位置)。
joinpoint:连接点,程序执行的某个特定位置,如某个方法调用前后等。
weaving:织入,将增强添加到目标类的具体连接点的过程。
advice:通知,是织入到目标类连接点上的一段代码,就是增强到什么地方?增强什么内容?
target:目标对象,通知织入的目标类。
aop Proxy:代理对象,即增强后产生的对象。
Spring AOP底层实现,是通过JDK动态代理或CGLib代理在运行时期在对象初始化阶段织入代码的。
Aspect中的通知类型
- ① 前置通知:切入点方法执行之前执行的方法@Before
- ② 返回通知:切入点方法执行之后执行的方法@AfterReturning:可以得到切入点执行的返回值,有异常后就不执行
- ③ 环绕通知:切入点前后都执行 @Around
- ④ 异常通知:切入发点方法执行异常后执行的方法@AfterThrowing:切入点里面的出现异常如10/0,才会执行的方法。
- ⑤ 后置通知:相当于Java中的try/cath中的finally,不论前面执行结果如何,都会执行的方法 @After:执行后执行,有没有异常都执行。
AOP实操
springboot项目工程
AOP依赖:
<!-- 添加aop注解依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
目标实现AOP动态添加日志
首先定义一个controller:
package com.spring.aop.springaop.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class hellocontroller {
@GetMapping("/hello")
public String hello(@RequestParam("name") String name)
{
return "hello "+name;
}
@GetMapping("/sex")
public String sex (@RequestParam("sex") String sex)
{
return "hello "+sex;
}
}
定义切面类
package com.spring.aop.springaop;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
//告诉spring这是一个切面
@Aspect
//让spring去扫描注入容器
@Component
public class Myadvice {
// 在IDE控制台打印日志,便于开发,一般加在最上面
private Logger logger = LoggerFactory.getLogger(Myadvice.class);
//定义切点,即要增强哪些类或方法,确定位置
@Pointcut(value = "execution(* com.spring.aop.springaop.controller.*.*(..))")
public void mypointcut()
{
}
// 环绕通知,切入点前后都执行
@Around("mypointcut()")
public Object myLogger(ProceedingJoinPoint pjp) throws Throwable {
// 获得类名
String className = pjp.getTarget().getClass().getName();
// 获得方法名
String methodName = pjp.getSignature().getName();
// 获得参数列表
Object[] args = pjp.getArgs();
// 对参数进行识别
ObjectMapper mapper = new ObjectMapper();
logger.info("调用前"+className+" 方法名"+methodName + " 传递参数"+mapper.writeValueAsString(args));
// 对业务方法的模拟,我的理解是调用切点中的方法位置
Object obj=pjp.proceed();
logger.info("调用后"+className+" 方法名"+methodName + " 返回值"+mapper.writeValueAsString(obj));
return obj;
}
}
运行结果: