誰(shuí)動(dòng)了你的jar包
哈,你想改變jar包某個(gè)類的運(yùn)行方式?
或是因?yàn)闃I(yè)務(wù)需要,或是因?yàn)檫@個(gè)jar暫時(shí)不能滿足你你欲望...
或者只是for Fun!
但是你無(wú)法改變這個(gè)jar包,可能因?yàn)槭枪玫?可能因?yàn)楫a(chǎn)品的生成依賴于標(biāo)準(zhǔn)倉(cāng)庫(kù),或者僅僅是你不想用 "編譯一下你的java類,然后把你的.class替換進(jìn)去" 這么...這么..這么...的方法,那你要怎么做?
例如:
有類 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();
- }
- }
測(cè)試類 Test,運(yùn)行結(jié)果為 null . 而你想讓他輸出hello kitty
Java代碼
- public class Test {
- /**
- * @param args
- */
- public static void main(String[] args) {
- Function function = new Function();
- function.show();
- }
- }
***想到的方法是就是直接建一個(gè)同包同名類,在你的eclipse或者其它編譯環(huán)境下;然后把相關(guān)方法,改成自己想要的方法;如下:
Java代碼
- public class Feature {
- private String content;
- public Feature(){
- this.content = "hello kitty";
- }
- public void show() {
- System.out.println(this.content);
- }
- }
然后,你會(huì)發(fā)現(xiàn),使用的是你的class吖,而不是jar包里的吖.
但是,如果你的程序也打成jar包,和原jar一起運(yùn)跑,會(huì)是什么情況呢;
你可能發(fā)現(xiàn),會(huì)輸出 空,如果運(yùn)氣好,也可能 輸出 hello kitty ;
為啥呢? 因?yàn)楫?dāng)有同包同名類時(shí),classLoader總會(huì)嘗試先加載到一個(gè),而且加載到這個(gè)class文件后,后面就不會(huì)再加載;這個(gè)先加載到的類一般和classpath設(shè)置的先后有關(guān);
在eclipse環(huán)境下,會(huì)先加載編輯器下的類,然后優(yōu)先加載,先導(dǎo)入的類庫(kù);
如果先加載到你的類,那么就會(huì)輸出 "hellokitty".
假如需要在生產(chǎn)環(huán)境指定加載你的類,
而且,
你無(wú)法預(yù)知客戶如何設(shè)置classPath的先后順序,那么,要怎么辦呢?
可否自己寫一個(gè)classLoader只加載目標(biāo)類,而讓你的調(diào)用程序在此classLoader環(huán)境下運(yùn)行?
Let us try try : 先寫出這個(gè)特別的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;
- }
- }
回到我們的測(cè)試類,修改如下
Java代碼
- public class Test {
- public static void main(String[] args) throws Exception {
- // 根據(jù)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啟動(dòng)我們的運(yùn)行環(huán)境
- 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);
- }
運(yùn)行結(jié)果:
Java代碼
- hello kitty
#p#
用classLoader的方法,將建立一個(gè)小的運(yùn)行機(jī)制,和業(yè)務(wù)代碼的相關(guān)性很低,冗余代碼多;
而且,新建的和原類相同的包名和類不便于維護(hù);
有什么更好的方法么?
對(duì)于(一)中描述的需求,其實(shí),我們只需改變下Feature的私有屬性content,是否可以通過反射來(lái)實(shí)現(xiàn)呢?
嘗試以下代碼:
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的內(nèi)容
- f_function.setAccessible(true);
- f_function.set(f_feature.get(function), "hello kitty");
- function.show();
- }
- }
執(zhí)行,得到結(jié)果
Java代碼
- hello kitty
冗余代碼減少,目的更加明確了,但對(duì)于改變的業(yè)務(wù)代碼,任然不清晰;不容易維護(hù);
通常,如果我們要得到有我們的特性的類,通常用繼承的方法,但是有時(shí)候,會(huì)發(fā)現(xiàn),如果是你要調(diào)用的調(diào)用的調(diào)用的類,要改變一點(diǎn)動(dòng)作,那你為了改調(diào)用的調(diào)用的調(diào)用,不得不繼承調(diào)用和調(diào)用的調(diào)用;
假如我們只改變目標(biāo)類,只繼承目標(biāo)類,結(jié)合反射的方式,改調(diào)用,是否可行呢?
例如,繼承Feature創(chuàng)建類MyFeature
Java代碼
- public class MyFeature extends Feature {
- private String mycontent;
- public MyFeature(){
- this.mycontent = "hello kitty";
- }
- public void show() {
- System.out.println(this.mycontent);
- }
- }
這樣我們改變的邏輯清晰很多,容易維護(hù),我們?cè)賮?lái)修改下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的內(nèi)容
- f_feature.setAccessible(true);
- f_feature.set(function, new MyFeature());
- function.show();
- }
- }
此時(shí),Test的邏輯也清晰很多,我們可以清楚的看到,我們需要改變哪個(gè)類
運(yùn)行一下,看下結(jié)果

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