Java编程的动态性(author Dennis M. Sosnoski )学习——学习笔记(1)

来源:互联网 发布:中国出口武器 知乎 编辑:程序博客网 时间:2024/05/20 08:24

类加载器

装入到JVM的类由类装入器控制。JVM中构建了引导程序类装入器。由引导程序对类进行验证。
同时应用程序可以自定义类装载器(派生自java.lang.ClassLoader)。每个构造好的类某种意义上由类装载器所“拥有”。类装入器通常保留它们所装入类的映射,从而当再次请求某个类时,能通过名称找到该类。
类装载器为树状结构。树根是引导程序装载器。在类装载器处理实例请求时,会递归检查父类。这意味着类装载器中的类对其后代均可见,并且当多个类装载器可以装在某个类时,最上端的类装载器是实际装入器。
在J2EE框架中,每个j2ee程序都有自己独立的类装入器,
Tomcat类装入器
这里写图片描述
其中, Common 类装入器从 Tomcat 安装的某个特定目录的 JAR 文件进行装入,旨在用于在服务器和所有 Web 应用程序之间共享代码。Catalina 装入器用于装入 Tomcat 自己的类,而 Shared 装入器用于装入 Web 应用程序之间共享的类。最后,每个 Web 应用程序有自己的装入器用于其私有类。

引入反射

1.基于类的反射

Class[] types = new Class[]{};//传入构造器的参数Constructor cons = Person.class.getConstructor(types);//按照参数寻找构造器Object[] a = new Object[]{"a","b",1};//实际传入参数的值Person p = cons.newInstance(a);//构造

缺省调用:

Person.class.getConstructor().newInstance();

2.通过反射增加字段
Field用来获取字段存储的位置
getField()和getDeclaredField()不同在于第二个可以访问私有变量。

//获取值Field field = obj.getClass().getDeclaredField(name);Object i = field.get(obj);//设定值field.set(obj,value);

3.反射获取方法
getMethod/getDeclaredMethod
命名方法的方式是驼峰法

Method method  = obj.getClass().getMethod(methodName,types);//types可以缺省,为方法参数类型Object result = method.invoke(obj,object[]);//object[]为方法参数,可以缺省

4.反射数组
通过反射来扩展数组

public Object growArray(Object array, int size) {    Class type = array.getClass().getComponentType();//获取数组中数据的类型    Object grown = Array.newInstance(type, size);//创建新的数组    System.arraycopy(array, 0, grown, 0,        Math.min(Array.getLength(array), size));//调整数组的大小    return grown;}

5.反射的安全性
Constructor,Field,Method均扩展了java.lang.reflect.AccessibleObject实例。调用setAccessible()方法,可以启动或关闭对一个实例的接入检测。

file = class.getDeclaredField("a");//a是私有变量file.get(obj);//抛出异常//如果关闭检测filed.setAccessible(true);file.get(obj);//正常

特别的,在关闭检测的情况下,如果运行时添加JVM参数 -Djava.security.manager 以实现安全性管理器,它将再次失败,除非您定义了 ReflectSecurity 类的许可权限。
6.反射对性能的影响
反射获取防方法和字段均在直接获取时间的700倍以上,获取对象相对较短,在12倍。

使用Javassist进行类型转换

1.概念:javassist是使用javassist.ClassPool类来跟踪和控制所操作的类。与JVM类装载器不同的是,它不是装载,而是通过Javassist API作为数据使用。
2.常用的方法:
ClassPool:获取指定路径中的类

CtClass ct = ClassPool.getDefault().get(Object)//获取默认路径中的类ct.getDeclaredMethod();//方法

字段、方法和构造函数分别由 javassist.CtField、javassist.CtMethod 和 javassist.CtConstructor 的实例表示。这些类定义了修改由它们所表示的对象的所有方法的方法,包括方法或者构造函数中的实际字节码内容。
3.解决思路
·获取方法
·复制一个新的方法,把旧的方法名改为:name+”implnname+($$);\n”为执行之前的函数体。
·由ctclass写入。
需要计时的方法

public class StringBuilder{//该方法是对一个string进行反复构造来延长执行时间,需要添加计时功能,注释内是希望达到的效果    private String buildString(int length) {    // long start = System.currentTimeMillis();        String result = "";        for (int i = 0; i < length; i++) {            result += (char)(i%26 + 'a');        }     //System.out.println("Call to buildString took " + (System.currentTimeMillis()-start) + " ms.");        return result;    }    public static void main(String[] argv) {        StringBuilder inst = new StringBuilder();        for (int i = 0; i < argv.length; i++) {            String result = inst.buildString(Integer.parseInt(argv[i]));            System.out.println("Constructed string of length " +                result.length());        }    }}

添加计时:

public class JassistTiming {    public static void main(String[] argv) {        if (argv.length == 2) {//argv[0]表示类,argv[1]表示方法名            try {                CtClass clas = ClassPool.getDefault().get(argv[0]);                if (clas == null) {                    System.err.println("Class " + argv[0] + " not found");                } else {                    addTiming(clas, argv[1]);                    clas.writeFile();//更新.class文件                    System.out.println("Added timing to method " +                        argv[0] + "." + argv[1]);                  }            } catch (CannotCompileException ex) {                ex.printStackTrace();            } catch (NotFoundException ex) {                ex.printStackTrace();            } catch (IOException ex) {                ex.printStackTrace();            }        } else {            System.out.println("Usage: JassistTiming class method-name");        }    }    private static void addTiming(CtClass clas, String mname)        throws NotFoundException, CannotCompileException {        CtMethod mold = clas.getDeclaredMethod(mname);        //把原本的方法重命名为func+$impl,新建一个方法,把它命名为原本的func        String nname = mname+"$impl";        mold.setName(nname);        CtMethod mnew = CtNewMethod.copy(mold, mname, clas, null);        //type是原函数的返回值的类型        String type = mold.getReturnType().getName();        StringBuffer body = new StringBuffer();//待添加的函数主体        body.append("{\nlong start = System.currentTimeMillis();\n");//开始计时        if (!"void".equals(type)) {//如果无返回值            body.append(type + " result = ");        }        body.append(nname + "($$);\n");//调用$impl方法        //结束计时        body.append("System.out.println(\"Call to method " + mname +            " took \" +\n (System.currentTimeMillis()-start) + " +            "\" ms.\");\n");        if (!"void".equals(type)) {            body.append("return result;\n");//如果返回值为空,则不处理        }        body.append("}");        mnew.setBody(body.toString());        clas.addMethod(mnew);        System.out.println("Interceptor method body:");        System.out.println(body.toString());    }}

在经过jassistTiming修改后,原本stringBuilder中的方法实际变成了

private String buildString$impl(int length) {        String result = "";        for (int i = 0; i < length; i++) {            result += (char)(i%26 + 'a');        }        return result;    }    private String buildString(int length) {        long start = System.currentTimeMillis();        String result = buildString$impl(length);        System.out.println("Call to buildString took " +            (System.currentTimeMillis()-start) + " ms.");        return result;    }

4.使用Jassist带来的风险
由于jassist采用比较宽松的编译时代码检查,所以可能带来问题。比如将long赋值给int,又或者把string存储到int中。
但同时,javassist带来的aop这种新的设计模式

0 0