美文网首页
引入shiro后userService事务不生效原因(is no

引入shiro后userService事务不生效原因(is no

作者: 多关心老人 | 来源:发表于2018-11-08 12:08 被阅读0次

    系统spring集成了shiro,配置shiroFilter:

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
    </bean>
    

    shiroFilter依赖了securityManagersecurityManager依赖了userRealmuserRealm为了获取AuthenticationInfo和AuthorizationInfo又依赖了userService

    启动项目,日志级别info,会出现下面的提示:

    PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization(327) - Bean 'userService' of type [com.xxx.service.impl.UserServiceImpl] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
    
    PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization(327) - Bean 'userJdbcRealm' of type [com.xxx.UserJdbcRealm] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
    
    PostProcessorRegistrationDelegate$BeanPostProcessorChecker.postProcessAfterInitialization(327) - Bean 'securityManager' of type [com.xxx.LoginCustomSecurityManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
    

    上面是截取的3段,表面意思是:这几个bean(称为业务bean)不会被所有BeanPostProcessor(BPP)处理。BPP是spring的内置的基础bean,用于在实例化业务bean前后处理一些逻辑,可以更改业务bean的行为甚至返回另一个bean(大名鼎鼎的AOP就是通过BPP实现)。

    上面的ShiroFitlerFactoryBean就实现了BPP接口,会对所有bean判断,如果实现了Filter接口,会加入到filterMap中,后续构造filterChainManager时会用到。

    /**
         * Inspects a bean, and if it implements the {@link Filter} interface, automatically adds that filter
         * instance to the internal {@link #setFilters(java.util.Map) filters map} that will be referenced
         * later during filter chain construction.
         */
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            if (bean instanceof Filter) {
                log.debug("Found filter chain candidate filter '{}'", beanName);
                Filter filter = (Filter) bean;
                applyGlobalPropertiesIfNecessary(filter);
                getFilters().put(beanName, filter);
            } else {
                log.trace("Ignoring non-Filter bean '{}'", beanName);
            }
            return bean;
        }
    

    上面的日志是在BeanPostProcessorChecker中输出的,这个类的注释说明:

    /**
         * BeanPostProcessor that logs an info message when a bean is created during
         * BeanPostProcessor instantiation, i.e. when a bean is not eligible for
         * getting processed by all BeanPostProcessors.
         */
    private static class BeanPostProcessorChecker implements BeanPostProcessor
    

    这个类只在一个地方用到:

    // Register BeanPostProcessorChecker that logs an info message when
            // a bean is created during BeanPostProcessor instantiation, i.e. when
            // a bean is not eligible for getting processed by all BeanPostProcessors.
            int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
            beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
    

    看到这2段注释,我们知道出问题的原因:业务bean在BPP实例化前被实例化了。理想情况下应该让spring先实例化所有的BPP后,再实例化我们的业务bean,这样业务bean才能被所有BPP处理,如果有业务bean先于任何BPP实例化,那么这个业务bean就不会被还未实例化的BPP处理了,这个就是日志提示的原因。

    到现在你应该理解了是shiroFilter导致了userService没有被@Transactional 对应的BPP处理,导致事务不起作用。虽然spring打印很多bean,一般最后打印的那个bean是导致问题的罪魁祸首,因为spring是先实例化被依赖的bean。

    (参考 https://stackoverflow.com/questions/1201726/tracking-down-cause-of-springs-not-eligible-for-auto-proxying 第二个作者的回答)。

    解决方法1
    shiroFilter中的securityManager可以先用一个代理bean。

    public class DelegatingSecurityManager extends AbstractFactoryBean<WebSecurityManager> implements InvocationHandler, BeanClassLoaderAware {
    
        @Autowired
        @Lazy
        private DefaultWebSecurityManager defaultWebSecurityManager;
    
        private ClassLoader classLoader;
    
        @Override
        public void setBeanClassLoader(ClassLoader classLoader) {
            this.classLoader = classLoader;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if(method.getDeclaringClass() == Object.class){
                return method.invoke(proxy, args);
            }
            try{
                return method.invoke(this.defaultWebSecurityManager, args);
            }catch(InvocationTargetException e){
                //代理调用,要剥掉最外层的InvocationTargetException
                throw e.getTargetException();
            }
        }
    
        @Override
        public Class<?> getObjectType() {
            return WebSecurityManager.class;
        }
    
        @Override
        protected WebSecurityManager createInstance() throws Exception {
            return (WebSecurityManager) Proxy.newProxyInstance(this.classLoader, new Class<?>[]{WebSecurityManager.class}, this);
        }
    }
    

    解决方法2:
    获取 AuthenticationInfoAuthorizationInfo不要通过UserService/UserRoleService,可以单独写个Service或Dao供登录使用。

    相关文章

      网友评论

          本文标题:引入shiro后userService事务不生效原因(is no

          本文链接:https://www.haomeiwen.com/subject/noamxqtx.html