亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

一個applicationContext 加載錯誤導致的阻塞問題及解決方法

2024-07-14 08:42:50
字體:
來源:轉載
供稿:網友

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

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

還是 web.xml, 原本是這樣的: (很簡潔!)

<?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 搞定收工!

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

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

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

下面,開啟debug模式!

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

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

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

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

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

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

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

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

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

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

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

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

  然而,這大部分情況下捕獲的異常,僅僅正常的 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;  }

  由于這么多無效的異常,導致我反復換了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獲取,不應該有問題??!不過接下來一句會讓我們明白一切:

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

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

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

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

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

  ok, 截止這里,我們已經找到了問題的根源。是一個被引入的jar的優雅方式阻止了你的前進。冬天已現,春天不會遠!

如何解決?

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

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

如何優先加載 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 中,會優先加載 contextInitialized(); 從而初始化整個 spring 的生命周期!

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

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

  驗證結果,果然如此!

最后,附上一段 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);        // 調用 listener.contextInitialized() 觸發 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);  }

總結

以上所述是小編給大家介紹的一個applicationContext 加載錯誤導致的阻塞問題及解決方法,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對VeVb武林網網站的支持!


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品海角社区在线观看| 最新国产精品亚洲| 日韩av免费网站| 久久久久久久久久久人体| 国内精品伊人久久| 在线播放国产一区中文字幕剧情欧美| 久久99精品久久久久久青青91| 97在线视频观看| 亚洲综合自拍一区| 欧美wwwwww| 91免费人成网站在线观看18| 精品久久久久久久久久久久久| 精品美女永久免费视频| 国产精品女主播视频| 亚洲最大av网站| 亚洲天堂av电影| 欧美在线视频网站| 高清欧美性猛交xxxx黑人猛交| 成人欧美一区二区三区黑人孕妇| 亚洲欧美资源在线| 亚洲精品电影在线| 成人免费视频xnxx.com| 69av成年福利视频| 色无极影院亚洲| 亚洲xxxxx性| 欧美性69xxxx肥| 96精品视频在线| 欧美激情一区二区三区高清视频| 亚洲国产精品中文| 欧美性猛交xxxx免费看久久久| 国产精品久久97| 国内精品久久久久影院 日本资源| 国产日韩欧美视频在线| 一夜七次郎国产精品亚洲| 国产91精品高潮白浆喷水| 亚洲第一精品自拍| 久热精品视频在线观看| 国产精品福利在线观看网址| 成人a在线视频| 国产成人鲁鲁免费视频a| 国产精品免费久久久久久| 日韩在线激情视频| 欧美精品日韩www.p站| 欧美精品午夜视频| 国产精品自拍小视频| yw.139尤物在线精品视频| 欧美成人免费观看| 国产精品专区h在线观看| 亚洲国产女人aaa毛片在线| 日韩免费精品视频| 日韩av电影手机在线观看| 欧美日韩福利视频| 一区二区三区国产视频| 欧美成人小视频| 日韩av成人在线| 国产精品久久一区| 97香蕉超级碰碰久久免费软件| 欧美色视频日本版| 日产日韩在线亚洲欧美| 久久久久久久999精品视频| 国产亚洲精品91在线| 国产精品视频永久免费播放| 亚洲免费中文字幕| 1769国内精品视频在线播放| 日韩精品极品在线观看播放免费视频| 欧美性猛交xxxx乱大交蜜桃| 91手机视频在线观看| 狠狠干狠狠久久| 一夜七次郎国产精品亚洲| 欧美午夜激情在线| 欧美性xxxxx极品| 欧美亚洲成人xxx| 丝袜亚洲欧美日韩综合| 一区二区亚洲欧洲国产日韩| 亚洲一区二区在线播放| 91深夜福利视频| 亚洲一级片在线看| 欧美成人h版在线观看| 欧美电影在线免费观看网站| 在线观看中文字幕亚洲| 综合国产在线视频| 伊人伊人伊人久久| 亚洲综合小说区| 91在线视频精品| 久久成人精品电影| 午夜精品一区二区三区在线播放| 国产精品第10页| 狠狠色狠色综合曰曰| xxx成人少妇69| 亚洲级视频在线观看免费1级| 欧美国产高跟鞋裸体秀xxxhd| 欧美性受xxxx黑人猛交| 91久久国产综合久久91精品网站| 欧美在线一区二区视频| 欧美丝袜一区二区| 亚洲人精选亚洲人成在线| 最近2019年好看中文字幕视频| 久久免费视频网| 亚洲国产精久久久久久| 亚洲成av人乱码色午夜| 国产精品第二页| 伊人亚洲福利一区二区三区| 国产精品久久久久77777| 国产精品久久久久久久久| 在线日韩精品视频| 亚洲人成伊人成综合网久久久| 久久免费国产精品1| 国产精品激情自拍| 久99九色视频在线观看| 久久精品一偷一偷国产| 亚洲片国产一区一级在线观看| 亚洲欧美中文在线视频| 91精品久久久久久久久久久久久久| 久精品免费视频| 亚洲free性xxxx护士白浆| 国产亚洲精品美女久久久| 97色在线视频| 日韩欧美福利视频| 青青a在线精品免费观看| 欧美性猛交xxxxx水多| 午夜精品一区二区三区av| 国产精品自产拍高潮在线观看| 日韩在线观看成人| 一夜七次郎国产精品亚洲| 久久亚洲精品成人| 岛国av一区二区三区| 国产成人自拍视频在线观看| 欧美成人黑人xx视频免费观看| 欧美性videos高清精品| 精品视频中文字幕| 91精品国产综合久久香蕉最新版| 尤物99国产成人精品视频| 81精品国产乱码久久久久久| 欧美激情亚洲国产| 在线看欧美日韩| 欧美激情三级免费| 欧美激情欧美激情在线五月| 亚洲天堂av综合网| 中国人与牲禽动交精品| 这里只有精品视频在线| 日韩在线视频观看| 激情久久av一区av二区av三区| 欧美视频在线视频| 日韩亚洲精品电影| 日日噜噜噜夜夜爽亚洲精品| 亚洲精品久久7777777| 欧美xxxx做受欧美.88| 成人黄色av网| 亚洲成人黄色在线| 一本色道久久综合亚洲精品小说| 欧美亚洲成人免费| 亚洲国语精品自产拍在线观看| 亚洲国语精品自产拍在线观看| 欧美日韩免费区域视频在线观看| 国产日韩欧美综合| 欧美性69xxxx肥| 性欧美激情精品| 久久久久久18| 福利一区福利二区微拍刺激| 国产91色在线播放| 久久久久久久成人| 91sa在线看| 色综合色综合久久综合频道88| 欧美大学生性色视频|