*
* @since 2.7.7
+ * @see org.apache.dubbo.config.spring.reference.ReferenceBeanBuilder
+ * @see org.apache.dubbo.config.spring.ReferenceBean
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@@ -66,12 +101,16 @@ public @interface DubboReference {
/**
* Whether to enable generic invocation, default value is false
+ * @deprecated Do not need specify generic value, judge by injection type and interface class
*/
+ @Deprecated
boolean generic() default false;
/**
* When enable, prefer to call local service in the same JVM if it's present, default value is true
+ * @deprecated using scope="local" or scope="remote" instead
*/
+ @Deprecated
boolean injvm() default true;
/**
@@ -80,9 +119,10 @@ public @interface DubboReference {
boolean check() default true;
/**
- * Whether eager initialize the reference bean when all properties are set, default value is false
+ * Whether eager initialize the reference bean when all properties are set, default value is true ( null as true)
+ * @see ReferenceConfigBase#shouldInit()
*/
- boolean init() default false;
+ boolean init() default true;
/**
* Whether to make connection when the client is created, the default value is false
@@ -129,14 +169,14 @@ public @interface DubboReference {
/**
* Maximum connections service provider can accept, default value is 0 - connection is shared
*/
- int connections() default 0;
+ int connections() default -1;
/**
* The callback instance limit peer connection
*
* see org.apache.dubbo.rpc.Constants#DEFAULT_CALLBACK_INSTANCES
*/
- int callbacks() default 0;
+ int callbacks() default -1;
/**
* Callback method name when connected, default value is empty string
@@ -163,7 +203,7 @@ public @interface DubboReference {
*
* see Constants#DEFAULT_RETRIES
*/
- int retries() default 2;
+ int retries() default -1;
/**
* Load balance strategy, legal values include: random, roundrobin, leastactive
@@ -180,7 +220,7 @@ public @interface DubboReference {
/**
* Maximum active requests allowed, default value is 0
*/
- int actives() default 0;
+ int actives() default -1;
/**
* Whether the async request has already been sent, the default value is false
@@ -200,7 +240,7 @@ public @interface DubboReference {
/**
* Timeout value for service invocation, default value is 0
*/
- int timeout() default 0;
+ int timeout() default -1;
/**
* Specify cache implementation for service invocation, legal values include: lru, threadlocal, jcache
@@ -222,7 +262,7 @@ public @interface DubboReference {
String[] listener() default {};
/**
- * Customized parameter key-value pair, for example: {key1, value1, key2, value2}
+ * Customized parameter key-value pair, for example: {key1, value1, key2, value2} or {"key1=value1", "key2=value2"}
*/
String[] parameters() default {};
@@ -276,7 +316,7 @@ public @interface DubboReference {
/**
* The id
- *
+ * NOTE: The id attribute is ignored when using @DubboReference on @Bean method
* @return default value is empty
* @since 2.7.3
*/
@@ -296,4 +336,11 @@ public @interface DubboReference {
* @see RegistryConstants#PROVIDED_BY
*/
String[] providedBy() default {};
+
+ /**
+ * the scope for referring/exporting a service, if it's local, it means searching in current JVM only.
+ * @see org.apache.dubbo.rpc.Constants#SCOPE_LOCAL
+ * @see org.apache.dubbo.rpc.Constants#SCOPE_REMOTE
+ */
+ String scope() default "";
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboService.java b/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboService.java
index 3b27d0486b..33178edf4c 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboService.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/DubboService.java
@@ -24,9 +24,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_LOADBALANCE;
-import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_RETRIES;
-
/**
* Class-level annotation used for declaring Dubbo service
*
@@ -91,7 +88,7 @@ public @interface DubboService {
/**
* Maximum concurrent executes for the service, default value is 0 - no limits
*/
- int executes() default 0;
+ int executes() default -1;
/**
* Whether to register the service to register center, default value is true
@@ -101,7 +98,7 @@ public @interface DubboService {
/**
* Service weight value, default value is 0
*/
- int weight() default 0;
+ int weight() default -1;
/**
* Service doc, default value is ""
@@ -111,7 +108,7 @@ public @interface DubboService {
/**
* Delay time for service registration, default value is 0
*/
- int delay() default 0;
+ int delay() default -1;
/**
* @see DubboService#stub()
@@ -137,14 +134,14 @@ public @interface DubboService {
/**
* Maximum connections service provider can accept, default value is 0 - connection is shared
*/
- int connections() default 0;
+ int connections() default -1;
/**
* The callback instance limit peer connection
*
- * see org.apache.dubbo.rpc.Constants#DEFAULT_CALLBACK_INSTANCES
+ * see org.apache.dubbo.common.constants.CommonConstants.DEFAULT_CALLBACK_INSTANCES
*/
- int callbacks() default org.apache.dubbo.common.constants.CommonConstants.DEFAULT_CALLBACK_INSTANCES;
+ int callbacks() default -1;
/**
* Callback method name when connected, default value is empty string
@@ -171,14 +168,14 @@ public @interface DubboService {
*
* @see org.apache.dubbo.common.constants.CommonConstants#DEFAULT_RETRIES
*/
- int retries() default DEFAULT_RETRIES;
+ int retries() default -1;
/**
* Load balance strategy, legal values include: random, roundrobin, leastactive
*
* @see org.apache.dubbo.common.constants.CommonConstants#DEFAULT_LOADBALANCE
*/
- String loadbalance() default DEFAULT_LOADBALANCE;
+ String loadbalance() default "";
/**
* Whether to enable async invocation, default value is false
@@ -188,7 +185,7 @@ public @interface DubboService {
/**
* Maximum active requests allowed, default value is 0
*/
- int actives() default 0;
+ int actives() default -1;
/**
* Whether the async request has already been sent, the default value is false
@@ -208,7 +205,7 @@ public @interface DubboService {
/**
* Timeout value for service invocation, default value is 0
*/
- int timeout() default 0;
+ int timeout() default -1;
/**
* Specify cache implementation for service invocation, legal values include: lru, threadlocal, jcache
@@ -275,4 +272,12 @@ public @interface DubboService {
* @return
*/
Method[] methods() default {};
+
+ /**
+ * the scope for referring/exporting a service, if it's local, it means searching in current JVM only.
+ * @see org.apache.dubbo.rpc.Constants#SCOPE_LOCAL
+ * @see org.apache.dubbo.rpc.Constants#SCOPE_REMOTE
+ */
+ String scope() default "";
+
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/Method.java b/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/Method.java
index c1fe08885c..eb4bc74abf 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/Method.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/Method.java
@@ -45,9 +45,9 @@ public @interface Method {
boolean sent() default true;
- int actives() default 0;
+ int actives() default -1;
- int executes() default 0;
+ int executes() default -1;
boolean deprecated() default false;
@@ -68,4 +68,10 @@ public @interface Method {
String merger() default "";
Argument[] arguments() default {};
+
+ /**
+ * Customized parameter key-value pair, for example: {key1, value1, key2, value2} or {"key1=value1", "key2=value2"}
+ */
+ String[] parameters() default {};
+
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/Reference.java b/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/Reference.java
index abb250f50a..c12267e450 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/Reference.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/Reference.java
@@ -16,6 +16,8 @@
*/
package org.apache.dubbo.config.annotation;
+import org.apache.dubbo.config.ReferenceConfigBase;
+
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -67,7 +69,9 @@ public @interface Reference {
/**
* Whether to enable generic invocation, default value is false
+ * @deprecated Do not need specify generic value, judge by injection type and interface class
*/
+ @Deprecated
boolean generic() default false;
/**
@@ -81,9 +85,10 @@ public @interface Reference {
boolean check() default true;
/**
- * Whether eager initialize the reference bean when all properties are set, default value is false
+ * Whether eager initialize the reference bean when all properties are set, default value is true ( null as true)
+ * @see ReferenceConfigBase#shouldInit()
*/
- boolean init() default false;
+ boolean init() default true;
/**
* Whether to make connection when the client is created, the default value is false
@@ -130,14 +135,14 @@ public @interface Reference {
/**
* Maximum connections service provider can accept, default value is 0 - connection is shared
*/
- int connections() default 0;
+ int connections() default -1;
/**
* The callback instance limit peer connection
*
* see org.apache.dubbo.rpc.Constants#DEFAULT_CALLBACK_INSTANCES
*/
- int callbacks() default 0;
+ int callbacks() default -1;
/**
* Callback method name when connected, default value is empty string
@@ -164,7 +169,7 @@ public @interface Reference {
*
* see Constants#DEFAULT_RETRIES
*/
- int retries() default 2;
+ int retries() default -1;
/**
* Load balance strategy, legal values include: random, roundrobin, leastactive
@@ -181,7 +186,7 @@ public @interface Reference {
/**
* Maximum active requests allowed, default value is 0
*/
- int actives() default 0;
+ int actives() default -1;
/**
* Whether the async request has already been sent, the default value is false
@@ -201,7 +206,7 @@ public @interface Reference {
/**
* Timeout value for service invocation, default value is 0
*/
- int timeout() default 0;
+ int timeout() default -1;
/**
* Specify cache implementation for service invocation, legal values include: lru, threadlocal, jcache
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/Service.java b/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/Service.java
index e46e07562f..94dc83be6d 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/Service.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/annotation/Service.java
@@ -24,9 +24,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_LOADBALANCE;
-import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_RETRIES;
-
/**
* Service annotation
*
@@ -94,7 +91,7 @@ public @interface Service {
/**
* Maximum concurrent executes for the service, default value is 0 - no limits
*/
- int executes() default 0;
+ int executes() default -1;
/**
* Whether to register the service to register center, default value is true
@@ -104,7 +101,7 @@ public @interface Service {
/**
* Service weight value, default value is 0
*/
- int weight() default 0;
+ int weight() default -1;
/**
* Service doc, default value is ""
@@ -114,7 +111,7 @@ public @interface Service {
/**
* Delay time for service registration, default value is 0
*/
- int delay() default 0;
+ int delay() default -1;
/**
* @see Service#stub()
@@ -140,14 +137,14 @@ public @interface Service {
/**
* Maximum connections service provider can accept, default value is 0 - connection is shared
*/
- int connections() default 0;
+ int connections() default -1;
/**
* The callback instance limit peer connection
*
- * see org.apache.dubbo.rpc.Constants#DEFAULT_CALLBACK_INSTANCES
+ * see org.apache.dubbo.common.constants.CommonConstants.DEFAULT_CALLBACK_INSTANCES
*/
- int callbacks() default org.apache.dubbo.common.constants.CommonConstants.DEFAULT_CALLBACK_INSTANCES;
+ int callbacks() default -1;
/**
* Callback method name when connected, default value is empty string
@@ -174,14 +171,14 @@ public @interface Service {
*
* @see org.apache.dubbo.common.constants.CommonConstants#DEFAULT_RETRIES
*/
- int retries() default DEFAULT_RETRIES;
+ int retries() default -1;
/**
* Load balance strategy, legal values include: random, roundrobin, leastactive
*
* @see org.apache.dubbo.common.constants.CommonConstants#DEFAULT_LOADBALANCE
*/
- String loadbalance() default DEFAULT_LOADBALANCE;
+ String loadbalance() default "";
/**
* Whether to enable async invocation, default value is false
@@ -191,7 +188,7 @@ public @interface Service {
/**
* Maximum active requests allowed, default value is 0
*/
- int actives() default 0;
+ int actives() default -1;
/**
* Whether the async request has already been sent, the default value is false
@@ -211,7 +208,7 @@ public @interface Service {
/**
* Timeout value for service invocation, default value is 0
*/
- int timeout() default 0;
+ int timeout() default -1;
/**
* Specify cache implementation for service invocation, legal values include: lru, threadlocal, jcache
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java
index 95c860f4c7..e62ef97bd2 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java
@@ -521,6 +521,7 @@ public class ConfigManager extends LifecycleAdapter implements FrameworkExt {
if (existedConfig != null && !config.equals(existedConfig)) {
if (configWarnLogEnabled) {
+ //TODO throw exception
if (logger.isWarnEnabled()) {
String type = config.getClass().getSimpleName();
logger.warn(String.format("Duplicate %s found, there already has one default %s or more than two %ss have the same id, " +
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
index 656b994a9b..e5544e48bc 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
@@ -66,6 +66,7 @@ import org.apache.dubbo.metadata.MetadataServiceExporter;
import org.apache.dubbo.metadata.WritableMetadataService;
import org.apache.dubbo.metadata.report.MetadataReportFactory;
import org.apache.dubbo.metadata.report.MetadataReportInstance;
+import org.apache.dubbo.metadata.report.support.AbstractMetadataReportFactory;
import org.apache.dubbo.registry.client.DefaultServiceInstance;
import org.apache.dubbo.registry.client.ServiceInstance;
import org.apache.dubbo.registry.client.metadata.MetadataUtils;
@@ -202,12 +203,21 @@ public class DubboBootstrap extends GenericEventListener {
return instance;
}
+ /**
+ * Try reset dubbo status for new instance.
+ * For testing purposes only
+ */
public static void reset() {
if (instance != null) {
instance.destroy();
+ instance = null;
}
ApplicationModel.reset();
- instance = null;
+ MetadataReportInstance.destroy();
+ AbstractMetadataReportFactory.clear();
+ ExtensionLoader.resetExtensionLoader(DynamicConfigurationFactory.class);
+ ExtensionLoader.resetExtensionLoader(MetadataReportFactory.class);
+ ExtensionLoader.destroyAll();
}
private DubboBootstrap() {
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MethodConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MethodConfigTest.java
index b190d5c2cd..c064f95605 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MethodConfigTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/MethodConfigTest.java
@@ -53,9 +53,12 @@ public class MethodConfigTest {
private static final int EXECUTES = 5;
private static final boolean DEPERECATED = true;
private static final boolean STICKY = true;
- private static final String ONINVOKE = "i";
- private static final String ONTHROW = "t";
- private static final String ONRETURN = "r";
+ private static final String ONINVOKE = "invokeNotify";
+ private static final String ONINVOKE_METHOD = "onInvoke";
+ private static final String ONTHROW = "throwNotify";
+ private static final String ONTHROW_METHOD = "onThrow";
+ private static final String ONRETURN = "returnNotify";
+ private static final String ONRETURN_METHOD = "onReturn";
private static final String CACHE = "c";
private static final String VALIDATION = "v";
private static final int ARGUMENTS_INDEX = 24;
@@ -63,10 +66,12 @@ public class MethodConfigTest {
private static final String ARGUMENTS_TYPE = "sss";
@Reference(methods = {@Method(name = METHOD_NAME, timeout = TIMEOUT, retries = RETRIES, loadbalance = LOADBALANCE, async = ASYNC,
- actives = ACTIVES, executes = EXECUTES, deprecated = DEPERECATED, sticky = STICKY, oninvoke = ONINVOKE, onthrow = ONTHROW, onreturn = ONRETURN, cache = CACHE, validation = VALIDATION,
+ actives = ACTIVES, executes = EXECUTES, deprecated = DEPERECATED, sticky = STICKY, oninvoke = ONINVOKE+"."+ONINVOKE_METHOD,
+ onthrow = ONTHROW+"."+ONTHROW_METHOD, onreturn = ONRETURN+"."+ONRETURN_METHOD, cache = CACHE, validation = VALIDATION,
arguments = {@Argument(index = ARGUMENTS_INDEX, callback = ARGUMENTS_CALLBACK, type = ARGUMENTS_TYPE)})})
private String testField;
+ //TODO remove this test
@Test
public void testStaticConstructor() throws NoSuchFieldException {
Method[] methods = this.getClass().getDeclaredField("testField").getAnnotation(Reference.class).methods();
@@ -82,9 +87,12 @@ public class MethodConfigTest {
assertThat(EXECUTES, equalTo(methodConfig.getExecutes().intValue()));
assertThat(DEPERECATED, equalTo(methodConfig.getDeprecated()));
assertThat(STICKY, equalTo(methodConfig.getSticky()));
- assertThat(ONINVOKE, equalTo(methodConfig.getOninvoke()));
- assertThat(ONTHROW, equalTo(methodConfig.getOnthrow()));
- assertThat(ONRETURN, equalTo(methodConfig.getOnreturn()));
+// assertThat(ONINVOKE, equalTo(methodConfig.getOninvoke()));
+// assertThat(ONINVOKE_METHOD, equalTo(methodConfig.getOninvokeMethod()));
+// assertThat(ONTHROW, equalTo(methodConfig.getOnthrow()));
+// assertThat(ONTHROW_METHOD, equalTo(methodConfig.getOnthrowMethod()));
+// assertThat(ONRETURN, equalTo(methodConfig.getOnreturn()));
+// assertThat(ONRETURN_METHOD, equalTo(methodConfig.getOnreturnMethod()));
assertThat(CACHE, equalTo(methodConfig.getCache()));
assertThat(VALIDATION, equalTo(methodConfig.getValidation()));
assertThat(ARGUMENTS_INDEX, equalTo(methodConfig.getArguments().get(0).getIndex().intValue()));
diff --git a/dubbo-config/dubbo-config-spring/pom.xml b/dubbo-config/dubbo-config-spring/pom.xml
index c1255cce11..b96ab268e9 100644
--- a/dubbo-config/dubbo-config-spring/pom.xml
+++ b/dubbo-config/dubbo-config-spring/pom.xml
@@ -27,6 +27,7 @@
The spring config module of dubbo projectfalse
+ 2.3.1.RELEASE
@@ -80,6 +81,7 @@
org.apache.dubbodubbo-rpc-injvm${project.parent.version}
+ testorg.apache.dubbo
@@ -118,17 +120,23 @@
spring-testtest
-
- junit
- junit
- 4.12
- test
- org.apache.tomcat.embedtomcat-embed-coretest
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring-boot.version}
+ test
+
+
+ junit-vintage-engine
+ org.junit.vintage
+
+
+
@@ -169,6 +177,21 @@
nacos-clienttest
-
+
+
+
+
+ src/test/resources
+
+
+ src/test/java
+
+ **/*.xml
+ **/*.yml
+ **/*.properties
+
+
+
+
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/Constants.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/Constants.java
new file mode 100644
index 0000000000..2a525c67a1
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/Constants.java
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+/**
+ * Constants of dubbo spring config
+ */
+public interface Constants {
+
+ String REFERENCE_PROPS = "referenceProps";
+}
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 a3a4c5026f..2b07ebfde3 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,56 +16,124 @@
*/
package org.apache.dubbo.config.spring;
-import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.utils.Assert;
-import org.apache.dubbo.common.utils.ReflectUtils;
-import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.common.utils.ClassUtils;
import org.apache.dubbo.config.ReferenceConfig;
+import org.apache.dubbo.config.ReferenceConfigBase;
+import org.apache.dubbo.config.spring.reference.ReferenceBeanManager;
+import org.apache.dubbo.config.spring.reference.ReferenceBeanSupport;
+import org.apache.dubbo.config.spring.reference.ReferenceAttributes;
import org.apache.dubbo.config.support.Parameter;
import org.apache.dubbo.config.utils.ReferenceConfigCache;
import org.apache.dubbo.rpc.proxy.AbstractProxyFactory;
-import org.apache.dubbo.rpc.support.ProtocolUtils;
-
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;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
+import java.util.LinkedHashMap;
import java.util.Map;
/**
- * ReferenceFactoryBean
+ *
+ * Spring FactoryBean for {@link ReferenceConfig}.
+ *
+ *
+ *
+ * @see org.apache.dubbo.config.annotation.DubboReference
+ * @see org.apache.dubbo.config.spring.reference.ReferenceBeanBuilder
*/
public class ReferenceBean implements FactoryBean,
- ApplicationContextAware, BeanClassLoaderAware, InitializingBean, DisposableBean {
+ ApplicationContextAware, BeanClassLoaderAware, BeanNameAware, InitializingBean, DisposableBean {
private transient ApplicationContext applicationContext;
+
private ClassLoader beanClassLoader;
- private DubboReferenceLazyInitTargetSource referenceTargetSource;
- private Object referenceLazyProxy;
+
+ // lazy proxy of reference
+ private Object lazyProxy;
+
+ // beanName
+ protected String id;
+
+ // reference key
+ private String key;
+
/**
* The interface class of the reference service
*/
- protected Class> interfaceClass;
+ private Class> interfaceClass;
+
+ /**
+ * Actual interface class of this reference.
+ * The actual service type of remote provider.
+ * see {@link ReferenceConfigBase#getActualInterface()}
+ */
+ private Class actualInterface;
+
+ /*
+ * actual interface class name
+ */
+ // Compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc()
+ private String interfaceName;
- //beanName
- protected String id;
//from annotation attributes
private Map referenceProps;
- //from bean definition
+
+ //from xml bean definition
private MutablePropertyValues propertyValues;
+
//actual reference config
private ReferenceConfig referenceConfig;
- private String generic;
- private String interfaceName;
public ReferenceBean() {
super();
@@ -85,12 +153,17 @@ public class ReferenceBean implements FactoryBean,
this.beanClassLoader = classLoader;
}
+ @Override
+ public void setBeanName(String name) {
+ this.setId(name);
+ }
+
@Override
public Object getObject() {
- if (referenceLazyProxy == null) {
- createReferenceLazyProxy();
+ if (lazyProxy == null) {
+ createLazyProxy();
}
- return referenceLazyProxy;
+ return lazyProxy;
}
@Override
@@ -106,12 +179,43 @@ public class ReferenceBean implements FactoryBean,
@Override
public void afterPropertiesSet() throws Exception {
- if (referenceProps == null) {
- Assert.notEmptyString(getId(), "The id of ReferenceBean cannot be empty");
- ConfigurableListableBeanFactory beanFactory = getBeanFactory();
- BeanDefinition beanDefinition = beanFactory.getBeanDefinition(getId());
- propertyValues = beanDefinition.getPropertyValues();
+ ConfigurableListableBeanFactory beanFactory = getBeanFactory();
+
+ // pre init xml reference bean or @DubboReference annotation
+ Assert.notEmptyString(getId(), "The id of ReferenceBean cannot be empty");
+ BeanDefinition beanDefinition = beanFactory.getBeanDefinition(getId());
+ this.interfaceClass = (Class>) beanDefinition.getAttribute(ReferenceAttributes.INTERFACE_CLASS);
+ this.actualInterface = (Class) beanDefinition.getAttribute(ReferenceAttributes.ACTUAL_INTERFACE);
+ Assert.notNull(this.interfaceClass, "The interface class of ReferenceBean is not initialized");
+
+ if (beanDefinition.hasAttribute(Constants.REFERENCE_PROPS)) {
+ // @DubboReference annotation at java-config class @Bean method
+ // @DubboReference annotation at reference field or setter method
+ referenceProps = (Map) beanDefinition.getAttribute(Constants.REFERENCE_PROPS);
+ } else {
+ if (beanDefinition instanceof AnnotatedBeanDefinition) {
+ // Return ReferenceBean in java-config class @Bean method
+ if (referenceProps == null) {
+ referenceProps = new LinkedHashMap<>();
+ }
+ ReferenceBeanSupport.convertReferenceProps(referenceProps, interfaceClass);
+ if (this.actualInterface == null) {
+ try {
+ this.actualInterface = ClassUtils.forName((String) referenceProps.get(ReferenceAttributes.INTERFACE));
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+ } else {
+ // xml reference bean
+ propertyValues = beanDefinition.getPropertyValues();
+ }
}
+ Assert.notNull(this.actualInterface, "The actual interface of ReferenceBean is not initialized");
+ this.interfaceName = actualInterface.getName();
+
+ ReferenceBeanManager referenceBeanManager = beanFactory.getBean(ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class);
+ referenceBeanManager.addReference(this);
}
private ConfigurableListableBeanFactory getBeanFactory() {
@@ -123,14 +227,9 @@ public class ReferenceBean implements FactoryBean,
// do nothing
}
- /**
- * TODO remove get() method
- *
- * @return
- */
@Deprecated
public Object get() {
- throw new UnsupportedOperationException("Should not call this method");
+ return referenceConfig.get();
}
public String getId() {
@@ -141,17 +240,27 @@ public class ReferenceBean implements FactoryBean,
this.id = id;
}
- /* Compatible with seata: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc() */
- @Deprecated
- public String getGroup() {
- Object version = propertyValues.get(CommonConstants.GROUP_KEY);
- return version == null ? null : String.valueOf(version);
+ /* Compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc() */
+ public Class> getInterfaceClass() {
+ return interfaceClass;
}
- @Deprecated
+ public Class getActualInterface() {
+ return actualInterface;
+ }
+
+ /* Compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc() */
+ public String getGroup() {
+ return referenceConfig.getGroup();
+ }
+
+ /* Compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc() */
public String getVersion() {
- Object version = propertyValues.get(CommonConstants.VERSION_KEY);
- return version == null ? null : String.valueOf(version);
+ return referenceConfig.getVersion();
+ }
+
+ public String getKey() {
+ return key;
}
public Map getReferenceProps() {
@@ -166,74 +275,36 @@ public class ReferenceBean implements FactoryBean,
return referenceConfig;
}
- public void setReferenceConfig(ReferenceConfig referenceConfig) {
+ public void setKeyAndReferenceConfig(String key, ReferenceConfig referenceConfig) {
+ this.key = key;
this.referenceConfig = referenceConfig;
}
- public Class> getInterfaceClass() {
- // get interface class
- if (interfaceClass == null) {
- if (referenceProps != null) {
- //get interface class name of @DubboReference
- String interfaceName = (String) referenceProps.get("interfaceName");
- if (interfaceName == null) {
- Class clazz = (Class) referenceProps.get("interfaceClass");
- if (clazz != null) {
- interfaceName = clazz.getName();
- }
- }
- if (StringUtils.isBlank(interfaceName)) {
- throw new RuntimeException("Need to specify the 'interfaceName' or 'interfaceClass' attribute of '@DubboReference'");
- }
- this.interfaceName = interfaceName;
-
- //get generic
- Object genericValue = referenceProps.get("generic");
- generic = genericValue != null ? genericValue.toString() : null;
- String consumer = (String) referenceProps.get("consumer");
- if (StringUtils.isBlank(generic) && consumer != null) {
- // get generic from consumerConfig
- BeanDefinition consumerBeanDefinition = getBeanFactory().getBeanDefinition(consumer);
- if (consumerBeanDefinition != null) {
- generic = (String) consumerBeanDefinition.getPropertyValues().get("generic");
- }
- }
- } else if (propertyValues != null) {
- generic = (String) propertyValues.get("generic");
- interfaceName = (String) propertyValues.get("interface");
- } else {
- throw new RuntimeException("Required 'referenceProps' or beanDefinition");
- }
-
- interfaceClass = ReferenceConfig.determineInterfaceClass(generic, interfaceName);
- }
- return interfaceClass;
- }
-
- private void createReferenceLazyProxy() {
- this.referenceTargetSource = new DubboReferenceLazyInitTargetSource();
+ /**
+ * create lazy proxy for reference
+ */
+ private void createLazyProxy() {
//set proxy interfaces
//see also: org.apache.dubbo.rpc.proxy.AbstractProxyFactory.getProxy(org.apache.dubbo.rpc.Invoker, boolean)
ProxyFactory proxyFactory = new ProxyFactory();
- proxyFactory.setTargetSource(referenceTargetSource);
+ proxyFactory.setTargetSource(new DubboReferenceLazyInitTargetSource());
proxyFactory.addInterface(getInterfaceClass());
Class>[] internalInterfaces = AbstractProxyFactory.getInternalInterfaces();
for (Class> anInterface : internalInterfaces) {
proxyFactory.addInterface(anInterface);
}
- if (ProtocolUtils.isGeneric(generic)) {
+ if (actualInterface != interfaceClass){
//add actual interface
- proxyFactory.addInterface(ReflectUtils.forName(interfaceName));
+ proxyFactory.addInterface(actualInterface);
}
- this.referenceLazyProxy = proxyFactory.getProxy(this.beanClassLoader);
+ this.lazyProxy = proxyFactory.getProxy(this.beanClassLoader);
}
private Object getCallProxy() throws Exception {
-
if (referenceConfig == null) {
- throw new IllegalStateException("ReferenceBean is not ready yet, maybe dubbo engine is not started");
+ throw new IllegalStateException("ReferenceBean is not ready yet, please make sure to call reference interface method after dubbo is started.");
}
//get reference proxy
return ReferenceConfigCache.getCache().get(referenceConfig);
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBeanManager.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBeanManager.java
deleted file mode 100644
index 46be2e5e82..0000000000
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ReferenceBeanManager.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * 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;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.dubbo.common.utils.Assert;
-import org.apache.dubbo.config.ArgumentConfig;
-import org.apache.dubbo.config.MethodConfig;
-import org.apache.dubbo.config.ReferenceConfig;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.config.spring.beans.factory.annotation.AnnotationPropertyValuesAdapter;
-import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceBeanBuilder;
-import org.springframework.beans.BeansException;
-import org.springframework.beans.MutablePropertyValues;
-import org.springframework.beans.PropertyValue;
-import org.springframework.beans.factory.config.BeanDefinition;
-import org.springframework.beans.factory.config.BeanDefinitionHolder;
-import org.springframework.beans.factory.config.RuntimeBeanReference;
-import org.springframework.beans.factory.config.TypedStringValue;
-import org.springframework.beans.factory.support.ManagedList;
-import org.springframework.beans.factory.support.ManagedMap;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
-import org.springframework.core.annotation.AnnotationAttributes;
-import org.springframework.core.env.Environment;
-import org.springframework.core.env.PropertyResolver;
-import org.springframework.validation.DataBinder;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-public class ReferenceBeanManager implements ApplicationContextAware {
- public static final String BEAN_NAME = "dubboReferenceBeanManager";
- private final Log logger = LogFactory.getLog(getClass());
- private Map configMap = new ConcurrentHashMap<>();
- private ApplicationContext applicationContext;
- private volatile boolean initialized = false;
-
-
- public void addReference(ReferenceBean referenceBean) throws Exception {
- Assert.notNull(referenceBean.getId(), "The id of ReferenceBean cannot be empty");
- //TODO generate reference bean id and unique cache key
- String key = referenceBean.getId();
- ReferenceBean oldReferenceBean = configMap.get(key);
- if (oldReferenceBean != null) {
- if (referenceBean != oldReferenceBean) {
- logger.warn("Found duplicated ReferenceBean id: " + key);
- }
- return;
- }
- configMap.put(key, referenceBean);
-
- // if add reference after prepareReferenceBeans(), should init it immediately.
- if (initialized) {
- initReferenceBean(referenceBean);
- }
- }
-
- public ReferenceBean get(String id) {
- return configMap.get(id);
- }
-
- public Collection getReferences() {
- return configMap.values();
- }
-
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- this.applicationContext = applicationContext;
- }
-
- /**
- * Initialize all reference beans, call at Dubbo starting
- * @throws Exception
- */
- public void prepareReferenceBeans() throws Exception {
- // prepare all reference beans
- Map referenceBeanMap = applicationContext.getBeansOfType(ReferenceBean.class, true, false);
- for (ReferenceBean referenceBean : referenceBeanMap.values()) {
- addReference(referenceBean);
- }
-
- for (ReferenceBean referenceBean : getReferences()) {
- initReferenceBean(referenceBean);
- }
- initialized = true;
- }
-
- /**
- * NOTE: This method should only call after all dubbo config beans and all property resolvers is loaded.
- *
- * @param referenceBean
- * @throws Exception
- */
- private void initReferenceBean(ReferenceBean referenceBean) throws Exception {
-
- if (referenceBean.getReferenceConfig() != null) {
- return;
- }
-
- Environment environment = applicationContext.getEnvironment();
- Map referenceProps = referenceBean.getReferenceProps();
- if (referenceProps == null) {
- MutablePropertyValues propertyValues = referenceBean.getPropertyValues();
- if (propertyValues == null) {
- throw new RuntimeException("ReferenceBean is invalid, missing 'propertyValues'");
- }
- referenceProps = toReferenceProps(propertyValues, environment);
- }
-
- //resolve placeholders
- resolvePlaceholders(referenceProps, environment);
-
- //create real ReferenceConfig
- ReferenceConfig referenceConfig = ReferenceBeanBuilder.create(new AnnotationAttributes(new LinkedHashMap<>(referenceProps)), applicationContext)
- .defaultInterfaceClass(referenceBean.getObjectType())
- .build();
-
- referenceBean.setReferenceConfig(referenceConfig);
-
- // register ReferenceConfig
- DubboBootstrap.getInstance().reference(referenceConfig);
-
- }
-
- private void resolvePlaceholders(Map referenceProps, PropertyResolver propertyResolver) {
- for (Map.Entry entry : referenceProps.entrySet()) {
- Object value = entry.getValue();
- if (value instanceof String) {
- String valueToResovle = (String) value;
- entry.setValue(propertyResolver.resolveRequiredPlaceholders(valueToResovle));
- } else if (value instanceof String[]) {
- String[] strings = (String[]) value;
- for (int i = 0; i < strings.length; i++) {
- strings[i] = propertyResolver.resolveRequiredPlaceholders(strings[i]);
- }
- entry.setValue(strings);
- }
- }
- }
-
- private Map toReferenceProps(MutablePropertyValues propertyValues, PropertyResolver propertyResolver) {
- Map referenceProps;
- referenceProps = new LinkedHashMap<>();
- for (PropertyValue propertyValue : propertyValues.getPropertyValueList()) {
- String propertyName = propertyValue.getName();
- Object value = propertyValue.getValue();
- if ("methods".equals(propertyName)) {
- ManagedList managedList = (ManagedList) value;
- List methodConfigs = new ArrayList<>();
- for (Object el : managedList) {
- MethodConfig methodConfig = createMethodConfig(((BeanDefinitionHolder) el).getBeanDefinition(), propertyResolver);
- methodConfigs.add(methodConfig);
- }
- value = methodConfigs.toArray(new MethodConfig[0]);
- } else if ("parameters".equals(propertyName)) {
- value = createParameterMap((ManagedMap) value, propertyResolver);
- }
- if (value instanceof RuntimeBeanReference) {
- RuntimeBeanReference beanReference = (RuntimeBeanReference) value;
- value = applicationContext.getBean(beanReference.getBeanName());
- }
- referenceProps.put(propertyName, value);
- }
- return referenceProps;
- }
-
- private MethodConfig createMethodConfig(BeanDefinition beanDefinition, PropertyResolver propertyResolver) {
- Map attributes = new LinkedHashMap<>();
- MutablePropertyValues pvs = beanDefinition.getPropertyValues();
- for (PropertyValue propertyValue : pvs.getPropertyValueList()) {
- String propertyName = propertyValue.getName();
- Object value = propertyValue.getValue();
- if ("arguments".equals(propertyName)) {
- ManagedList managedList = (ManagedList) value;
- List argumentConfigs = new ArrayList<>();
- for (Object el : managedList) {
- ArgumentConfig argumentConfig = createArgumentConfig(((BeanDefinitionHolder) el).getBeanDefinition(), propertyResolver);
- argumentConfigs.add(argumentConfig);
- }
- value = argumentConfigs.toArray(new ArgumentConfig[0]);
- } else if ("parameters".equals(propertyName)) {
- value = createParameterMap((ManagedMap) value, propertyResolver);
- }
-
- if (value instanceof RuntimeBeanReference) {
- RuntimeBeanReference beanReference = (RuntimeBeanReference) value;
- value = applicationContext.getBean(beanReference.getBeanName());
- }
- attributes.put(propertyName, value);
- }
- MethodConfig methodConfig = new MethodConfig();
- DataBinder dataBinder = new DataBinder(methodConfig);
- dataBinder.bind(new AnnotationPropertyValuesAdapter(attributes, propertyResolver));
- return methodConfig;
- }
-
- private ArgumentConfig createArgumentConfig(BeanDefinition beanDefinition, PropertyResolver propertyResolver) {
- ArgumentConfig argumentConfig = new ArgumentConfig();
- DataBinder dataBinder = new DataBinder(argumentConfig);
- dataBinder.bind(beanDefinition.getPropertyValues());
- return argumentConfig;
- }
-
- private Map createParameterMap(ManagedMap managedMap, PropertyResolver propertyResolver) {
- Map map = new LinkedHashMap<>();
- Set> entrySet = managedMap.entrySet();
- for (Map.Entry entry : entrySet) {
- map.put(entry.getKey(), entry.getValue().getValue());
- }
- return map;
- }
-
-
-}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationBeanPostProcessor.java
index 2581b1e0c0..07712440cf 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationBeanPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationBeanPostProcessor.java
@@ -26,7 +26,6 @@ import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
-import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.InjectionMetadata;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -34,8 +33,8 @@ import org.springframework.beans.factory.config.InstantiationAwareBeanPostProces
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.EnvironmentAware;
+import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
-import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
@@ -65,7 +64,7 @@ import static org.springframework.core.BridgeMethodResolver.isVisibilityBridgeMe
*/
@SuppressWarnings("unchecked")
public abstract class AbstractAnnotationBeanPostProcessor extends
- InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered,
+ InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, Ordered,
BeanFactoryAware, BeanClassLoaderAware, EnvironmentAware, DisposableBean {
private final static int CACHE_SIZE = Integer.getInteger("", 32);
@@ -85,10 +84,7 @@ public abstract class AbstractAnnotationBeanPostProcessor extends
private ClassLoader classLoader;
- /**
- * make sure higher priority than {@link AutowiredAnnotationBeanPostProcessor}
- */
- private int order = Ordered.LOWEST_PRECEDENCE - 3;
+ private int order = Ordered.LOWEST_PRECEDENCE;
/**
* @param annotationTypes the multiple types of {@link Annotation annotations}
@@ -136,20 +132,20 @@ public abstract class AbstractAnnotationBeanPostProcessor extends
try {
prepareInjection(metadata);
} catch (Exception e) {
- logger.warn("Prepare injection of @"+getAnnotationType().getSimpleName()+" failed", e);
+ logger.error("Prepare injection of @"+getAnnotationType().getSimpleName()+" failed", e);
}
}
}
@Override
public PropertyValues postProcessPropertyValues(
- PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
+ PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
try {
AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
prepareInjection(metadata);
metadata.inject(bean, beanName, pvs);
- } catch (BeanCreationException ex) {
+ } catch (BeansException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getSimpleName()
@@ -216,6 +212,10 @@ public abstract class AbstractAnnotationBeanPostProcessor extends
return;
}
+ if (method.getAnnotation(Bean.class) != null) {
+ // DO NOT inject to Java-config class's @Bean method
+ return;
+ }
for (Class extends Annotation> annotationType : getAnnotationTypes()) {
@@ -223,16 +223,10 @@ public abstract class AbstractAnnotationBeanPostProcessor extends
if (attributes != null && method.equals(ClassUtils.getMostSpecificMethod(method, beanClass))) {
if (Modifier.isStatic(method.getModifiers())) {
- if (logger.isWarnEnabled()) {
- logger.warn("@" + annotationType.getName() + " annotation is not supported on static methods: " + method);
- }
- return;
+ throw new IllegalStateException("When using @"+annotationType.getName() +" to inject interface proxy, it is not supported on static methods: "+method);
}
- if (method.getParameterTypes().length == 0) {
- if (logger.isWarnEnabled()) {
- logger.warn("@" + annotationType.getName() + " annotation should only be used on methods with parameters: " +
- method);
- }
+ if (method.getParameterTypes().length != 1) {
+ throw new IllegalStateException("When using @"+annotationType.getName() +" to inject interface proxy, the method must have only one parameter: "+method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, beanClass);
elements.add(new AnnotatedMethodElement(method, pd, attributes));
@@ -244,7 +238,6 @@ public abstract class AbstractAnnotationBeanPostProcessor extends
return elements;
}
-
private AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class> beanClass) {
Collection fieldElements = findFieldAnnotationMetadata(beanClass);
Collection methodElements = findAnnotatedMethodMetadata(beanClass);
@@ -259,6 +252,7 @@ public abstract class AbstractAnnotationBeanPostProcessor extends
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
+
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
@@ -343,18 +337,18 @@ public abstract class AbstractAnnotationBeanPostProcessor extends
protected Object getInjectedObject(AnnotationAttributes attributes, Object bean, String beanName, Class> injectedType,
AnnotatedInjectElement injectedElement) throws Exception {
- String cacheKey = buildInjectedObjectCacheKey(attributes, bean, beanName, injectedType, injectedElement);
-
- Object injectedObject = injectedObjectsCache.get(cacheKey);
-
- if (injectedObject == null) {
- injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement);
- // Customized inject-object if necessary
- injectedObjectsCache.put(cacheKey, injectedObject);
- }
-
- return injectedObject;
+// String cacheKey = buildInjectedObjectCacheKey(attributes, bean, beanName, injectedType, injectedElement);
+//
+// Object injectedObject = injectedObjectsCache.get(cacheKey);
+//
+// if (injectedObject == null) {
+// injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement);
+// // Customized inject-object if necessary
+// injectedObjectsCache.put(cacheKey, injectedObject);
+// }
+// return injectedObject;
+ return doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement);
}
/**
@@ -401,15 +395,16 @@ public abstract class AbstractAnnotationBeanPostProcessor extends
* @param injectedElement {@link AnnotatedInjectElement}
* @return Bean cache key
*/
- protected abstract String buildInjectedObjectCacheKey(AnnotationAttributes attributes, Object bean, String beanName,
- Class> injectedType,
- AnnotatedInjectElement injectedElement);
+// protected abstract String buildInjectedObjectCacheKey(AnnotationAttributes attributes, Object bean, String beanName,
+// Class> injectedType,
+// AnnotatedInjectElement injectedElement);
/**
* {@link Annotation Annotated} {@link InjectionMetadata} implementation
*/
protected class AnnotatedInjectionMetadata extends InjectionMetadata {
+ private Class> targetClass;
private final Collection fieldElements;
private final Collection methodElements;
@@ -417,6 +412,7 @@ public abstract class AbstractAnnotationBeanPostProcessor extends
public AnnotatedInjectionMetadata(Class> targetClass, Collection fieldElements,
Collection methodElements) {
super(targetClass, combine(fieldElements, methodElements));
+ this.targetClass = targetClass;
this.fieldElements = fieldElements;
this.methodElements = methodElements;
}
@@ -428,6 +424,18 @@ public abstract class AbstractAnnotationBeanPostProcessor extends
public Collection getMethodElements() {
return methodElements;
}
+
+ @Override
+ protected boolean needsRefresh(Class> clazz) {
+ if (this.targetClass == clazz) {
+ return false;
+ }
+ //IGNORE Spring CGLIB enhanced class
+ if (targetClass.isAssignableFrom(clazz) && clazz.getName().contains("$$EnhancerBySpringCGLIB$$")) {
+ return false;
+ }
+ return true;
+ }
}
/**
@@ -437,7 +445,9 @@ public abstract class AbstractAnnotationBeanPostProcessor extends
protected final AnnotationAttributes attributes;
- protected volatile String refKey;
+ protected volatile Object injectedObject;
+
+ private Class> injectedType;
protected AnnotatedInjectElement(Member member, PropertyDescriptor pd, AnnotationAttributes attributes) {
super(member, pd);
@@ -447,8 +457,7 @@ public abstract class AbstractAnnotationBeanPostProcessor extends
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
- Class> injectedType = getResourceType();
- Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this);
+ Object injectedObject = getInjectedObject(attributes, bean, beanName, getInjectedType(), this);
if (member instanceof Field) {
Field field = (Field) member;
@@ -461,8 +470,37 @@ public abstract class AbstractAnnotationBeanPostProcessor extends
}
}
- public Class> getInjectedType() {
- return getResourceType();
+ public Class> getInjectedType() throws ClassNotFoundException {
+ if (injectedType == null) {
+ if (this.isField) {
+ injectedType = ((Field) this.member).getType();
+ }
+ else if (this.pd != null) {
+ return this.pd.getPropertyType();
+ }
+ else {
+ Method method = (Method) this.member;
+ if (method.getParameterTypes().length > 0) {
+ injectedType = method.getParameterTypes()[0];
+ } else {
+ throw new IllegalStateException("get injected type failed");
+ }
+ }
+ }
+ return injectedType;
+ }
+
+ public String getPropertyName() {
+ if (member instanceof Field) {
+ Field field = (Field) member;
+ return field.getName();
+ } else if (this.pd != null) {
+ // If it is method element, using propertyName of PropertyDescriptor
+ return pd.getName();
+ } else {
+ Method method = (Method) this.member;
+ return method.getName();
+ }
}
}
@@ -484,6 +522,5 @@ public abstract class AbstractAnnotationBeanPostProcessor extends
super(field, null, attributes);
this.field = field;
}
-
}
}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
index e6a97153fb..b4231f3ee6 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
@@ -18,48 +18,65 @@ package org.apache.dubbo.config.spring.beans.factory.annotation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.dubbo.common.utils.Assert;
import org.apache.dubbo.common.utils.ClassUtils;
+import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.Reference;
-import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.config.context.ConfigManager;
+import org.apache.dubbo.config.spring.Constants;
+import org.apache.dubbo.config.spring.reference.ReferenceAttributes;
import org.apache.dubbo.config.spring.ReferenceBean;
-import org.apache.dubbo.config.spring.ReferenceBeanManager;
-import org.apache.dubbo.config.spring.ServiceBean;
-import org.apache.dubbo.config.spring.util.DubboBeanUtils;
+import org.apache.dubbo.config.spring.reference.ReferenceBeanManager;
+import org.apache.dubbo.config.spring.reference.ReferenceBeanSupport;
+import org.apache.dubbo.rpc.service.GenericService;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.InjectionMetadata;
import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
-import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
+import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationAttributes;
-import org.springframework.util.Assert;
-import org.springframework.util.ObjectUtils;
+import org.springframework.core.annotation.MergedAnnotations;
+import org.springframework.core.type.MethodMetadata;
import java.beans.PropertyDescriptor;
-import java.util.ArrayList;
-import java.util.Arrays;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Member;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static com.alibaba.spring.util.AnnotationUtils.getAttribute;
-import static org.apache.dubbo.config.spring.beans.factory.annotation.ServiceBeanNameBuilder.create;
import static org.springframework.util.StringUtils.hasText;
/**
- * {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
- * that Consumer service {@link Reference} annotated fields
+ *
+ * Step 1:
+ * The purpose of implementing {@link BeanFactoryPostProcessor} is to scan the registration reference bean definition earlier,
+ * so that it can be shared with the xml bean configuration.
+ *
+ *
+ *
+ * Step 2:
+ * By implementing {@link org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor},
+ * inject the reference bean instance into the fields and setter methods which annotated with {@link DubboReference}.
+ *
*
* @see DubboReference
* @see Reference
@@ -67,7 +84,7 @@ import static org.springframework.util.StringUtils.hasText;
* @since 2.5.7
*/
public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor
- implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor {
+ implements ApplicationContextAware, BeanFactoryPostProcessor {
/**
* The bean name of {@link ReferenceAnnotationBeanPostProcessor}
@@ -81,10 +98,10 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
private final Log logger = LogFactory.getLog(getClass());
- private final ConcurrentMap> injectedFieldReferenceBeanCache =
+ private final ConcurrentMap injectedFieldReferenceBeanCache =
new ConcurrentHashMap<>(CACHE_SIZE);
- private final ConcurrentMap> injectedMethodReferenceBeanCache =
+ private final ConcurrentMap injectedMethodReferenceBeanCache =
new ConcurrentHashMap<>(CACHE_SIZE);
private ApplicationContext applicationContext;
@@ -101,14 +118,8 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
super(DubboReference.class, Reference.class, com.alibaba.dubbo.config.annotation.Reference.class);
}
- @Override
- public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
- this.beanDefinitionRegistry = registry;
- }
-
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
- DubboBeanUtils.registerBeansIfNotExists(beanDefinitionRegistry);
String[] beanNames = beanFactory.getBeanDefinitionNames();
for (String beanName : beanNames) {
@@ -118,7 +129,17 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
if (isReferenceBean(beanDefinition)) {
continue;
}
+ if (isAnnotatedReferenceBean(beanDefinition)) {
+ // process @DubboReference at java-config @bean method
+ processReferenceAnnotatedBeanDefinition(beanName, (AnnotatedBeanDefinition) beanDefinition);
+ continue;
+ }
+
String beanClassName = beanDefinition.getBeanClassName();
+// if (beanDefinition instanceof AnnotatedBeanDefinition) {
+// AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition;
+// beanClassName = annotatedBeanDefinition.getFactoryMethodMetadata().getDeclaringClassName();
+// }
beanType = ClassUtils.resolveClass(beanClassName, getClassLoader());
} else {
beanType = beanFactory.getType(beanName);
@@ -127,13 +148,118 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null);
try {
prepareInjection(metadata);
+ } catch (BeansException e) {
+ throw e;
} catch (Exception e) {
- logger.warn("Prepare dubbo reference injection element failed", e);
+ throw new RuntimeException("Prepare dubbo reference injection element failed", e);
}
}
}
}
+ /**
+ * check whether is @DubboReference at java-config @bean method
+ */
+ private boolean isAnnotatedReferenceBean(BeanDefinition beanDefinition) {
+ if (beanDefinition instanceof AnnotatedBeanDefinition) {
+ AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition;
+ String beanClassName = annotatedBeanDefinition.getFactoryMethodMetadata().getReturnTypeName();
+ if (ReferenceBean.class.getName().equals(beanClassName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * process @DubboReference at java-config @bean method
+ *
+ * @Configuration
+ * public class ConsumerConfig {
+ *
+ * @Bean
+ * @DubboReference(group="demo", version="1.2.3")
+ * public ReferenceBean<DemoService> demoService() {
+ * return new ReferenceBean();
+ * }
+ *
+ * }
+ *
+ * @param beanName
+ * @param beanDefinition
+ */
+ private void processReferenceAnnotatedBeanDefinition(String beanName, AnnotatedBeanDefinition beanDefinition) {
+
+ // Extract beanClass from generic return type of java-config bean method: ReferenceBean
+ // see org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBeanFromMethod
+ Class beanClass = getBeanFactory().getType(beanName);
+ if (beanClass == null) {
+ MethodMetadata factoryMethodMetadata = beanDefinition.getFactoryMethodMetadata();
+ String beanMethodSignature = factoryMethodMetadata.getDeclaringClassName()+"#"+factoryMethodMetadata.getMethodName()+"()";
+ throw new BeanCreationException("The ReferenceBean is missing necessary generic type, which returned by the @Bean method of Java-config class. " +
+ "The generic type of the returned ReferenceBean must be specified as the referenced interface type, " +
+ "such as ReferenceBean. Please check bean method: "+beanMethodSignature);
+ }
+
+ // get dubbo reference annotation attributes
+ Map annotationAttributes = null;
+ MergedAnnotations mergedAnnotations = beanDefinition.getFactoryMethodMetadata().getAnnotations();
+ Class referenceAnnotationType = null;
+ // try all dubbo reference annotation types
+ for (Class extends Annotation> annotationType : getAnnotationTypes()) {
+ if (mergedAnnotations.isPresent(annotationType)) {
+ referenceAnnotationType = annotationType;
+ annotationAttributes = mergedAnnotations.get(annotationType).filterDefaultValues().asMap();
+ break;
+ }
+ }
+
+ if (annotationAttributes != null) {
+ // @DubboReference on @Bean method
+ LinkedHashMap attributes = new LinkedHashMap<>(annotationAttributes);
+ // reset id attribute
+ attributes.put(ReferenceAttributes.ID, beanName);
+ // convert annotation props
+ ReferenceBeanSupport.convertReferenceProps(attributes, beanClass);
+
+ // get interface
+ String interfaceName = (String) attributes.get(ReferenceAttributes.INTERFACE);
+ // check beanClass and reference interface class
+ if (!StringUtils.isEquals(interfaceName, beanClass.getName()) && beanClass != GenericService.class) {
+ MethodMetadata factoryMethodMetadata = beanDefinition.getFactoryMethodMetadata();
+ String beanMethodSignature = factoryMethodMetadata.getDeclaringClassName()+"#"+factoryMethodMetadata.getMethodName()+"()";
+ throw new BeanCreationException("The 'interfaceClass' or 'interfaceName' attribute value of @DubboReference annotation " +
+ "is inconsistent with the generic type of the ReferenceBean returned by the bean method. " +
+ "The interface class of @DubboReference is: "+interfaceName+", but return ReferenceBean<"+beanClass.getName()+">. " +
+ "Please remove the 'interfaceClass' and 'interfaceName' attributes from @DubboReference annotation. " +
+ "Please check bean method: "+beanMethodSignature);
+ }
+
+ Class interfaceClass = beanClass;
+ Class actualInterface = null;
+ try {
+ actualInterface = ClassUtils.forName(interfaceName);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+
+ // set attribute instead of property values
+ beanDefinition.setAttribute(Constants.REFERENCE_PROPS, attributes);
+ beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, interfaceClass);
+ beanDefinition.setAttribute(ReferenceAttributes.ACTUAL_INTERFACE, actualInterface);
+ } else {
+ // raw reference bean
+ // the ReferenceBean is not yet initialized
+ beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, beanClass);
+ if (beanClass != GenericService.class) {
+ beanDefinition.setAttribute(ReferenceAttributes.ACTUAL_INTERFACE, beanClass);
+ }
+ }
+
+ // set id
+ beanDefinition.getPropertyValues().add(ReferenceAttributes.ID, beanName);
+ }
+
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class> beanType, String beanName) {
if (beanType != null) {
@@ -143,27 +269,35 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
for (PropertyValue propertyValue : propertyValues) {
propertyValue.setOptional(true);
}
+ } else if (isAnnotatedReferenceBean(beanDefinition)) {
+ // extract beanClass from java-config bean method generic return type: ReferenceBean
+ //Class beanClass = getBeanFactory().getType(beanName);
} else {
AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
try {
prepareInjection(metadata);
} catch (Exception e) {
- logger.warn("Prepare dubbo reference injection element failed", e);
+ throw new RuntimeException("Prepare dubbo reference injection element failed", e);
}
}
}
}
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ return super.postProcessBeforeInitialization(bean, beanName);
+ }
+
@Override
public PropertyValues postProcessPropertyValues(
- PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
+ PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
try {
AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
prepareInjection(metadata);
metadata.inject(bean, beanName, pvs);
- } catch (BeanCreationException ex) {
+ } catch (BeansException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getSimpleName()
@@ -176,223 +310,187 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
return ReferenceBean.class.getName().equals(beanDefinition.getBeanClassName());
}
- protected void prepareInjection(AnnotatedInjectionMetadata metadata) throws Exception {
- //find and registry bean definition for @DubboReference/@Reference
- for (AnnotatedFieldElement fieldElement : metadata.getFieldElements()) {
- if (fieldElement.refKey != null) {
- continue;
+ protected void prepareInjection(AnnotatedInjectionMetadata metadata) throws BeansException {
+ try {
+ //find and registry bean definition for @DubboReference/@Reference
+ for (AnnotatedFieldElement fieldElement : metadata.getFieldElements()) {
+ if (fieldElement.injectedObject != null) {
+ continue;
+ }
+ Class> injectedType = fieldElement.field.getType();
+ AnnotationAttributes attributes = fieldElement.attributes;
+ String referenceBeanName = registerReferenceBean(fieldElement.getPropertyName(), injectedType, attributes, fieldElement.field);
+
+ //associate fieldElement and reference bean
+ fieldElement.injectedObject = referenceBeanName;
+ injectedFieldReferenceBeanCache.put(fieldElement, referenceBeanName);
+
}
- Class> injectedType = fieldElement.field.getType();
- AnnotationAttributes attributes = fieldElement.attributes;
- ReferenceBean referenceBean = getReferenceBean(injectedType, attributes);
- //associate fieldElement and reference bean
- fieldElement.refKey = referenceBean.getId();
- injectedFieldReferenceBeanCache.put(fieldElement, referenceBean);
+ for (AnnotatedMethodElement methodElement : metadata.getMethodElements()) {
+ if (methodElement.injectedObject != null) {
+ continue;
+ }
+ Class> injectedType = methodElement.getInjectedType();
+ AnnotationAttributes attributes = methodElement.attributes;
+ String referenceBeanName = registerReferenceBean(methodElement.getPropertyName(), injectedType, attributes, methodElement.method);
- }
-
- for (AnnotatedMethodElement methodElement : metadata.getMethodElements()) {
- if (methodElement.refKey != null) {
- continue;
+ //associate fieldElement and reference bean
+ methodElement.injectedObject = referenceBeanName;
+ injectedMethodReferenceBeanCache.put(methodElement, referenceBeanName);
}
- Class> injectedType = methodElement.getInjectedType();
- AnnotationAttributes attributes = methodElement.attributes;
- ReferenceBean referenceBean = getReferenceBean(injectedType, attributes);
-
- //associate fieldElement and reference bean
- methodElement.refKey = referenceBean.getId();
- injectedMethodReferenceBeanCache.put(methodElement, referenceBean);
+ } catch (ClassNotFoundException e) {
+ throw new BeanCreationException("prepare reference annotation failed", e);
}
}
- private ReferenceBean getReferenceBean(Class> injectedType, AnnotationAttributes attributes) throws Exception {
+ public String registerReferenceBean(String propertyName, Class> injectedType, Map attributes, Member member) throws BeansException {
+
+ boolean renameable = true;
// referenceBeanName
- String referenceBeanName = getReferenceBeanName(attributes, injectedType);
-
- // reuse exist reference bean?
- ReferenceBean referenceBean = referenceBeanManager.get(referenceBeanName);
-
- //create referenceBean
- if (referenceBean == null) {
- //handle injvm/localServiceBean
- /**
- * The name of bean that annotated Dubbo's {@link Service @Service} in local Spring {@link ApplicationContext}
- */
- String localServiceBeanName = buildReferencedBeanName(attributes, injectedType);
- boolean localServiceBean = isLocalServiceBean(localServiceBeanName, attributes);
- if (localServiceBean) { // If the local @Service Bean exists
- attributes.put("injvm", Boolean.TRUE);
- // Issue : https://github.com/apache/dubbo/issues/6224
- //exportServiceBeanIfNecessary(localServiceBeanName); // If the referenced ServiceBean exits, export it immediately
- }
-
- //check interfaceClass
- if (attributes.get("interfaceName") == null && attributes.get("interfaceClass") == null) {
- Class> interfaceClass = injectedType;
- Assert.isTrue(interfaceClass.isInterface(),
- "The class of field or method that was annotated @DubboReference is not an interface!");
- attributes.put("interfaceClass", interfaceClass);
- }
-
- //init reference bean
- try {
- //registry referenceBean
- RootBeanDefinition beanDefinition = new RootBeanDefinition();
- beanDefinition.setBeanClassName(ReferenceBean.class.getName());
- //set autowireCandidate to false for local call, avoiding multiple candidate beans for @Autowire
- beanDefinition.setAutowireCandidate(!localServiceBean);
- //beanDefinition.getPropertyValues()
-
- referenceBean = new ReferenceBean(attributes);
- referenceBean.setId(referenceBeanName);
- referenceBean.setApplicationContext(applicationContext);
- referenceBean.setBeanClassLoader(getClassLoader());
- referenceBean.afterPropertiesSet();
-
- beanDefinitionRegistry.registerBeanDefinition(referenceBeanName, beanDefinition);
- getBeanFactory().registerSingleton(referenceBeanName, referenceBean);
- //cache reference bean, avoid re-inject same element after prepare reference bean
- referenceBeanManager.addReference(referenceBean);
- } catch (Exception e) {
- throw new Exception("Create dubbo reference bean failed", e);
- }
+ String referenceBeanName = getAttribute(attributes, ReferenceAttributes.ID);
+ if (hasText(referenceBeanName)) {
+ renameable = false;
+ } else {
+ referenceBeanName = propertyName;
}
- return referenceBean;
+
+ String checkLocation = "Please check " + member.toString();
+
+ // convert annotation props
+ ReferenceBeanSupport.convertReferenceProps(attributes, injectedType);
+
+ // get interface
+ String interfaceName = (String) attributes.get(ReferenceAttributes.INTERFACE);
+ if (StringUtils.isBlank(interfaceName)) {
+ throw new BeanCreationException("Need to specify the 'interfaceName' or 'interfaceClass' attribute of '@DubboReference' if enable generic. "+checkLocation);
+ }
+
+ // check reference key
+ String referenceKey = ReferenceBeanSupport.generateReferenceKey(attributes, applicationContext.getEnvironment());
+
+ // check registered reference beans in referenceBeanManager
+ List registeredReferenceBeanNames = referenceBeanManager.getByKey(referenceKey);
+ if (registeredReferenceBeanNames.contains(referenceBeanName)) {
+ return referenceBeanName;
+ }
+
+ //check bean definition
+ if (beanDefinitionRegistry.containsBeanDefinition(referenceBeanName)) {
+ BeanDefinition prevBeanDefinition = beanDefinitionRegistry.getBeanDefinition(referenceBeanName);
+ String prevBeanType = prevBeanDefinition.getBeanClassName();
+ String prevBeanDesc = referenceBeanName + "[" + prevBeanType + "]";
+ String newBeanDesc = referenceBeanName + "[" + referenceKey + "]";
+
+ if (isReferenceBean(prevBeanDefinition)) {
+ //check reference key
+ String prevReferenceKey = ReferenceBeanSupport.generateReferenceKey(prevBeanDefinition, applicationContext.getEnvironment());
+ if (StringUtils.isEquals(prevReferenceKey, referenceKey)) {
+ //found matched dubbo reference bean, ignore register
+ return referenceBeanName;
+ }
+ //get interfaceName from attribute
+ Class prevInterfaceClass = (Class) prevBeanDefinition.getAttribute(ReferenceAttributes.INTERFACE_CLASS);
+ Assert.notNull(prevBeanDefinition, "The interface class of ReferenceBean is not initialized");
+ prevBeanType = prevInterfaceClass.getName();
+ prevBeanDesc = referenceBeanName + "[" + prevReferenceKey + "]";
+ //check bean type
+ if (StringUtils.isEquals(prevBeanType, interfaceName)) {
+ throw new BeanCreationException("Already exists another reference bean with the same bean name and type but difference attributes. " +
+ "In order to avoid injection confusion, please modify the name of one of the beans: " +
+ "prev: " + prevBeanDesc + ", new: " + newBeanDesc+". "+checkLocation);
+ }
+ } else {
+ //check bean type
+ if (StringUtils.isEquals(prevBeanType, interfaceName)) {
+ throw new BeanCreationException("Already exists another bean definition with the same bean name and type. " +
+ "In order to avoid injection confusion, please modify the name of one of the beans: " +
+ "prev: " + prevBeanDesc + ", new: " + newBeanDesc+". "+checkLocation);
+ }
+ }
+
+ // bean name from attribute 'id' or java-config bean, cannot be renamed
+ if (!renameable) {
+ throw new BeanCreationException("Already exists another bean definition with the same bean name, " +
+ "but cannot rename the reference bean name (specify the id attribute or java-config bean), " +
+ "please modify the name of one of the beans: " +
+ "prev: " + prevBeanDesc + ", new: " + newBeanDesc+". "+checkLocation);
+ }
+
+ // the prev bean type is different, rename the new reference bean
+ int index = 2;
+ String newReferenceBeanName = null;
+ while (newReferenceBeanName == null || beanDefinitionRegistry.containsBeanDefinition(newReferenceBeanName)) {
+ newReferenceBeanName = referenceBeanName + "#" + index;
+ index++;
+ }
+ newBeanDesc = newReferenceBeanName + "[" + referenceKey + "]";
+
+ logger.warn("Already exists another bean definition with the same bean name but difference type, " +
+ "rename dubbo reference bean to: " + newReferenceBeanName + ". " +
+ "It is recommended to modify the name of one of the beans to avoid injection problems. " +
+ "prev: " + prevBeanDesc + ", new: " + newBeanDesc+". "+checkLocation);
+ referenceBeanName = newReferenceBeanName;
+ }
+ attributes.put(ReferenceAttributes.ID, referenceBeanName);
+
+ // If registered matched reference before, add alias
+ if (registeredReferenceBeanNames.size() > 0) {
+ beanDefinitionRegistry.registerAlias(registeredReferenceBeanNames.get(0), referenceBeanName);
+ return referenceBeanName;
+ }
+
+ Class interfaceClass = injectedType;
+ Class actualInterface = null;
+ try {
+ actualInterface = ClassUtils.forName(interfaceName);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+
+ // TODO Only register one reference bean for same (group, interface, version)
+
+ // Register the reference bean definition to the beanFactory
+ RootBeanDefinition beanDefinition = new RootBeanDefinition();
+ beanDefinition.setBeanClassName(ReferenceBean.class.getName());
+ beanDefinition.getPropertyValues().add(ReferenceAttributes.ID, referenceBeanName);
+
+ // set attribute instead of property values
+ beanDefinition.setAttribute(Constants.REFERENCE_PROPS, attributes);
+ beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, interfaceClass);
+ beanDefinition.setAttribute(ReferenceAttributes.ACTUAL_INTERFACE, actualInterface);
+
+ // create decorated definition for reference bean, Avoid being instantiated when getting the beanType of ReferenceBean
+ // see org.springframework.beans.factory.support.AbstractBeanFactory#getTypeForFactoryBean()
+ GenericBeanDefinition targetDefinition = new GenericBeanDefinition();
+ targetDefinition.setBeanClass(interfaceClass);
+ String id = (String) beanDefinition.getPropertyValues().get(ReferenceAttributes.ID);
+ beanDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, id+"_decorated"));
+
+ // signal object type since Spring 5.2
+ beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, interfaceClass);
+
+ beanDefinitionRegistry.registerBeanDefinition(referenceBeanName, beanDefinition);
+ logger.info("Register dubbo reference bean: "+referenceBeanName+" = "+referenceKey+" at "+member);
+ return referenceBeanName;
}
@Override
protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class> injectedType,
AnnotatedInjectElement injectedElement) throws Exception {
- if (injectedElement.refKey == null) {
+ if (injectedElement.injectedObject == null) {
throw new IllegalStateException("The AnnotatedInjectElement of @DubboReference should be inited before injection");
}
- return getBeanFactory().getBean(injectedElement.refKey);
- }
-
- /**
- * Get the bean name of {@link ReferenceBean} if {@link Reference#id() id attribute} is present,
- * or {@link #generateReferenceBeanName(AnnotationAttributes, Class) generate}.
- *
- * @param attributes the {@link AnnotationAttributes attributes} of {@link Reference @Reference}
- * @param interfaceClass the {@link Class class} of Service interface
- * @return non-null
- * @since 2.7.3
- */
- private String getReferenceBeanName(AnnotationAttributes attributes, Class> interfaceClass) {
- // id attribute appears since 2.7.3
- String beanName = getAttribute(attributes, "id");
- if (!hasText(beanName)) {
- beanName = generateReferenceBeanName(attributes, interfaceClass);
- }
- return beanName;
- }
-
- /**
- * Build the bean name of {@link ReferenceBean}
- *
- * @param attributes the {@link AnnotationAttributes attributes} of {@link Reference @Reference}
- * @param interfaceClass the {@link Class class} of Service interface
- * @return
- * @since 2.7.3
- */
- private String generateReferenceBeanName(AnnotationAttributes attributes, Class> interfaceClass) {
- StringBuilder beanNameBuilder = new StringBuilder("@Reference");
-
- if (!attributes.isEmpty()) {
- beanNameBuilder.append('(');
- //sort attributes keys
- List sortedAttrKeys = new ArrayList<>(attributes.keySet());
- Collections.sort(sortedAttrKeys);
- for (String key : sortedAttrKeys) {
- Object value = attributes.get(key);
- //handle method array, generic array
- if (value!=null && value.getClass().isArray()) {
- Object[] array = ObjectUtils.toObjectArray(value);
- value = Arrays.toString(array);
- }
- beanNameBuilder.append(key)
- .append('=')
- .append(value)
- .append(',');
- }
- // replace the latest "," to be ")"
- beanNameBuilder.setCharAt(beanNameBuilder.lastIndexOf(","), ')');
- }
-
- beanNameBuilder.append(" ").append(interfaceClass.getName());
-
- //TODO remove invalid chars
- //TODO test @DubboReference with Method config
- //.replaceAll("[<>]", "_")
- return beanNameBuilder.toString();
- }
-
- /**
- * Is Local Service bean or not?
- *
- * @param referencedBeanName the bean name to the referenced bean
- * @return If the target referenced bean is existed, return true, or false
- * @since 2.7.6
- */
- private boolean isLocalServiceBean(String referencedBeanName, AnnotationAttributes attributes) {
- return existsServiceBean(referencedBeanName) && !isRemoteReferenceBean(attributes);
- }
-
- /**
- * Check the {@link ServiceBean} is exited or not
- *
- * @param referencedBeanName the bean name to the referenced bean
- * @return if exists, return true, or false
- * @revised 2.7.6
- */
- private boolean existsServiceBean(String referencedBeanName) {
- return applicationContext.containsBean(referencedBeanName) &&
- applicationContext.isTypeMatch(referencedBeanName, ServiceBean.class);
-
- }
-
- private boolean isRemoteReferenceBean(AnnotationAttributes attributes) {
- //TODO Can the interface be called locally when injvm is empty? https://github.com/apache/dubbo/issues/6842
- boolean remote = Boolean.FALSE.equals(attributes.get("injvm"));
- return remote;
- }
-
- private void exportServiceBeanIfNecessary(String referencedBeanName) {
- if (existsServiceBean(referencedBeanName)) {
- ServiceBean serviceBean = getServiceBean(referencedBeanName);
- if (!serviceBean.isExported()) {
- serviceBean.export();
- }
- }
- }
-
- private ServiceBean getServiceBean(String referencedBeanName) {
- return applicationContext.getBean(referencedBeanName, ServiceBean.class);
- }
-
- @Override
- protected String buildInjectedObjectCacheKey(AnnotationAttributes attributes, Object bean, String beanName,
- Class> injectedType, AnnotatedInjectElement injectedElement) {
- return generateReferenceBeanName(attributes, injectedType);
- }
-
- /**
- * @param attributes the attributes of {@link Reference @Reference}
- * @param serviceInterfaceType the type of Dubbo's service interface
- * @return The name of bean that annotated Dubbo's {@link Service @Service} in local Spring {@link ApplicationContext}
- */
- private String buildReferencedBeanName(AnnotationAttributes attributes, Class> serviceInterfaceType) {
- ServiceBeanNameBuilder serviceBeanNameBuilder = create(attributes, serviceInterfaceType, getEnvironment());
- return serviceBeanNameBuilder.build();
+ return getBeanFactory().getBean((String) injectedElement.injectedObject);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
this.referenceBeanManager = applicationContext.getBean(ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class);
+ this.beanDefinitionRegistry = (BeanDefinitionRegistry) applicationContext.getAutowireCapableBeanFactory();
}
@Override
@@ -418,7 +516,11 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
* @since 2.5.11
*/
public Map> getInjectedFieldReferenceBeanMap() {
- return Collections.unmodifiableMap(injectedFieldReferenceBeanCache);
+ Map> map = new HashMap<>();
+ for (Map.Entry entry : injectedFieldReferenceBeanCache.entrySet()) {
+ map.put(entry.getKey(), referenceBeanManager.getById(entry.getValue()));
+ }
+ return Collections.unmodifiableMap(map);
}
/**
@@ -428,6 +530,10 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
* @since 2.5.11
*/
public Map> getInjectedMethodReferenceBeanMap() {
- return Collections.unmodifiableMap(injectedMethodReferenceBeanCache);
+ Map> map = new HashMap<>();
+ for (Map.Entry entry : injectedMethodReferenceBeanCache.entrySet()) {
+ map.put(entry.getKey(), referenceBeanManager.getById(entry.getValue()));
+ }
+ return Collections.unmodifiableMap(map);
}
}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceClassPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceClassPostProcessor.java
index f6dbe69e1b..9d0ac25b94 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceClassPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceClassPostProcessor.java
@@ -305,6 +305,7 @@ public class ServiceClassPostProcessor implements BeanDefinitionRegistryPostProc
} else {
+ //TODO throw exception
if (logger.isWarnEnabled()) {
logger.warn("The Duplicated BeanDefinition[" + serviceBeanDefinition +
"] of ServiceBean[ bean name : " + beanName +
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/DubboConfigInitializationPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboConfigInitializationPostProcessor.java
similarity index 95%
rename from dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/DubboConfigInitializationPostProcessor.java
rename to dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboConfigInitializationPostProcessor.java
index d9c5181bac..741e7dbb7b 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/DubboConfigInitializationPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboConfigInitializationPostProcessor.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.config.spring;
+package org.apache.dubbo.config.spring.context;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ConsumerConfig;
@@ -26,6 +26,8 @@ import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.ProviderConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.SslConfig;
+import org.apache.dubbo.config.spring.ConfigCenterBean;
+import org.apache.dubbo.config.spring.reference.ReferenceBeanManager;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.BeanFactory;
@@ -48,7 +50,7 @@ import static org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncl
*/
public class DubboConfigInitializationPostProcessor implements BeanPostProcessor, BeanFactoryAware, Ordered {
- public static String BEAN_NAME = "dubboBeanFactoryPostProcessor";
+ public static String BEAN_NAME = "dubboConfigInitializationPostProcessor";
/**
* This bean post processor should run before seata GlobalTransactionScanner(1024)
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboInfraBeanRegisterPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboInfraBeanRegisterPostProcessor.java
new file mode 100644
index 0000000000..a0865c6bd3
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboInfraBeanRegisterPostProcessor.java
@@ -0,0 +1,51 @@
+/*
+ * 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.context;
+
+import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor;
+import org.apache.dubbo.config.spring.util.DubboBeanUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
+
+
+/**
+ * Register some infrastructure beans if not exists.
+ * This post-processor MUST impl BeanDefinitionRegistryPostProcessor,
+ * in order to enable the registered BeanFactoryPostProcessor bean to be loaded and executed.
+ * @see org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List)
+ */
+public class DubboInfraBeanRegisterPostProcessor implements BeanDefinitionRegistryPostProcessor {
+
+ /**
+ * The bean name of {@link ReferenceAnnotationBeanPostProcessor}
+ */
+ public static final String BEAN_NAME = "dubboInfraBeanRegisterPostProcessor";
+
+ private BeanDefinitionRegistry registry;
+
+ @Override
+ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
+ this.registry = registry;
+ }
+
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ DubboBeanUtils.registerBeansIfNotExists(registry);
+ }
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceAttributes.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceAttributes.java
new file mode 100644
index 0000000000..751bb7b03b
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceAttributes.java
@@ -0,0 +1,137 @@
+/*
+ * 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.reference;
+
+/**
+ * Attribute names of {@link org.apache.dubbo.config.annotation.DubboReference}
+ * and {@link org.apache.dubbo.config.ReferenceConfig}
+ */
+public interface ReferenceAttributes {
+
+ String ID = "id";
+
+ String INTERFACE = "interface";
+
+ String INTERFACE_NAME = "interfaceName";
+
+ String INTERFACE_CLASS = "interfaceClass";
+
+ String ACTUAL_INTERFACE = "actualInterface";
+
+ String GENERIC = "generic";
+
+ String REGISTRY = "registry";
+
+ String REGISTRIES = "registries";
+
+ String REGISTRY_IDS = "registryIds";
+
+ String GROUP = "group";
+
+ String VERSION = "version";
+
+ String ARGUMENTS = "arguments";
+
+ String METHODS = "methods";
+
+ String PARAMETERS = "parameters";
+
+ String PROVIDED_BY = "providedBy";
+
+ String URL = "url";
+
+ String CLIENT = "client";
+
+// /**
+// * When enable, prefer to call local service in the same JVM if it's present, default value is true
+// * @deprecated using scope="local" or scope="remote" instead
+// */
+// @Deprecated
+ String INJVM = "injvm";
+
+ String CHECK = "check";
+
+ String INIT = "init";
+
+ String LAZY = "lazy";
+
+ String STUBEVENT = "stubevent";
+
+ String RECONNECT = "reconnect";
+
+ String STICKY = "sticky";
+
+ String PROXY = "proxy";
+
+ String STUB = "stub";
+
+ String CLUSTER = "cluster";
+
+ String CONNECTIONS = "connections";
+
+ String CALLBACKS = "callbacks";
+
+ String ONCONNECT = "onconnect";
+
+ String ONDISCONNECT = "ondisconnect";
+
+ String OWNER = "owner";
+
+ String LAYER = "layer";
+
+ String RETRIES = "retries";
+
+ String LOAD_BALANCE = "loadbalance";
+
+ String ASYNC = "async";
+
+ String ACTIVES = "actives";
+
+ String SENT = "sent";
+
+ String MOCK = "mock";
+
+ String VALIDATION = "validation";
+
+ String TIMEOUT = "timeout";
+
+ String CACHE = "cache";
+
+ String FILTER = "filter";
+
+ String LISTENER = "listener";
+
+ String APPLICATION = "application";
+
+ String MODULE = "module";
+
+ String CONSUMER = "consumer";
+
+ String MONITOR = "monitor";
+
+ String PROTOCOL = "protocol";
+
+ String TAG = "tag";
+
+ String MERGER = "merger";
+
+ String SERVICES = "services";
+
+ String SCOPE = "scope";
+
+// String ROUTER = "router";
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceBeanBuilder.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceBeanBuilder.java
new file mode 100644
index 0000000000..775d879680
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceBeanBuilder.java
@@ -0,0 +1,383 @@
+/*
+ * 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.reference;
+
+import org.apache.dubbo.config.ConsumerConfig;
+import org.apache.dubbo.config.MethodConfig;
+import org.apache.dubbo.config.MonitorConfig;
+import org.apache.dubbo.config.RegistryConfig;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.apache.dubbo.config.spring.ReferenceBean;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ * Builder for ReferenceBean, used to return ReferenceBean instance in Java-config @Bean method,
+ * equivalent to {@link DubboReference} annotation.
+ *
+ *
+ *
+ * It is recommended to use {@link DubboReference} on the @Bean method in the Java-config class.
+ *