shiro web應用入口之EnvironmentLoaderListener類

EnvironmentLoaderListener 是shiro web應用的入口, 它是一個 ServletContext 的監聽器, 用於監聽容器的啓動和關閉事件, 分別對應它的兩個方法, contextInitialized(ServletContextEvent sce) 和contextDestroyed(ServletContextEvent sce), 可以從 ServletContextEvent 中直接獲取 ServletContext.
請參考: http://shiro.apache.org/static/1.4.0/apidocs/org/apache/shiro/web/env/EnvironmentLoaderListener.html

我們通過在 web.xml 中定義下列代碼來設置shiro web應用的入口

<listener>
     <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>

查看 EnvironmentLoaderListener 的源碼我們可以發現, 真正用來處理邏輯的是它的父類 EnvironmentLoader, EnvironmentLoader 通過 initEnvironment
(ServletContext servletContext) 方法來初始化Shiro運行所需要的環境.

接下來我們就來看下 EnvironmentLoader 這個類. 我們先來看看這個類之中兩個關鍵的變量

public static final String ENVIRONMENT_CLASS_PARAM = "shiroEnvironmentClass";

public static final String CONFIG_LOCATIONS_PARAM = "shiroConfigLocations";

shiroEnvironmentClass : 用來在 web.xml 中設置自定義的 WebEnvironment 具體實現類. 默認實現類是 IniWebEnvironment.
shiroConfigLocations : 用來在 web.xml 中指定 WebEnvironment 的配置文件. 默認路徑是
1. /WEB-INF/shiro.ini
2. classpath:shiro.ini
我們來舉個列子:

<context-param>
     <param-name>shiroEnvironmentClass</param-name>
     <param-value>com.foo.bar.shiro.MyWebEnvironment</param-value>
</context-param>
<context-param>
     <param-name>shiroConfigLocations</param-name>
     <param-value>/WEB-INF/someLocation/shiro.ini</param-value>
</context-param>

我們看下這個類的入口方法initEnvironment(ServletContext servletContext)方法(去掉了日誌輸出等代碼)

public WebEnvironment initEnvironment(ServletContext servletContext) throws IllegalStateException {

    // 確保 WebEnvironment 在 ServletContext 中只能創建一次
    if (servletContext.getAttribute(ENVIRONMENT_ATTRIBUTE_KEY) != null) {
        String msg = "There is already a Shiro environment associated with the current ServletContext.  " +
                "Check if you have multiple EnvironmentLoader* definitions in your web.xml!";
        throw new IllegalStateException(msg);
    }
    try {
        // 創建 WebEnvironment
        WebEnvironment environment = createEnvironment(servletContext);
        // 將 WebEnvironment 放入 ServletContext 中
        servletContext.setAttribute(ENVIRONMENT_ATTRIBUTE_KEY,environment);
        return environment;
    } catch (RuntimeException ex) {
        log.error("Shiro environment initialization failed", ex);
        servletContext.setAttribute(ENVIRONMENT_ATTRIBUTE_KEY, ex);
        throw ex;
    } catch (Error err) {
        log.error("Shiro environment initialization failed", err);
        servletContext.setAttribute(ENVIRONMENT_ATTRIBUTE_KEY, err);
        throw err;
    }
}

我們可以發現邏輯處理的主要實現實際上在createEnvironment(ServletContext sc)方法中, 那麼我們再來看下createEnvironment(ServletContext sc)方法.

protected WebEnvironment createEnvironment(ServletContext sc) {

    // 確定用來創建 WebEnvironment 的具體實現類, 默認實現類是 IniWebEnvironment
    WebEnvironment webEnvironment = determineWebEnvironment(sc);
    // 確保 webEnvironment 實現了 MutableWebEnvironment 接口
    if (!MutableWebEnvironment.class.isInstance(webEnvironment)) {
        throw new ConfigurationException("Custom WebEnvironment class [" + webEnvironment.getClass().getName() +
                "] is not of required type [" + MutableWebEnvironment.class.getName() + "]");
    }

    // 獲取 web.xml 中的 shiroConfigLocations 配置
    String configLocations = sc.getInitParameter(CONFIG_LOCATIONS_PARAM);
    boolean configSpecified = StringUtils.hasText(configLocations);

    // 判斷 web.xml 中是否有配置 shiroConfigLocations, 並確保 WebEnvironment 的實現類實現了 ResourceConfigurable 接口
    if (configSpecified && !(ResourceConfigurable.class.isInstance(webEnvironment))) {
        String msg = "WebEnvironment class [" + webEnvironment.getClass().getName() + "] does not implement the " +
                ResourceConfigurable.class.getName() + "interface.  This is required to accept any " +
                "configured " + CONFIG_LOCATIONS_PARAM + "value(s).";
        throw new ConfigurationException(msg);
    }

    MutableWebEnvironment environment = (MutableWebEnvironment) webEnvironment;

    // 將 ServletContext 放入該 WebEnvironment 實例中
    environment.setServletContext(sc);

    if (configSpecified && (environment instanceof ResourceConfigurable)) {
        ((ResourceConfigurable) environment).setConfigLocations(configLocations);
    }

    // 空方法 忽略
    customizeEnvironment(environment);

    // WebEnvironment 初始化
    LifecycleUtils.init(environment);

    return environment;
}

總結:
1. 通過在 web.xml 中配置licenser, 可以使容器在啓動時, 通過 EnvironmentLoaderListener 類拿到 ServletContext, 然後通過其父類 EnvironmentLoader 去創建 WebEnvironment
2. EnvironmentLoader 會根據 web.xml 中配置的 shiroEnvironmentClass 去找到對應的 WebEnvironment 實現類, 如果沒有配置, 默認使用 IniWebEnvironment 類, 要求自定義的 WebEnvironment 實現類必須實現MutableWebEnvironment 接口.
3. EnvironmentLoader 會根據 web.xml 中配置的 shiroConfigLocations 去找到對應的shiro配置文件, 如果沒有配置, 默認先去找 /WEB-INF/shiro.ini, 如果還沒有, 再去找 classpath:shiro.ini
4. 最後執行 WebEnvironment 實現類的初始化操作

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章