誰動了你的jar包
哈,你想改變jar包某個類的運行方式?
或是因為業務需要,或是因為這個jar暫時不能滿足你你欲望...
或者只是for Fun!
但是你無法改變這個jar包,可能因為是公用的,可能因為產品的生成依賴于標準倉庫,或者僅僅是你不想用 "編譯一下你的java類,然后把你的.class替換進去" 這么...這么..這么...的方法,那你要怎么做?
例如:
有類 Feature
Java代碼
- public class Feature {
- private String content;
- public void show() {
- System.out.println(this.content);
- }
- }
及類 Function
Java代碼
- public class Function {
- private Feature f;
- public void show() {
- this.f.show();
- }
- }
測試類 Test,運行結果為 null . 而你想讓他輸出hello kitty
Java代碼
- public class Test {
- /**
- * @param args
- */
- public static void main(String[] args) {
- Function function = new Function();
- function.show();
- }
- }
***想到的方法是就是直接建一個同包同名類,在你的eclipse或者其它編譯環境下;然后把相關方法,改成自己想要的方法;如下:
Java代碼
- public class Feature {
- private String content;
- public Feature(){
- this.content = "hello kitty";
- }
- public void show() {
- System.out.println(this.content);
- }
- }
然后,你會發現,使用的是你的class吖,而不是jar包里的吖.
但是,如果你的程序也打成jar包,和原jar一起運跑,會是什么情況呢;
你可能發現,會輸出 空,如果運氣好,也可能 輸出 hello kitty ;
為啥呢? 因為當有同包同名類時,classLoader總會嘗試先加載到一個,而且加載到這個class文件后,后面就不會再加載;這個先加載到的類一般和classpath設置的先后有關;
在eclipse環境下,會先加載編輯器下的類,然后優先加載,先導入的類庫;
如果先加載到你的類,那么就會輸出 "hellokitty".
假如需要在生產環境指定加載你的類,
而且,
你無法預知客戶如何設置classPath的先后順序,那么,要怎么辦呢?
可否自己寫一個classLoader只加載目標類,而讓你的調用程序在此classLoader環境下運行?
Let us try try : 先寫出這個特別的classLoader
Java代碼
- public class HoneyLoader extends URLClassLoader {
- public HoneyLoader(URL[] urls, ClassLoader parent){
- super(urls, parent);
- }
- public synchronized Class> loadClass(String name) throws ClassNotFoundException {
- Class> c = findLoadedClass(name);
- if (c != null) {
- return c;
- }
- //先自己在指定位置(通過urls指定)找,找不到交給父類
- try {
- c = this.findClass(name);
- } catch (Exception e) {
- c = super.loadClass(name);
- }
- return c;
- }
- }
回到我們的測試類,修改如下
Java代碼
- public class Test {
- public static void main(String[] args) throws Exception {
- // 根據jar包名稱,獲取我們需要的jar包的名稱的url
- String jarName = "feature2.jar";
- URL url = null;
- ClassLoader loader = Thread.currentThread().getContextClassLoader();
- Enumeration
urls = loader.getResources( "Feature.class");- int i = 0;
- while (urls.hasMoreElements()) {
- url = urls.nextElement();
- i = url.getPath().indexOf(jarName);
- if (i > -1) {
- break;
- }
- }
- // 用honeyLoader啟動我們的運行環境
- ClassLoader myLoader = new HoneyLoader(new URL[] { new URL(url.getPath().substring(0, i) + jarName) }, loader);
- Object object = myLoader.loadClass("Feature").newInstance();
- object.getClass().getMethod("show").invoke(object);
- }
運行結果:
Java代碼
- hello kitty
#p#
用classLoader的方法,將建立一個小的運行機制,和業務代碼的相關性很低,冗余代碼多;
而且,新建的和原類相同的包名和類不便于維護;
有什么更好的方法么?
對于(一)中描述的需求,其實,我們只需改變下Feature的私有屬性content,是否可以通過反射來實現呢?
嘗試以下代碼:
Java代碼
- public class Test {
- // 獲取object 的屬性 fieldName
- public static Field getField(Object object, String fieldName) throws Exception {
- Field field = object.getClass().getDeclaredField(fieldName);
- return field;
- }
- public static void main(String[] args) throws Exception {
- Function function = new Function();
- // 獲取function的feature
- Field f_feature = getField(function, "f");
- // 通過feature 獲取 其屬性 content
- f_feature.setAccessible(true);
- Field f_function = getField(f_feature.get(function), "content");
- // 改變content的內容
- f_function.setAccessible(true);
- f_function.set(f_feature.get(function), "hello kitty");
- function.show();
- }
- }
執行,得到結果
Java代碼
- hello kitty
冗余代碼減少,目的更加明確了,但對于改變的業務代碼,任然不清晰;不容易維護;
通常,如果我們要得到有我們的特性的類,通常用繼承的方法,但是有時候,會發現,如果是你要調用的調用的調用的類,要改變一點動作,那你為了改調用的調用的調用,不得不繼承調用和調用的調用;
假如我們只改變目標類,只繼承目標類,結合反射的方式,改調用,是否可行呢?
例如,繼承Feature創建類MyFeature
Java代碼
- public class MyFeature extends Feature {
- private String mycontent;
- public MyFeature(){
- this.mycontent = "hello kitty";
- }
- public void show() {
- System.out.println(this.mycontent);
- }
- }
這樣我們改變的邏輯清晰很多,容易維護,我們再來修改下Test類
Java代碼
- public class Test {
- // 獲取object 的屬性 fieldName
- public static Field getField(Object object, String fieldName) throws Exception {
- Field field = object.getClass().getDeclaredField(fieldName);
- return field;
- }
- public static void main(String[] args) throws Exception {
- Function function = new Function();
- // 獲取function的feature
- Field f_feature = getField(function, "f");
- // 改變feature的內容
- f_feature.setAccessible(true);
- f_feature.set(function, new MyFeature());
- function.show();
- }
- }
此時,Test的邏輯也清晰很多,我們可以清楚的看到,我們需要改變哪個類
運行一下,看下結果

Java代碼
- hello kitty
原文鏈接:http://ilab.iteye.com/blog/1002629
【編輯推薦】