標題說明了一切,我想說的就是如果你在做Eclipse插件開發,想真正搞清楚Eclipse插件類加載,那么Java基本類加載原理是基礎,無法逾越?。?!我會舉一個小例子來簡單說明一下
【情景】
例如我們在底層模塊插件(就叫做Host)中做了一個解析器實現(什么解析器就別管了),對應類型為IParser(com.my.host.parser.IParser),簡單定義如下:
1 public interface IParser {
2 public Object parse(Object input) throws CoreException;
3 }
基本的原則告訴我們:如果一個實例的創建過程較為負責,則因該把這種實例的創建和實例的使用解耦合。于是,我們在Host插件中提供了一個如下的工廠類:
1 public class ParserFactory {
2 /**
3 * create IParser instance
4 *
5 * @param clazz qualified parser type name
6 * @return
7 * @throws CoreException
8 */
9 public static IParser createParser(String clazz) throws CoreException{
10 try {
11 Object object = Class.forName(clazz).newInstance();
12 if (object instanceof IParser)
13 return (IParser)object;
14
15 return null;
16 } catch (Exception e) {
17 throw new CoreException(new Status(IStatus.ERROR, "host", 101, "創建工廠失敗:" + clazz, e));
18 }
19 }
20
21 /**
22 * create IParser instance with corresponding class loader
23 *
24 * @param clazz qualified parser type name
25 * @param classLoader corresponding class loader
26 * @return
27 * @throws CoreException
28 */
29 public static IParser createParser(String clazz, ClassLoader classLoader) throws CoreException{
30 try {
31 Object object = classLoader.loadClass(clazz);
32 if (object instanceof IParser)
33 return (IParser)object;
34
35 return null;
36 } catch (Exception e) {
37 throw new CoreException(new Status(IStatus.ERROR, "host", 101, "創建工廠失?。?/span>" + clazz, e));
38 }
39 }
40 }
41
這里的簡單工廠中創建方法返回的是IParser接口類型,目的就是為了保證可以創建子類型的靈活性,同時確保對外提供的接口是一致的,有助于客戶端基于接口編程(說的有點上綱上線了~_~)
同時我們在Host插件中,也提供了一個默認的IParser實現,就叫做DefaultParser吧。那到這里我們的Host插件共提供了如下類型:
com.my.host.parser.IParser 解析器接口定義
com.my.host.parser.ParserFactory 解析器工廠
com.my.host.parser.impl.DefaultParser 解析器接口默認實現
下面我們在Host插件的基礎之上建立了上層功能插件(就叫做Extension插件吧,Extension插件依賴于Host插件),提供了解析器實現,對應類型為com.my.extension.parser.MyParserImpl。我們在Extension插件中想使用這個實現,假設有如下幾種中創建方式:
第一種:直接創建實例,偏不用你Host中的工廠。 俗了點,但是沒問題。
1 package com.my.extension.model;
2
3 public class Customer {
4 public void doOperation(Object input) throws CoreException{
5 //直接創建實例的方式
6 IParser parser = new MyParserImpl();
7
8 Object model = parser.parse(input);
9 //TODO 拿到模型之后干其他活
10 }
11 }
第二種,直接使用Class.forName(),還是不用你Host中的工廠。也是俗了點,但也沒問題,為什么沒問題,可能就沒有想清楚了~_~
1 package com.my.extension.model;
2
3 public class Customer {
4 public void doOperation(Object input) throws CoreException{
5 try {
6 //使用默認的Class.forName()的方式
7 IParser parser = (IParser)Class.forName("com.my.extension.parser.MyParserImpl").newInstance();
8
9 Object model = parser.parse(input);
10 //TODO 拿到模型之后干其他活
11 } catch (Exception e) {
12 // TODO: handle exception
13 }
14
15 }
16 }
第三種:用一把工廠
---》》ClassNotFoundException
1 package com.my.extension.model;
2
3 public class Customer {
4 public void doOperation(Object input) throws CoreException{
5 //使用工廠,不指定類加載器
6 IParser parser = ParserFactory.createParser("com.my.extension.parser.MyParserImpl");
7
8 Object model = parser.parse(input);
9 //TODO 拿到模型之后干其他活
10 }
11 }
疑惑了:第二種使用方式為什么沒問題,第三種怎么就有問題了?代碼實現中都是Class.forName(
"").newInstance();調用?。???
大致錯誤原因:
這個時候如果你只基本熟悉Eclipse的插件類加載機制(會去找所依賴的插件中的類型...),可能一時半會想不清楚。
Java的類加載基本原理中有一條:Class.forName()調用方式加載類型所默認使用的類加載器實例是加載當前類型的類加載器。對于在Extension插件Customer中調用Class.forName()使用的是加載Customer類型的類加載器,也就是Extension插件的唯一類加載器;對于在Host插件中的ParserFactory中調用Class.forName()則使用的加載ParserFactory類型的類加載器實例,也就是Host插件的唯一類加載器。你想讓Host插件類加載器去加載一個在Extension插件中定義的類型,注意Extension插件是依賴于Host插件的啊,根據Eclipse插件類記載規則,這種加載請求無法從Host插件類加載器委托到Extension類加載器(說明:使用擴展點機制可以,extension registry會幫你委托這一個請求,找到對應的BundleHost...)。如果你想讓Extension插件的類加載器去加載Host中定義的切export出來的類型,Extension插件的類加載器會把這個請求合理的委托給Host插件類加載器。
第四種:用一把工廠,指定類加載器。由于
com.my.extension.parser.MyParserImpl本身就和Customer定義在同一插件中,所以公用同一插件類加載器實例,兩者對于Extension插件來說都是local class,加載肯定沒問題。
1 package com.my.extension.model;
2
3 public class Customer {
4 public void doOperation(Object input) throws CoreException{
5 //使用工廠,指定類加載器
6 IParser parser = ParserFactory.createParser("com.my.extension.parser.MyParserImpl", this.getClass().getClassLoader());
7
8 Object model = parser.parse(input);
9 //TODO 拿到模型之后干其他活
10 }
11 }
以上是舉了一個小例子,僅僅是為了說明一個道理:
掌握Java類加載基本原理是掌握Eclipse插件類加載原理的基礎,不可逾越?。?!如果讓你在一個插件應用中寫一個較為負責的自定義類加載器,但靠背一點Eclipse類加載的規則,那更不夠了...
Java類加載基本原理還有很多,我博客中有隨筆(老早之前寫的,土了點,頭一段時間貼到了博客上了)。
本博客中的所有文章、隨筆除了標題中含有引用或者轉載字樣的,其他均為原創。轉載請注明出處,謝謝!