哈,你想改变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
【编辑推荐】