谁动了你的jar包

开发 后端
哈,你想改变jar包某个类的运行方式?或是因为业务需要,或是因为这个jar暂时不能满足你你欲望...或者只是for Fun!但是你无法改变这个jar包,可能因为是公用的,可能因为产品的生成依赖于标准仓库,或者仅仅是你不想用 "编译一下你的java类,然后把你的.class替换进去" 这么...这么..这么...的方法,那你要怎么做?

哈,你想改变jar包某个类的运行方式?

或是因为业务需要,或是因为这个jar暂时不能满足你你欲望...

或者只是for Fun!

但是你无法改变这个jar包,可能因为是公用的,可能因为产品的生成依赖于标准仓库,或者仅仅是你不想用 "编译一下你的java类,然后把你的.class替换进去" 这么...这么..这么...的方法,那你要怎么做?

例如:

有类 Feature

Java代码

  1. public class Feature {     
  2.     
  3.     private String content;     
  4.     
  5.     public void show() {     
  6.         System.out.println(this.content);     
  7.     }     
  8. }    

 

及类 Function

Java代码

  1. public class Function {     
  2.     
  3.     private Feature f;     
  4.     
  5.     public void show() {     
  6.         this.f.show();     
  7.     }     
  8.          
  9. }   

 

测试类 Test,运行结果为 null . 而你想让他输出hello kitty

Java代码

  1. public class Test {     
  2.     
  3.     /**    
  4.      * @param args    
  5.      */    
  6.     public static void main(String[] args) {     
  7.         Function function = new Function();     
  8.         function.show();     
  9.     }     
  10.     
  11. }  

 

***想到的方法是就是直接建一个同包同名类,在你的eclipse或者其它编译环境下;然后把相关方法,改成自己想要的方法;如下:

Java代码

  1. public class Feature {     
  2.     
  3.     private String content;     
  4.     
  5.     public Feature(){     
  6.         this.content = "hello kitty";     
  7.     }     
  8.     
  9.     public void show() {     
  10.         System.out.println(this.content);     
  11.     }     
  12. }   

 

然后,你会发现,使用的是你的class吖,而不是jar包里的吖.

但是,如果你的程序也打成jar包,和原jar一起运跑,会是什么情况呢;

你可能发现,会输出 空,如果运气好,也可能 输出 hello kitty ;

为啥呢? 因为当有同包同名类时,classLoader总会尝试先加载到一个,而且加载到这个class文件后,后面就不会再加载;这个先加载到的类一般和classpath设置的先后有关;

在eclipse环境下,会先加载编辑器下的类,然后优先加载,先导入的类库;

如果先加载到你的类,那么就会输出 "hellokitty".

假如需要在生产环境指定加载你的类,

而且,

你无法预知客户如何设置classPath的先后顺序,那么,要怎么办呢?

可否自己写一个classLoader只加载目标类,而让你的调用程序在此classLoader环境下运行?

Let us try try : 先写出这个特别的classLoader

Java代码

  1. public class HoneyLoader extends URLClassLoader {     
  2.     
  3.     public HoneyLoader(URL[] urls, ClassLoader parent){     
  4.         super(urls, parent);     
  5.     }     
  6.     
  7.     public synchronized Class loadClass(String name) throws ClassNotFoundException {     
  8.         Class c = findLoadedClass(name);     
  9.         if (c != null) {     
  10.             return c;     
  11.         }     
  12. //先自己在指定位置(通过urls指定)找,找不到交给父类     
  13.         try {     
  14.             c = this.findClass(name);     
  15.         } catch (Exception e) {     
  16.             c = super.loadClass(name);     
  17.         }     
  18.         return c;     
  19.     }     
  20. }   

 

回到我们的测试类,修改如下

Java代码

  1. public class Test {     
  2.     
  3.     public static void main(String[] args) throws Exception {     
  4.         // 根据jar包名称,获取我们需要的jar包的名称的url     
  5.         String jarName = "feature2.jar";     
  6.         URL url = null;     
  7.     
  8.         ClassLoader loader = Thread.currentThread().getContextClassLoader();     
  9.         Enumeration urls = loader.getResources("Feature.class");     
  10.         int i = 0;     
  11.         while (urls.hasMoreElements()) {     
  12.             url = urls.nextElement();     
  13.             i = url.getPath().indexOf(jarName);     
  14.             if (i > -1) {     
  15.                 break;     
  16.             }     
  17.         }     
  18.     
  19.         // 用honeyLoader启动我们的运行环境     
  20.         ClassLoader myLoader = new HoneyLoader(new URL[] { new URL(url.getPath().substring(0, i) + jarName) }, loader);     
  21.         Object object = myLoader.loadClass("Feature").newInstance();     
  22.         object.getClass().getMethod("show").invoke(object);     
  23.     
  24.     }    

 

运行结果:

Java代码

  1. hello kitty  

 

#p#

用classLoader的方法,将建立一个小的运行机制,和业务代码的相关性很低,冗余代码多;

而且,新建的和原类相同的包名和类不便于维护;

有什么更好的方法么?

对于(一)中描述的需求,其实,我们只需改变下Feature的私有属性content,是否可以通过反射来实现呢?

尝试以下代码:

Java代码

  1. public class Test {     
  2.     
  3.     // 获取object 的属性 fieldName     
  4.     public static Field getField(Object object, String fieldName) throws Exception {     
  5.         Field field = object.getClass().getDeclaredField(fieldName);     
  6.         return field;     
  7.     }     
  8.     
  9.     public static void main(String[] args) throws Exception {     
  10.         Function function = new Function();     
  11.         // 获取function的feature     
  12.         Field f_feature = getField(function, "f");     
  13.     
  14.         // 通过feature 获取 其属性 content     
  15.         f_feature.setAccessible(true);     
  16.         Field f_function = getField(f_feature.get(function), "content");     
  17.     
  18.         // 改变content的内容     
  19.         f_function.setAccessible(true);     
  20.         f_function.set(f_feature.get(function), "hello kitty");     
  21.     
  22.         function.show();     
  23.     }     
  24. }    

 

执行,得到结果

Java代码

  1. hello kitty   

 

冗余代码减少,目的更加明确了,但对于改变的业务代码,任然不清晰;不容易维护;

通常,如果我们要得到有我们的特性的类,通常用继承的方法,但是有时候,会发现,如果是你要调用的调用的调用的类,要改变一点动作,那你为了改调用的调用的调用,不得不继承调用和调用的调用;

假如我们只改变目标类,只继承目标类,结合反射的方式,改调用,是否可行呢?

例如,继承Feature创建类MyFeature

Java代码

  1. public class MyFeature extends Feature {     
  2.     
  3.     private String mycontent;     
  4.     
  5.     public MyFeature(){     
  6.         this.mycontent = "hello kitty";     
  7.     }     
  8.     
  9.     public void show() {     
  10.         System.out.println(this.mycontent);     
  11.     }     
  12. }  

 

这样我们改变的逻辑清晰很多,容易维护,我们再来修改下Test类

Java代码

  1. public class Test {     
  2.     
  3.     // 获取object 的属性 fieldName     
  4.     public static Field getField(Object object, String fieldName) throws Exception {     
  5.         Field field = object.getClass().getDeclaredField(fieldName);     
  6.         return field;     
  7.     }     
  8.     
  9.     public static void main(String[] args) throws Exception {     
  10.         Function function = new Function();     
  11.         // 获取function的feature     
  12.         Field f_feature = getField(function, "f");     
  13.     
  14.         // 改变feature的内容     
  15.         f_feature.setAccessible(true);     
  16.         f_feature.set(function, new MyFeature());     
  17.     
  18.         function.show();     
  19.     }     
  20. }    

 

此时,Test的逻辑也清晰很多,我们可以清楚的看到,我们需要改变哪个类

运行一下,看下结果

 

Java代码

  1. hello kitty 

原文链接:http://ilab.iteye.com/blog/1002629

【编辑推荐】

  1. 高手Java核心技术学习笔记
  2. 如何使用 JavaScript XSLT 处理 XML 文件
  3. JSP结合XML+XSLT将输出转换HTML
  4. Java通过JNI调用C语言的方法
  5. JAVA环境变量的设置
责任编辑:金贺 来源: ITEYE博客
相关推荐

2017-02-14 14:23:52

大数据春晚

2017-07-14 09:13:53

2015-06-05 15:47:47

2014-06-30 16:19:43

eHR管理软件

2010-05-20 09:29:14

谷歌微软云计算

2020-01-10 09:06:10

Activity系统 通信

2012-12-12 09:56:40

EC2AWSAmazon

2015-10-09 11:02:02

2010-08-26 15:34:12

2016-10-19 11:00:26

2015-04-17 10:30:13

2020-03-24 14:57:05

戴尔

2015-09-25 11:35:56

2023-12-13 10:36:38

Long算法代码

2012-03-28 13:02:40

2021-09-01 20:27:34

数据安全法数据安全信息安全

2021-04-26 10:24:52

Linux 开发操作系统

2021-04-19 07:35:01

Linuxhistory命令

2021-01-08 09:35:41

LinuxHistory命令

2011-01-06 13:14:40

Android短信误发
点赞
收藏

51CTO技术栈公众号