将方法和参数传递给Java中的一个函数。

huangapple go评论57阅读模式
英文:

Pass method and args to a function in Java

问题

所以我执行了在我的存储库中查找条目的简单操作。如果条目不存在,就抛出异常。

@NotNull
public static User getUserFromUuid(UUID userUuid) {
    Optional<User> userOptional = userRepository.findByUserIdentifier(userUuid);
    if (!userOptional.isPresent()) {
        if (logger.isInfoEnabled()) logger.info(String.format("无法找到 UUID 为 %s 的用户", userUuid.toString()));
        throw new ResponseStatusException(HttpStatus.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("不存在 ID 为 %s 的群组", groupId));
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "未找到群组");
    }
    return groupOptional.get();
}

我意识到我将不得不为所有我的查找方法重复执行此操作。而且其中大多数方法将执行非常相似的任务。

一种方法是扩展 CrudRepository 为我的版本,但我想为所有查找方法实现这种模式。

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

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

有没有办法可以解决这个问题呢?

编辑:

我还想处理这种情况:

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

在这里,我可能会有多个查找参数。

在 Python 中类似的操作是:

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

def action1(args):
    # 做些什么

def action2(args):
    # 做些什么

perform(action1)
perform(action2, p)
perform(action3, p, r)
英文:

So I do simple operation of finding an entry in my repository. If the entry is not present, the throw an exception.

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

I realize I would end up doing this many times for all my find methods. And most of them will be doing a very similar task.

One way is to extend the CrudRepository with my version, but I want to implement this pattern for al by finds.

Another way would be to pass the class, method, parameters, and the error message to search with. Lambda method seems to be the way, but I was unable to understand how I would be able to apply that to my problem.

This comes close to solving the problem, but the return type is changing. I would be passing a variable number of arguments as well.

Is there any approach I can take to do this?

EDIT:

I would also like to handle this case

Optional&lt;GroupUser&gt; groupUser = groupUserRepository.findByUserAndGroup(user, group

Where I could end up having more than one find parameters.

Something similar in python would be

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

def action1( args ):
    something

def action2( args ):
    something

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

答案1

得分: 1

你是否在寻找类似这样的内容?

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();
}

注意:代码部分未进行实际翻译。

英文:

Are you looking for something like that?

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

答案2

得分: 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("提供的类:" + targetClass.getCanonicalName() + " 不是此方法可解析的有效类。");
    }

    if(result.isPresent()) {
        logger.info(String.format("ID为 %s 的群组不存在", groupId));
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "未找到群组");
    }

    return result.get();
}

据我所知,无需检查日志记录器是否启用了 info 级别,因为这由您的 logback 配置定义。因此,如果没有针对 info 的日志记录,它就不会被记录。

该函数的使用方式可能如下(类是随机的,因为我想要进行语法高亮):

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

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

有两种方法:要么使用类似下面的注册表:

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

    Optional<?> result;

    // 此映射应该从一个 Singleton 中获取,您可以在 @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) {
            // 做更多的错误处理
            return null;
        }
    } else {
        throw new IllegalArgumentException("提供的类:" + targetClass.getCanonicalName() + " 不是此方法可解析的有效类。");
    }

    if(!result.isPresent()) {
        logger.info(String.format("ID为 %s 的群组不存在", groupId));
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "未找到群组");
    }

    return result.get();
}

或者,一个更智能的方法就是始终按照相同的模式命名方法,然后通过 Class.forName(String) 获取目标 repository:

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("在 repository 中找不到方法:" + methodName);
    } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
        return null;
    }

    if(!result.isPresent()) {
        logger.info(String.format("ID为 %s 的群组不存在", groupId));
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "未找到群组");
    }

    return result.get();
}

对于这两种方法,您的查找函数必须以 Object 作为参数,然后必须进行类型转换才能使用它:

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

但是在我思考了两次之后,我发现您想要的已经存在:EntityManager.find(Class<T> entityClass, Object primaryKey)

英文:

Since all your values might be represented by a String you could do the following:

public static Object getAccountDetails(String primaryKey, Class&lt;?&gt; targetClass) {	
		Optional&lt;?&gt; result;
		
		switch(targetClass.getSimpleName())
		{
		case &quot;Group&quot;:
			 result = Test.dummyFind(primaryKey);
			 break;
		case &quot;User&quot;:
			result =  Test.dummyFind(primaryKey);
			break;
		default:
			throw new IllegalArgumentException(&quot;The provided class: &quot;+targetClass.getCanonicalName()+&quot; was not a valid class to be resolved by this method.&quot;);		
		}
		
		if(result.isPresent())
		{
	        logger.info(String.format(&quot;Group with id %s does not exist&quot;, groupId));
	        throw new ResponseStatusException(HttpStatus.NOT_FOUND, &quot;Group Not Found&quot;);	
		}

	    return result.get();
	}

As far as I know, there is no need to check if the logger has the info level enabled, since this is defined by your logback configuration. So if there's no logging for info, it won't get logged.

The use of the function would be something like that (Classes are random since I wanted to have my syntax highlighting):

UserDataHandler data = (UserDataHandler) getAccountDetails(&quot;1234&quot;, UserDataHandler.class);

This is regardless of any functionality of Spring since I don't work with it.
The &lt;?&gt; is a wildcard operator. Since your result however is the content of an Optional, you have to return an Object which then has to be parsed to the according type.

Two ways: Either you use a registry like that:

public static Object getAccountDetails(Object primaryKey, Class&lt;?&gt; targetClass) {	
	
	Optional&lt;?&gt; result;
	
	// This map should be acquired from a Singleton where you register these classes once in @PostConstruct.
	Map&lt;String, Method&gt; methodMap = new TreeMap&lt;&gt;();
	try {
		methodMap.put(&quot;UserDataHandler&quot;, Test.class.getMethod(&quot;dummyFind&quot;));
	} catch (NoSuchMethodException | SecurityException e) {}
	
	
	if(methodMap.containsKey(targetClass.getName()))
	{
		Method method = methodMap.get(targetClass.getName());
		try {
			result = (Optional&lt;?&gt;) method.invoke(primaryKey);
		} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
			// do some more error handling
			return null;
		}
	}
	else throw new IllegalArgumentException(&quot;The provided class: &quot;+targetClass.getCanonicalName()+&quot; was not a valid class to be resolved by this method.&quot;);
	
	
	if(!result.isPresent())
	{
        logger.info(String.format(&quot;Group with id %s does not exist&quot;, groupId));
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, &quot;Group Not Found&quot;);	
	}

    return result.get();
}

or a even smarter method would be just to always name the methods in the same pattern any acquire the target repository by Class.forName(String) :

public static Object getAccountDetails(Object primaryKey, Class&lt;?&gt; targetClass) {	
	
	Optional&lt;?&gt; result;
	
	Class&lt;?&gt; myRepository = Class.forName(targetClass.getSimpleName()+&quot;Repository&quot;);
	
	String methodName = &quot;findBy&quot;+targetClass.getName();
	
	try
	{
		Method findMethod = targetClass.getMethod(methodName);
		result = (Optional&lt;?&gt;) findMethod.invoke(primaryKey);
	}
	catch (NoSuchMethodException e){throw new IllegalArgumentException(&quot;The method &quot;+methodName+&quot; couldn&#39;t be found in the repository&quot;);}
	catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {return null;}

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

    return result.get();
}

For both methods, your find function has to take an Object as argument which has to be casted in order to use it:

public static Optional&lt;Long&gt; dummyFind(Object primaryKey)
{
	long typedPrimaryKey = (long) primaryKey;
	return Optional.of(typedPrimaryKey);
}

But as I thought about this twice, everything you want already exists: EntityManager.find(Class&lt;T&gt; entityClass,Object primaryKey)

答案3

得分: 1

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

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

    if (!optional.isPresent()) {
        if (logger.isInfoEnabled()) logger.info(String.format("ID为 %s 的组不存在", id));
        throw new ResponseStatusException(HttpStatus.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();
}
英文:

You can create generic method that accepts Optional of any type and string for log message. It will returns the object if present, or else it will exception

public &lt;T&gt; T returnIfPresent(Optional&lt;T&gt; optional, String id){
	
	if (!optional.isPresent()) {
        if (logger.isInfoEnabled()) logger.info(String.format(&quot;Group with id %s does not exist&quot;, id));
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, &quot;Group Not Found&quot;);
    }
    return optional.get();
}

And you can call this method from every method

@NotNull
public static User getUserFromUuid(UUID userUuid) {

    Optional&lt;User&gt; userOptional = userRepository.findByUserIdentifier(userUuid);
    return returnIfPresent(userOptional, userUuid.toString());
 }

 @NotNull
 public static Group getGroupFromId(Long groupId) {
     Optional&lt;Group&gt; groupOptional = groupRepository.findById(groupId);

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

The another suggestion i would recommend is, having message as second parameter so you can build the message in original method and pass it through

public &lt;T&gt; T returnIfPresent(Optional&lt;T&gt; optional, String message){
	
	if (!optional.isPresent()) {
        if (logger.isInfoEnabled()) logger.info(message);
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, message);
    }
    return optional.get();
}

huangapple
  • 本文由 发表于 2020年5月5日 20:34:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/61613276.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定