You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2023/06/01 10:46:36 UTC
[dubbo] branch 3.2 updated: Use Dubbo Proxy instead of Spring Proxy (#12439)
This is an automated email from the ASF dual-hosted git repository.
albumenj pushed a commit to branch 3.2
in repository https://gitbox.apache.org/repos/asf/dubbo.git
The following commit(s) were added to refs/heads/3.2 by this push:
new f60ac914f4 Use Dubbo Proxy instead of Spring Proxy (#12439)
f60ac914f4 is described below
commit f60ac914f463034df4a50e6f871ddfb146c3b541
Author: Albumen Kevin <jh...@gmail.com>
AuthorDate: Thu Jun 1 18:46:29 2023 +0800
Use Dubbo Proxy instead of Spring Proxy (#12439)
* Use Dubbo Proxy instead of Spring Proxy
* Fix exception
---
.../apache/dubbo/config/spring/ReferenceBean.java | 80 +++++++++++----
.../spring/util/LazyTargetInvocationHandler.java | 69 +++++++++++++
.../dubbo/config/spring/util/LazyTargetSource.java | 21 ++++
.../javaconfig/JavaConfigReferenceBeanTest.java | 110 +++++++++++++++++++--
4 files changed, 252 insertions(+), 28 deletions(-)
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java
index c22a280f0b..975df8a2b2 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBean.java
@@ -16,6 +16,10 @@
*/
package org.apache.dubbo.config.spring;
+import org.apache.dubbo.common.bytecode.Proxy;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.Assert;
import org.apache.dubbo.common.utils.ClassUtils;
import org.apache.dubbo.common.utils.StringUtils;
@@ -25,11 +29,11 @@ import org.apache.dubbo.config.spring.reference.ReferenceAttributes;
import org.apache.dubbo.config.spring.reference.ReferenceBeanManager;
import org.apache.dubbo.config.spring.reference.ReferenceBeanSupport;
import org.apache.dubbo.config.spring.schema.DubboBeanDefinitionParser;
+import org.apache.dubbo.config.spring.util.LazyTargetInvocationHandler;
+import org.apache.dubbo.config.spring.util.LazyTargetSource;
import org.apache.dubbo.config.support.Parameter;
import org.apache.dubbo.rpc.proxy.AbstractProxyFactory;
-import org.springframework.aop.framework.ProxyFactory;
-import org.springframework.aop.target.AbstractLazyCreationTargetSource;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanNameAware;
@@ -44,10 +48,13 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROXY_FAILED;
+
/**
* <p>
@@ -99,7 +106,7 @@ import java.util.Map;
*/
public class ReferenceBean<T> implements FactoryBean<T>,
ApplicationContextAware, BeanClassLoaderAware, BeanNameAware, InitializingBean, DisposableBean {
-
+ private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
private transient ApplicationContext applicationContext;
private ClassLoader beanClassLoader;
@@ -124,6 +131,9 @@ public class ReferenceBean<T> implements FactoryBean<T>,
// 'interfaceName' field for compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc()
private String interfaceName;
+ // proxy style
+ private String proxy;
+
//from annotation attributes
private Map<String, Object> referenceProps;
@@ -235,6 +245,10 @@ public class ReferenceBean<T> implements FactoryBean<T>,
propertyValues = beanDefinition.getPropertyValues();
}
}
+
+ if (referenceProps != null) {
+ this.proxy = (String) referenceProps.get(ReferenceAttributes.PROXY);
+ }
Assert.notNull(this.interfaceName, "The interface name of ReferenceBean is not initialized");
ReferenceBeanManager referenceBeanManager = beanFactory.getBean(ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class);
@@ -319,24 +333,56 @@ public class ReferenceBean<T> implements FactoryBean<T>,
//set proxy interfaces
//see also: org.apache.dubbo.rpc.proxy.AbstractProxyFactory.getProxy(org.apache.dubbo.rpc.Invoker<T>, boolean)
- ProxyFactory proxyFactory = new ProxyFactory();
- proxyFactory.setTargetSource(new DubboReferenceLazyInitTargetSource());
- proxyFactory.addInterface(interfaceClass);
+ List<Class<?>> interfaces = new ArrayList<>();
+ interfaces.add(interfaceClass);
Class<?>[] internalInterfaces = AbstractProxyFactory.getInternalInterfaces();
- for (Class<?> anInterface : internalInterfaces) {
- proxyFactory.addInterface(anInterface);
- }
+ Collections.addAll(interfaces, internalInterfaces);
if (!StringUtils.isEquals(interfaceClass.getName(), interfaceName)) {
//add service interface
try {
Class<?> serviceInterface = ClassUtils.forName(interfaceName, beanClassLoader);
- proxyFactory.addInterface(serviceInterface);
+ interfaces.add(serviceInterface);
} catch (ClassNotFoundException e) {
// generic call maybe without service interface class locally
}
}
- this.lazyProxy = proxyFactory.getProxy(this.beanClassLoader);
+ if (StringUtils.isEmpty(this.proxy) || CommonConstants.DEFAULT_PROXY.equalsIgnoreCase(this.proxy)) {
+ generateFromJavassistFirst(interfaces);
+ }
+
+ if (this.lazyProxy == null) {
+ generateFromJdk(interfaces);
+ }
+ }
+
+ private void generateFromJavassistFirst(List<Class<?>> interfaces) {
+ try {
+ this.lazyProxy = Proxy.getProxy(interfaces.toArray(new Class[0])).newInstance(new LazyTargetInvocationHandler(new DubboReferenceLazyInitTargetSource()));
+ } catch (Throwable fromJavassist) {
+ // try fall back to JDK proxy factory
+ try {
+ this.lazyProxy = java.lang.reflect.Proxy.newProxyInstance(beanClassLoader, interfaces.toArray(new Class[0]), new LazyTargetInvocationHandler(new DubboReferenceLazyInitTargetSource()));
+ logger.error(PROXY_FAILED, "", "", "Failed to generate proxy by Javassist failed. Fallback to use JDK proxy success. " +
+ "Interfaces: " + interfaces, fromJavassist);
+ } catch (Throwable fromJdk) {
+ logger.error(PROXY_FAILED, "", "", "Failed to generate proxy by Javassist failed. Fallback to use JDK proxy is also failed. " +
+ "Interfaces: " + interfaces + " Javassist Error.", fromJavassist);
+ logger.error(PROXY_FAILED, "", "", "Failed to generate proxy by Javassist failed. Fallback to use JDK proxy is also failed. " +
+ "Interfaces: " + interfaces + " JDK Error.", fromJdk);
+ throw fromJavassist;
+ }
+ }
+ }
+
+ private void generateFromJdk(List<Class<?>> interfaces) {
+ try {
+ this.lazyProxy = java.lang.reflect.Proxy.newProxyInstance(beanClassLoader, interfaces.toArray(new Class[0]), new LazyTargetInvocationHandler(new DubboReferenceLazyInitTargetSource()));
+ } catch (Throwable fromJdk) {
+ logger.error(PROXY_FAILED, "", "", "Failed to generate proxy by Javassist failed. Fallback to use JDK proxy is also failed. " +
+ "Interfaces: " + interfaces + " JDK Error.", fromJdk);
+ throw fromJdk;
+ }
}
private Object getCallProxy() throws Exception {
@@ -344,7 +390,7 @@ public class ReferenceBean<T> implements FactoryBean<T>,
throw new IllegalStateException("ReferenceBean is not ready yet, please make sure to call reference interface method after dubbo is started.");
}
//get reference proxy
- //Subclasses should synchronize on the given Object if they perform any sort of extended singleton creation phase.
+ //Subclasses should synchronize on the given Object if they perform any sort of extended singleton creation phase.
// In particular, subclasses should not have their own mutexes involved in singleton creation, to avoid the potential for deadlocks in lazy-init situations.
//The redundant type cast is to be compatible with earlier than spring-4.2
synchronized (((DefaultSingletonBeanRegistry)getBeanFactory()).getSingletonMutex()) {
@@ -352,17 +398,11 @@ public class ReferenceBean<T> implements FactoryBean<T>,
}
}
- private class DubboReferenceLazyInitTargetSource extends AbstractLazyCreationTargetSource {
-
+ private class DubboReferenceLazyInitTargetSource implements LazyTargetSource {
@Override
- protected Object createObject() throws Exception {
+ public Object getTarget() throws Exception {
return getCallProxy();
}
-
- @Override
- public synchronized Class<?> getTargetClass() {
- return getInterfaceClass();
- }
}
}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/LazyTargetInvocationHandler.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/LazyTargetInvocationHandler.java
new file mode 100644
index 0000000000..cb414f93f0
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/LazyTargetInvocationHandler.java
@@ -0,0 +1,69 @@
+/*
+ * 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.config.spring.util;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class LazyTargetInvocationHandler implements InvocationHandler {
+ private final LazyTargetSource lazyTargetSource;
+ private volatile Object target;
+
+ public LazyTargetInvocationHandler(LazyTargetSource lazyTargetSource) {
+ this.lazyTargetSource = lazyTargetSource;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ String methodName = method.getName();
+ Class<?>[] parameterTypes = method.getParameterTypes();
+ if (parameterTypes.length == 0) {
+ if ("toString".equals(methodName)) {
+ if (target != null) {
+ return target.toString();
+ } else {
+ return this.toString();
+ }
+ } else if ("hashCode".equals(methodName)) {
+ return this.hashCode();
+ }
+ } else if (parameterTypes.length == 1 && "equals".equals(methodName)) {
+ return this.equals(args[0]);
+ }
+
+ if (target == null) {
+ synchronized (this) {
+ if (target == null) {
+ target = lazyTargetSource.getTarget();
+ }
+ }
+ }
+ if (method.getDeclaringClass().isInstance(target)) {
+ try {
+ return method.invoke(target, args);
+ } catch (InvocationTargetException exception) {
+ Throwable targetException = exception.getTargetException();
+ if (targetException != null) {
+ throw targetException;
+ }
+ }
+ }
+ throw new IllegalStateException("The proxied interface [" + method.getDeclaringClass() +
+ "] contains a method [" + method + "] that is not implemented by the proxy class [" + target.getClass() + "]");
+ }
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/LazyTargetSource.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/LazyTargetSource.java
new file mode 100644
index 0000000000..57f58e1ecd
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/LazyTargetSource.java
@@ -0,0 +1,21 @@
+/*
+ * 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.config.spring.util;
+
+public interface LazyTargetSource {
+ Object getTarget() throws Exception;
+}
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/javaconfig/JavaConfigReferenceBeanTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/javaconfig/JavaConfigReferenceBeanTest.java
index af379ca7fe..d951dd03df 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/javaconfig/JavaConfigReferenceBeanTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/javaconfig/JavaConfigReferenceBeanTest.java
@@ -41,6 +41,7 @@ import org.springframework.context.annotation.PropertySource;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -97,6 +98,63 @@ class JavaConfigReferenceBeanTest {
}
}
+ @Test
+ void testLazyProxy1() {
+ AnnotationConfigApplicationContext context = null;
+ try {
+ context = new AnnotationConfigApplicationContext(CommonConfig.class,
+ LazyProxyConfiguration1.class);
+ HelloService helloServiceClient = context.getBean("helloServiceClient", HelloService.class);
+ Assertions.assertNotNull(helloServiceClient);
+ Assertions.assertInstanceOf(HelloService.class, helloServiceClient);
+ Class<? extends HelloService> clientClass = helloServiceClient.getClass();
+ Assertions.assertFalse(Modifier.isFinal(clientClass.getModifiers()));
+ Assertions.assertEquals("Hello, dubbo", helloServiceClient.sayHello("dubbo"));
+ } finally {
+ if (context != null) {
+ context.close();
+ }
+ }
+ }
+
+ @Test
+ void testLazyProxy2() {
+ AnnotationConfigApplicationContext context = null;
+ try {
+ context = new AnnotationConfigApplicationContext(CommonConfig.class,
+ LazyProxyConfiguration2.class);
+ HelloService helloServiceClient = context.getBean("helloServiceClient", HelloService.class);
+ Assertions.assertNotNull(helloServiceClient);
+ Assertions.assertInstanceOf(HelloService.class, helloServiceClient);
+ Class<? extends HelloService> clientClass = helloServiceClient.getClass();
+ Assertions.assertFalse(Modifier.isFinal(clientClass.getModifiers()));
+ Assertions.assertEquals("Hello, dubbo", helloServiceClient.sayHello("dubbo"));
+ } finally {
+ if (context != null) {
+ context.close();
+ }
+ }
+ }
+
+ @Test
+ void testLazyProxy3() {
+ AnnotationConfigApplicationContext context = null;
+ try {
+ context = new AnnotationConfigApplicationContext(CommonConfig.class,
+ LazyProxyConfiguration3.class);
+ HelloService helloServiceClient = context.getBean("helloServiceClient", HelloService.class);
+ Assertions.assertNotNull(helloServiceClient);
+ Assertions.assertInstanceOf(HelloService.class, helloServiceClient);
+ Class<? extends HelloService> clientClass = helloServiceClient.getClass();
+ Assertions.assertTrue(Modifier.isFinal(clientClass.getModifiers()));
+ Assertions.assertEquals("Hello, dubbo", helloServiceClient.sayHello("dubbo"));
+ } finally {
+ if (context != null) {
+ context.close();
+ }
+ }
+ }
+
private String getStackTrace(Throwable ex) {
StringWriter stringWriter = new StringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
@@ -106,7 +164,7 @@ class JavaConfigReferenceBeanTest {
@Test
void testAnnotationBean() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CommonConfig.class,
- AnnotationBeanConfiguration.class);
+ AnnotationBeanConfiguration.class);
try {
Map<String, HelloService> helloServiceMap = context.getBeansOfType(HelloService.class);
@@ -165,7 +223,7 @@ class JavaConfigReferenceBeanTest {
@Test
void testReferenceBean() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CommonConfig.class,
- ReferenceBeanConfiguration.class);
+ ReferenceBeanConfiguration.class);
try {
Map<String, HelloService> helloServiceMap = context.getBeansOfType(HelloService.class);
@@ -316,6 +374,42 @@ class JavaConfigReferenceBeanTest {
}
+ @Configuration
+ public static class LazyProxyConfiguration1 {
+
+ @DubboReference(group = "${myapp.group}")
+ private HelloService helloService;
+
+ @Bean(name = "helloServiceClient")
+ public HelloService helloService() {
+ return helloService;
+ }
+ }
+
+ @Configuration
+ public static class LazyProxyConfiguration2 {
+
+ @DubboReference(group = "${myapp.group}", proxy = "javassist")
+ private HelloService helloService;
+
+ @Bean(name = "helloServiceClient")
+ public HelloService helloService() {
+ return helloService;
+ }
+ }
+
+ @Configuration
+ public static class LazyProxyConfiguration3 {
+
+ @DubboReference(group = "${myapp.group}", proxy = "jdk")
+ private HelloService helloService;
+
+ @Bean(name = "helloServiceClient")
+ public HelloService helloService() {
+ return helloService;
+ }
+ }
+
@Configuration
public static class AnnotationAtFieldConfiguration {
@@ -365,8 +459,8 @@ class JavaConfigReferenceBeanTest {
@Bean
public ReferenceBean<HelloService> helloService() {
return new ReferenceBeanBuilder()
- .setGroup("${myapp.group}")
- .build();
+ .setGroup("${myapp.group}")
+ .build();
}
@Bean
@@ -394,9 +488,9 @@ class JavaConfigReferenceBeanTest {
@Bean
public ReferenceBean helloService() {
return new ReferenceBeanBuilder()
- .setGroup("${myapp.group}")
- .setInterface(HelloService.class)
- .build();
+ .setGroup("${myapp.group}")
+ .setInterface(HelloService.class)
+ .build();
}
}
@@ -435,4 +529,4 @@ class JavaConfigReferenceBeanTest {
}
}
-}
\ No newline at end of file
+}