事务(十六)

使用了shiro,导致事务失效的情形

场景

shiroconfig中配置如下:

 1 /**
 2      * 安全管理器
 3      */
 4     @Bean
 5     public DefaultWebSecurityManager securityManager(ApplicationContext context, CookieRememberMeManager rememberMeManager, CacheManager cacheShiroManager, SessionManager defaultWebSessionManager) {
 6         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
 7         Object shiroDbRealm = context.getBean("testService");  //代码一
 8         ShiroDbRealm bean = context.getBean(ShiroDbRealm.class);  //代码二
 9         securityManager.setAuthenticator(modularRealmAuthenticator());
10         List<Realm> realms = new ArrayList<>(2);
11         //密码登录realm
12         realms.add(this.shiroDbRealm());
13         //免密登录realm
14         realms.add(this.shiroFreeRealm()); //代码三
15         securityManager.setRealms(realms);
16         securityManager.setCacheManager(cacheShiroManager);
17         securityManager.setRememberMeManager(rememberMeManager);
18         securityManager.setSessionManager(defaultWebSessionManager);
19         return securityManager;
20     }

/**
* 自定义的免密登录Realm
*/
@Bean
public ShiroFreeRealm shiroFreeRealm() {
return new ShiroFreeRealm();
}
1  /**
2      * Shiro的过滤器链
3      */
4     @Bean
5     public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
6         ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
7         shiroFilter.setSecurityManager(securityManager);
8 }
public class ShiroFilterFactoryBean implements FactoryBean, BeanPostProcessor {
1 public class ShiroFreeRealm extends AuthorizingRealm {
2 
3     private Logger log = LoggerFactory.getLogger(this.getClass());
4 
5     @Resource
6     private UserWxService userWxService;
7     @Resource
8     private UserService userService;
9 }

分析:

1.shiro的过滤器实现了BeanPostProcessor,会在spring 初始化bean之前进行初始化,而此时事务的bean后置处理器并没有加载进来。

2.shiro过滤器需要注入DefaultWebSecurityManager,所以会进而初始化上面的DefaultWebSecurityManager

3.代码一中在初始化DefaultWebSecurityManager的时候初始化了业务的testService 的bean,而此时事务bean后置处理器不存在,所以导致初始化的testService对象,并不是代理对象,不存在事务,导致事务失效了;

4.代码二也是一样的道理

注意:只要是在shirofilter中初始化的所有的bean都会事务失效,包括代码一和代码二,由于spring初始化对象时,如果对象中需要依赖注入别的对象,那么也会将依赖对象进行初始化,也就是说如果testService类中通过@Autowired等注解依赖注入了其他对象,那么初始化testService的时候也会将依赖对象初始化,依赖的对象也会事务失效,会一直层层依赖下去,所有的对象都会进行初始化,事务会失效;

 我们项目中就是代码三处自定义的免密登录Realm,其中注入了两个servcie:UserWxService和userService,而这两个service中依赖了别的service,所以shirofilter初始化时依次初始化了大量的业务service,导致这些servcie都事务失效了,我们进行了一下修改,讲注入service修改为注入mapper,这样就不会初始化service 的bean对象,从而不会让事务失效;修改如下:

1 public class ShiroFreeRealm extends AuthorizingRealm {
2 
3     private Logger log = LoggerFactory.getLogger(this.getClass());
4 
5     @Resource
6     private UserWxMapper userWxMapper;
7     @Resource
8     private UserMapper userMapper;
9 }

其他解决方案

既然ShiroFreeRealm中不能通过@Autowired注入userService,那我们变通一下,不用第一时间注入,等需要用到的时候再向Spring索取就好了。

这里第一个想到的肯定就是ApplicationContext了,这好办,写一个ApplicationContext工具类:

 1 @Component
 2 public class ApplicationContextUtils implements ApplicationContextAware {
 3     public static ApplicationContext applicationContext;
 4     @Override
 5     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
 6         ApplicationContextUtils.applicationContext = applicationContext;
 7     }
 8     public static Object getBean(String beanName) {
 9         return applicationContext.getBean(beanName);
10     }
11     public static <T> T getBean(Class<T> type) {
12         return applicationContext.getBean(type);
13     }
14 }

通过实现ApplicationContextAware接口拿到ApplicationContext,后面就可以随心所以了,ShiroFreeRealm中需要用到userService的时候我们可以这么写:

UserService userService = ApplicationContextUtils.getBean(UserService.class);

在其他类似的地方,如果需要支持事务或者用到代理对象的地方,都可以通过这种方式获取。另外顺带提一下,如果需要用到对象原始的实例(非代理对象),我们可以通过在Bean名称前面加一个&获取,还是以UserService举例子:

UserService userService = ApplicationContextUtils.getBean("&userService");

这样拿到的就是常规实例对象了。

参考:

https://www.guitu18.com/post/2019/10/30/56.html

https://blog.csdn.net/qq_30930805/article/details/104059732

带着疑问去思考,然后串联,进而归纳总结,不断追问自己,进行自我辩证,像侦查嫌疑案件一样看待技术问题,漆黑的街道,你我一起寻找线索,你就是技术界大侦探福尔摩斯
原文地址:https://www.cnblogs.com/cainiao-Shun666/p/14738823.html