环境
springboot:2.4.4
aspectjweaver: 1.8.7
前言
今天调试接口时,遇到的aop拦截,做权限校验,但是有个参数总是没有传,又因为以前这块代码逻辑有问题,总报null指针。所以回家后,研究了aspectj包。
这里网上有一句这样的概括:spring-aop
:AOP核心功能,例如代理工厂等等
aspectjweaver
:简单理解,支持切入点表达式等等
aspectjrt
:简单理解,支持aop相关注解等等
但是aspectjweaver
是包含aspectjrt
的,所以上面那句话,应该改为:
包 | 描述 |
| AOP核心功能,例如代理工厂等等 |
| 简单理解,支持切入点表达式等等、支持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包
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()
方法中获取的是[]
也就是说是获取不到name
和ids
这两个参数的数据。
必须方法上有这两个参数,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]