標(biāo)題說(shuō)明了一切,我想說(shuō)的就是如果你在做Eclipse插件開(kāi)發(fā),想真正搞清楚Eclipse插件類加載,那么Java基本類加載原理是基礎(chǔ),無(wú)法逾越!!!我會(huì)舉一個(gè)小例子來(lái)簡(jiǎn)單說(shuō)明一下
【情景】
例如我們?cè)诘讓幽K插件(就叫做Host)中做了一個(gè)解析器實(shí)現(xiàn)(什么解析器就別管了),對(duì)應(yīng)類型為IParser(com.my.host.parser.IParser),簡(jiǎn)單定義如下:
1 public interface IParser {
2 public Object parse(Object input) throws CoreException;
3 }
基本的原則告訴我們:如果一個(gè)實(shí)例的創(chuàng)建過(guò)程較為負(fù)責(zé),則因該把這種實(shí)例的創(chuàng)建和實(shí)例的使用解耦合。于是,我們?cè)贖ost插件中提供了一個(gè)如下的工廠類:
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, "創(chuàng)建工廠失敗:" + 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, "創(chuàng)建工廠失敗:" + clazz, e));
38 }
39 }
40 }
41
這里的簡(jiǎn)單工廠中創(chuàng)建方法返回的是IParser接口類型,目的就是為了保證可以創(chuàng)建子類型的靈活性,同時(shí)確保對(duì)外提供的接口是一致的,有助于客戶端基于接口編程(說(shuō)的有點(diǎn)上綱上線了~_~)
同時(shí)我們?cè)贖ost插件中,也提供了一個(gè)默認(rèn)的IParser實(shí)現(xiàn),就叫做DefaultParser吧。那到這里我們的Host插件共提供了如下類型:
com.my.host.parser.IParser 解析器接口定義
com.my.host.parser.ParserFactory 解析器工廠
com.my.host.parser.impl.DefaultParser 解析器接口默認(rèn)實(shí)現(xiàn)
下面我們?cè)贖ost插件的基礎(chǔ)之上建立了上層功能插件(就叫做Extension插件吧,Extension插件依賴于Host插件),提供了解析器實(shí)現(xiàn),對(duì)應(yīng)類型為com.my.extension.parser.MyParserImpl。我們?cè)贓xtension插件中想使用這個(gè)實(shí)現(xiàn),假設(shè)有如下幾種中創(chuàng)建方式:
第一種:直接創(chuàng)建實(shí)例,偏不用你Host中的工廠。 俗了點(diǎn),但是沒(méi)問(wèn)題。
1 package com.my.extension.model;
2
3 public class Customer {
4 public void doOperation(Object input) throws CoreException{
5 //直接創(chuàng)建實(shí)例的方式
6 IParser parser = new MyParserImpl();
7
8 Object model = parser.parse(input);
9 //TODO 拿到模型之后干其他活
10 }
11 }
第二種,直接使用Class.forName(),還是不用你Host中的工廠。也是俗了點(diǎn),但也沒(méi)問(wèn)題,為什么沒(méi)問(wèn)題,可能就沒(méi)有想清楚了~_~
1 package com.my.extension.model;
2
3 public class Customer {
4 public void doOperation(Object input) throws CoreException{
5 try {
6 //使用默認(rèn)的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 }
疑惑了:第二種使用方式為什么沒(méi)問(wèn)題,第三種怎么就有問(wèn)題了?代碼實(shí)現(xiàn)中都是Class.forName(
"").newInstance();調(diào)用啊???
大致錯(cuò)誤原因:
這個(gè)時(shí)候如果你只基本熟悉Eclipse的插件類加載機(jī)制(會(huì)去找所依賴的插件中的類型...),可能一時(shí)半會(huì)想不清楚。
Java的類加載基本原理中有一條:Class.forName()調(diào)用方式加載類型所默認(rèn)使用的類加載器實(shí)例是加載當(dāng)前類型的類加載器。對(duì)于在Extension插件Customer中調(diào)用Class.forName()使用的是加載Customer類型的類加載器,也就是Extension插件的唯一類加載器;對(duì)于在Host插件中的ParserFactory中調(diào)用Class.forName()則使用的加載ParserFactory類型的類加載器實(shí)例,也就是Host插件的唯一類加載器。你想讓Host插件類加載器去加載一個(gè)在Extension插件中定義的類型,注意Extension插件是依賴于Host插件的啊,根據(jù)Eclipse插件類記載規(guī)則,這種加載請(qǐng)求無(wú)法從Host插件類加載器委托到Extension類加載器(說(shuō)明:使用擴(kuò)展點(diǎn)機(jī)制可以,extension registry會(huì)幫你委托這一個(gè)請(qǐng)求,找到對(duì)應(yīng)的BundleHost...)。如果你想讓Extension插件的類加載器去加載Host中定義的切export出來(lái)的類型,Extension插件的類加載器會(huì)把這個(gè)請(qǐng)求合理的委托給Host插件類加載器。
第四種:用一把工廠,指定類加載器。由于
com.my.extension.parser.MyParserImpl本身就和Customer定義在同一插件中,所以公用同一插件類加載器實(shí)例,兩者對(duì)于Extension插件來(lái)說(shuō)都是local class,加載肯定沒(méi)問(wèn)題。
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 }
以上是舉了一個(gè)小例子,僅僅是為了說(shuō)明一個(gè)道理:
掌握J(rèn)ava類加載基本原理是掌握Eclipse插件類加載原理的基礎(chǔ),不可逾越!!!如果讓你在一個(gè)插件應(yīng)用中寫(xiě)一個(gè)較為負(fù)責(zé)的自定義類加載器,但靠背一點(diǎn)Eclipse類加載的規(guī)則,那更不夠了...
Java類加載基本原理還有很多,我博客中有隨筆(老早之前寫(xiě)的,土了點(diǎn),頭一段時(shí)間貼到了博客上了)。
本博客中的所有文章、隨筆除了標(biāo)題中含有引用或者轉(zhuǎn)載字樣的,其他均為原創(chuàng)。轉(zhuǎn)載請(qǐng)注明出處,謝謝!