Javaweb-AOP

1 简介

  • AOP:面向切面编程,指面向特定方法编程
  • 实现:
    • 动态代理
    • SpringAOP旨在管理bean对象的过程中,主要通过底层的动态代理机制,对特定的方法进行编程。
  • 场景:记录操作日志、权限控制、事务管理…
  • 优势:代码无侵入、减少重复代码、提高开发效率、维护方便

引入依赖:

<!-- AOP-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

案例:记录方法运行时间:

TimeAspect.java:

package org.example.ssmpratice1.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Slf4j
@Component//注意,同样需要交给IOC容器管理
@Aspect //声明为一个aop类
public class TimeAspect {

    @Around("execution(* org.example.ssmpratice1.service.*.*(..))")//切入点表达式
    public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
        //1.记录开始时间
        long begin=System.currentTimeMillis();
        //2.调用原始方法
        Object result= joinPoint.proceed();
        //3.记录结束时间,得到运行耗时
        long end = System.currentTimeMillis();
        log.info(joinPoint.getSignature()+"方法执行耗时:{}ms",end-begin);

        return result;
    }
}

2 核心概念:

  • 连接点: JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)
  • 通知: Advice,指方法中重复的逻辑、共性功能,最终体现为一个方法(如上文 recordTime)
  • 切入点: pointCut,匹配连接点的条件通知只会在切入点方法执行时被应用。(实际被AOP控制的方法,由切入点表达式execution(XXX )决定)
    • @Point()可以用于抽取切入点表达式,使用时在通知参数内调用所修饰方法
  • 切面:通知+切入点=切面,即@Aspect这个类,描述通知和切入点的关系
  • 目标对象:通知应用的对象
  • AOP执行流程中:运行的不再是原始的目标对象,而是基于原始对象生成且增强的代理对象

3 通知类型

  1. @Around:环绕通知,这种通知类型会在目标方法执行前后都执行。它允许你完全控制目标方法的执行,包括在执行前后添加自定义的行为。
    • 方法中需要:
      • 声明参数: ProceedingJoinPoint joinPoint
      • 调用目标对象的原始方法执行:joinPoint.proceed();
      • 注意还要返回原始方法的结果(注意将返回类型设为 Object)
  2. @Before:前置通知,这种通知类型会在目标方法执行之前执行。它用于在目标方法执行前添加一些前置处理逻辑。
  3. @After:后置通知,这种通知类型会在目标方法执行之后执行,无论目标方法是否抛出异常。它用于在目标方法执行后添加一些后置处理逻辑。
  4. @AfterReturning:返回后通知,这种通知类型会在目标方法正常返回后执行。它用于在目标方法成功执行并返回结果后添加一些处理逻辑。
  5. @AfterThrowing:异常后通知,这种通知类型会在目标方法抛出异常后执行。它用于处理目标方法执行过程中发生的异常情况。

4 通知顺序:

默认类名字典序

可以通过@Order(x)来手动指定顺序

5 切入点表达式:

  • 描述切入点方法的一种表达式
  • 主要用来指定要加入通知的方法
  • 常见形式:
    • execution(….):根据方法的签名来匹配
    • @annotation(…):根据注解匹配

5.1 execution

execution([访问修饰符] 返回值 [包名.类名.]方法名(方法参数) [throws 异常])

[]表示可省略

  • * :单个独立的通配符
  • ..:多个连续的通配符

可以使用 && || ! 来组合复杂的表达式

5.2 @annotation

  • @annotation(特定注解/自定义注解 的全类名):切入点表达式,用于匹配标识有特定注解的方法

6 连接点:

在spring中用JoinPoint抽象了连接点,用它可获得方法执行时的相关信息,如目标类名、方法名、方法参数等。

  • @Around 通知,只能使用 ProceedingJoinPoint
  • 其他四种通知, 只能使用 JoinPoint

String className = joinPoint.getTarget().getClass().getName(); // 获取目标类名

Signature signature = joinPoint.getSignature(); // 获取目标方法签名

String methodName = joinPoint.getSignature().getName(); // 获取目标方法名

Object[] args = joinPoint.getArgs(); // 获取目标方法运行参数

Object res = joinPoint.proceed(); // 执行原始方法,获取返回值(环绕通知)

综合案例

将操作日志记录到数据库表

信息:操作人,操作时间,执行方法全类名,方法名,运行时参数,返回值,执行时长

自定义注解:

package org.example.ssmpratice1.anno;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;

//元注解
@Retention(RetentionPolicy.RUNTIME)//指定注解在运行时生效
@Target(ElementType.METHOD)//指定注解修饰的是方法
public @interface Log {
}

AOP类:重点:获取当前操作人的id的操作!!!

package org.example.ssmpratice1.aop;

import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.example.ssmpratice1.mapper.OperateLogMapper;
import org.example.ssmpratice1.pojo.OperateLog;
import org.example.ssmpratice1.utils.JwtUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Map;

@Slf4j
@Component
@Aspect
public class LogAspect {
    @Autowired//Spring 能自主注入 HttpServletRequest,而无需手动托管至IOC,依赖 Web 请求绑定机制,需在 Servlet 容器且 Spring Web 配置完备。
    HttpServletRequest request;

    @Autowired
    OperateLogMapper operateLogMapper;

    @Around("@annotation(org.example.ssmpratice1.anno.Log)")
    public Object recordLog(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        //获取操作人id
        String jwt = request.getHeader("token");
        Map maps = JwtUtils.parseJWT(jwt);
        Integer operateUser= (Integer) maps.get("id");

        //获取操作时间
        LocalDateTime localDateTime=LocalDateTime.now();

        //获取操作类名
        String className= proceedingJoinPoint.getTarget().getClass().getName();

        //获取操作方法名
        String methodName=proceedingJoinPoint.getSignature().getName();

        //获取操作方法参数
        Object args[] = proceedingJoinPoint.getArgs();
        String methodParams = Arrays.toString(args);

        long begin=System.currentTimeMillis();

        //获取操作方法返回值
        Object result = proceedingJoinPoint.proceed();
        String returnValue = JSONObject.toJSONString(result);

        //获取操作耗时
        long costTime=System.currentTimeMillis()-begin;

        //写入日志
        OperateLog operateLog= new OperateLog(null,operateUser,localDateTime,className,methodName,methodParams,returnValue,costTime);
        operateLogMapper.insert(operateLog);
        log.info("AOP记录日志:{}",operateLog);

        return result;
    }
}

注意注解@Log加在Controller层的方法上。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇