springboot 集成 shiro 致使事務無效

問題描述

前兩天測試一個寫事務,發現這個事務出現異常不會回滾了,一直在事務上找問題,一直沒有找到,結果發現是shirobean先於Spring事務將userService實例化了,結果致使spring事務初始化時好沒法掃描到該bean,致使這個bean上沒有綁定事務,致使事務無效java

尋找問題在哪

1、在事務自己找問題

經過百度發現,你們都有如下幾個緣由致使事務失效web

  1. 數據庫的引擎是不是innoDB
  2. 啓動類上是否加入@EnableTransactionManagement註解
  3. 方法是否爲public
  4. 是不是由於拋出了Exceptionchecked異常

通過排查,發現以上緣由都經過了,那麼應該不是寫的問題。spring

2、在運行中找問題

在上面4個緣由檢查時,發現將已有的service 類 copy下如今有兩個除了名字其餘都如出一轍的類,這時運行下發現,在原來的類中@Transational失效,在新copy中的類中@Transational就起效了,這個問題好莫名奇妙,什麼都沒改就一個有效一個無效,如今的思路就是比較下這兩個類在運行時有什麼不一樣數據庫

經過log發現打出了一下信息,說是jdbcconnection 不是Spring管的
clipboard.pngapache

而正常回歸的service類則是,調用了 JtaTransactionManager 類,並且 spring是管理jdbcconnection
clipboard.pngspringboot

經過這個分析,能夠知道這spring對於這兩個類的處理是不同的,應該是spring代理或者初始化的問題,翻了下log 發現service
ProxyTransactionManagementConfiguration 配置以前就被建立了,那應該是這裏的問題了,這裏就要分析下service爲啥提早被建立了,發如今開始啓動的是shiro ,而shiro中有個realm中引用了這些服務,因此這些服務在Transaction建立掃描以前建立了
clipboard.pngsession

引起問題緣由總結

致使問題的真正緣由是bean建立順序問題,解決問題方法就是,在Transaction以後建立service
ps:呵呵,可是我仍是不知道咋樣才能解決建立順序問題,繼續百度之,關鍵詞shiro 致使 事務不生效果真有解決方案spring-boot

解決方案

通過百度找到了如下的解決方法,和如下解釋測試

  1. shiro致使springboot事務不起效解決辦法
  2. BeanPostProcessor加載次序及其對Bean形成的影響分析
  3. spring boot shiro 事務無效
  4. Shrio 多realms集成:No realms have been configured! One or more realms must be present
  5. spring + shiro 配置中部分事務失效分析及解決方案(這個方案無論用)

解決方法一:

realm引用的service服務上加@lazy註解,可是這個方法我測試了下並無起效!!!spa

解決方法二:

把在 ShiroConfig裏面初始化的RealmbeansecurityManagerbean方法移動到一個新建的ShiroComponent中,利用監聽器中去初始化,主要配置以下,其中ShiroComponentUserNamePassWordRealmWeiXinRealm是我自定義的兩個Realm,換成本身的就好,

ShiroConfig.java

import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;

import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;

/**
 * 自定義繼承shiro 沒有使用shiro-spring-boot-web-starter 的shiro 套件
 * 
 * @author gaoxiuya
 *
 */
@Configuration
public class ShiroConfig {

    /**
     * FilterRegistrationBean
     * 
     * @return
     */
    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
        filterRegistration.setEnabled(true);
        filterRegistration.addUrlPatterns("/*");
        filterRegistration.setDispatcherTypes(DispatcherType.REQUEST);
        return filterRegistration;
    }

    /**
     * @param securityManager 
     * @see org.apache.shiro.spring.web.ShiroFilterFactorupload.visit.pathyBean
     * @return
     */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {

        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager);
        bean.setLoginUrl("/");
        bean.setSuccessUrl("/index");
        bean.setUnauthorizedUrl("/403");
        Map<String, Filter> filters = new LinkedHashMap<>();
        filters.put("permsc", new CustomPermissionsAuthorizationFilter());
        bean.setFilters(filters);
        Map<String, String> chains = new LinkedHashMap<>();
        chains.put("/favicon.ico", "anon");

        bean.setFilterChainDefinitionMap(chains);
        return bean;
    }

    @Bean
    public EhCacheManager cacheManager() {
        EhCacheManager cacheManager = new EhCacheManager();
        cacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
        return cacheManager;
    }
 
    /**
     * @see DefaultWebSessionManager
     * @return
     */
    @Bean(name = "sessionManager")
    public DefaultWebSessionManager defaultWebSessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setCacheManager(cacheManager());
        sessionManager.setGlobalSessionTimeout(1800000);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setDeleteInvalidSessions(true);
        return sessionManager;
    }

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

}

ShiroComponent.java

import java.util.ArrayList;
import java.util.List;

import org.apache.shiro.authc.Authenticator;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class ShiroComponent {
    @Bean
    public Realm userNamePassWordRealm(CacheManager cacheManager) {
        UserNamePassWordRealm userNamePassWordRealm = new UserNamePassWordRealm();
        userNamePassWordRealm.setCacheManager(cacheManager);
        return userNamePassWordRealm;
    }

    @Bean
    public Realm myWeiXinRealm(CacheManager cacheManager) {
        WeiXinRealm weiXinRealm = new WeiXinRealm();
        weiXinRealm.setCacheManager(cacheManager);
        return weiXinRealm;
    }

    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager(Authenticator modularRealmAuthenticator, CacheManager cacheManager,
                    SessionManager defaultWebSessionManager) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setAuthenticator(modularRealmAuthenticator);
        manager.setCacheManager(cacheManager);
        manager.setSessionManager(defaultWebSessionManager);

        return manager;
    }

    @Bean
    public Authenticator modularRealmAuthenticator() {
        ModularRealmAuthenticator modularRealmAuthenticator = new ModularRealmAuthenticator();
        modularRealmAuthenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
        return modularRealmAuthenticator;

    }

    @EventListener
    public void handleContextRefresh(ContextRefreshedEvent event) {
        ApplicationContext context = event.getApplicationContext();
        DefaultWebSecurityManager manager = (DefaultWebSecurityManager) context.getBean("securityManager");
        Realm userNamePassWordRealm = (Realm) context.getBean("userNamePassWordRealm");
        Realm myWeiXinRealm = (Realm) context.getBean("myWeiXinRealm");
        ModularRealmAuthenticator modularRealmAuthenticator = (ModularRealmAuthenticator) context
                        .getBean("modularRealmAuthenticator");
        List<Realm> realms = new ArrayList<>();
        realms.add(userNamePassWordRealm);
        realms.add(myWeiXinRealm);
        modularRealmAuthenticator.setRealms(realms);
        manager.setAuthenticator(modularRealmAuthenticator);
        manager.setRealms(realms);
    }
}

總結

之後須要補課的地方

  1. spring bean 初始化順序
  2. spring 事務原理
  3. spring bean 預加載 BeanPostProces 原理
  4. @Lazy 原理和爲啥不起效
相關文章
相關標籤/搜索