本文正在参加「Java主题月 - Java Debug笔记活动」,详情查看 活动链接
前言
注:本例子中讲述的情况为简账(开源记账软件)开发过程中的遇到的问题,及解决方案
因简账中需要记录某些API接口的调用情况,并以日志的的形式记录到数据库中
需求大致如下所示:
graph TD
用户请求 --> 获取花费类别
用户请求 --> 微信登录授权
微信登录授权 --> 记录日志
1.获取花费类别接口:此接口调用较为频繁,无实际含义,故无需记录调用日志
2.微信登录授权接口:此接口因涉及到与微信的交互,需将相关信息记录至日志表,方便生产环境排查授权失败的原因
一、发现问题
确定需求后,就开始了编码的工作。编码结束,一启动项目就出现了如下的错误信息:
二、解决问题
通过上图的报错信息大概可以知道以下两点:
1.错误发生在GlobalMethodSecurityConfiguration这个Bean创建的过程
2.创建Bean错误的原因是因为参数不合法,ProceedingJoinPoint切点只支持环绕通知
根据上面的第2点,将使用ProceedingJoinPoint的地方换成JoinPoint即可
修改前:
修改后:
这样就可以正常的启动项目了~
三、问题复盘
不知道你有没有考虑过一个问题,为什么堆栈错误信息中没有自定义切面类的信息呢?
明明是切面类写错了啊,为什么没有相关的信息呢?
带着这个疑惑我们一起往下看吧!
下面以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可以看到在实例化切面类后,会校验方法的参数
具体的校验逻辑如下:
那么为什么是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地址:
往期链接