【问题标题】:Pass method and args to a function in Java将方法和参数传递给 Java 中的函数
【发布时间】:2020-05-05 12:34:25
【问题描述】:

所以我做了简单的操作,在我的存储库中找到一个条目。如果条目不存在,则抛出异常。

@NotNull
public static User getUserFromUuid(UUID userUuid) {
    Optional<User> userOptional = userRepository.findByUserIdentifier(userUuid);
    if (!userOptional.isPresent()) {
        if (logger.isInfoEnabled()) logger.info(String.format("Unable to find user with uuid %s", userUuid.toString()));
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User Not Found");
    }
    return userOptional.get();
}
@NotNull
public static Group getGroupFromId(Long groupId) {
    Optional<Group> groupOptional = groupRepository.findById(groupId);
    if (!groupOptional.isPresent()) {
        if (logger.isInfoEnabled()) logger.info(String.format("Group with id %s does not exist", groupId));
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Group Not Found");
    }
    return groupOptional.get();
}

我意识到我最终会为我所有的 find 方法做很多次。他们中的大多数人将做一项非常相似的任务。

一种方法是用我的版本扩展CrudRepository,但我想通过 finds 为 al 实现这种模式。

另一种方法是传递类、方法、参数和错误消息以进行搜索。 Lambda 方法似乎是一种方法,但我无法理解如何将其应用于我的问题。

This 接近解决问题,但返回类型正在改变。我也会传递可变数量的参数。

我可以采取什么方法来做到这一点?

编辑:

我也想处理这个案子

Optional<GroupUser> groupUser = groupUserRepository.findByUserAndGroup(user, group

我最终可能会拥有多个查找参数。

python 中类似的东西是

def perform( fun, *args ):
    fun( *args )

def action1( args ):
    something

def action2( args ):
    something

perform( action1 )
perform( action2, p )
perform( action3, p, r )

【问题讨论】:

  • 您好,您是否会考虑使用 Spring AOP 方面,以便使用正确的切入点来包装您的存储库的方法返回 Optional?
  • @ThomasEscolan 我实际上不知道它是如何工作的。但我可以研究一下,看看它如何适用于此。任何帮助将不胜感激!
  • 这是一个关于使用 Spring baeldung.com/spring-aop 的面向方面编程的良好开端,您可以通过拦截 Spring bean 中的公共方法调用来编织一些行为

标签: java spring


【解决方案1】:

由于您的所有值都可能由字符串表示,因此您可以执行以下操作:

public static Object getAccountDetails(String primaryKey, Class<?> targetClass) {   
        Optional<?> result;

        switch(targetClass.getSimpleName())
        {
        case "Group":
             result = Test.dummyFind(primaryKey);
             break;
        case "User":
            result =  Test.dummyFind(primaryKey);
            break;
        default:
            throw new IllegalArgumentException("The provided class: "+targetClass.getCanonicalName()+" was not a valid class to be resolved by this method.");      
        }

        if(result.isPresent())
        {
            logger.info(String.format("Group with id %s does not exist", groupId));
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Group Not Found"); 
        }

        return result.get();
    }

据我所知,没有必要检查记录器是否启用了信息级别,因为这是由您的 logback 配置定义的。因此,如果没有记录信息,它将不会被记录。

函数的使用是这样的(类是随机的,因为我想让我的语法突出显示):

UserDataHandler data = (UserDataHandler) getAccountDetails("1234", UserDataHandler.class);

这与 Spring 的任何功能无关,因为我不使用它。 &lt;?&gt; 是通配符运算符。但是,由于您的结果是 Optional 的内容,因此您必须返回一个 Object,然后必须将其解析为相应的类型。

两种方式:要么使用这样的注册表:

public static Object getAccountDetails(Object primaryKey, Class<?> targetClass) {   

    Optional<?> result;

    // This map should be acquired from a Singleton where you register these classes once in @PostConstruct.
    Map<String, Method> methodMap = new TreeMap<>();
    try {
        methodMap.put("UserDataHandler", Test.class.getMethod("dummyFind"));
    } catch (NoSuchMethodException | SecurityException e) {}


    if(methodMap.containsKey(targetClass.getName()))
    {
        Method method = methodMap.get(targetClass.getName());
        try {
            result = (Optional<?>) method.invoke(primaryKey);
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            // do some more error handling
            return null;
        }
    }
    else throw new IllegalArgumentException("The provided class: "+targetClass.getCanonicalName()+" was not a valid class to be resolved by this method.");


    if(!result.isPresent())
    {
        logger.info(String.format("Group with id %s does not exist", groupId));
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Group Not Found"); 
    }

    return result.get();
}

或者更聪明的方法是始终以相同的模式命名方法,任何通过Class.forName(String)获取目标存储库:

public static Object getAccountDetails(Object primaryKey, Class<?> targetClass) {   

    Optional<?> result;

    Class<?> myRepository = Class.forName(targetClass.getSimpleName()+"Repository");

    String methodName = "findBy"+targetClass.getName();

    try
    {
        Method findMethod = targetClass.getMethod(methodName);
        result = (Optional<?>) findMethod.invoke(primaryKey);
    }
    catch (NoSuchMethodException e){throw new IllegalArgumentException("The method "+methodName+" couldn't be found in the repository");}
    catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {return null;}


    if(!result.isPresent())
    {
        logger.info(String.format("Group with id %s does not exist", groupId));
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Group Not Found"); 
    }

    return result.get();
}

对于这两种方法,您的 find 函数都必须将 Object 作为参数,该参数必须被强制转换才能使用它:

public static Optional<Long> dummyFind(Object primaryKey)
{
    long typedPrimaryKey = (long) primaryKey;
    return Optional.of(typedPrimaryKey);
}

但正如我两次考虑的那样,你想要的一切都已经存在:EntityManager.find(Class&lt;T&gt; entityClass,Object primaryKey)

【讨论】:

  • 目前就是这种情况,但我看到它正在扩展到更多查找和多种类型。我会有一个很大的开关。
  • 我添加了另外两个变体来避免开关/大小写。最后一个可能是最好的。
  • 我想这就是我想要的。非常感谢。只是另一个小问题。您认为这会对性能产生影响吗?因为很多这种情况会在运行时发生。
  • 我不会说这是一个巨大的影响,但肯定会有一个。尽管我鼓励您只使用 EntityManager 的 find 方法,只要您使用 JPA。这也更容易阅读,没有自编程的黑色反射魔法并且经过良好测试。据我所知,find() 方法也很快。
  • find 方法不允许传递多个参数。在您建议的解决方案中,调用方法需要类以及在哪里实例化操作,它无法为存储库获取该类。我被困在那部分
【解决方案2】:

您是否正在寻找类似的东西?

public static <T, ID>  T process(Class<T> cls, CrudRepository<T,ID> r, ID id, String errTemplate){
    Optional<T> groupOptional = r.findById(id);
    if (groupOptional.isEmpty()) {
        if (logger.isInfoEnabled()) logger.info(String.format(errTemplate, id));
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, cls.getName() + " Not Found");
    }
    return groupOptional.get();
}

【讨论】:

  • 我想要这样的东西,但是 crud 存储库方法也在改变。在这些情况下,我只使用一个 id 参数。我已经更新了我想要多个参数的问题。
【解决方案3】:

您可以创建通用方法,该方法接受任何类型的Optional 和用于日志消息的字符串。如果存在,它将返回对象,否则将异常

public <T> T returnIfPresent(Optional<T> optional, String id){

    if (!optional.isPresent()) {
        if (logger.isInfoEnabled()) logger.info(String.format("Group with id %s does not exist", id));
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Group Not Found");
    }
    return optional.get();
}

你可以从每个方法中调用这个方法

@NotNull
public static User getUserFromUuid(UUID userUuid) {

    Optional<User> userOptional = userRepository.findByUserIdentifier(userUuid);
    return returnIfPresent(userOptional, userUuid.toString());
 }

 @NotNull
 public static Group getGroupFromId(Long groupId) {
     Optional<Group> groupOptional = groupRepository.findById(groupId);

     return returnIfPresent(groupOptional, groupId.toString());
 }

我推荐的另一个建议是,将消息作为第二个参数,这样您就可以在原始方法中构建消息并传递它

public <T> T returnIfPresent(Optional<T> optional, String message){

    if (!optional.isPresent()) {
        if (logger.isInfoEnabled()) logger.info(message);
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, message);
    }
    return optional.get();
}

【讨论】:

  • 我想我喜欢它可以帮助我优雅地处理Optional&lt;t&gt;,但如果我不能为每个getSomethingFromSomething 提供一个函数,那就太好了。
  • 如果我错了,请纠正我,传递给 perform 方法的附加参数的目的只是为了记录,所以在这种情况下,在原始方法中构建日志消息,然后将该消息作为第二个传递参数@AkshatMalik
猜你喜欢
  • 2014-09-10
  • 2013-10-04
  • 2016-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多