一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 一個applicationContext 加載錯誤導致的阻塞問題及解決方法

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

2021-06-11 14:01等你歸去來 Java教程

這篇文章主要介紹了一個applicationContext 加載錯誤導致的阻塞問題及解決方法,需要的朋友可以參考下

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?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 如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<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() 異常,這在類加載模型中,是正常拋出的異常。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 如: 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個姿勢,總算到達正確的位置。

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

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

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

最后,跟蹤到了一個靜態塊中,發現這里被中斷了!

?
1
2
3
4
static {
  // 原罪在這里
  cas_edis_client_template = casspringcontextutils.getbean("casredisclienttemplate", casredisclienttemplate.class);
}

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

?
1
2
3
public static <t> t getbean(string name, class<t> beantype) {
  return getapplicationcontext().getbean(name, beantype);
}

這句看起來更像是 spring 的bean獲取,不應該有問題啊!不過接下來一句會讓我們明白一切:

?
1
2
3
4
5
6
7
8
9
10
11
12
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了。然后想像還是太美,原因如上:

?
1
2
3
4
5
6
7
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, 即可:

?
1
2
3
4
5
6
7
8
<!-- 提前加載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 的生命周期!

?
1
2
3
4
5
6
7
/**
 * initialize the root web application context.
 */
@override
public void contextinitialized(servletcontextevent event) {
  initwebapplicationcontext(event.getservletcontext());
}

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

  驗證結果,果然如此!

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/**
   * 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 加載錯誤導致的阻塞問題及解決方法,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!

原文鏈接:https://www.cnblogs.com/yougewe/p/9948909.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产免费丝袜调教视频 | 国产欧美日韩精品高清二区综合区 | 国产精品女主播大秀在线 | 亚洲精品国产一区二区三区在 | 四虎成人免费视频 | 99久久伊人一区二区yy5099 | 国产精品全国探花在线观看 | 婷婷中文网 | 男gay男gay男gay野外 | 日韩在线一区 | 91精品手机国产在线观 | 亚洲福利视频一区 | 国产伊人久久 | 国产精品露脸国语对白手机视频 | 色成人综合网 | 91私密保健女子养生spa | 日韩欧美亚洲国产高清在线 | 99在线精品免费视频 | 日本三级在线观看免费 | 免费看又黄又爽又猛的视频软件- | 国语自产自拍秒拍在线视频 | 艾秋麻豆果冻剧传媒在线播放 | 性bbbb妇女激情 | 天堂网在线.www天堂在线视频 | 国产亚洲99影院 | 国产情侣视频观看 | chaopeng在线观看 | 国产99视频精品免视看7 | 日本一道高清不卡免费 | 91精品乱码一区二区三区 | 国产精品高清一区二区三区不卡 | 亚洲欧美一区二区三区不卡 | 精品无人区乱码1区2区3区免费 | 日本动漫打扑克动画片樱花动漫 | 成人福利影院 | 成年人在线观看视频 | 九九影院午夜理论片无码 | japanesen女同 | 亚洲精品综合一区二区 | 亚洲精品久久啪啪网站成年 | 欧美日韩国产一区二区三区伦 |