例子下載在此
最近搞了個小實驗,發(fā)現(xiàn)Eclipse 插件的類加載的一個問題。Eclipse使用Equinox實現(xiàn)OSGi的框架,可以在插件的配置中確定哪些類expose出去,哪些類不能為外部所見。我發(fā)現(xiàn)的問題是,可以通過ClassLoader繞過這個限制,在外部插件中加載到插件里那些不為外部所見的類,并且能夠創(chuàng)建類的實例,可以通過反射調(diào)用其方法(當(dāng)然,如果被加載的類實現(xiàn)了某些接口,也可以通過接口的引用直接調(diào)用相應(yīng)的方法)。
為了演示這個問題,先在eclipse中創(chuàng)建一個插件UtilityLibrary:

其中utilitylibrary.expose包中的類會暴露給外部,而utilitylibrary.hide包中的類不會暴露給外部。在MANIFEST.MF中增加這個設(shè)置:

VisiableClass和VisiableClass類的內(nèi)容很簡單:
其實主要就是打印出相應(yīng)的信息。類代碼幾乎是一樣的。
下面創(chuàng)建另一個插件UsePlugin,依賴并使用UtilityLibrary中的類。插件其實就是Eclipse自帶的Hello World程序,它會在eclipse 的toolbar上增加一個按鈕,點擊后會彈出一個MessageBox。好,MessageBox上顯示的就是從UtilityLibrary中類的方法的返回值。首先增加插件依賴關(guān)系:

在SampleAction中的Run方法里,如果直接使用InvisiableClass,插件完全找不到這個類,修改建議里面建議expose這個類:

當(dāng)然,使用VisiableClass是沒問題的。下面通過VisiableClass來將InvisiableClass拽出來,SampleAction類的源代碼如下,只要關(guān)心run方法就可以了:
在run方法里面,直接使用VisiableClass.class.getClassLoader().loadClass("utilitylibrary.hide.InvisiableClass");來加載本不應(yīng)該被外部所見的Invisiable類。因為在Eclipse中,每個插件使用一個ClassLoader,所以用來加載VisiableClass類的ClassLoader也同樣負(fù)責(zé)加載在同一個插件中的InvisiableClass類。這樣InvisiableClass就在插件外部被加載成功了。類加載成功后,剩下的事情就是順?biāo)浦哿耍瑒?chuàng)建個實例然后使用反射調(diào)用相應(yīng)的方法。
程序運行的時候,點擊toolbar上那個button,會彈出如下對話框:

程序運行也沒啥錯誤。
問題分析:
其實我覺得這個問題是很難繞過去的。對于同一個插件,因為內(nèi)部的類需要互相引用和互相使用,所以必須使用同一個類加載器來加載。所以,這個插件只要expose出來一個包,那么外部的插件就可以通過包中的任何一個類來得到加載這個插件中的類的類加載器,然后就可以通過reflect愛做啥做啥了。
換一個角度可能更好理解這個問題為什么難以繞過去。假設(shè)VisiableClass需要用到InvisiableClass,雖然InvisiableClass沒有暴露出來,但是在正常的使用VisiableClass的時候,需要先加載VisiableClass類,而加載VisiableClass的時候JVM就會隱式的加載InvisiableClass。這個過程和例子里現(xiàn)式的加載InvisiableClass沒啥本質(zhì)不同。也就是說,從ClassLoader的角度,很難判斷一個類的加載是正常的代碼還是為了突破bundle的訪問限制——它們都是在執(zhí)行run方法時發(fā)生的類加載行為。
或者是我有什么地方?jīng)]設(shè)置好?求解答。例子下載在此。
最近搞了個小實驗,發(fā)現(xiàn)Eclipse 插件的類加載的一個問題。Eclipse使用Equinox實現(xiàn)OSGi的框架,可以在插件的配置中確定哪些類expose出去,哪些類不能為外部所見。我發(fā)現(xiàn)的問題是,可以通過ClassLoader繞過這個限制,在外部插件中加載到插件里那些不為外部所見的類,并且能夠創(chuàng)建類的實例,可以通過反射調(diào)用其方法(當(dāng)然,如果被加載的類實現(xiàn)了某些接口,也可以通過接口的引用直接調(diào)用相應(yīng)的方法)。
為了演示這個問題,先在eclipse中創(chuàng)建一個插件UtilityLibrary:

其中utilitylibrary.expose包中的類會暴露給外部,而utilitylibrary.hide包中的類不會暴露給外部。在MANIFEST.MF中增加這個設(shè)置:

VisiableClass和VisiableClass類的內(nèi)容很簡單:
package utilitylibrary.expose;
public class VisiableClass {
public VisiableClass() {
System.out.println("This is VisiableClass");
}
public String getMessage() {
return "From VisiableClass:\r\n"
+ this.getClass().getClassLoader().toString() + "\t";
}
}
public class VisiableClass {
public VisiableClass() {
System.out.println("This is VisiableClass");
}
public String getMessage() {
return "From VisiableClass:\r\n"
+ this.getClass().getClassLoader().toString() + "\t";
}
}
package utilitylibrary.hide;
public class InvisiableClass {
public InvisiableClass() {
System.out.println("InvisiableClass");
}
public String getMessage() {
return "From InvisiableClass:\r\n"
+ this.getClass().getClassLoader().toString() + "\t";
}
}
public class InvisiableClass {
public InvisiableClass() {
System.out.println("InvisiableClass");
}
public String getMessage() {
return "From InvisiableClass:\r\n"
+ this.getClass().getClassLoader().toString() + "\t";
}
}
其實主要就是打印出相應(yīng)的信息。類代碼幾乎是一樣的。
下面創(chuàng)建另一個插件UsePlugin,依賴并使用UtilityLibrary中的類。插件其實就是Eclipse自帶的Hello World程序,它會在eclipse 的toolbar上增加一個按鈕,點擊后會彈出一個MessageBox。好,MessageBox上顯示的就是從UtilityLibrary中類的方法的返回值。首先增加插件依賴關(guān)系:

在SampleAction中的Run方法里,如果直接使用InvisiableClass,插件完全找不到這個類,修改建議里面建議expose這個類:

當(dāng)然,使用VisiableClass是沒問題的。下面通過VisiableClass來將InvisiableClass拽出來,SampleAction類的源代碼如下,只要關(guān)心run方法就可以了:
package useplugin.actions;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import utilitylibrary.expose.VisiableClass;
/**
* Our sample action implements workbench action delegate. The action proxy will
* be created by the workbench and shown in the UI. When the user tries to use
* the action, this delegate will be created and execution will be delegated to
* it.
*
* @see IWorkbenchWindowActionDelegate
*/
public class SampleAction implements IWorkbenchWindowActionDelegate {
private IWorkbenchWindow window;
/**
* The constructor.
*/
public SampleAction() {
}
/**
* The action has been activated. The argument of the method represents the
* 'real' action sitting in the workbench UI.
*
* @see IWorkbenchWindowActionDelegate#run
*/
public void run(IAction action) {
try {
Class<?> clazz = VisiableClass.class.getClassLoader().loadClass(
"utilitylibrary.hide.InvisiableClass");
Object obj = clazz.newInstance();
Method method = clazz.getMethod("getMessage");
Object ret = method.invoke(obj, new Object[] {});
System.out.println(ret);
MessageDialog.openInformation(window.getShell(), "UsePlugin", ret
.toString());
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Selection in the workbench has been changed. We can change the state of
* the 'real' action here if we want, but this can only happen after the
* delegate has been created.
*
* @see IWorkbenchWindowActionDelegate#selectionChanged
*/
public void selectionChanged(IAction action, ISelection selection) {
}
/**
* We can use this method to dispose of any system resources we previously
* allocated.
*
* @see IWorkbenchWindowActionDelegate#dispose
*/
public void dispose() {
}
/**
* We will cache window object in order to be able to provide parent shell
* for the message dialog.
*
* @see IWorkbenchWindowActionDelegate#init
*/
public void init(IWorkbenchWindow window) {
this.window = window;
}
}
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import utilitylibrary.expose.VisiableClass;
/**
* Our sample action implements workbench action delegate. The action proxy will
* be created by the workbench and shown in the UI. When the user tries to use
* the action, this delegate will be created and execution will be delegated to
* it.
*
* @see IWorkbenchWindowActionDelegate
*/
public class SampleAction implements IWorkbenchWindowActionDelegate {
private IWorkbenchWindow window;
/**
* The constructor.
*/
public SampleAction() {
}
/**
* The action has been activated. The argument of the method represents the
* 'real' action sitting in the workbench UI.
*
* @see IWorkbenchWindowActionDelegate#run
*/
public void run(IAction action) {
try {
Class<?> clazz = VisiableClass.class.getClassLoader().loadClass(
"utilitylibrary.hide.InvisiableClass");
Object obj = clazz.newInstance();
Method method = clazz.getMethod("getMessage");
Object ret = method.invoke(obj, new Object[] {});
System.out.println(ret);
MessageDialog.openInformation(window.getShell(), "UsePlugin", ret
.toString());
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Selection in the workbench has been changed. We can change the state of
* the 'real' action here if we want, but this can only happen after the
* delegate has been created.
*
* @see IWorkbenchWindowActionDelegate#selectionChanged
*/
public void selectionChanged(IAction action, ISelection selection) {
}
/**
* We can use this method to dispose of any system resources we previously
* allocated.
*
* @see IWorkbenchWindowActionDelegate#dispose
*/
public void dispose() {
}
/**
* We will cache window object in order to be able to provide parent shell
* for the message dialog.
*
* @see IWorkbenchWindowActionDelegate#init
*/
public void init(IWorkbenchWindow window) {
this.window = window;
}
}
在run方法里面,直接使用VisiableClass.class.getClassLoader().loadClass("utilitylibrary.hide.InvisiableClass");來加載本不應(yīng)該被外部所見的Invisiable類。因為在Eclipse中,每個插件使用一個ClassLoader,所以用來加載VisiableClass類的ClassLoader也同樣負(fù)責(zé)加載在同一個插件中的InvisiableClass類。這樣InvisiableClass就在插件外部被加載成功了。類加載成功后,剩下的事情就是順?biāo)浦哿耍瑒?chuàng)建個實例然后使用反射調(diào)用相應(yīng)的方法。
程序運行的時候,點擊toolbar上那個button,會彈出如下對話框:

程序運行也沒啥錯誤。
問題分析:
其實我覺得這個問題是很難繞過去的。對于同一個插件,因為內(nèi)部的類需要互相引用和互相使用,所以必須使用同一個類加載器來加載。所以,這個插件只要expose出來一個包,那么外部的插件就可以通過包中的任何一個類來得到加載這個插件中的類的類加載器,然后就可以通過reflect愛做啥做啥了。
換一個角度可能更好理解這個問題為什么難以繞過去。假設(shè)VisiableClass需要用到InvisiableClass,雖然InvisiableClass沒有暴露出來,但是在正常的使用VisiableClass的時候,需要先加載VisiableClass類,而加載VisiableClass的時候JVM就會隱式的加載InvisiableClass。這個過程和例子里現(xiàn)式的加載InvisiableClass沒啥本質(zhì)不同。也就是說,從ClassLoader的角度,很難判斷一個類的加載是正常的代碼還是為了突破bundle的訪問限制——它們都是在執(zhí)行run方法時發(fā)生的類加載行為。
或者是我有什么地方?jīng)]設(shè)置好?求解答。例子下載在此。
posted @ 2010-05-17 12:09 深夜兩點 閱讀(4806) | 評論 (8) | 編輯 收藏