issue 5900:add bytebuddy support (#11117)

* issue 5900:add bytebuddy support

* remove unused import

* edit comment

* rename CompositeMethodInvoker

* add unit test

* fix ut
This commit is contained in:
一个不知名的Java靓仔 2022-12-20 23:38:08 +08:00 committed by GitHub
parent fe4d99dfaa
commit 3c103d40cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 712 additions and 1 deletions

View File

@ -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";

View File

@ -93,6 +93,7 @@
<!-- <spring_version>4.3.30.RELEASE</spring_version> -->
<spring_version>5.2.20.RELEASE</spring_version>
<javassist_version>3.28.0-GA</javassist_version>
<bytebuddy.version>1.12.19</bytebuddy.version>
<netty_version>3.2.5.Final</netty_version>
<netty4_version>4.1.72.Final</netty4_version>
<mina_version>2.1.5</mina_version>
@ -213,6 +214,11 @@
<artifactId>javassist</artifactId>
<version>${javassist_version}</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>${bytebuddy.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.netty</groupId>
<artifactId>netty</artifactId>

View File

@ -45,6 +45,10 @@
<artifactId>dubbo-remoting-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>hessian-lite</artifactId>

View File

@ -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 <T> Invoker<T> getInvoker(T proxy, Class<T> 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<T> 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> T getProxy(Invoker<T> 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 <T> Invoker<T> doGetInvoker(T proxy, Class<T> type, URL url);
protected abstract <T> T doGetProxy(Invoker<T> invoker, Class<?>[] types);
}

View File

@ -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<Method> 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<String, MethodInvoker> invokers;
public CompositeMethodInvoker(Map<String, MethodInvoker> invokers) {
this.invokers = invokers;
}
/**
* for test
*
* @return all MethodInvoker
*/
Map<String, MethodInvoker> 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<String, MethodInvoker> invokers = new HashMap<>();
Map<String, List<Method>> map = new HashMap<>();
for (Method method : methods) {
map.computeIfAbsent(method.getName(), name -> new ArrayList<>()).add(method);
}
Set<Map.Entry<String, List<Method>>> entries = map.entrySet();
for (Map.Entry<String, List<Method>> entry : entries) {
String methodName = entry.getKey();
List<Method> 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);
}
}

View File

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

View File

@ -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<ClassLoader, Map<CacheKey, ByteBuddyProxy>> 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<CacheKey, ByteBuddyProxy> 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<ByteCodeElement> 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);
}
}
}

View File

@ -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 <T> Invoker<T> doGetInvoker(T proxy, Class<T> type, URL url) {
return ByteBuddyProxyInvoker.newInstance(proxy, type, url);
}
@Override
@SuppressWarnings("unchecked")
protected <T> T doGetProxy(Invoker<T> invoker, Class<?>[] interfaces) {
ClassLoader classLoader = invoker.getInterface().getClassLoader();
return (T) ByteBuddyProxy.newInstance(classLoader, interfaces, new InvokerInvocationHandler(invoker));
}
}

View File

@ -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<T> extends AbstractProxyInvoker<T> {
private final MethodInvoker methodInvoker;
private ByteBuddyProxyInvoker(T proxy,
Class<T> 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 <T> ByteBuddyProxyInvoker<T> newInstance(T proxy, Class<T> type, URL url) {
return new ByteBuddyProxyInvoker<>(proxy, type, url, MethodInvoker.newInstance(proxy.getClass()));
}
}

View File

@ -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
bytebuddy=org.apache.dubbo.rpc.proxy.bytebuddy.ByteBuddyProxyFactory
nativestub=org.apache.dubbo.rpc.stub.StubProxyFactory

View File

@ -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<String, MethodInvoker> 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");
}
}

View File

@ -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;
}
}

View File

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

View File

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

View File

@ -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<RemoteService> 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"}));
}
}

View File

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