You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by me...@apache.org on 2018/10/17 02:15:09 UTC
[incubator-dubbo] branch master updated: [Dubbo -fix annotation
bug] Fix @Reference bug (#2649)
This is an automated email from the ASF dual-hosted git repository.
mercyblitz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-dubbo.git
The following commit(s) were added to refs/heads/master by this push:
new d8282fe [Dubbo -fix annotation bug] Fix @Reference bug (#2649)
d8282fe is described below
commit d8282fe1b8075a453c7b44b4ae7982a4285df7b7
Author: yì jí <yi...@apache.org>
AuthorDate: Wed Oct 17 10:14:45 2018 +0800
[Dubbo -fix annotation bug] Fix @Reference bug (#2649)
It's fine.
---
.../ReferenceAnnotationBeanPostProcessor.java | 107 +++++++++++++++++++--
.../ReferenceAnnotationBeanPostProcessorTest.java | 84 +++++++++++++++-
2 files changed, 180 insertions(+), 11 deletions(-)
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 f893786..ed0fb5c 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
@@ -16,10 +16,12 @@
*/
package org.apache.dubbo.config.spring.beans.factory.annotation;
-import org.apache.dubbo.config.annotation.Reference;
-import org.apache.dubbo.config.spring.ReferenceBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.dubbo.common.Constants;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.config.annotation.Reference;
+import org.apache.dubbo.config.spring.ReferenceBean;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
@@ -35,15 +37,18 @@ import org.springframework.context.ApplicationContextAware;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.env.Environment;
import org.springframework.util.ClassUtils;
+import org.springframework.util.ConcurrentReferenceHashMap;
+import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
-import org.springframework.util.StringUtils;
import java.beans.PropertyDescriptor;
+import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
@@ -83,6 +88,9 @@ public class ReferenceAnnotationBeanPostProcessor extends InstantiationAwareBean
private final ConcurrentMap<String, ReferenceBean<?>> referenceBeansCache =
new ConcurrentHashMap<String, ReferenceBean<?>>();
+ private static final Map<Class<? extends Annotation>, List<Method>> annotationMethodsCache =
+ new ConcurrentReferenceHashMap<Class<? extends Annotation>, List<Method>>(256);
+
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
@@ -193,7 +201,7 @@ public class ReferenceAnnotationBeanPostProcessor extends InstantiationAwareBean
private InjectionMetadata findReferenceMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
- String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
+ String cacheKey = (StringUtils.isNotEmpty(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
ReferenceInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
@@ -402,11 +410,7 @@ public class ReferenceAnnotationBeanPostProcessor extends InstantiationAwareBean
*/
private String generateReferenceBeanCacheKey(Reference reference, Class<?> beanClass) {
- String interfaceName = resolveInterfaceName(reference, beanClass);
-
- String key = reference.url() + "/" + interfaceName +
- "/" + reference.version() +
- "/" + reference.group();
+ String key = resolveReferenceKey(annotationValues(reference));
Environment environment = applicationContext.getEnvironment();
@@ -501,5 +505,90 @@ public class ReferenceAnnotationBeanPostProcessor extends InstantiationAwareBean
}
+ /**
+ * Generate a key based on the annotation.
+ *
+ * @param annotations annotatoin value
+ * @return unique key, never null will be returned.
+ * @since 2.7.0
+ */
+ private String resolveReferenceKey(Map<String, Object> annotations) {
+ Iterator<Map.Entry<String, Object>> annotationVisitor = annotations.entrySet().iterator();
+ StringBuilder builder = new StringBuilder();
+ while (annotationVisitor.hasNext()) {
+ Map.Entry<String, Object> attribute = annotationVisitor.next();
+ String attributeValue = null;
+ if (attribute.getValue() instanceof String[]) {
+ attributeValue = toPlainString((String[]) attribute.getValue());
+ } else {
+ attributeValue = attribute.getValue() == null ? "" : attribute.getValue().toString();
+ }
+
+ if (StringUtils.isNotEmpty(attributeValue)) {
+ if (builder.length() > 0) {
+ builder.append(Constants.PATH_SEPARATOR);
+ }
+ builder.append(attributeValue);
+ }
+ }
+ return builder.toString();
+ }
+
+ private Map<String, Object> annotationValues(Annotation annotation) {
+ Map<String, Object> annotations = new LinkedHashMap<>();
+ for (Method method : getAnnotationMethods(annotation.annotationType())) {
+ try {
+ Object attributeValue = method.invoke(annotation);
+ Object defaultValue = method.getDefaultValue();
+ if (nullSafeEquals(attributeValue, defaultValue)) {
+ continue;
+ }
+ annotations.put(method.getName(), attributeValue);
+ } catch (Throwable e) {
+ throw new IllegalStateException("Failed to obtain annotation attribute value for " + method, e);
+ }
+ }
+ return annotations;
+ }
+
+ private static List<Method> getAnnotationMethods(Class<? extends Annotation> annotationType) {
+ List<Method> methods = annotationMethodsCache.get(annotationType);
+ if (methods != null) {
+ return methods;
+ }
+
+ methods = new ArrayList<Method>();
+ for (Method method : annotationType.getDeclaredMethods()) {
+ if (isAnnotationMethod(method)) {
+ ReflectionUtils.makeAccessible(method);
+ methods.add(method);
+ }
+ }
+
+ annotationMethodsCache.put(annotationType, methods);
+ return methods;
+ }
+
+ private static boolean isAnnotationMethod(Method method) {
+ return (method != null
+ && method.getParameterTypes().length == 0
+ && method.getReturnType() != void.class);
+ }
+
+ private static boolean nullSafeEquals(Object first, Object another) {
+ return ObjectUtils.nullSafeEquals(first, another);
+ }
+
+ private String toPlainString(String[] array) {
+ if (array == null || array.length == 0) return "";
+ StringBuilder buffer = new StringBuilder();
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0) {
+ buffer.append(Constants.COMMA_SEPARATOR);
+ }
+ buffer.append(array[i]);
+ }
+ return buffer.toString();
+ }
}
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java
index e45b025..959bfa6 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java
@@ -106,7 +106,12 @@ public class ReferenceAnnotationBeanPostProcessorTest {
Collection<ReferenceBean<?>> referenceBeans = beanPostProcessor.getReferenceBeans();
- Assert.assertEquals(1, referenceBeans.size());
+ /**
+ * 1 -> demoService、demoServiceShouldBeSame
+ * 1 -> demoServiceShouldNotBeSame
+ * 1 -> demoServiceWithArray、demoServiceWithArrayShouldBeSame
+ */
+ Assert.assertEquals(3, referenceBeans.size());
ReferenceBean<?> referenceBean = referenceBeans.iterator().next();
@@ -130,7 +135,10 @@ public class ReferenceAnnotationBeanPostProcessorTest {
Map<InjectionMetadata.InjectedElement, ReferenceBean<?>> referenceBeanMap =
beanPostProcessor.getInjectedFieldReferenceBeanMap();
- Assert.assertEquals(1, referenceBeanMap.size());
+ /**
+ * contains 5 fields.
+ */
+ Assert.assertEquals(5, referenceBeanMap.size());
for (Map.Entry<InjectionMetadata.InjectedElement, ReferenceBean<?>> entry : referenceBeanMap.entrySet()) {
@@ -197,6 +205,49 @@ public class ReferenceAnnotationBeanPostProcessorTest {
}
}
+ @Test
+ public void testReferenceCache() throws Exception {
+
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestBean.class);
+
+ TestBean testBean = context.getBean(TestBean.class);
+
+ Assert.assertNotNull(testBean.getDemoServiceFromAncestor());
+ Assert.assertNotNull(testBean.getDemoServiceFromParent());
+ Assert.assertNotNull(testBean.getDemoService());
+
+ Assert.assertEquals(testBean.getDemoServiceFromAncestor(), testBean.getDemoServiceFromParent());
+ Assert.assertEquals(testBean.getDemoService(), testBean.getDemoServiceFromParent());
+
+ DemoService demoService = testBean.getDemoService();
+
+ Assert.assertEquals(demoService, testBean.getDemoServiceShouldBeSame());
+ Assert.assertNotEquals(demoService, testBean.getDemoServiceShouldNotBeSame());
+
+ context.close();
+
+ }
+
+ @Test
+ public void testReferenceCacheWithArray() throws Exception {
+
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestBean.class);
+
+ TestBean testBean = context.getBean(TestBean.class);
+
+ Assert.assertNotNull(testBean.getDemoServiceFromAncestor());
+ Assert.assertNotNull(testBean.getDemoServiceFromParent());
+ Assert.assertNotNull(testBean.getDemoService());
+
+ Assert.assertEquals(testBean.getDemoServiceFromAncestor(), testBean.getDemoServiceFromParent());
+ Assert.assertEquals(testBean.getDemoService(), testBean.getDemoServiceFromParent());
+
+ Assert.assertEquals(testBean.getDemoServiceWithArray(), testBean.getDemoServiceWithArrayShouldBeSame());
+
+ context.close();
+
+ }
+
private static class AncestorBean {
@@ -239,6 +290,19 @@ public class ReferenceAnnotationBeanPostProcessorTest {
private DemoService demoService;
+ @Reference(version = "1.2", url = "dubbo://127.0.0.1:12345")
+ private DemoService demoServiceShouldBeSame;
+
+ @Reference(version = "1.2", url = "dubbo://127.0.0.1:12345", async = true)
+ private DemoService demoServiceShouldNotBeSame;
+
+
+ @Reference(version = "1.2", url = "dubbo://127.0.0.1:12345", parameters = { "key1", "value1"})
+ private DemoService demoServiceWithArray;
+
+ @Reference(version = "1.2", url = "dubbo://127.0.0.1:12345", parameters = { "key1", "value1"})
+ private DemoService demoServiceWithArrayShouldBeSame;
+
@Autowired
private ApplicationContext applicationContext;
@@ -250,6 +314,22 @@ public class ReferenceAnnotationBeanPostProcessorTest {
public void setDemoService(DemoService demoService) {
this.demoService = demoService;
}
+
+ public DemoService getDemoServiceShouldNotBeSame() {
+ return demoServiceShouldNotBeSame;
+ }
+
+ public DemoService getDemoServiceShouldBeSame() {
+ return demoServiceShouldBeSame;
+ }
+
+ public DemoService getDemoServiceWithArray() {
+ return demoServiceWithArray;
+ }
+
+ public DemoService getDemoServiceWithArrayShouldBeSame() {
+ return demoServiceWithArrayShouldBeSame;
+ }
}
}
\ No newline at end of file