Java - 基于注解实现AOP的使用案例

1. 案例

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.0</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.0</version>
</dependency>

1.1 验权服务

1.1.1 验权服务切面注解

package me.common.framework.log;

import java.lang.annotation.*;

/**
 * 验权服务切面注解
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BackendPermission {

    /**
     * API服务协议名称
     */
    String protocol();
	
	/**
     * 是否打印响应
     */
    boolean printResponseBody() default true;

}

1.1.2 验权服务切面

package me.common.framework.log;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import me.common.api.exceptions.PermissionException;
import me.common.enums.PermissionExceptionEnum;
import org.apache.commons.lang3.ArrayUtils;
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.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * 验权服务切面
 */
@Aspect
@Component
@Slf4j
@Order(1)// 在同一个地方有多个AOP注解引用时的排序, 数字越小越优先执行
public class BackendPermissionAop {

    @Autowired
    private 业务处理的Service service;

    @Pointcut("@annotation(me.common.framework.log.BackendPermission)")
    public void checkPoint() {
    }

    /*@Before("checkPoint() && @annotation(backendPermission)")
    public void before(BackendPermission backendPermission) {
        try {
            log.info(">>> [权限校验] BackendPermissionAop");
            if (!service.checkPermission(backendPermission.protocol())) {
                throw new InvoiceBizException("您没有当前操作权限");
            }
        } catch (InvoiceBizException e) {
            throw e;
        } catch (Exception e) {
            throw new InvoiceBizException("验权异常");
        }
    }*/

    @Around("checkPoint() && @annotation(backendPermission)")
    public Object around(ProceedingJoinPoint joinPoint, BackendPermission backendPermission) throws Throwable {
        String protocol = backendPermission.protocol();
        String funcName = joinPoint.getSignature().getName();
        // 获取方法入参
        String requestBody = getRequestBody(joinPoint.getArgs());
        log.info(">>> [权限校验] - {}; 方法: {}; 入参: {}", protocol, funcName, requestBody);
        Boolean checkResult = service.checkBackendPermission(protocol, requestBody);
        if (checkResult) {
            throw new PermissionException(PermissionExceptionEnum.UNAUTHORIZED_CODE, "" + checkResult.get("权限申请链接"));
        }
        // 获取方法出参
        Object originalResponse = joinPoint.proceed();
        boolean printResponseBody = apiService.printResponseBody();
        String responseBody = getResponseBody(originalResponse, printResponseBody);
        log.info(">>> [权限校验] - {}; 方法: {}; 出参: {}", protocol, funcName, responseBody);
        return originalResponse;
    }

    /**
     * 根据请求参数生成请求体JSON串
     * 建议API入参为一个实体,不要分成多个参数,因此这里仅将第一个参数JSON化
     *
     * @param args 入参
     * @return 请求体JSON串
     */
    private String getRequestBody(Object[] args) {
        if (ArrayUtils.isEmpty(args)) {
            return "null";
        }
        return JSON.toJSONString(args[0]);
    }
	
	private String getResponseBody(Object originalResponse, boolean printResponseBody) {
        if (!printResponseBody) {
            return "";
        }
        return JSON.toJSONString(originalResponse);
    }

}

1.1.3 使用

@BackendPermission(protocol = "权限校验")
public void queryInfo() throws InvoiceServiceException {
    return null;
}

1.2 异常统一处理服务

1.2.1 异常抓取切面注解

package me.common.framework.log;

import java.lang.annotation.*;

/**
 * 验权服务异常抓取切面注解
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BackendPermissionException {
}

1.2.2 异常抓取切面

package me.common.framework.log;

import lombok.extern.slf4j.Slf4j;
import me.backend.api.model.response.GatewayResult;
import me.common.api.exceptions.PermissionException;
import me.common.api.exceptions.InvoiceBizException;
import me.exception.InvoiceServiceException;
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.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * 验权服务异常抓取切面
 */
@Aspect
@Component
@Slf4j
@Order(2)// 在同一个地方有多个AOP注解引用时的排序, 数字越小越优先执行
public class BackendPermissionExceptionAop {

    @Pointcut("@annotation(me.common.framework.log.BackendPermissionException)")
    public void checkPoint() {
    }

    @Around("checkPoint() && @annotation(backendPermissionException)")
    public Object around(ProceedingJoinPoint joinPoint, BackendPermissionException backendPermissionException) throws Throwable {
        try {
            String funcName = joinPoint.getSignature().getName();
            log.info(">>> [权限校验异常抓取] - 方法: {}", funcName);
            return joinPoint.proceed();
        } catch (PermissionException e) {
            log.error(">>> [权限校验异常抓取] - 权限校验失败: ", e);
            GatewayResult result = new GatewayResult();
            Map<String, Object> extInfo = new HashMap<>();
            extInfo.put("权限申请链接", e.getPermissionApplyLink());
            result.setExtInfo(extInfo);
            result.setResultCode(e.getPermissionExceptionEnum().getCode());
            result.setSuccess(false);
            result.setResultMessage(e.getPermissionExceptionEnum().getMsg());
            return result;
        } catch (InvoiceBizException | InvoiceServiceException e) {
            log.error(">>> [权限校验异常抓取] - 业务异常: ", e);
            GatewayResult result = new GatewayResult();
            result.setSuccess(false);
            result.setResultMessage(e.getMessage());
            return result;
        } catch (Exception e) {
            log.error(">>> [权限校验异常抓取] - 系统异常: ", e);
            GatewayResult result = new GatewayResult();
            result.setSuccess(false);
            result.setResultMessage("系统异常");
            return result;
        }
    }

}

1.2.3 使用

@Override
@BackendPermissionException
public void testMethod(TestRequest request) throws InvoiceServiceException {
    return null;
}

2. AOP注意点

2.1 @Before

入参不能引入ProceedingJoinPoint joinPoint, 也就无法获取方法的入参和出参

2.2 在方法上使用

如果要在方法上使用AOP注解, 必须手动指定切入点@Pointcut

2.3 在类上使用

在类上使用不需指定切入点@Pointcut, 但是若这种写法的注解用在方法上, 不会生效

2.4 多个AOP注解在同一个方法上使用

如案例中的AOP, 在同一个方法上即使用验权切面, 又使用异常切面时, 异常切面无法抓取到验权切面中抛出的异常, 切面里的异常会往更上层抛出, 例如:

@BackendPermission(protocol = "权限校验")
@BackendPermissionException
public void queryInfo() throws InvoiceServiceException {
    return null;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值