内射老阿姨1区2区3区4区_久久精品人人做人人爽电影蜜月_久久国产精品亚洲77777_99精品又大又爽又粗少妇毛片

一個applicationContext加載錯誤導(dǎo)致的阻塞問題及解決方法

問題為對接一個sso的驗證模塊,正確的對接姿勢為,接入一個 filter, 然后接入一個 SsoListener 。

十載的長樂網(wǎng)站建設(shè)經(jīng)驗,針對設(shè)計、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應(yīng)快,48小時及時工作處理。全網(wǎng)營銷推廣的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動調(diào)整長樂建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計,從而大程度地提升瀏覽體驗。創(chuàng)新互聯(lián)公司從事“長樂網(wǎng)站設(shè)計”,“長樂網(wǎng)站推廣”以來,每個客戶項目都認真落實執(zhí)行。

然而在接入之后,卻導(dǎo)致了應(yīng)用無法正常啟動,或者說看起來很奇怪,來看下都遇到什么樣的問題,以及是如何處理的?

還是 web.xml, 原本是這樣的: (很簡潔?。?/p>

<?xml version="1.0" encoding="UTF-8" ?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
     version="3.0">
 <display-name>xx-test</display-name>
 <filter>
  <filter-name>encodingFilter</filter-name>
  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  <init-param>
   <param-name>encoding</param-name>
   <param-value>UTF-8</param-value>
  </init-param>
  <init-param>
   <param-name>forceEncoding</param-name>
   <param-value>true</param-value>
  </init-param>
 </filter>
 <filter-mapping>
  <filter-name>encodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>
 <servlet>
  <servlet-name>spring</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>classpath:spring/spring-servlet.xml</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
  <servlet-name>spring</servlet-name>
  <url-pattern>/</url-pattern>
 </servlet-mapping>
</web-app>

而需要添加的 filter 如下:

 <filter>
  <filter-name>SessionFilter</filter-name>
  <filter-class>com.xxx.session.redisSessionFilter</filter-class>
 </filter>
 <filter-mapping>
  <filter-name>SessionFilter</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>
 <listener>
  <listener-class>com.xx.session.SSOHttpSessionListener</listener-class>
 </listener>
 <filter>
  <filter-name>SSOFilter</filter-name>
  <filter-class>com.xxx.auth.SSOFilter</filter-class>
 </filter>
 <filter-mapping>
  <filter-name>SSOFilter</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>
 <context-param>
  <param-name>configFileLocation</param-name>
  <param-value>abc</param-value>
 </context-param>

另外再加幾個必要的配置文件掃描!對接完成!不費事!

然后,我坑哧坑哧把代碼copy過來,準備 commit 搞定收工!

結(jié)果,不出所料,server 起不來了。也不完全是啟不來了,就只是啟起來之后,啥也沒有了。

sso 中也沒啥東西,就是攔截下 header 中的值,判定如果沒有登錄就的話,就直接返回到 sso 的登錄頁去了。

那么,到底是哪里的問題呢?思而不得后,自然就開啟了飛行模式了!

下面,開啟debug模式!

本想直接 debug spring 的,結(jié)果,很明顯,失敗了。壓根就沒有進入 spring 的 ClassPathXmlApplicationContext 中,得出一個結(jié)論,spring 沒有被正確的打開!

好吧,那讓我們退回一步,既然 servlet 啟不來,那么,可能就是 filter 有問題了。

不過,請稍等,filter 不是在有請求進來的時候,才會起作用嗎?沒道理在初始化的時候就把應(yīng)用給搞死了?。。ú贿^其實這是有可能的)

那么,到底問題出在了哪里?

簡單掃略下代碼,不多,還有一個 listener 沒有被引起注意,去看看吧。

先了解下,web.xml 中的 listener 作用:

listener 即 監(jiān)聽器,其實也是 tomcat 的一個加載節(jié)點。加載順序與它們在 web.xml 文件中的先后順序無關(guān)。即不會因為 filter 寫在 listener 的前面而會先加載 filter。

其加載順序為: listener -> filter -> servlet

接下來,就知道, listener 先加載,既然沒有到 servlet, 也排除了 filter, 那就 debug listener 唄!

果然,debug進入無誤!單步后,發(fā)現(xiàn)應(yīng)用在某此被中斷,線程找不到了,有點懵。(其實只是因為線程中被調(diào)用了線程切換而已)

我想著,可能是某處發(fā)生了異常,而此處又沒有被 try-catch, 所以也是很傷心。要是能臨時打 try-catch 就好了。

其實 idea 中 是可以對沒有捕獲的異常進行收集的,即開啟當發(fā)生異常時就捕獲的功能就可以了。

然而,這大部分情況下捕獲的異常,僅僅正常的 loadClass() 異常,這在類加載模型中,是正常拋出的異常。

 // 如: java.net.URLClassLoader.findClass() 拋出的異常
  protected Class<?> findClass(final String name)
    throws ClassNotFoundException
  {
    final Class<?> result;
    try {
      result = AccessController.doPrivileged(
        new PrivilegedExceptionAction<Class<?>>() {
          public Class<?> run() throws ClassNotFoundException {
            String path = name.replace('.', '/').concat(".class");
            Resource res = ucp.getResource(path, false);
            if (res != null) {
              try {
                return defineClass(name, res);
              } catch (IOException e) {
                throw new ClassNotFoundException(name, e);
              }
            } else {
              return null;
            }
          }
        }, acc);
    } catch (java.security.PrivilegedActionException pae) {
      throw (ClassNotFoundException) pae.getException();
    }
    if (result == null) {
      // 此處拋出的異??梢员?idea 捕獲
      throw new ClassNotFoundException(name);
    }
    return result;
  }

由于這么多無效的異常,導(dǎo)致我反復(fù)換了n個姿勢,總算到達正確的位置。

然而當跟蹤到具體的一行時,還是發(fā)生了錯誤。

既然用單步調(diào)試無法找到錯誤,那么是不是在我沒有單步的地方,出了問題?

對咯,就是 靜態(tài)方法塊!這個地方,是在首次調(diào)用該類的任意方法時,進行初始化的!也許這是我們的方向。

最后,跟蹤到了一個靜態(tài)塊中,發(fā)現(xiàn)這里被中斷了!

  static {
    // 原罪在這里
    CAS_EDIS_CLIENT_TEMPLATE = CasSpringContextUtils.getBean("casRedisClientTemplate", CasRedisClientTemplate.class);
  }

這一句看起來是向 spring 的 bean工廠請求一個實例,為什么能被卡死呢?
只有再深入一點,才能了解其情況:

  public static <T> T getBean(String name, Class<T> beanType) {
    return getApplicationContext().getBean(name, beanType);
  }

這句看起來更像是 spring 的bean獲取,不應(yīng)該有問題?。〔贿^接下來一句會讓我們明白一切:

  public static ApplicationContext getApplicationContext() {
    synchronized (CasSpringContextUtils.class) {
      while (applicationContext == null) {
        try {
          // 沒錯,就是這里了, 這里設(shè)置了死鎖,線程交出,等待1分鐘超時,繼續(xù)循環(huán)
          CasSpringContextUtils.class.wait(60000);
        } catch (InterruptedException ex) {
        }
      }
      return applicationContext;
    }
  }

很明顯,這里已經(jīng)導(dǎo)致了某種意義上的死鎖。因為 web.xml 在加載到此處時,使用的是一個 main 線程,而加載到此處時,卻被該處判斷阻斷。

那么我們可能想, applicationContext 是一個 sping 管理的類,那么只要他被加載后,不可以了嗎?就像下面一樣:

沒錯,spring 在加載到此類時,會調(diào)用一個 setApplicationContext, 此時 applicationContext 就不會null了。然后想像還是太美,原因如上:

  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    synchronized (CasSpringContextUtils.class) {
      CasSpringContextUtils.applicationContext = applicationContext;
      // 夢想總是很美好,當加載完成后,通知 wait()
      CasSpringContextUtils.class.notifyAll();
    }
  }

ok, 截止這里,我們已經(jīng)找到了問題的根源。是一個被引入的jar的優(yōu)雅方式阻止了你的前進。冬天已現(xiàn),春天不會遠!

如何解決?

很明顯,你是不可能去改動這段代碼的,那么你要做的,就是想辦法繞過它。

即:在執(zhí)行 getApplicationContext() 之前,把 applicationContext 處理好!

如何優(yōu)先加載 spring 上下文?配置一個 context-param, 再加一個 ContextLoaderListener, 即可:

 <!-- 提前加載spring -->
 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:spring/applicationContext.xml</param-value>
 </context-param>
 <listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>

在 ContextLoaderListener 中,會優(yōu)先加載 contextInitialized(); 從而初始化整個 spring 的生命周期!

  /**
   * Initialize the root web application context.
   */
  @Override
  public void contextInitialized(ServletContextEvent event) {
    initWebApplicationContext(event.getServletContext());
  }

也就是說,只要把這個配置放到新增的 filter 之前,即可實現(xiàn)正常情況下的加載!

驗證結(jié)果,果然如此!

最后,附上一段 tomcat 加載 context 的魯棒代碼,以供參考:

/**
   * Configure the set of instantiated application event listeners
   * for this Context.
   * @return <code>true</code> if all listeners wre
   * initialized successfully, or <code>false</code> otherwise.
   */
  public boolean listenerStart() {
    if (log.isDebugEnabled())
      log.debug("Configuring application event listeners");
    // Instantiate the required listeners
    String listeners[] = findApplicationListeners();
    Object results[] = new Object[listeners.length];
    boolean ok = true;
    for (int i = 0; i < results.length; i++) {
      if (getLogger().isDebugEnabled())
        getLogger().debug(" Configuring event listener class '" +
          listeners[i] + "'");
      try {
        String listener = listeners[i];
        results[i] = getInstanceManager().newInstance(listener);
      } catch (Throwable t) {
        t = ExceptionUtils.unwrapInvocationTargetException(t);
        ExceptionUtils.handleThrowable(t);
        getLogger().error(sm.getString(
            "standardContext.applicationListener", listeners[i]), t);
        ok = false;
      }
    }
    if (!ok) {
      getLogger().error(sm.getString("standardContext.applicationSkipped"));
      return false;
    }
    // Sort listeners in two arrays
    ArrayList<Object> eventListeners = new ArrayList<>();
    ArrayList<Object> lifecycleListeners = new ArrayList<>();
    for (int i = 0; i < results.length; i++) {
      if ((results[i] instanceof ServletContextAttributeListener)
        || (results[i] instanceof ServletRequestAttributeListener)
        || (results[i] instanceof ServletRequestListener)
        || (results[i] instanceof HttpSessionIdListener)
        || (results[i] instanceof HttpSessionAttributeListener)) {
        eventListeners.add(results[i]);
      }
      if ((results[i] instanceof ServletContextListener)
        || (results[i] instanceof HttpSessionListener)) {
        lifecycleListeners.add(results[i]);
      }
    }
    // Listener instances may have been added directly to this Context by
    // ServletContextInitializers and other code via the pluggability APIs.
    // Put them these listeners after the ones defined in web.xml and/or
    // annotations then overwrite the list of instances with the new, full
    // list.
    for (Object eventListener: getApplicationEventListeners()) {
      eventListeners.add(eventListener);
    }
    setApplicationEventListeners(eventListeners.toArray());
    for (Object lifecycleListener: getApplicationLifecycleListeners()) {
      lifecycleListeners.add(lifecycleListener);
      if (lifecycleListener instanceof ServletContextListener) {
        noPluggabilityListeners.add(lifecycleListener);
      }
    }
    setApplicationLifecycleListeners(lifecycleListeners.toArray());
    // Send application start events
    if (getLogger().isDebugEnabled())
      getLogger().debug("Sending application start events");
    // Ensure context is not null
    getServletContext();
    context.setNewServletContextListenerAllowed(false);
    Object instances[] = getApplicationLifecycleListeners();
    if (instances == null || instances.length == 0) {
      return ok;
    }
    ServletContextEvent event = new ServletContextEvent(getServletContext());
    ServletContextEvent tldEvent = null;
    if (noPluggabilityListeners.size() > 0) {
      noPluggabilityServletContext = new NoPluggabilityServletContext(getServletContext());
      tldEvent = new ServletContextEvent(noPluggabilityServletContext);
    }
    for (int i = 0; i < instances.length; i++) {
      if (!(instances[i] instanceof ServletContextListener))
        continue;
      ServletContextListener listener =
        (ServletContextListener) instances[i];
      try {
        fireContainerEvent("beforeContextInitialized", listener);
        // 調(diào)用 listener.contextInitialized() 觸發(fā) listener
        if (noPluggabilityListeners.contains(listener)) {
          listener.contextInitialized(tldEvent);
        } else {
          listener.contextInitialized(event);
        }
        fireContainerEvent("afterContextInitialized", listener);
      } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        fireContainerEvent("afterContextInitialized", listener);
        getLogger().error
          (sm.getString("standardContext.listenerStart",
                 instances[i].getClass().getName()), t);
        ok = false;
      }
    }
    return (ok);
  }

總結(jié)

以上所述是小編給大家介紹的一個applicationContext 加載錯誤導(dǎo)致的阻塞問題及解決方法,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對創(chuàng)新互聯(lián)網(wǎng)站的支持!

本文名稱:一個applicationContext加載錯誤導(dǎo)致的阻塞問題及解決方法
本文網(wǎng)址:http://www.rwnh.cn/article24/ihjsje.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供移動網(wǎng)站建設(shè)網(wǎng)站排名、、App開發(fā)定制開發(fā)、營銷型網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站制作
扎鲁特旗| 辽源市| 抚宁县| 嵊州市| 忻城县| 长汀县| 玉屏| 克什克腾旗| 云和县| 满城县| 平顺县| 通渭县| 图木舒克市| 马公市| 长武县| 潮安县| 贵德县| 松阳县| 桂林市| 罗江县| 七台河市| 公主岭市| 吴江市| 昭苏县| 八宿县| 桃江县| 甘德县| 曲松县| 南宫市| 鄂托克旗| 平利县| 南岸区| 佛坪县| 海原县| 太康县| 呈贡县| 桓仁| 农安县| 芷江| 从化市| 太原市|