AOP(面向切面编程)
AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP术语
AOP 领域中的特性术语:
- 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
- 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
- 切点(PointCut): 可以插入增强处理的连接点。
- 切面(Aspect): 切面是通知和切点的结合。
- 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
- 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入
AOP实例
AOP的实现有几种方式,这里我们用Aspect(切面)来实现一个日志记录的功能
1.基于SpringBoot项目添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.METHOD)
public @interface WebLog {
}
3.定义日志记录实体类
@Data
public class LogRecord {
//访问用户
private String userName;
//访问时间
private String startTime;
//接口地址
private String url;
//请求方式
private String method;
//访问者ip地址
private String ip;
@Override
public String toString() {
return "LogRecord{" +
"userName='" + userName + '\'' +
", startTime='" + startTime + '\'' +
", url='" + url + '\'' +
", method='" + method + '\'' +
", ip='" + ip + '\'' +
'}';
}
}
4.定义日志切面类
/**
* 日志切面类
*/
@Aspect
@Component
public class LogAspect {
private final static Logger log = LoggerFactory.getLogger(LogAspect.class);
/**
* 切点
*/
// @Pointcut("execution(* com.csdn.controller..*.*(..))")
@Pointcut("@annotation(com.csdn.annotation.WebLog)")
private void LogAspectPointcut(){};
/**
* 前置通知
* 通知方法会在目标方法调用之前执行
* @param joinPoint 连接点
*/
@Before("LogAspectPointcut()")
public void doBefore(JoinPoint joinPoint) {
//获取当前请求对象
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
SimpleDateFormat sim=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time=sim.format(new Date());
LogRecord logRecord = new LogRecord();
logRecord.setIp(request.getRemoteAddr());
logRecord.setMethod(request.getMethod());
logRecord.setUrl(request.getRequestURL().toString());
logRecord.setStartTime(time);
logRecord.setUserName("测试用户");
//输出日志
log.info(logRecord.toString());
}
}
5.接口类
@RestController
@RequestMapping("/log")
public class LogController {
@WebLog
@GetMapping("")
public String getList(){
return "{\"message\":\"SUCCESS\",\"code\":200}";
}
}
6.用PostMan访问接口
7.日志记录结果
2021-05-23 19:04:42.362 INFO 14800 --- [nio-9020-exec-1] com.csdn.aspect.LogAspect : LogRecord{userName='测试用户', startTime='2021-05-23 19:04:42', url='http://127.0.0.1:9020/log/', method='GET', ip='127.0.0.1'}
完美记录日志
@Pointcut的另一种表达形式(execution)
例:execution(* com.csdn.controller...(..)) 代表切com.csdn.controller包下所有类
整个表达式可以分为五个部分:
1、execution()::表达式主体。
2、第一个号:表示返回类型, 号表示所有的类型。
3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service包、子包下所有类的方法。
4、第二个号:表示类名,号表示所有的类。
5、(..):最后这个星号表示方法名,号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数