解决SpringAOP报错:ProceedingJoinPoint is only supported ... | Java Debug 笔记

1,093 阅读3分钟

本文正在参加「Java主题月 - Java Debug笔记活动」,详情查看 活动链接

前言

注:本例子中讲述的情况为简账(开源记账软件)开发过程中的遇到的问题,及解决方案

因简账中需要记录某些API接口的调用情况,并以日志的的形式记录到数据库中

需求大致如下所示:

graph TD
用户请求 -->  获取花费类别
用户请求 --> 微信登录授权
微信登录授权 --> 记录日志

1.获取花费类别接口:此接口调用较为频繁,无实际含义,故无需记录调用日志
2.微信登录授权接口:此接口因涉及到与微信的交互,需将相关信息记录至日志表,方便生产环境排查授权失败的原因


一、发现问题

确定需求后,就开始了编码的工作。编码结束,一启动项目就出现了如下的错误信息:

image.png

二、解决问题

通过上图的报错信息大概可以知道以下两点:
1.错误发生在GlobalMethodSecurityConfiguration这个Bean创建的过程
2.创建Bean错误的原因是因为参数不合法,ProceedingJoinPoint切点只支持环绕通知

根据上面的第2点,将使用ProceedingJoinPoint的地方换成JoinPoint即可

修改前: image.png

修改后: image.png

这样就可以正常的启动项目了~

三、问题复盘

不知道你有没有考虑过一个问题,为什么堆栈错误信息中没有自定义切面类的信息呢?
明明是切面类写错了啊,为什么没有相关的信息呢?
带着这个疑惑我们一起往下看吧!

下面以Spring源码作为入口,从两个个方面分析问题

1.了解GlobalMethodSecurityConfiguration

GlobalMethodSecurityConfiguration定义如下

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class GlobalMethodSecurityConfiguration
		implements ImportAware, SmartInitializingSingleton, BeanFactoryAware {

	// aop动态代理
        @Bean
	public MethodInterceptor methodSecurityInterceptor(MethodSecurityMetadataSource methodSecurityMetadataSource) {
		this.methodSecurityInterceptor = isAspectJ()
				? new AspectJMethodSecurityInterceptor()
				: new MethodSecurityInterceptor();
		methodSecurityInterceptor.setAccessDecisionManager(accessDecisionManager());
		methodSecurityInterceptor.setAfterInvocationManager(afterInvocationManager());
		methodSecurityInterceptor
				.setSecurityMetadataSource(methodSecurityMetadataSource);
		RunAsManager runAsManager = runAsManager();
		if (runAsManager != null) {
			methodSecurityInterceptor.setRunAsManager(runAsManager);
		}

		return this.methodSecurityInterceptor;
	}

	// 单例完成后执行
	@Override
	public void afterSingletonsInstantiated() {
		try {
			initializeMethodSecurityInterceptor();
		}
		catch (Exception e) {
			throw new RuntimeException(e);
		}

		PermissionEvaluator permissionEvaluator = getSingleBeanOrNull(
				PermissionEvaluator.class);
		if (permissionEvaluator != null) {
			this.defaultMethodExpressionHandler
					.setPermissionEvaluator(permissionEvaluator);
		}

		RoleHierarchy roleHierarchy = getSingleBeanOrNull(RoleHierarchy.class);
		if (roleHierarchy != null) {
			this.defaultMethodExpressionHandler.setRoleHierarchy(roleHierarchy);
		}

		AuthenticationTrustResolver trustResolver = getSingleBeanOrNull(
				AuthenticationTrustResolver.class);
		if (trustResolver != null) {
			this.defaultMethodExpressionHandler.setTrustResolver(trustResolver);
		}

		GrantedAuthorityDefaults grantedAuthorityDefaults = getSingleBeanOrNull(
				GrantedAuthorityDefaults.class);
		if (grantedAuthorityDefaults != null) {
			this.defaultMethodExpressionHandler.setDefaultRolePrefix(
					grantedAuthorityDefaults.getRolePrefix());
		}
	}

}

2.Debug找到错误根源

通过Debug可以看到在实例化切面类后,会校验方法的参数 image.png

具体的校验逻辑如下: image.png

那么为什么是GlobalMethodSecurityConfiguration这里报错呢?

如果你在切面类的构造方法中打个断点会发现,在将切面类交给BeanFactory前会先实例化一个GlobalMethodSecurityConfiguration来进行对应的校验
具体流程图如下所示:

graph TD
A[项目启动] --> C(是否为切面类)
C --> |F| D[实例化后交给IOC容器]
C --> |T| E[实例化GlobalMethodSecurityConfiguration]
E --> F[校验]
F --> |通过| D
F --> |不通过| 启动异常
D --> 启动正常

3.解决问题

我相信通过1、2两步,你应该可以很轻松的解决这个问题了😊

Spring不亏是JavaEE唯一的框架,考虑的也太周到了~

四、总结

都看到这里啦~说明你也和我一样热爱技术、善于发掘问题的根源,不妨给我的博客点个赞8👍

最后我想推广一下自己的开源项目【简帐】(后端、前端、小程序三端开源),如果感兴趣的话可以点个Star~

github地址:

往期链接