从外部类动态加载方法

时间:2016-09-01 17:27:00

标签: java reflection

我正在尝试从我的类Configuration中加载方法Customer.cypher和Customer.cypherCBC方法。客户类是从不同环境渲染的,因此很少有环境使用cypherCBC()和cypher()方法,而很少有环境只有cypher()方法。

现在我想检查cypherCBC是否在Customer类中,然后加载cypher()方法。我的功能到目前为止;

   try {
        Class<?> customerClass = Class.forName("com.myapp.impl.service.Customer");

        Object  obj = customerClass.newInstance();

        //here getting "NoSuchMethodException" exception
        Method methodCBC = customerClass.getDeclaredMethod("cypherCBC", String.class); //line - 7


         if(methodCBC.getName().equals("cypherCBC")){
            methodCBC.invoke(obj, new String(dbshPass));
            System.out.println("CYPHER_CBC: "
               + methodCBC.invoke(obj, new String(dbshPass)));
        }else{

            Method method = customerClass.getDeclaredMethod("cypher", String.class);
            method.invoke(obj, new String(dbshPass));
            System.out.println("CYPHER: " + method.invoke(obj, new String(dbshPass)));
        }

    }catch (Exception e){
        e.printStackTrace();
    }

在第7行获取错误。

  

NoSuchMethodException:   com.myapp.impl.service.Customer.cypherCBC(java.lang.String中)

这意味着对于特定的环境类,客户没有cypherCBC()方法,但理想情况下它应该是其他部分并执行cypher()方法。

Class<?> client = null;
    Object  obj = null;
        try{
            client = Class.forName("com.myapp.impl.service.Client");
            obj = client.newInstance();

        }catch (InstantiationException ex) {
            System.err.println("Not able to create Instance of Class");
        } catch (IllegalAccessException ex) {
            System.err.println("Not able to access Class");
        } catch (ClassNotFoundException ex) {
            System.err.println("Not able to find Class");
        }

        try {

            Method methodCBC = client.getDeclaredMethod("cypherCBC", String.class);
 System.out.println("CYPHER_CBC: " + methodCBC.invoke(obj, new String(dbshPass)));   
        }catch (NoSuchMethodException ex) {
            System.err.println("Not able to find Method on class");
            ex.printStackTrace();
        }  catch (Exception e){
            e.printStackTrace();
        }

3 个答案:

答案 0 :(得分:2)

这正是预期的结果:getDeclaredMethod()在没有符合您的规范的方法时抛出该异常。你想知道如果缺少必要的方法会引发异常吗?提示:下次更好地阅读javadoc。不要认为某些事情能做某事,而是要验证你的假设!

此外:再次阅读您的代码。它在做什么?你问“给我一个名为'foo'的方法”。然后,您的下一步是要求该方法“是您的名字'foo'”。所以即使没有阅读javadoc,也应该清楚你的逻辑存在缺陷。

作为解决方案,您可以自己实现非投掷查找,例如

private Method lookupCypher(Class<?> client, String methodName) {
  for (Method declaredMethod : client.getDeclardMethods()) {
   if (declaredMethod.getName().equals(methodName))  {
     Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
     if (parameterTypes.length == 1 && parameterTypes[0].equals(String.class)) {
        // so declaredMethod has the given name, and takes one string as argument!
        return declaredMethod;
     } 
   }
 // our search didn't reveal any matching method!
 return null;
 }

使用该帮助器方法,您可以将代码重写为:

Method toInvoke = lookupCypher(client, "cypherCBC");
if (toInvoke == null) {
  toInvoke = lookupCypher(client, "cypher");
}
toInvoke(obj, new String ...

或者,考虑到猎人的想法;一个更“喜欢”的版本:

interface CustomerCypherWrapper {
   void cypher(String phrase);
}

class NewCustomerWrapper() implements CustomerCypherWrapper {
   @Override
   void cypher(String phrase) {
     new Customer.cypherCBC(phrase);
  }
}

class oldCustomerWrapper() implements CustomerCypherWrapper {
   @Override
   void cypher(String phrase) {
     new Customer.cypher(phrase);
  }
}

您的客户端代码归结为:

CustomerCypherWrapper wrapper = 
 (lookupCypher(..., "cypherCBC") == null) 
 ? new NewCustomerWrapper() 
 : new OldCustomerWrapper();

wrapper.cypher();

[我希望你注意到我的版本A)更容易阅读和B)不再包含任何重复的代码。 ]

是的,查找方法的替代实现可能就像

private Method lookupCyper(Client<?>, String methodName) {
   try {
     return client.getDeclaredMethod(methodName, String.class);
   } catch ....
     and return null;
}
     ... return your public cypherCBC method

但这在Java中是一种“不常见的做法”。在Java中,我们要求许可;而不是原谅。与其他languages

不同

答案 1 :(得分:1)

如果您使用具有这两种方法的Customer类编译应用程序,您可以使用反射一次来检查cypherCBC方法是否在运行时可用,那么您可以保持该状态,您可以在不使用反射的情况下调用该方法< / p>

if(newVersion)
{
customer.cypherCBC(arg);
}
else
{
customer.cypher(arg);
}

但是要编写更好的应用程序,您应该使用两个版本基准。 即使这是一个小代码片段,您应该设置另一个模块来隐藏此Customer类及其交互,该模块应该有两个版本。但是您的主模块只有单个版本。现在,当您交付应用程序时,应根据目标环境的兼容性将产品打包为正确的版本基准。

答案 2 :(得分:0)

虽然反射有效(如其他答案中所述)。如果您可以控制Customer类,则可以尝试非反射方法。

interface CBCCypherable {
    public String cypherCBC(String pass);
}

您现在可以拥有两个版本的Customer类,一个实现CBCCypherable,另一个不实现Customer c = new Customer(); if (c instanceof CBCCypherable) { ((CBCCypherable)c).cypherCBC(pass); } else { c.cypher(pass); } 。当你打电话时,它看起来像这样:

.then(response => {
      response.text().then(text => {
        // handle response content
        })
      })

使用此解决方案可以获得更简单的代码,作为奖励,编译器将检查您是否使用了正确的方法名称和参数类型。与反射不同,那就是你的全部工作,你必须运行代码来查明是否有错误。

Ps:我不知道这只是示例代码,或者你真的在这里加密/散列密码,但通常认为推出自己的安全代码是个坏主意。