Java-如何分析功能代码

时间:2018-07-02 08:37:08

标签: java reflection

我们正在使用mvc设计模式,其中所有数据都存储在map下。

我想遍历系统中的所有类,并为每个类检查该方法在地图上放置的内容以及该方法从地图中得到什么。

例如下一个代码:

private void myFunc()
{
Object obj = model.get("mykey");
Object obj2 = model.get("mykey2");
.....
model.put("mykey3", "aaa");
}

我想知道在此函数中,我们有2个键:mykey和mykey2,还有1个键:mykey3

我该如何使用代码。

谢谢。

2 个答案:

答案 0 :(得分:2)

您将此标签标记为“反射”,但这将不起作用。反射仅允许您检查“签名”。您可以使用它来识别类的方法以及方法的参数。

这绝对不会帮助您识别每种方法的做什么

为了找到答案,您需要解析Java源代码端或字节码类。如:在编写代码中读取该内容,并理解其中的“足够”内容以找到此类位置。这是非常的挑战性工作。当然,通过执行以下操作很容易绕过所有此类“扫描程序”代码:

List<String> keysToUpdate = Arrays.asList("key1", "key2");
for (String key : keysToUpdate) { 
  ... does something about each key

B。您将如何编写可靠的代码以找到其关键所在? 当您找到该代码时,现在想像一下该列表没有在此处实例化,而是在很远的地方作为参数传递?当您找到解决方法时,现在考虑使用 reflection 来获取模型对象并在其上调用方法的代码。看到?对于您写下的任何“扫描仪”,都会有方法使其失败。

因此,真正的答案是您已经走错了兔子洞:

您应该从不写过:

Object obj = model.get("mykey");

但类似

 Object obj = model.get(SOME_CONSTANT_FOR_KEY_X);

含义:没有控制这种东西的好方法。您可以做的 best 是确保所有键都是常量,来自中心位置。因为这样您至少可以进入,对于该常数列表中的每个键,您可以让您的IDE告诉他们其用法。

答案 1 :(得分:0)

注释

  • 我认为您的情况非常复杂,因此在代码库中进行简单或高级的文本搜索不会对您有帮助。
  • 这是一个黑客,不是通用解决方案,仅用于测试和诊断目的。
  • 要使用此技巧,您必须能够更改代码,并在测试/诊断时将实际模型替换为代理实例。如果您无法执行此操作,则必须使用更高级的技巧,即使用BCEL,ASM等进行字节码设计。
  • 动态代理在代码性能上有缺点,因此不是生产模式的理想选择。
  • 使用地图存储模型不是一个好主意。相反,应该使用定义良好的类型系统,即Java类。

此类问题的一般设计模式是 proxy 。您的实际模型与调用者之间的中间对象,可以拦截呼叫,收集统计信息甚至干扰原始呼叫。代理模型最终将所有内容发送到实际模型。

一个明显的代理是将实际模型简单地包装到另一个地图中,例如

public class MapProxy<K, V> implements Map<K, V> {

   public MapProxy(final Map<K, V> actual) {
   }

   // implement ALL methods and redirect them to the actual model

}

现在,反射并不能直接为您提供帮助,而是可以使用动态代理Dynamic Proxy Classes)(例如

)更快地实现代理。
    @SuppressWarnings("unchecked")
    private Map<String, Object> proxy(final Map<String, Object> model) {

        final InvocationHandler handler = new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // Collect usage stats or intervene
                return method.invoke(model, args);
            }
        };

        return (Map<String, Object>) Proxy.newProxyInstance(Map.class.getClassLoader(),
            new Class<?>[] { Map.class }, handler);
    }

注意:无论哪种情况,至少在测试期间,您都需要能够用代理模型替换 actual模型

使用另一种技巧,您可以找出谁调用了模型的哪种方法。只需访问Thread.currentThread().getStackTrace()并检索适当的元素即可。

现在将所有片段放在一起:

InvocationLog.java

public final class InvocationLog {

    private Method method;
    private Object[] arguments;
    private StackTraceElement caller;

    public InvocationLog(Method method, Object[] arguments, StackTraceElement caller) {
        this.method = method;
        this.arguments = arguments;
        this.caller = caller;
    }

    public Method getMethod() { return this.method; }

    public Object[] getArguments() { return this.arguments; }

    public StackTraceElement getCaller() { return this.caller; }

    @Override
    public String toString() {
        return String.format("%s (%s): %s", 
            method == null ? "<init>" : method.getName(), 
                arguments == null ? "" : Arrays.toString(arguments), 
                    caller == null ? "" : caller.toString());
    }

}

ModelWatch.java

public final class ModelWatch  {

    private final Map<String, Object> modelProxy;
    private final List<InvocationLog> logs = new ArrayList<>();

    public ModelWatch(final Map<String, Object> model) {
        modelProxy = proxy(model);
    }

    @SuppressWarnings("unchecked")
    private Map<String, Object> proxy(final Map<String, Object> model) {

        final InvocationHandler handler = new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                log(method, args, Thread.currentThread().getStackTrace());
                return method.invoke(model, args);
            }
        };

        return (Map<String, Object>) Proxy.newProxyInstance(Map.class.getClassLoader(),
            new Class<?>[] { Map.class }, handler);
    }

    private void log(Method method, Object[] arguments, StackTraceElement[] stack) {
        logs.add(new InvocationLog(method, arguments, stack[3]));
        // 0: Thread.getStackTrace
        // 1: InvocationHandler.invoke
        // 2: <Proxy>
        // 3: <Caller>
    }

    public Map<String, Object> getModelProxy() { return modelProxy; }

    public List<InvocationLog> getLogs() { return logs; }

}

要使用它:

private Map<String, Object> actualModel = new HashMap<String, Object>();
private ModelWatch modelWatch = new ModelWatch(model);
private Map<String, Object> model = modelWatch.getModelProxy(); 

// Calls to model ...

modelWatch.getLogs() // Retrieve model activity