国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
走出ClassLoader的迷宮
1、問題:在何種情形下使用thread.getcontextclassloader()?

盡管沒經(jīng)常遇到這個問題,但是想獲得準確的答案并不那么容易,特別是在開發(fā)應(yīng)用框架的時候,你需要動態(tài)的加載一些類和資源,不可避免的你會被此困擾。一般來說,動態(tài)載入資源有三種ClassLoader可以選擇,System ClassLoader(也叫AppClassLoader)、當前類的ClassLoader和CurrentThread的Context ClassLoader。那么,如何選擇使用?

首先可以簡單排除的是SystemClassLoader,這個ClassLoader負責從參數(shù)-classpath、-cp、和操作系統(tǒng)CLASSPATH中載入資源。并且,任何ClassLoader的getSystemXXX()方法都是有以上幾個路徑指定的。我們應(yīng)該很少需要編寫直接使用ClassLoader的程序,否則你的代碼將只能在命令行運行,發(fā)布你的代碼成為ejb、web應(yīng)用或者java web start應(yīng)用,我肯定他們會崩潰!

接下來,我們只剩下兩個選擇了:當前ClassLoader和Thread Context ClassLoader

Current ClassLoader:當前類所屬的ClassLoader,在虛擬機中類之間引用,默認就是使用這個ClassLoader。另外,當你使用Class.forName(), Class.getResource()這幾個不帶ClassLoader參數(shù)的方法是,默認同樣適用當前類的ClassLoader。你可以通過方法XX.class.GetClassLoader()獲取。

Thread Context ClassLoader,沒一個Thread有一個相關(guān)聯(lián)系的Context ClassLoader(由native方法建立的除外),可以通過Thread.setContextClassLoader()方法設(shè)置。如果你沒有主動設(shè)置,Thread默認集成Parent Thread的 Context ClassLoader(注意,是parentThread 不是父類)。如果 你整個應(yīng)用中都沒有對此作任何處理,那么 所有的Thread都會以SystemClassLoader作為ContextClassLoader。知道這一點很重要,因為從web服務(wù)器,java企業(yè)服務(wù)器使用一些復雜而且精巧的ClassLoader結(jié)構(gòu)去實現(xiàn)諸如JNDI、線程池和熱部署等功能以來,這種簡單的情況越發(fā)的少見了。

這篇文章中為什么把Thread Context ClassLoader放在首要的位置,別人并沒有大張旗鼓的介紹它?很多開發(fā)者都對此不甚了解,因為sun沒有提供很好的說明文檔。

事實上,ContextClassLoader提供一個突破委托代理機制的后門。虛擬機通過父子層次關(guān)系組織管理ClassLoader,沒有個ClassLoader都有一個ParentClassLoader(BootStartp不在此范圍之內(nèi)),當要求一個ClassLoader裝載一個類是,他首先請求ParentClassLoader去裝載,只有parent ClassLoader裝載失敗,才會嘗試自己裝載。

但是,某些時候這種順序機制會造成困擾,特別是jvm需要動態(tài)載入有開發(fā)者提供的資源時。就以JNDI為例,JNDI的類是由bootstarpClassLoader從rt.jar中間載入的,但是JNDI具體的核心驅(qū)動是由正式的實現(xiàn)提供的,并且通常會處于-cp參數(shù)之下(注:也就是默認的System ClassLoader管理),這就要求bootstartpClassLoader去載入只有SystemClassLoader可見的類,正常的邏輯就沒辦法處理。怎么辦呢?parent可以通過獲得當前調(diào)用Thread的方法獲得調(diào)用線程的Context ClassLoder 來載入類。

順帶補充一句,JAXP從1.4之后也換成了類似JNDI的ClassLoader實現(xiàn),嘿嘿,剛剛我說什么來著,SUN文檔缺乏 ^_^

介紹完這些之后,我們走到的十字路口,任一選擇都不是萬能的。一些人認為Context ClassLoader將會是新的標準。但是一旦你的多線程需要通訊某些共享數(shù)據(jù),你會發(fā)現(xiàn),你將有一張極其丑陋的ClassLoader分布圖,除非所有的線程使用一樣的ContextClassLoader。并且委派使用當前ClassLoder對一些方法來說是默認繼承來的,比如說Class.forName()。盡管你明確的在任何你能控制的地方使用Context ClassLoader,但是畢竟還有很多代碼不歸你管(備注:想起一個關(guān)于UNIX名字來源的笑話)。

某些應(yīng)用服務(wù)器使用不同的ClassLoder作為ContextClassLoader和當前ClassLoader,并且這些ClassLoader有著相同的ClassPath,但沒有父子關(guān)系,這使得情況更復雜。請列位看官,花幾秒鐘時間想一想,為什么這樣不好?被載入的類在虛擬機內(nèi)部有一個全名稱,不同的ClassLoader載入的相同名稱的類是不一樣的,這就隱藏了類型轉(zhuǎn)換錯誤的隱患。(注:奶奶的 俺就遇到過,JBOSSClassLoader機制蠻挫的)

這種混亂事實上在java類中也有,試著去猜測任何一個包含動態(tài)加載的java規(guī)范的ClassLoader機制,以下是一個清單:

  • JNDI uses context classloaders

  • Class.getResource() and Class.forName() use the current classloader

  • JAXP uses context classloaders (as of J2SE 1.4)

  • java.util.ResourceBundle uses the caller's current classloader

  • URL protocol handlers specified via java.protocol.handler.pkgs system property are looked up in the bootstrap and system classloaders only

  • Java Serialization API uses the caller's current classloader by default

而且關(guān)于這些資源的類加載機制文檔時很少。

java開發(fā)人員應(yīng)該怎么做?

如果你的實現(xiàn)是利用特定的框架,那么恭喜你,實現(xiàn)它遠比實現(xiàn)框架要簡單得多!例如,在web應(yīng)用和EJB應(yīng)用中,你僅僅只要使用 Class.getResource()就足夠了。

其他的情形下,俺有個建議(這個原則是俺工作中發(fā)現(xiàn)的)

下面這個類可以在整個應(yīng)用中的任何地方使用,作為一個全局的ClassLoader(所有的示例代碼可以從download下載):

 1 public abstract class ClassLoaderResolver {
 2 /**
 3 * This method selects the best classloader instance to be used for
 4 * class/resource loading by whoever calls this method. The decision
 5 * typically involves choosing between the caller's current, thread context,
 6 * system, and other classloaders in the JVM and is made by the
 7 * {@link IClassLoadStrategy} instance established by the last call to
 8 * {@link #setStrategy}.
 9 *
10 @return classloader to be used by the caller ['null' indicates the
11 * primordial loader]
12 */
13 public static synchronized ClassLoader getClassLoader() {
14 final Class caller = getCallerClass(0);
15 final ClassLoadContext ctx = new ClassLoadContext(caller);
16 
17 return s_strategy.getClassLoader(ctx);
18 }
19 
20 public static synchronized IClassLoadStrategy getStrategy() {
21 return s_strategy;
22 }
23 
24 public static synchronized IClassLoadStrategy setStrategy(
25 final IClassLoadStrategy strategy) {
26 final IClassLoadStrategy old = s_strategy;
27 s_strategy = strategy;
28 
29 return old;
30 }
31 
32 /**
33 * A helper class to get the call context. It subclasses SecurityManager to
34 * make getClassContext() accessible. An instance of CallerResolver only
35 * needs to be created, not installed as an actual security manager.
36 */
37 private static final class CallerResolver extends SecurityManager {
38 protected Class[] getClassContext() {
39 return super.getClassContext();
40 }
41 
42 // End of nested class
43 
44 /*
45 * Indexes into the current method call context with a given offset.
46 */
47 private static Class getCallerClass(final int callerOffset) {
48 return CALLER_RESOLVER.getClassContext()[CALL_CONTEXT_OFFSET
49 + callerOffset];
50 }
51 
52 private static IClassLoadStrategy s_strategy; // initialized in <clinit>
53 
54 private static final int CALL_CONTEXT_OFFSET = 3// may need to change if
55 // this class is
56 // redesigned
57 private static final CallerResolver CALLER_RESOLVER; // set in <clinit>
58 
59 static {
60 try {
61 // This can fail if the current SecurityManager does not allow
62 // RuntimePermission ("createSecurityManager"):
63 
64 CALLER_RESOLVER = new CallerResolver();
65 catch (SecurityException se) {
66 throw new RuntimeException(
67 "ClassLoaderResolver: could not create CallerResolver: "
68 + se);
69 }
70 
71 s_strategy = new DefaultClassLoadStrategy();
72 }
73 // End of class.
74 
75 
76 

通過ClassLoaderResolver.getClassLoader()方法獲得一個ClassLoader的引用,并且利用正常的ClassLoader的api去加載資源,你也可以使用 ResourceLoader API作為備選方案

 1 public abstract class ResourceLoader {
 2 
 3 /**
 4  * @see java.lang.ClassLoader#loadClass(java.lang.String)
 5  */
 6 public static Class loadClass (final String name)throws ClassNotFoundException{
 7 
 8 final ClassLoader loader = ClassLoaderResolver.getClassLoader (1);
 9 
10 return Class.forName (name, false, loader);
11 
12 }
13 
14 /**
15 
16 @see java.lang.ClassLoader#getResource(java.lang.String)
17 
18 */    
19 
20 
21 public static URL getResource (final String name){
22 
23 final ClassLoader loader = ClassLoaderResolver.getClassLoader (1);
24 
25 if (loader != null)return loader.getResource (name);
26 else return ClassLoader.getSystemResource (name);
27 }
28  more methods 
29 
30 // End of class

而決定使用何種ClassLoader策略是由接口實現(xiàn)的,這是一種插件機制,方便變更。

public interface IClassLoadStrategy{
ClassLoader getClassLoader (ClassLoadContext ctx);
// End of interface

它需要一個ClassLoader Context 對象去決定使用何種ClassLoader策略。
 1 public class ClassLoadContext{
 2 
 3 public final Class getCallerClass (){
 4 return m_caller;
 5 }
 6 
 7 ClassLoadContext (final Class caller){
 8 m_caller = caller;
 9 
10 }
11 
12 private final Class m_caller;
13 
14 // End of class

ClassLoadContext.getCallerClass()返回調(diào)用者給ClassLoaderResolver 或者ResourceLoader,因此能獲得調(diào)用者的ClassLoader。需要注意的是,調(diào)用者是不會變的(注:作者使用的final修飾字)。俺的方法不需要對現(xiàn)有的業(yè)務(wù)方法做擴展,而且可以作為靜態(tài)方法是用。而且,你可以根據(jù)自己的業(yè)務(wù)場景實現(xiàn)獨特的ClassLoaderContext。

看出來沒,這是一種很熟悉的設(shè)計模式,XD,把獲得ClassLoader的策略從業(yè)務(wù)中獨立出來,這個策略可以是"總是用ContextClassLoader"或者"總是用當前ClassLoader"。想預先知道那種策略是正確的比較困難,那么這種模式可以讓你簡單的改變策略。

俺寫了一個默認的實現(xiàn),基本可以對付95%的場景(enjoy yourself)

 1 public class DefaultClassLoadStrategy implements IClassLoadStrategy{
 2 
 3 public ClassLoader getClassLoader (final ClassLoadContext ctx){
 4 
 5 final ClassLoader callerLoader = ctx.getCallerClass ().getClassLoader ();
 6 
 7 final ClassLoader contextLoader = Thread.currentThread ().getContextClassLoader ();
 8 
 9 ClassLoader result;
10 // If 'callerLoader' and 'contextLoader' are in a parent-child
11 // relationship, always choose the child:
12 if (isChild (contextLoader, callerLoader))result = callerLoader;
13 else if (isChild (callerLoader, contextLoader))result = contextLoader;
14 else{
15 // This else branch could be merged into the previous one,
16 // but I show it here to emphasize the ambiguous case:
17 result = contextLoader;
18 }
19 final ClassLoader systemLoader = ClassLoader.getSystemClassLoader ();
20 
21 
22 // Precaution for when deployed as a bootstrap or extension class:
23 if (isChild (result, systemLoader))result = systemLoader;
24 return result;
25 }
26 
27 
28 
29 
 more methods 

30 
31 // End of class
32 


上面的邏輯比較簡單,如果當前ClassLoader和Context ClassLoader是父子關(guān)系,那就總選兒子,根據(jù)委托原則,這個很容易理解。

如果兩人平級,選擇正確的ClassLoader很重要,運行時不允許含糊。這種情況下,我的代碼選擇ContextClassLoader(這是俺個人的經(jīng)驗之談),當然也不要擔心不能改變,你能隨便根據(jù)需要改變。一般而言,ContextClassLoader比較適合框架,而Current ClassLoader在業(yè)務(wù)邏輯中用的更多。

最后,檢查確保選中的ClassLoader不是System ClassLoader的parent,一旦高于System ClassLoader ,請使用System ClassLoader(你的類部署在Ext路徑下面,就會出現(xiàn)這種情況)。

請注意,俺故意沒關(guān)注被載入資源的名稱。Java XML API 成為java核心api的經(jīng)歷告訴我們,根據(jù)資源名稱過濾是很不cool的idea。而且我也沒有去確認到底哪個ClassLoader被取得了,因為只要清楚原理,這很容易被推理出來。(哈哈,俺是強淫)

盡管討論java 的ClassLoader不是一個很cool的話題(譯者注,當年不cool,但是現(xiàn)在很cool),而且JavaEE的ClassLoader策略越發(fā)的依賴各種平臺的升級。如果這沒有一個更好的設(shè)計的話,將會變成一個大大的問題。不敢您是否同意俺的觀點,俺尊重你說話的權(quán)利,所以請給俺分享您的意見經(jīng)驗。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
淺析ContextClassLoader
Find a way out of the ClassLoader maze(走出ClassLoader迷宮)
關(guān)于class.forname和contextClassloader
Java類加載器
老大難的 Java ClassLoader 再不理解就老了
ClassLoader(2)Boot、App、Ext、線程加載器
更多類似文章 >>
生活服務(wù)
分享 收藏 導長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服