Java 从外部类动态加载方法

Java 从外部类动态加载方法,java,reflection,Java,Reflection,我正在尝试从类配置中加载Customer.cypher和Customer.cypherCBC方法。Customer类是从不同的环境进行渲染的,因此很少有环境使用cypherCBC()和cypher()方法,也很少有环境仅使用cypher()方法 现在我想检查cypherCBC是否存在于Customer类中,然后加载cypher()方法。我的职能是到目前为止 try { Class<?> customerClass = Class.forName("com.mya

我正在尝试从类配置中加载Customer.cypher和Customer.cypherCBC方法。Customer类是从不同的环境进行渲染的,因此很少有环境使用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();
    }
试试看{
Class customerClass=Class.forName(“com.myapp.impl.service.Customer”);
Object obj=customerClass.newInstance();
//这里获取“NoSuchMethodException”异常
方法methodCBC=customerClass.getDeclaredMethod(“cypherCBC”,String.class);//第7行
if(methodCBC.getName().equals(“cypherCBC”)){
调用(obj,新字符串(dbshPass));
System.out.println(“CYPHER\u CBC:”
+调用(obj,新字符串(dbshPass));
}否则{
方法Method=customerClass.getDeclaredMethod(“cypher”,String.class);
invoke(obj,新字符串(dbshPass));
System.out.println(“CYPHER:+method.invoke(obj,新字符串(dbshPass)));
}
}捕获(例外e){
e、 printStackTrace();
}
在第7行得到一个错误

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

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

Class客户端=null;
objectobj=null;
试一试{
client=Class.forName(“com.myapp.impl.service.client”);
obj=client.newInstance();
}catch(实例化异常){
System.err.println(“无法创建类的实例”);
}捕获(非法访问例外){
System.err.println(“无法访问类”);
}捕获(ClassNotFoundException ex){
System.err.println(“无法找到类”);
}
试一试{
方法methodCBC=client.getDeclaredMethod(“cypherCBC”,String.class);
System.out.println(“CYPHER_CBC:+methodCBC.invoke(obj,新字符串(dbshPass)));
}catch(NoSuchMethodException-ex){
System.err.println(“无法在类上找到方法”);
例如printStackTrace();
}捕获(例外e){
e、 printStackTrace();
}

这正是我们所期望的:当不存在符合您的规范的方法时抛出该异常。您想知道如果缺少所需的方法,它会抛出异常吗?提示:下次最好阅读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;
 }
或者,考虑到亨特的想法;一个更“类似OO”的版本:

您的客户端代码可以归结为:

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
private方法lookupCyper(客户端,字符串方法名){
试一试{
返回client.getDeclaredMethod(methodName,String.class);
}抓住。。。。
并返回null;
}
... 返回您的公共cypherCBC方法

但这在Java中是一种“不常见的做法”。在Java中,我们请求许可;而不是宽恕。与其他方法不同,这正是我们所期望的:当不存在满足您的规范的方法时抛出该异常。您想知道如果缺少所需的方法,它会抛出异常吗?提示:下次最好阅读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;
 }
或者,考虑到亨特的想法;一个更“类似OO”的版本:

您的客户端代码可以归结为:

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
private方法lookupCyper(客户端,字符串方法名){
试一试{
返回client.getDeclaredMethod(methodName,String.class);
}抓住。。。。
并返回null;
}
... 返回您的公共cypherCBC方法

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

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

if(newVersion)
{
customer.cypherCBC(arg);
}
else
{
customer.cypher(arg);
}
但是为了编写更好的应用程序,您应该使用两个版本基线。
即使这是一个小代码片段,您也应该设置另一个模块来隐藏这个客户类及其交互,该模块应该有两个版本。但您的主模块只有一个版本。现在,当您交付应用程序时,应根据与目标环境的兼容性,使用正确的版本基线打包产品。

如果您使用同时具有两种方法的客户类编译应用程序,您可以使用反射一次来检查cypherCBC方法是否可用
interface CBCCypherable {
    public String cypherCBC(String pass);
}
Customer c = new Customer();
if (c instanceof CBCCypherable) {
   ((CBCCypherable)c).cypherCBC(pass);
} else {
   c.cypher(pass);
}