Javassist分析类依赖和动态代理实现

前言

Javassist作为一款强大的class编辑器,它能够读取class文件内容,class文件的常量池中包含了当前类所有需要交互的其他类。要获取当前类所有依赖的类,只需要先获取当前类直接依赖的类,再继续广度优先遍历直接依赖类依赖的类,最终遍历了整棵依赖树之后就能获取当前类依赖。对于动态代理其实也是通过在运行过程中动态新的代理类,代理类不但会增加额外的用户逻辑,还会调用被代理对象的对应方法,Javassist强大的类编辑功能能够很好的完成这个任务。

类依赖实现

首先需要定义当前类直接依赖的类有哪些,首先继承的父类很显然是当前类一定会依赖的,接下来是当前类实现的接口,接着是定义的字段值类型,最后就是方法传入参数类型、返回参数类型还有方法中使用到其他工具类。得到这些直接依赖的类之后,需要再获取这些直接依赖类直接依赖的类,我们发现这时候问题变成了上面的问题,只不过当前类变成了当前类依赖的其中一个类,递归调用就可以解决这种重复问题。

前面讲解Class文件格式的时候讨论过如何使用Javassist来获取父类、接口和方法中依赖的类,不过里面并不完全,还缺少实现接口类、方法签名里的类型,我们可以通过方法签名的分析得到使用到的类。

/**
 * 查看某个类引用的所有类
 * @param clazz
 * @param set
 * @throws NotFoundException
 */
private static void bfs(String clazz, Set<String> set) throws NotFoundException {
    CtClass ctClass = ClassPool.getDefault().get(clazz);
    // 当前类直接依赖的类
    Set<String> levelClasses = new TreeSet<>();
    ClassFile classFile = ctClass.getClassFile();

    /**
     * 遍历代码内使用的类,包含方法实现里使用的类,不包含方法签名里的类
     */
    for (String className : classFile.getConstPool().getClassNames()) {
        if (className.startsWith("[L")) {
            className = className.substring(2, className.length() - 1);
        } else if (className.startsWith("[")) {
            continue;
        }
        className = getClassName(className);
        addClassName(set, levelClasses, className);
    }

    /**
     * 获取父类
     */
    String superClass = classFile.getSuperclass();
    if (!"".equals(superClass) && superClass != null && !set.contains(superClass)) {
        levelClasses.add(superClass);
        set.add(superClass);
    }

    /**
     * 获取所有接口
     */
    String[] interfaces = classFile.getInterfaces();
    if (interfaces != null) {
        for (String face : interfaces) {
            String className = getClassName(face);
            addClassName(set, levelClasses, className);
        }
    }

    /**
     * 获取字段的类型
     */
    List<FieldInfo> fieldInfoList = classFile.getFields();
    if (fieldInfoList != null) {
        for (FieldInfo fieldInfo : fieldInfoList) {
            String descriptor = fieldInfo.getDescriptor();
            if (descriptor.startsWith("L") && descriptor.endsWith(";")) {
                String className = descriptor.substring(1, descriptor.length() - 1);
                className = getClassName(className);
                addClassName(set, levelClasses, className);
            }

            if (descriptor.startsWith("[L") && descriptor.endsWith(";")) {
                String className = descriptor.substring(2, descriptor.length() - 1);
                className = getClassName(className);
                addClassName(set, levelClasses, className);
            }
        }
    }

    /**
     * 获取方法声明的参数和返回值包含的所有类
     */
    List<MethodInfo> methodInfoList = classFile.getMethods();
    if (methodInfoList != null) {
        for (MethodInfo methodInfo : methodInfoList) {
            String descriptor = methodInfo.getDescriptor();
            extractClassNames(descriptor, set, levelClasses);
        }
    }

    /**
     * 对当前类直接依赖的类,继续查寻它们依赖的其他类
     */
    if (!levelClasses.isEmpty()) {
        for (String className : levelClasses) {
            bfs(className, set);
        }
    }
}

private static void addClassName(Set<String> set, Set<String> levelClasses, String className) {
    // 如果当前节点已经被访问过,不再将它添加到当前类的直接依赖中
    if (!set.contains(className)) {
        levelClasses.add(className);
        set.add(className);
    }
}

private static String getClassName(String className) {
    return className.replaceAll("/", ".");
}

private static void extractClassNames(String descriptor, Set<String> set, Set<String> levelClasses) {
    String reg = "(L.+?;)";
    Pattern pattern = Pattern.compile(reg);
    Matcher matcher = pattern.matcher(descriptor);
    while (matcher.find()) {
        String className = matcher.group();
        className = className.substring(1, className.length() - 1);
        className = getClassName(className);
        addClassName(set, levelClasses, className);
    }
}

上面的extractClassNames会根据方法的descriptor来获取方法签名中的类型,注意这里使用了勉强匹配符“+?“也就是说只要匹配到重L开始的第一个“;”就代表查找到一个类型,当所有匹配到被找到了,当前这个方法里所有传入参数和返回参数的类型都被查找到了。

动态代理实现

动态代理最主要的是理解生成的代理类内部会执行InvocationHandler的回调,而InvocationHandler内部又会调用被代理对象对应的方法,用户添加的额外逻辑是放在InvocationHandler的invoke方法里的,先看JDK的默认实现。

// 被代理接口
public interface IManager {
    public void add();
    public void remove();
}

// InvocationHandler实例
public static class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(IManager manager) {
        this.target = manager;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 添加计算执行时间的逻辑
        long startTime = System.currentTimeMillis();
        System.out.println("StartTime = " + startTime);
        // 调用实际的执行方法逻辑
        method.invoke(target, args);
        long endTime = System.currentTimeMillis();
        System.out.println("EndTime = " + endTime);
        System.out.println("Total Time = " + (endTime - startTime));
        return null;
    }
}

// 实际执行方法对象,也就是被代理对象
public static class BookManager implements IManager {

    @Override
    public void add() {
        System.out.println("Start add books");
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("finish add book");
    }

    @Override
    public void remove() {
        System.out.println("Start remove books");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("finish remove book");

    }

}

public static void main(String[] args) {
    IManager obj = (IManager) MyProxy.newProxyInstance(ProxyTest.class.getClassLoader(), new Class<?>[] {
        IManager.class
    }, new MyInvocationHandler(new BookManager()));

//      IManager obj = new ProxyClass(new MyInvocationHandler(new BookManager()));
    obj.add();
}

上面的JDK实际上是生成了一个代理对象,代理对象会调用InvocationHandler的invoke方法,这里写一个简单的代理对象来代表实际执行的逻辑。

public static class ProxyClass implements IManager {
    // InvocationHandler对象,包含统计时间逻辑和实际执行逻辑
    private InvocationHandler h;

    public ProxyClass(InvocationHandler h) {
        this.h = h;
    }

    @Override
    public void add() {
        try {
            // 获取要执行的method对象
            Method method = IManager.class.getDeclaredMethod("add");
            // 调用InvocationHandler的invoke方法
            h.invoke(this, method, null);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void remove() {
        try {
            Method method = IManager.class.getDeclaredMethod("remove");
            h.invoke(this, method, null);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

}

所以动态代理实际上就是生成ProxyClass这个类,这个类需要在运行是动态生成编译加载到内存中产生动态代理对象,用户调用动态代理对象的方法会被派发给InvocationHandler。现在通过Javassist来动态的生成一个ProxyClass类。

public static class MyProxy {
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) {
        Class<?> inter = interfaces[0];
        Method[] methods = inter.getDeclaredMethods();
        ClassPool pool = ClassPool.getDefault();

        // 生成ProxyClass对象
        CtClass proxy = pool.makeClass("com.test.ProxyClass");
        try {
            CtClass interfaceCt = pool.get(inter.getName());
            CtClass invocation = pool.get(InvocationHandler.class.getName());
            // 动态代理类实现了接口
            proxy.addInterface(interfaceCt);

            // 动态代理类包含InvocationHandler的字段h
            CtField h = new CtField(invocation, "h", proxy);
            h.setModifiers(Modifier.PRIVATE);
            proxy.addField(h);

            // 动态代理类包含一个包含InvocationHandler参数的构造函数
            CtConstructor constructor = new CtConstructor(new CtClass[] { invocation }, proxy);
            constructor.setBody("{$0.h = $1;}");
            proxy.addConstructor(constructor);

            // 为每个方法添加派发到InvocationHandler的执行逻辑
            for (Method method : methods) {
                String name = method.getName();
                String retType = method.getReturnType().getName();
                StringBuilder builder = new StringBuilder();
                builder.append("public ").append(retType).append(" ").append(name).append("() {");
                builder.append("try { ");
                builder.append("java.lang.reflect.Method method = ").append(inter.getName())
                    .append(".class.getDeclaredMethod(\"").append(name).append("\", null);");
                builder.append("h.invoke(this, method, null);");
                builder.append("} catch (Throwable e)");
                builder.append("{ throw new RuntimeException(e); } }");

//                  System.out.println(builder.toString());
                CtMethod m = CtNewMethod.make(builder.toString(), proxy);
                proxy.addMethod(m);
            }

            // 通过反射生成动态代理对象
            Class<?> clazz = proxy.toClass();
            Constructor<?> construct = clazz.getConstructor(InvocationHandler.class);
            return construct.newInstance(handler);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

}

猜你喜欢

转载自blog.csdn.net/xingzhong128/article/details/80931882
今日推荐