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

类加载器

装入到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这种新的设计模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值