环境

springboot:2.4.4
aspectjweaver: 1.8.7

前言

今天调试接口时,遇到的aop拦截,做权限校验,但是有个参数总是没有传,又因为以前这块代码逻辑有问题,总报null指针。所以回家后,研究了aspectj包。

这里网上有一句这样的概括:
spring-aop:AOP核心功能,例如代理工厂等等

aspectjweaver:简单理解,支持切入点表达式等等

aspectjrt:简单理解,支持aop相关注解等等

但是aspectjweaver是包含aspectjrt的,所以上面那句话,应该改为:


描述

spring-aop

AOP核心功能,例如代理工厂等等

aspectjweaver

简单理解,支持切入点表达式等等、支持aop相关注解等等

引入依赖

这里假设你已经有了一个springboot的项目。

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.7</version>
</dependency>
<!--aspectjweaver是包含aspectjrt,所以不需要引入aspectjrt-->
<!--aspectjweaver是包含aspectjrt,所以不需要引入aspectjrt-->
<!--aspectjweaver是包含aspectjrt,所以不需要引入aspectjrt-->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.13</version>
</dependency>

使用

先创建一个AOP包

Springboot 使用Axis2 springboot aspectj_aspectj

package com.ssm.boot.admin.user.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.SourceLocation;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Aspect
@Component
public class UserAop {
//    @Pointcut("execution(* com.ssm.boot.admin.*(..))")
    @Pointcut("execution(* com.ssm.boot.admin..*(..))")
    public void userServiceAspect() {}

    @Before("userServiceAspect()")
    public void platformPermissionPoint(JoinPoint joinPoint) throws Throwable {
        //前端传来的参数,如果拦截的方法有参数,但是前端没有传,则[null...]
        //joinPoint原本args是null,在调用getArgs()后,会从实现类中clone进行赋值
        Object[] args = joinPoint.getArgs();
        System.out.println(Arrays.toString(args));
        //由spring框架实现类写死:method-execution
        String kind = joinPoint.getKind();
        System.out.println("kind--" + kind);
        //签名(包括返回值)List com.ssm.boot.admin.user.controller.UserAopController.getAll(String,List)
        Signature signature = joinPoint.getSignature();
        System.out.println("signature--" + signature);
        //代理类的路径:也就是全类名路径
        SourceLocation sourceLocation = joinPoint.getSourceLocation();
        System.out.println("sourceLocation--" + sourceLocation);
        //代理目标方法包含全包名路径
        Object target = joinPoint.getTarget();
        System.out.println("target--" + target);
        //解析切点表达式:翻译成具体的方法,和toString()输入结果一样
        JoinPoint.StaticPart staticPart = joinPoint.getStaticPart();
        System.out.println("staticPart--" + staticPart);
        String longString = joinPoint.toLongString();
        //输出完整全包路径的execution解析的表达式
        System.out.println("toLongString--" + longString);
        //简短输出
        String shortString = joinPoint.toShortString();
        System.out.println("shortString--" + shortString);
        //和getStaticPart()一样
        String s = joinPoint.toString();
        System.out.println("toString--" + s);

    }
}

写个测试Controller

package com.ssm.boot.admin.user.controller;

import com.ssm.boot.biz.user.UserAppService;
import com.ssm.boot.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class UserAopController {
    @Autowired(required = false)
    private UserAppService userAppService;

    @RequestMapping("/users/get/all")
    public List<User> getAll(String name,
                             @RequestParam(value = "ids", required = false)
                                     List<Integer> userIds) {
        return userAppService.getAll();
    }
}

执行

启动项目,然后访问接口:

http://localhost:8080/users/get/all
或
http://localhost:8080/users/get/all?name=yutao&ids=1,2,3

上面的输出结果:

并不是现在就可以执行了,而是我先把结果贴出来

[null, null]
kind--method-execution
signature--List com.ssm.boot.admin.user.controller.UserAopController.getAll(String,List)
sourceLocation--org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint$SourceLocationImpl@6d75f2f
target--com.ssm.boot.admin.user.controller.UserAopController@5c00dc98
staticPart--execution(List com.ssm.boot.admin.user.controller.UserAopController.getAll(String,List))
toLongString--execution(public java.util.List com.ssm.boot.admin.user.controller.UserAopController.getAll(java.lang.String,java.util.List))
shortString--execution(UserAopController.getAll(..))
toString--execution(List com.ssm.boot.admin.user.controller.UserAopController.getAll(String,List))

[yutao, [1, 2, 3]]
... ...

遇到的问题

假设方法没有参数,但是请求时有参数

也就是如下的情况:

@RequestMapping("/users/get/all")
public List<User> getAll() {
    return userAppService.getAll();
}

请求URL:http://localhost:8080/users/get/all?name=yutao&ids=1,2,3

那么从joinPoint.getArgs()方法中获取的是[] 也就是说是获取不到nameids这两个参数的数据。
必须方法上有这两个参数,joinPoint才会帮你获取到数据。
joinPoint实现类是:MethodInvocationProceedingJoinPoint,这个类是spring的,它应该是利用反射确定目标方法没有参数,也说也就不会从请求流中去获取这两个参数(值)。

No primary or single public constructor found for interface java.util.List - and no default construc

这个问题,因为我的controller参数这么写的:

@RequestMapping("/users/get/all")
public List<User> getAll(String name, List<Integer> userIds) {
    return userAppService.getAll();
}

参数List<Integer>这样是接收不了,所以需要改为:

@RequestMapping("/users/get/all")
public List<User> getAll(String name,
                         @RequestParam(value = "ids", required = false)
                                 List<Integer> userIds) {
    return userAppService.getAll();
}

warning no match for this type name: com.xxx.xxx [Xlint:invalidAbsoluteTypeName]

原因是切面表达式错误

一开始:

@Pointcut("execution(* com.ssm.boot.admin.*(..))")
public void userServiceAspect() { }

改为:也就是多加一个.

@Pointcut("execution(* com.ssm.boot.admin..*(..))")
public void userServiceAspect() { }

我们要找到Spring包下子包的类进行运行,而一个.代表了运行当前包下所有的方法,.. 代表运行当前包和子包的方法

总结

今天遇到这个问题时,当时觉得即使方法里没有这两个参数,也说可以获取到参数值。
这么测试下来,看来是想多了。

参考地址:

No primary or single public constructor found for interface java.util.List - and no default construc

Spring切面编程错误warning no match for this type name:com.xxx.xxx [Xlint:invalidAbsoluteTypeName]