Repository: apache/dubbo (Java)
Task type: Refactoring

Issue:
A question about configuring priority judgment, such as isAsync, isOneway
在dubbo的设计原则中, 配置优先级, 方法级优先，接口级次之，全局配置再次之, Java代码中的配置优先级大于初始化时的配置

In the design principles of dubbo, configuration priority, method first, interface second, global configuration at last, and configuration priority in Java code is higher than that in initialization.
  
举个例子:

For example:

```java
    public static boolean isOneway(URL url, Invocation inv) {
        boolean isOneway;
        if (Boolean.FALSE.toString().equals(inv.getAttachment(Constants.RETURN_KEY))) {
            isOneway = true;
        } else {
            isOneway = !url.getMethodParameter(getMethodName(inv), Constants.RETURN_KEY, true);
        }
        return isOneway;
    }
```

在判断是否`oneway`调用时, 先判断Java代码的配置, 然后再根据url中的方法级别配置判断

When judging whether oneway is called, first determine the configuration of the Java code, and then judge according to the method level configuration in the url.

  

但是我在实际的使用中, 某个方法配置的默认值就是`oneway`调用, 需要在少数情况下设置为同步调用

那么在上述逻辑中, 我无法将一个已经设置为`oneway`调用的方法(RETURN_KEY, fasle) 再设置为同步调用(RETURN_KEY, true), 因为在else分支中是从url中判断方法级别的RETURN_KEY, 而在调用之前无法修改url上的参数

But in my actual use, the default value of a method configuration is oneway call, which needs to be set to synchronous call in a few cases.

So in the above logic, I can't set a method (RETURN_KEY, fasle) that has been set to oneway call to synchronous call (RETURN_KEY, true), because in the else branch, the method level RETURN_KEY is determined from the url, but in unable to modify parameters on url before calling

  

因此我认为这里的判断优先级应该是如下所示比较合理: 

Therefore, I think the priority of judgment here should be reasonable as follows:

```java
    /**
     * {@link com.alibaba.dubbo.rpc.support.RpcUtils#isOneway(URL, Invocation)}
     */
    public static boolean isOneway(URL url, Invocation inv) {
        boolean isOneway;
        String config;
        if ((config = inv.getAttachment(getMethodName(inv) + Constants.HIDE_KEY_PREFIX + Constants.RETURN_KEY)) != null) {
            isOneway = !Boolean.valueOf(config);
        } else if ((config = inv.getAttachment(Constants.RETURN_KEY)) != null) {
            isOneway = !Boolean.valueOf(config);
        } else {
            isOneway = !url.getMethodParameter(getMethodName(inv), Constants.RETURN_KEY, true);
        }
        return isOneway;
    }    
```


  
类似的在`isAsync`判断中,

Similar in the isAsync judgment,

```java
    /**
     * {@link com.alibaba.dubbo.rpc.support.RpcUtils#isAsync(URL, Invocation)}
     */
    public static boolean isAsync(URL url, Invocation inv) {
        boolean isAsync;
        String config;
        if ((config = inv.getAttachment(getMethodName(inv) + Constants.HIDE_KEY_PREFIX + Constants.ASYNC_KEY)) != null) {
            isAsync = Boolean.valueOf(config);
        } else if ((config = inv.getAttachment(Constants.ASYNC_KEY)) != null) {
            isAsync = Boolean.valueOf(config);
        } else if ((config = url.getMethodParameter(getMethodName(inv), Constants.ASYNC_KEY)) != null) {
            isAsync = Boolean.valueOf(config);
        } else {
            isAsync = url.getParameter(Constants.ASYNC_KEY, false);
        }
        return isAsync;
    }    
```


Discussion:

Solution patch:
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/RpcUtils.java
@@ -34,6 +34,7 @@
 
 import static org.apache.dubbo.common.constants.CommonConstants.$INVOKE;
 import static org.apache.dubbo.common.constants.CommonConstants.$INVOKE_ASYNC;
+import static org.apache.dubbo.common.constants.CommonConstants.DOT_SEPARATOR;
 import static org.apache.dubbo.common.constants.CommonConstants.GENERIC_PARAMETER_DESC;
 import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_ATTACHMENT_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
@@ -165,10 +166,15 @@ public static boolean isAsync(URL url, Invocation inv) {
             }
         }
 
-        if (Boolean.TRUE.toString().equals(inv.getAttachment(ASYNC_KEY))) {
-            isAsync = true;
+        String config;
+        if ((config = inv.getAttachment(getMethodName(inv) + DOT_SEPARATOR + ASYNC_KEY)) != null) {
+            isAsync = Boolean.valueOf(config);
+        } else if ((config = inv.getAttachment(ASYNC_KEY)) != null) {
+            isAsync = Boolean.valueOf(config);
+        } else if ((config = url.getMethodParameter(getMethodName(inv), ASYNC_KEY)) != null) {
+            isAsync = Boolean.valueOf(config);
         } else {
-            isAsync = url.getMethodParameter(getMethodName(inv), ASYNC_KEY, false);
+            isAsync = url.getParameter(ASYNC_KEY, false);
         }
         return isAsync;
     }
@@ -216,8 +222,11 @@ public static InvokeMode getInvokeMode(URL url, Invocation inv) {
 
     public static boolean isOneway(URL url, Invocation inv) {
         boolean isOneway;
-        if (Boolean.FALSE.toString().equals(inv.getAttachment(RETURN_KEY))) {
-            isOneway = true;
+        String config;
+        if ((config = inv.getAttachment(getMethodName(inv) + DOT_SEPARATOR + RETURN_KEY)) != null) {
+            isOneway = !Boolean.valueOf(config);
+        } else if ((config = inv.getAttachment(RETURN_KEY)) != null) {
+            isOneway = !Boolean.valueOf(config);
         } else {
             isOneway = !url.getMethodParameter(getMethodName(inv), RETURN_KEY, true);
         }


Test patch:
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java
@@ -18,6 +18,7 @@
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.InvokeMode;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.RpcInvocation;
 
@@ -32,7 +33,9 @@
 import java.util.List;
 import java.util.Map;
 
+import static org.apache.dubbo.rpc.Constants.ASYNC_KEY;
 import static org.apache.dubbo.rpc.Constants.AUTO_ATTACH_INVOCATIONID_KEY;
+import static org.apache.dubbo.rpc.Constants.RETURN_KEY;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
@@ -254,6 +257,98 @@ public void testGetParameterTypes() {
         Assertions.assertEquals(String.class, parameterTypes5[2]);
     }
 
+    @Test
+    public void testIsOneway() {
+        URL url1 = URL.valueOf("dubbo://localhost/?test.return=false");
+        Invocation inv1 = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
+        inv1.setAttachment("test." + RETURN_KEY, Boolean.TRUE.toString());
+        Assertions.assertFalse(RpcUtils.isOneway(url1, inv1));
+
+        URL url2 = URL.valueOf("dubbo://localhost/?test.return=false");
+        Invocation inv2 = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
+        inv2.setAttachment(RETURN_KEY, Boolean.TRUE.toString());
+        Assertions.assertFalse(RpcUtils.isOneway(url2, inv2));
+
+        URL url3 = URL.valueOf("dubbo://localhost/?test");
+        Invocation inv3 = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
+        inv3.setAttachment(RETURN_KEY, Boolean.FALSE.toString());
+        Assertions.assertTrue(RpcUtils.isOneway(url3, inv3));
+
+        URL url4 = URL.valueOf("dubbo://localhost/?test.return=false");
+        Invocation inv4 = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
+        Assertions.assertTrue(RpcUtils.isOneway(url4, inv4));
+
+        URL url5 = URL.valueOf("dubbo://localhost/?test");
+        Invocation inv5 = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
+        Assertions.assertFalse(RpcUtils.isOneway(url5, inv5));
+
+        URL url6 = URL.valueOf("dubbo://localhost/?test");
+        Invocation inv6 = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
+        inv6.setAttachment("test." + RETURN_KEY, Boolean.FALSE.toString());
+        Assertions.assertTrue(RpcUtils.isOneway(url6, inv6));
+
+        URL url7 = URL.valueOf("dubbo://localhost/?test");
+        Invocation inv7 = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
+        inv7.setAttachment("testB." + RETURN_KEY, Boolean.FALSE.toString());
+        Assertions.assertFalse(RpcUtils.isOneway(url7, inv7));
+
+        URL url8 = URL.valueOf("dubbo://localhost/?test");
+        Invocation inv8 = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
+        inv8.setAttachment(RETURN_KEY, Boolean.FALSE.toString());
+        inv8.setAttachment("test." + RETURN_KEY, Boolean.TRUE.toString());
+        Assertions.assertFalse(RpcUtils.isOneway(url8, inv8));
+    }
+
+    @Test
+    public void testIsAsync() {
+        URL url1 = URL.valueOf("dubbo://localhost/?test.async=true");
+        Invocation inv1 = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
+        inv1.setAttachment("test." + ASYNC_KEY, Boolean.FALSE.toString());
+        Assertions.assertFalse(RpcUtils.isAsync(url1, inv1));
+
+        URL url2 = URL.valueOf("dubbo://localhost/?test.async=true");
+        Invocation inv2 = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
+        inv2.setAttachment(ASYNC_KEY, Boolean.FALSE.toString());
+        Assertions.assertFalse(RpcUtils.isAsync(url2, inv2));
+
+        URL url3 = URL.valueOf("dubbo://localhost/?test");
+        Invocation inv3 = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
+        inv3.setAttachment(ASYNC_KEY, Boolean.TRUE.toString());
+        Assertions.assertTrue(RpcUtils.isAsync(url3, inv3));
+
+        URL url4 = URL.valueOf("dubbo://localhost/?test.async=true");
+        Invocation inv4 = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
+        Assertions.assertTrue(RpcUtils.isAsync(url4, inv4));
+
+        URL url5 = URL.valueOf("dubbo://localhost/?test");
+        Invocation inv5 = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
+        Assertions.assertFalse(RpcUtils.isAsync(url5, inv5));
+
+        URL url6 = URL.valueOf("dubbo://localhost/?test");
+        Invocation inv6 = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
+        inv6.setAttachment("test." + ASYNC_KEY, Boolean.TRUE.toString());
+        Assertions.assertTrue(RpcUtils.isAsync(url6, inv6));
+
+        URL url7 = URL.valueOf("dubbo://localhost/?test.async=true&async=false");
+        Invocation inv7 = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
+        Assertions.assertTrue(RpcUtils.isAsync(url7, inv7));
+
+        URL url8 = URL.valueOf("dubbo://localhost/?test.async=false&async=true");
+        Invocation inv8 = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
+        Assertions.assertFalse(RpcUtils.isAsync(url8, inv8));
+
+        URL url9 = URL.valueOf("dubbo://localhost/?test.async=true");
+        Invocation inv9 = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
+        inv9.setAttachment("testB." + ASYNC_KEY, Boolean.FALSE.toString());
+        Assertions.assertTrue(RpcUtils.isAsync(url9, inv9));
+
+        URL url10 = URL.valueOf("dubbo://localhost/?test.async=true");
+        Invocation inv10 = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
+        ((RpcInvocation) inv10).setInvokeMode(InvokeMode.ASYNC);
+        Assertions.assertTrue(RpcUtils.isAsync(url10, inv9));
+    }
+
+
     @ParameterizedTest
     @CsvSource({
             "echo",