最近在使用Shiro做权限管理的时候,在继承了AuthorizingRealm类的UserRealm自动注入了用户服务的接口ISysUserService。
@Slf4j
public class UserRealm extends AuthorizingRealm {
@Autowired
ISysUserService sysUserService;
@Autowired
ISysRoleService sysRoleService;
}
在启动时发现,在Info日志中总是出现
2020-06-04 15:19:31.789 INFO 24676 — [ main] trationDelegate$BeanPostProcessorChecker : Bean ‘sysUserServiceImpl’ of type [org.springframework.aop.scope.ScopedProxyFactoryBean] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
显示ISysUserService 接口的实现类sysUserServiceImpl的后置处理器并没有完全使用,在网上找到了这一篇文章,有兴趣的小伙伴可以看一下。
【小家Spring】Spring IOC容器启动流程 AbstractApplicationContext#refresh()方法源码分析(一)
问题大致呢,是由于在UserRealm中采取自动注入的方式注入了ISysUserService的实体Bean,由于Shiro执行链的原因,会导致ISysUserService的实体Bean提前实例化完成并启动,导致后续的BeanPostProcessor接口并没有把该Bean处理完成
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUserEntity> implements ISysUserService {
@Autowired
public ISysuserRoleService iSysuserRoleService;
@Transactional(rollbackFor = Exception.class)
@Override
public boolean removeUserAndRolesByUserId(String id) {
//删除用户
QueryWrapper<SysUserEntity> sysUserEntityQueryWrapper = new QueryWrapper<>();
sysUserEntityQueryWrapper.eq("id",id);
this.getBaseMapper().delete(sysUserEntityQueryWrapper);
//删除用户---角色
QueryWrapper<SysuserRoleEntity> sysuserRoleEntityQueryWrapper = new QueryWrapper<>();
sysuserRoleEntityQueryWrapper.eq("UserId",id);
iSysuserRoleService.remove(sysuserRoleEntityQueryWrapper);
return true;
}
}
因为我的用户服务类中开启了事务,所以就实体Bean的提前初始化完成就造成了没有生成事务的代理对象。
可以看到在该类的调用栈中,从Controller直接调用到了Service的实体类,而并不是通过事务的代理类来进行调用,所以说就没有生成这个类的事务代理对象,那么事务的使用肯定是失败的。
解决方法
- 我的解决方法是在该类的上方添加了@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
@Service
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUserEntity> implements ISysUserService {
@Autowired
public ISysuserRoleService iSysuserRoleService;
强制生成一个Cglib代理对象,再次启动查看该类的调用栈
可以很清楚的看到该服务的调用由事务的Cglib代理对象发起调用,此时事务就开启成功了。
- 另外也可以将UserRealm中的自动注入改为懒加载,就可以当ISysUserService整个初始化流程结束后再注入UserRealm中,避免了Bean的提前启动。
@Slf4j
public class UserRealm extends AuthorizingRealm {
@Autowired
@Lazy
ISysUserService sysUserService;
@Autowired
ISysRoleService sysRoleService;
}
如果不是因为Shiro引起的事务失效的问题并且类之间的依赖关系比较难以整理,大家可以尝试用第一种方法。
发生这样的问题主要还是不清楚Bean的初始化流程和Bean相互之间的依赖调用关系,要补。