• 事务(十六)


    使用了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

    带着疑问去思考,然后串联,进而归纳总结,不断追问自己,进行自我辩证,像侦查嫌疑案件一样看待技术问题,漆黑的街道,你我一起寻找线索,你就是技术界大侦探福尔摩斯
  • 相关阅读:
    用vuex写了一个购物车H5页面的示例代码
    css如何引入外部字体?
    移动开发中更好的图片自适应
    常见样式问题七、word-break、word-wrap、white-space区别
    你真的了解word-wrap和word-break的区别吗?
    css中word-break、word-wrap和white-space的区别
    另辟蹊径:vue单页面,多路由,前进刷新,后退不刷新
    应该用forEach改变数组的值吗? 原生JS forEach()和map()遍历的异同点
    Vue 全家桶介绍
    Spring MVC配置MyBatis输出SQL
  • 原文地址:https://www.cnblogs.com/cainiao-Shun666/p/14738823.html
Copyright © 2020-2023  润新知