从外部类动态加载方法



我正在尝试加载方法Customer。密码和客户。cypherCBC方法从我的类配置。客户类是从不同的环境中呈现的,所以很少有环境具有cypherCBC()和cypher()方法,很少有环境仅具有cypher()方法。

现在我想检查如果cypherCBC如果没有在客户类然后加载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(以)

这意味着对于特定的环境类Customer没有cypherCBC()方法,但理想情况下,它应该出现在else部分并执行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();
        }

这正是所期望的:当不存在满足您的规范的方法时,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 ...

或者,带着猎人的想法;一个更像"OO"的版本:

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中,我们请求许可;而不是原谅。不像其他语言

如果你用一个有两个方法的Customer类来编译应用程序,你可以使用反射一次来检查cypherCBC方法是否在运行时可用,然后你可以保持该状态,你可以调用该方法而不使用反射

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

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

尽管反射是有效的(正如在其他答案中解释的那样)。如果您可以控制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);
}

使用这种解决方案得到的代码要简单得多,并且作为奖励,编译器将检查您是否使用了正确的方法名和参数类型。不像反射,这是你的全部工作,你必须运行代码来发现是否有问题。

注。

我不知道这只是示例代码还是你真的在加密/散列密码,但通常认为使用自己的安全代码是一个坏主意。

相关内容

  • 没有找到相关文章

最新更新