diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java
index c0c819cade..ba0f29a2f5 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java
@@ -212,6 +212,8 @@ public interface LoggerCodeConstants {
String PROXY_TIMEOUT_RESPONSE = "3-7";
+ String PROXY_FAILED = "3-8";
+
// protocol module
String PROTOCOL_UNSUPPORTED = "4-1";
diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml
index 8c7458fc40..0128f7852b 100644
--- a/dubbo-dependencies-bom/pom.xml
+++ b/dubbo-dependencies-bom/pom.xml
@@ -93,6 +93,7 @@
5.2.20.RELEASE
3.28.0-GA
+ 1.12.19
3.2.5.Final
4.1.72.Final
2.1.5
@@ -213,6 +214,11 @@
javassist
${javassist_version}
+
+ net.bytebuddy
+ byte-buddy
+ ${bytebuddy.version}
+
org.jboss.netty
netty
diff --git a/dubbo-rpc/dubbo-rpc-api/pom.xml b/dubbo-rpc/dubbo-rpc-api/pom.xml
index e92329b7ed..c2fa751743 100644
--- a/dubbo-rpc/dubbo-rpc-api/pom.xml
+++ b/dubbo-rpc/dubbo-rpc-api/pom.xml
@@ -45,6 +45,10 @@
dubbo-remoting-api
${project.parent.version}
+
+ net.bytebuddy
+ byte-buddy
+
com.alibaba
hessian-lite
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractFallbackJdkProxyFactory.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractFallbackJdkProxyFactory.java
new file mode 100644
index 0000000000..e48d12ea78
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractFallbackJdkProxyFactory.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.proxy;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.proxy.jdk.JdkProxyFactory;
+
+import java.util.Arrays;
+
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROXY_FAILED;
+
+public abstract class AbstractFallbackJdkProxyFactory extends AbstractProxyFactory {
+
+ protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(this.getClass());
+
+ private final JdkProxyFactory jdkProxyFactory = new JdkProxyFactory();
+
+ @Override
+ public Invoker getInvoker(T proxy, Class type, URL url) throws RpcException {
+ try {
+ return doGetInvoker(proxy, type, url);
+ } catch (Throwable throwable) {
+ // try fall back to JDK proxy factory
+ String factoryName = getClass().getSimpleName();
+ try {
+ Invoker invoker = jdkProxyFactory.getInvoker(proxy, type, url);
+ logger.error(PROXY_FAILED, "", "", "Failed to generate invoker by " + factoryName + " failed. Fallback to use JDK proxy success. " +
+ "Interfaces: " + type, throwable);
+ // log out error
+ return invoker;
+ } catch (Throwable fromJdk) {
+ logger.error(PROXY_FAILED, "", "", "Failed to generate invoker by " + factoryName + " failed. Fallback to use JDK proxy is also failed. " +
+ "Interfaces: " + type + " Javassist Error.", throwable);
+ logger.error(PROXY_FAILED, "", "", "Failed to generate invoker by " + factoryName + " failed. Fallback to use JDK proxy is also failed. " +
+ "Interfaces: " + type + " JDK Error.", fromJdk);
+ throw throwable;
+ }
+ }
+ }
+
+ @Override
+ public T getProxy(Invoker invoker, Class>[] interfaces) {
+ try {
+ return doGetProxy(invoker, interfaces);
+ } catch (Throwable throwable) {
+ // try fall back to JDK proxy factory
+ String factoryName = getClass().getSimpleName();
+ try {
+ T proxy = jdkProxyFactory.getProxy(invoker, interfaces);
+ logger.error(PROXY_FAILED, "", "", "Failed to generate proxy by " + factoryName + " failed. Fallback to use JDK proxy success. " +
+ "Interfaces: " + Arrays.toString(interfaces), throwable);
+ return proxy;
+ } catch (Throwable fromJdk) {
+ logger.error(PROXY_FAILED, "", "", "Failed to generate proxy by " + factoryName + " failed. Fallback to use JDK proxy is also failed. " +
+ "Interfaces: " + Arrays.toString(interfaces) + " Javassist Error.", throwable);
+ logger.error(PROXY_FAILED, "", "", "Failed to generate proxy by " + factoryName + " failed. Fallback to use JDK proxy is also failed. " +
+ "Interfaces: " + Arrays.toString(interfaces) + " JDK Error.", fromJdk);
+ throw throwable;
+ }
+ }
+ }
+
+ protected abstract Invoker doGetInvoker(T proxy, Class type, URL url);
+
+ protected abstract T doGetProxy(Invoker invoker, Class>[] types);
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/MethodInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/MethodInvoker.java
new file mode 100644
index 0000000000..8f7df1f86a
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/MethodInvoker.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.proxy;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public interface MethodInvoker {
+
+ Object invoke(Object instance, String methodName, Class>[] parameterTypes, Object[] arguments) throws Throwable;
+
+ /**
+ * no overload method invoker
+ */
+ class SingleMethodInvoker implements MethodInvoker {
+
+ private final Method method;
+
+ SingleMethodInvoker(Method method) {
+ this.method = method;
+ }
+
+ @Override
+ public Object invoke(Object instance, String methodName, Class>[] parameterTypes, Object[] arguments) throws Throwable {
+ return method.invoke(instance, arguments);
+ }
+ }
+
+ /**
+ * overload method invoker
+ */
+ class OverloadMethodInvoker implements MethodInvoker {
+
+ private final MethodMeta[] methods;
+
+ OverloadMethodInvoker(List methods) {
+ this.methods = methods.stream().map(MethodMeta::new).toArray(MethodMeta[]::new);
+ }
+
+ @Override
+ public Object invoke(Object instance, String methodName, Class>[] parameterTypes, Object[] arguments) throws Throwable {
+ for (MethodMeta meta : methods) {
+ if (Arrays.equals(meta.getParametersType(), parameterTypes)) {
+ return meta.getMethod().invoke(instance, arguments);
+ }
+ }
+ throw new NoSuchMethodException(instance.getClass().getName() + "." + methodName + Arrays.toString(parameterTypes));
+ }
+
+ private static class MethodMeta {
+
+ private final Method method;
+
+ private final Class>[] parametersType;
+
+ private MethodMeta(Method method) {
+ this.method = method;
+ this.parametersType = method.getParameterTypes();
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+
+ public Class>[] getParametersType() {
+ return parametersType;
+ }
+ }
+ }
+
+ class CompositeMethodInvoker implements MethodInvoker {
+
+ private final Map invokers;
+
+ public CompositeMethodInvoker(Map invokers) {
+ this.invokers = invokers;
+ }
+
+ /**
+ * for test
+ *
+ * @return all MethodInvoker
+ */
+ Map getInvokers() {
+ return invokers;
+ }
+
+ @Override
+ public Object invoke(Object instance, String methodName, Class>[] parameterTypes, Object[] arguments) throws Throwable {
+ MethodInvoker invoker = invokers.get(methodName);
+ if (invoker == null) {
+ throw new NoSuchMethodException(instance.getClass().getName() + "." + methodName + Arrays.toString(parameterTypes));
+ }
+ return invoker.invoke(instance, methodName, parameterTypes, arguments);
+ }
+ }
+
+ static MethodInvoker newInstance(Class> clazz) {
+ Method[] methods = clazz.getMethods();
+ Map invokers = new HashMap<>();
+ Map> map = new HashMap<>();
+ for (Method method : methods) {
+ map.computeIfAbsent(method.getName(), name -> new ArrayList<>()).add(method);
+ }
+ Set>> entries = map.entrySet();
+ for (Map.Entry> entry : entries) {
+ String methodName = entry.getKey();
+ List ms = entry.getValue();
+ if (ms.size() == 1) {
+ invokers.put(methodName, new SingleMethodInvoker(ms.get(0)));
+ continue;
+ }
+ invokers.put(methodName, new OverloadMethodInvoker(ms));
+ }
+ return new CompositeMethodInvoker(invokers);
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyInterceptor.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyInterceptor.java
new file mode 100644
index 0000000000..6670af89b8
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyInterceptor.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.proxy.bytebuddy;
+
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.implementation.bind.annotation.This;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+public class ByteBuddyInterceptor {
+
+ private final InvocationHandler handler;
+
+ ByteBuddyInterceptor(InvocationHandler handler) {
+ this.handler = handler;
+ }
+
+ @RuntimeType
+ public Object intercept(@This Object obj, @AllArguments Object[] allArguments,
+ @Origin Method method) throws Throwable {
+ return handler.invoke(obj, method, allArguments);
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxy.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxy.java
new file mode 100644
index 0000000000..ccc25b9df7
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxy.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.proxy.bytebuddy;
+
+import net.bytebuddy.ByteBuddy;
+import net.bytebuddy.description.ByteCodeElement;
+import net.bytebuddy.implementation.MethodDelegation;
+import net.bytebuddy.matcher.ElementMatcher;
+import net.bytebuddy.matcher.ElementMatchers;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.apache.dubbo.common.constants.CommonConstants.MAX_PROXY_COUNT;
+
+public class ByteBuddyProxy {
+
+ private static final Map> PROXY_CACHE_MAP = new WeakHashMap<>();
+
+ private final Class> proxyClass;
+
+ private final InvocationHandler handler;
+
+ private ByteBuddyProxy(Class> proxyClass, InvocationHandler handler) {
+ this.proxyClass = proxyClass;
+ this.handler = handler;
+ }
+
+ public static Object newInstance(ClassLoader cl, Class>[] interfaces, InvocationHandler handler) {
+ return getProxy(cl, interfaces, handler).newInstance();
+ }
+
+ private static ByteBuddyProxy getProxy(ClassLoader cl, Class>[] interfaces, InvocationHandler handler) {
+ if (interfaces.length > MAX_PROXY_COUNT) {
+ throw new IllegalArgumentException("interface limit exceeded");
+ }
+ interfaces = interfaces.clone();
+ Arrays.sort(interfaces, Comparator.comparing(Class::getName));
+ CacheKey key = new CacheKey(interfaces);
+ // get cache by class loader.
+ final Map cache;
+ synchronized (PROXY_CACHE_MAP) {
+ cache = PROXY_CACHE_MAP.computeIfAbsent(cl, k -> new ConcurrentHashMap<>());
+ }
+
+ ByteBuddyProxy proxy = cache.get(key);
+ if (proxy == null) {
+ synchronized (interfaces[0]) {
+ proxy = cache.get(key);
+ if (proxy == null) {
+ // create ByteBuddyProxy class.
+ proxy = new ByteBuddyProxy(buildProxyClass(cl, interfaces, handler), handler);
+ cache.put(key, proxy);
+ }
+ }
+ }
+ return proxy;
+ }
+
+ private Object newInstance() {
+ try {
+ Constructor> constructor = proxyClass.getDeclaredConstructor(InvocationHandler.class);
+ return constructor.newInstance(handler);
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static Class> buildProxyClass(ClassLoader cl, Class>[] ics, InvocationHandler handler) {
+ ElementMatcher.Junction methodMatcher = Arrays.stream(ics)
+ .map(ElementMatchers::isDeclaredBy).reduce(ElementMatcher.Junction::or)
+ .orElse(ElementMatchers.none()).and(ElementMatchers
+ .not(ElementMatchers.isDeclaredBy(Object.class)));
+ return new ByteBuddy()
+ .subclass(Proxy.class)
+ .implement(ics)
+ .method(methodMatcher)
+ .intercept(MethodDelegation.to(new ByteBuddyInterceptor(handler)))
+ .make()
+ .load(cl)
+ .getLoaded();
+ }
+
+ private static class CacheKey {
+
+ private final Class>[] classes;
+
+ private CacheKey(Class>[] classes) {
+ this.classes = classes;
+ }
+
+ public Class>[] getClasses() {
+ return classes;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CacheKey that = (CacheKey) o;
+ return Arrays.equals(classes, that.classes);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(classes);
+ }
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyFactory.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyFactory.java
new file mode 100644
index 0000000000..7f34686647
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyFactory.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.proxy.bytebuddy;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.proxy.AbstractFallbackJdkProxyFactory;
+import org.apache.dubbo.rpc.proxy.InvokerInvocationHandler;
+
+/**
+ * ByteBuddyRpcProxyFactory
+ */
+public class ByteBuddyProxyFactory extends AbstractFallbackJdkProxyFactory {
+
+ @Override
+ protected Invoker doGetInvoker(T proxy, Class type, URL url) {
+ return ByteBuddyProxyInvoker.newInstance(proxy, type, url);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected T doGetProxy(Invoker invoker, Class>[] interfaces) {
+ ClassLoader classLoader = invoker.getInterface().getClassLoader();
+ return (T) ByteBuddyProxy.newInstance(classLoader, interfaces, new InvokerInvocationHandler(invoker));
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyInvoker.java
new file mode 100644
index 0000000000..2f5761c39e
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyInvoker.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.proxy.bytebuddy;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.proxy.AbstractProxyInvoker;
+import org.apache.dubbo.rpc.proxy.MethodInvoker;
+
+class ByteBuddyProxyInvoker extends AbstractProxyInvoker {
+
+ private final MethodInvoker methodInvoker;
+
+ private ByteBuddyProxyInvoker(T proxy,
+ Class type,
+ URL url,
+ MethodInvoker methodInvoker) {
+ super(proxy, type, url);
+ this.methodInvoker = methodInvoker;
+ }
+
+ @Override
+ protected Object doInvoke(T instance, String methodName, Class>[] parameterTypes, Object[] arguments) throws Throwable {
+ if ("getClass".equals(methodName)) {
+ return instance.getClass();
+ }
+ if ("hashCode".equals(methodName)) {
+ return instance.hashCode();
+ }
+ if ("toString".equals(methodName)) {
+ return instance.toString();
+ }
+ if ("equals".equals(methodName)) {
+ if (arguments.length == 1) {
+ return instance.equals(arguments[0]);
+ }
+ throw new IllegalArgumentException("Invoke method [" + methodName + "] argument number error.");
+ }
+ return methodInvoker.invoke(instance, methodName, parameterTypes, arguments);
+ }
+
+ static ByteBuddyProxyInvoker newInstance(T proxy, Class type, URL url) {
+ return new ByteBuddyProxyInvoker<>(proxy, type, url, MethodInvoker.newInstance(proxy.getClass()));
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.ProxyFactory b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.ProxyFactory
index 900b54fb5f..6ad3db9043 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.ProxyFactory
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.ProxyFactory
@@ -1,4 +1,5 @@
stub=org.apache.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper
jdk=org.apache.dubbo.rpc.proxy.jdk.JdkProxyFactory
javassist=org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory
-nativestub=org.apache.dubbo.rpc.stub.StubProxyFactory
\ No newline at end of file
+bytebuddy=org.apache.dubbo.rpc.proxy.bytebuddy.ByteBuddyProxyFactory
+nativestub=org.apache.dubbo.rpc.stub.StubProxyFactory
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/MethodInvokerTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/MethodInvokerTest.java
new file mode 100644
index 0000000000..e2be1938d2
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/MethodInvokerTest.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.proxy;
+
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+
+class MethodInvokerTest {
+
+ @Test
+ void testNewInstance() throws Throwable {
+ String singleMethodName = "getThreadName";
+ String overloadMethodName = "sayHello";
+
+ MethodInvoker methodInvoker = MethodInvoker.newInstance(RemoteServiceImpl.class);
+ assertInstanceOf(MethodInvoker.CompositeMethodInvoker.class, methodInvoker);
+ MethodInvoker.CompositeMethodInvoker compositeMethodInvoker = (MethodInvoker.CompositeMethodInvoker) methodInvoker;
+ Map invokers = compositeMethodInvoker.getInvokers();
+ MethodInvoker getThreadNameMethodInvoker = invokers.get(singleMethodName);
+ assertInstanceOf(MethodInvoker.SingleMethodInvoker.class, getThreadNameMethodInvoker);
+ MethodInvoker sayHelloMethodInvoker = invokers.get(overloadMethodName);
+ assertInstanceOf(MethodInvoker.OverloadMethodInvoker.class, sayHelloMethodInvoker);
+
+ RemoteServiceImpl remoteService = Mockito.mock(RemoteServiceImpl.class);
+ //invoke success, SingleMethodInvoker does not check parameter types
+ methodInvoker.invoke(remoteService, singleMethodName, new Class[0], new Object[0]);
+ methodInvoker.invoke(remoteService, singleMethodName, new Class[]{Object.class, Object.class, Object.class}, new Object[0]);
+ Mockito.verify(remoteService, Mockito.times(2)).getThreadName();
+
+ methodInvoker.invoke(remoteService, overloadMethodName, new Class[]{String.class}, new Object[]{"Hello arg1"});
+ Mockito.verify(remoteService, Mockito.times(1)).sayHello("Hello arg1");
+ methodInvoker.invoke(remoteService, overloadMethodName, new Class[]{String.class, String.class}, new Object[]{"Hello arg1", "Hello arg2"});
+ Mockito.verify(remoteService, Mockito.times(1)).sayHello("Hello arg1", "Hello arg2");
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/RemoteServiceImpl.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/RemoteServiceImpl.java
index 50b82b1997..54b3420ba2 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/RemoteServiceImpl.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/RemoteServiceImpl.java
@@ -29,4 +29,8 @@ public class RemoteServiceImpl implements RemoteService {
public String sayHello(String name) throws RemoteException {
return "hello " + name + "@" + RemoteServiceImpl.class.getName();
}
+
+ public String sayHello(String name, String arg2) {
+ return "hello " + name + "@" + RemoteServiceImpl.class.getName() + ", arg2 " + arg2;
+ }
}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyInterceptorTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyInterceptorTest.java
new file mode 100644
index 0000000000..1ac609d3c9
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyInterceptorTest.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.proxy.bytebuddy;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+class ByteBuddyInterceptorTest {
+
+ @AfterEach
+ public void after(){
+ Mockito.clearAllCaches();
+ }
+
+ @Test
+ void testIntercept() throws Throwable {
+ InvocationHandler handler = Mockito.mock(InvocationHandler.class);
+ ByteBuddyInterceptor interceptor = new ByteBuddyInterceptor(handler);
+ Method method = Mockito.mock(Method.class);
+ Proxy proxy = Mockito.mock(Proxy.class);
+ Object[] args = new Object[0];
+ interceptor.intercept(proxy, args, method);
+ //'intercept' method will call 'invoke' method directly
+ Mockito.verify(handler, Mockito.times(1)).invoke(proxy, method, args);
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyFactoryTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyFactoryTest.java
new file mode 100644
index 0000000000..312c09c9c9
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyFactoryTest.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.proxy.bytebuddy;
+
+import org.apache.dubbo.rpc.proxy.AbstractProxyTest;
+
+class ByteBuddyProxyFactoryTest extends AbstractProxyTest {
+
+ static {
+ factory = new ByteBuddyProxyFactory();
+ }
+
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyInvokerTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyInvokerTest.java
new file mode 100644
index 0000000000..2a11557295
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyInvokerTest.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.proxy.bytebuddy;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.proxy.RemoteService;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+class ByteBuddyProxyInvokerTest {
+
+ @Test
+ void testNewInstance() throws Throwable {
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ RemoteService proxy = Mockito.mock(RemoteService.class);
+ ByteBuddyProxyInvoker invoker = ByteBuddyProxyInvoker.newInstance(proxy, RemoteService.class, url);
+ invoker.doInvoke(proxy, "sayHello", new Class[]{String.class}, new Object[]{"test"});
+ Mockito.verify(proxy, Mockito.times(1)).sayHello("test");
+
+ Assertions.assertThrows(IllegalArgumentException.class,
+ () -> invoker.doInvoke(proxy, "equals", new Class[]{String.class}, new Object[]{"test", "test2"}));
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyTest.java
new file mode 100644
index 0000000000..1e2295a0b8
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyTest.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.proxy.bytebuddy;
+
+import org.apache.dubbo.rpc.proxy.InvokerInvocationHandler;
+import org.apache.dubbo.rpc.proxy.RemoteService;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.lang.reflect.Proxy;
+
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.mockito.ArgumentMatchers.any;
+
+class ByteBuddyProxyTest {
+
+ @Test
+ void testNewInstance() throws Throwable {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ InvokerInvocationHandler handler = Mockito.mock(InvokerInvocationHandler.class);
+ Object proxy = ByteBuddyProxy.newInstance(cl, new Class>[]{RemoteService.class}, handler);
+ assertInstanceOf(RemoteService.class, proxy);
+ assertInstanceOf(Proxy.class, proxy);
+ RemoteService remoteService = (RemoteService) proxy;
+ remoteService.getThreadName();
+ remoteService.sayHello("test");
+ Mockito.verify(handler, Mockito.times(2)).invoke(any(), any(), any());
+ }
+}