面向切面编程
认识AOP
AOP(Aspect Oriented Program,面向切面编程)把业务功能分为核心、非核心两部分。
核心业务功能 | 非核心业务功能 |
---|---|
用户登录,增加数据,删除数据 | 性能统计,日志,事务管理 |
在Spring 的面向切面编程(AOP)思想里,非核心业务功能被定义为切面。核心业务功能和切面功能先被分别进行独立开发,然后把切面功能和核心业务功能编织在一起,这就是AOP。
AOP中的概念
- 切入点(pointcut):在哪些类、哪些方法上切入
- 通知(advice):在方法前、方法后、方法前后做什么
- 切面(aspect):切面=切入点+通知。也就是在什么时机、什么地方,做什么
- 织入(weaving):把切面加入对象,并创建出代理对象的过程
- 环绕通知:AOP中最强大、灵活的通知,它集成了前置和后置通知,保留了连接点原有的方法。
实例:用AOP方式管理日志
实验结果
登录网址
http://localhost:8080/aoptest
查看到
同时控制台显示信息
URL:http://localhost:8080/aoptest
HTTP方法:GET
IP地址:0:0:0:0:0:0:0:1
类的方法:com.example.demo.controller.AopLogController.aVoid
参数:null
应答值:hello aop test
费时:12
pom.xml添加依赖
这里初学者踩坑:配置切面@Aspect不管用,是因为没有下面的spring-boot-starter-aop依赖
<!-- SpringBoot 拦截器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
代码
编写AOP日志注解类
AopLog.java
下面代码的解释:
@Before:在切入点开始处切入内容
@After:在切入点结尾处切入内容
@AfterReturning:在切入点返回内容之后切入内容,可以用来对处理返回值做一些加工处理
@Around:在切入点前后切入内容,并控制何时执行切入点自身的内容
@AfterThrowing:用来处理当切入内容部分抛出异常之后的处理逻辑
@Aspect:标记为切面类
@Component:把切面类加入IoC容器中,让Spring来进行管理
package com.example.demo.aop;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;//这里包含.Aspect
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web. context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
@Aspect//使之成为切面类
@Component//把切面类加入loC容器中
public class AopLog {
private Logger logger=LoggerFactory.getLogger(this.getClass());
//线程局部的变量,用于解决多线程中相同变量的访问冲突问题
ThreadLocal<Long> startTime=new ThreadLocal<>();
//定义切点
@Pointcut("execution(public * com.example..*.*(..))")
public void aopWebLog(){
}
@Before("aopWebLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable{
startTime.set(System.currentTimeMillis());
//接收到请求,记录请求内容
ServletRequestAttributes attributes=(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request=attributes.getRequest();
//记录下请求内容
logger.info("URL:"+request.getRequestURL().toString());
logger.info("HTTP方法:"+request.getMethod());
logger.info("IP地址:"+request.getRemoteAddr());
logger.info("类的方法:"+joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName());
logger.info("参数:"+request.getQueryString());
}
@AfterReturning(pointcut = "aopWebLog()",returning = "retObject")
public void doAfterReturning(Object retObject) throws Throwable{
//处理完请求,返回内容
logger.info("应答值:"+retObject);
logger.info("费时:"+(System.currentTimeMillis()-startTime.get()));
}
//方法抛出异常退出时执行的通知
@AfterThrowing(pointcut = "aopWebLog()",throwing = "ex")
public void addAfterThrowingLogger(JoinPoint joinPoint,Exception ex){
logger.error("执行 "+" 异常",ex);
}
}
2.控制器
AopLogController.java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AopLogController {
@GetMapping("/aoptest")
public String aVoid(){
return "hello aop test";
}
}