自定义注解 + AOP

在实际运用中,当有一些公共的业务方法需要在多个不同方法执行前或执行后运行。

可以通过自定义注解与AOP结合使用。

在多个方法中仅添加注解即可。

通过AOP可以选择方法执行前后,或者是环绕等方式来执行公共方法。

注解实现

注解默认放在项目的 annotation 下(没有可创建,约定成俗)

package com.ruoyi.test.annotation;

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

/**
 * 测试注解
 * @Retention(RetentionPolicy.RUNTIME)    运行时级别
 * @Target(ElementType.METHOD)  方法注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTestAnnotation {
}

切面实现

切面默认放在项目的 aspect下,类名结尾为Aspect(没有可创建,约定成俗)

本次实现使用环绕通知举例

package com.ruoyi.test.aspect;

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.springframework.stereotype.Component;


/**
 *  切面实现
 *  @Aspect  切面注解
 *  @Component 将本切面类交给Spring进行管理
 */
@Aspect
@Component
public class MyTestAspect {

    /**
     * @Pointcut 定义切入点
     * value 将切入点指定为MyTestAnnotation注解
     */
    @Pointcut(value = "@annotation(com.ruoyi.test.annotation.MyTestAnnotation)")
    private void pointCut() {
    }

    /**
     * 切面方法
     * @param joinPoint 连接点
     * @return  被拦截方法的执行结果
     */
    @Around(value = "pointCut()")
    public Object process(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("切面方法执行之前");
        // 执行切面连接点方法
        Object object = joinPoint.proceed();
        System.out.println("切面方法执行之后");
        return object;
    }

}

业务方法(注解使用)

@Override
@MyTestAnnotation
public void testAnnotationMethod(TestDemo testDemo) {
    System.out.println("执行业务方法");
}

切面类获取方法参数

	@Around(value = "pointCut()")
    public Object process(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("切面方法执行之前");

//--------------------------------------------------------------
        // 获取方法签名
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        // 获取方法参数名称
        String[] paramNames = methodSignature.getParameterNames();
        // 获取方法实际传入的参数值
        Object[] arguments = joinPoint.getArgs();
        // 输出参数
        if (arguments != null && paramNames != null) {
            for (int i = 0; i < paramNames.length; i++) {
                System.out.println("参数名:" + paramNames[i] + ",参数值:" +	 arguments[i]);
            }
        }
//--------------------------------------------------------------

        // 执行切面连接点方法
        Object object = joinPoint.proceed();
        System.out.println("切面方法执行之后");
        return object;
    }

切面类获取请求头数据

这里以获取请求头中的token举例

	@Around(value = "pointCut()")
    public Object process(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("切面方法执行之前");

//--------------------------------------------------------------
        // 获取当前请求的上下文属性,并强制转换为Servlet请求属性
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)
                Objects.requireNonNull(RequestContextHolder.getRequestAttributes());
        // 从Servlet请求属性中获取当前的HTTP请求
        HttpServletRequest request = servletRequestAttributes.getRequest();
        // 从HTTP请求头中获取名为"token"的值
        String token = request.getHeader("token");
//--------------------------------------------------------------

        // 执行切面连接点方法
        Object object = joinPoint.proceed();
        System.out.println("切面方法执行之后");
        return object;
    }

切面类返回信息

有时候可能需要在切面类中返回特殊的信息,可以通过HttpServletResponse进行处理

这里以返回错误信息进行举例

注意:out.close(); 执行后,请求就会获得到返回值,但是后端代码没有抛出异常,所以后续代码任会执行。

	@Around(value = "pointCut()")
    public Object process(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("切面方法执行之前");
        // 执行切面连接点方法
        Object object = joinPoint.proceed();

//--------------------------------------------------------------
		// 获取当前请求的上下文属性,并强制转换为Servlet请求属性
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)
                Objects.requireNonNull(RequestContextHolder.getRequestAttributes());
		// 从Servlet请求属性中获取当前的HTTP响应
		HttpServletResponse response = servletRequestAttributes.getResponse();
		// 通过reponse输出信息
        render(response, R.fail("环绕后方法执行发生错误"));

        System.out.println("切面方法执行之后");
        return object;
    }

    /**
     * 将对象转为JSON字符串,写入响应体中,最后刷新并关闭输出流
     * @param response  HTTP响应对象
     * @param codeMsg   返回类
     */
    private void render(HttpServletResponse response, Object codeMsg) throws Exception {
        // 设置响应的内容类型为JSON,指定字符集为UTF-8
        response.setContentType("application/json;charset=UTF-8");
        // 获取响应的输出流
        OutputStream out = response.getOutputStream();
        // 创建ObjectMapper对象,用于对象到JSON字符串的转换
        ObjectMapper objectMapper = new ObjectMapper();
        // 将codeMsg对象转换为JSON字符串
        String result = objectMapper.writeValueAsString(codeMsg);
        // 将JSON字符串写入输出流,指定字符集为UTF-8
        out.write(result.getBytes(StandardCharsets.UTF_8));
        // 刷新输出流
        out.flush();
        // 关闭输出流
        out.close();
    }