Skip to content

Commit d8282fe

Browse files
zonghaishangmercyblitz
authored andcommitted
[Dubbo -fix annotation bug] Fix @reference bug (#2649)
It's fine.
1 parent 393ffce commit d8282fe

File tree

2 files changed

+180
-11
lines changed

2 files changed

+180
-11
lines changed

dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616
*/
1717
package org.apache.dubbo.config.spring.beans.factory.annotation;
1818

19-
import org.apache.dubbo.config.annotation.Reference;
20-
import org.apache.dubbo.config.spring.ReferenceBean;
2119
import org.apache.commons.logging.Log;
2220
import org.apache.commons.logging.LogFactory;
21+
import org.apache.dubbo.common.Constants;
22+
import org.apache.dubbo.common.utils.StringUtils;
23+
import org.apache.dubbo.config.annotation.Reference;
24+
import org.apache.dubbo.config.spring.ReferenceBean;
2325
import org.springframework.beans.BeanUtils;
2426
import org.springframework.beans.BeansException;
2527
import org.springframework.beans.PropertyValues;
@@ -35,15 +37,18 @@
3537
import org.springframework.core.PriorityOrdered;
3638
import org.springframework.core.env.Environment;
3739
import org.springframework.util.ClassUtils;
40+
import org.springframework.util.ConcurrentReferenceHashMap;
41+
import org.springframework.util.ObjectUtils;
3842
import org.springframework.util.ReflectionUtils;
39-
import org.springframework.util.StringUtils;
4043

4144
import java.beans.PropertyDescriptor;
45+
import java.lang.annotation.Annotation;
4246
import java.lang.reflect.Field;
4347
import java.lang.reflect.Method;
4448
import java.lang.reflect.Modifier;
4549
import java.util.ArrayList;
4650
import java.util.Collection;
51+
import java.util.Iterator;
4752
import java.util.LinkedHashMap;
4853
import java.util.LinkedList;
4954
import java.util.List;
@@ -83,6 +88,9 @@ public class ReferenceAnnotationBeanPostProcessor extends InstantiationAwareBean
8388
private final ConcurrentMap<String, ReferenceBean<?>> referenceBeansCache =
8489
new ConcurrentHashMap<String, ReferenceBean<?>>();
8590

91+
private static final Map<Class<? extends Annotation>, List<Method>> annotationMethodsCache =
92+
new ConcurrentReferenceHashMap<Class<? extends Annotation>, List<Method>>(256);
93+
8694
@Override
8795
public PropertyValues postProcessPropertyValues(
8896
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
@@ -193,7 +201,7 @@ private ReferenceInjectionMetadata buildReferenceMetadata(final Class<?> beanCla
193201

194202
private InjectionMetadata findReferenceMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
195203
// Fall back to class name as cache key, for backwards compatibility with custom callers.
196-
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
204+
String cacheKey = (StringUtils.isNotEmpty(beanName) ? beanName : clazz.getName());
197205
// Quick check on the concurrent map first, with minimal locking.
198206
ReferenceInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
199207
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
@@ -402,11 +410,7 @@ private ReferenceBean<?> buildReferenceBean(Reference reference, Class<?> refere
402410
*/
403411
private String generateReferenceBeanCacheKey(Reference reference, Class<?> beanClass) {
404412

405-
String interfaceName = resolveInterfaceName(reference, beanClass);
406-
407-
String key = reference.url() + "/" + interfaceName +
408-
"/" + reference.version() +
409-
"/" + reference.group();
413+
String key = resolveReferenceKey(annotationValues(reference));
410414

411415
Environment environment = applicationContext.getEnvironment();
412416

@@ -501,5 +505,90 @@ private <T> T getFieldValue(Object object, String fieldName, Class<T> fieldType)
501505

502506
}
503507

508+
/**
509+
* Generate a key based on the annotation.
510+
*
511+
* @param annotations annotatoin value
512+
* @return unique key, never null will be returned.
513+
* @since 2.7.0
514+
*/
515+
private String resolveReferenceKey(Map<String, Object> annotations) {
516+
Iterator<Map.Entry<String, Object>> annotationVisitor = annotations.entrySet().iterator();
517+
StringBuilder builder = new StringBuilder();
518+
while (annotationVisitor.hasNext()) {
519+
Map.Entry<String, Object> attribute = annotationVisitor.next();
520+
String attributeValue = null;
521+
if (attribute.getValue() instanceof String[]) {
522+
attributeValue = toPlainString((String[]) attribute.getValue());
523+
} else {
524+
attributeValue = attribute.getValue() == null ? "" : attribute.getValue().toString();
525+
}
526+
527+
if (StringUtils.isNotEmpty(attributeValue)) {
528+
if (builder.length() > 0) {
529+
builder.append(Constants.PATH_SEPARATOR);
530+
}
531+
builder.append(attributeValue);
532+
}
533+
}
534+
return builder.toString();
535+
}
536+
537+
private Map<String, Object> annotationValues(Annotation annotation) {
538+
Map<String, Object> annotations = new LinkedHashMap<>();
504539

540+
for (Method method : getAnnotationMethods(annotation.annotationType())) {
541+
try {
542+
Object attributeValue = method.invoke(annotation);
543+
Object defaultValue = method.getDefaultValue();
544+
if (nullSafeEquals(attributeValue, defaultValue)) {
545+
continue;
546+
}
547+
annotations.put(method.getName(), attributeValue);
548+
} catch (Throwable e) {
549+
throw new IllegalStateException("Failed to obtain annotation attribute value for " + method, e);
550+
}
551+
}
552+
return annotations;
553+
}
554+
555+
private static List<Method> getAnnotationMethods(Class<? extends Annotation> annotationType) {
556+
List<Method> methods = annotationMethodsCache.get(annotationType);
557+
if (methods != null) {
558+
return methods;
559+
}
560+
561+
methods = new ArrayList<Method>();
562+
for (Method method : annotationType.getDeclaredMethods()) {
563+
if (isAnnotationMethod(method)) {
564+
ReflectionUtils.makeAccessible(method);
565+
methods.add(method);
566+
}
567+
}
568+
569+
annotationMethodsCache.put(annotationType, methods);
570+
return methods;
571+
}
572+
573+
private static boolean isAnnotationMethod(Method method) {
574+
return (method != null
575+
&& method.getParameterTypes().length == 0
576+
&& method.getReturnType() != void.class);
577+
}
578+
579+
private static boolean nullSafeEquals(Object first, Object another) {
580+
return ObjectUtils.nullSafeEquals(first, another);
581+
}
582+
583+
private String toPlainString(String[] array) {
584+
if (array == null || array.length == 0) return "";
585+
StringBuilder buffer = new StringBuilder();
586+
for (int i = 0; i < array.length; i++) {
587+
if (i > 0) {
588+
buffer.append(Constants.COMMA_SEPARATOR);
589+
}
590+
buffer.append(array[i]);
591+
}
592+
return buffer.toString();
593+
}
505594
}

dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,12 @@ public void testGetReferenceBeans() {
106106

107107
Collection<ReferenceBean<?>> referenceBeans = beanPostProcessor.getReferenceBeans();
108108

109-
Assert.assertEquals(1, referenceBeans.size());
109+
/**
110+
* 1 -> demoService、demoServiceShouldBeSame
111+
* 1 -> demoServiceShouldNotBeSame
112+
* 1 -> demoServiceWithArray、demoServiceWithArrayShouldBeSame
113+
*/
114+
Assert.assertEquals(3, referenceBeans.size());
110115

111116
ReferenceBean<?> referenceBean = referenceBeans.iterator().next();
112117

@@ -130,7 +135,10 @@ public void testGetInjectedFieldReferenceBeanMap() {
130135
Map<InjectionMetadata.InjectedElement, ReferenceBean<?>> referenceBeanMap =
131136
beanPostProcessor.getInjectedFieldReferenceBeanMap();
132137

133-
Assert.assertEquals(1, referenceBeanMap.size());
138+
/**
139+
* contains 5 fields.
140+
*/
141+
Assert.assertEquals(5, referenceBeanMap.size());
134142

135143
for (Map.Entry<InjectionMetadata.InjectedElement, ReferenceBean<?>> entry : referenceBeanMap.entrySet()) {
136144

@@ -197,6 +205,49 @@ public void testModuleInfo() {
197205
}
198206
}
199207

208+
@Test
209+
public void testReferenceCache() throws Exception {
210+
211+
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestBean.class);
212+
213+
TestBean testBean = context.getBean(TestBean.class);
214+
215+
Assert.assertNotNull(testBean.getDemoServiceFromAncestor());
216+
Assert.assertNotNull(testBean.getDemoServiceFromParent());
217+
Assert.assertNotNull(testBean.getDemoService());
218+
219+
Assert.assertEquals(testBean.getDemoServiceFromAncestor(), testBean.getDemoServiceFromParent());
220+
Assert.assertEquals(testBean.getDemoService(), testBean.getDemoServiceFromParent());
221+
222+
DemoService demoService = testBean.getDemoService();
223+
224+
Assert.assertEquals(demoService, testBean.getDemoServiceShouldBeSame());
225+
Assert.assertNotEquals(demoService, testBean.getDemoServiceShouldNotBeSame());
226+
227+
context.close();
228+
229+
}
230+
231+
@Test
232+
public void testReferenceCacheWithArray() throws Exception {
233+
234+
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestBean.class);
235+
236+
TestBean testBean = context.getBean(TestBean.class);
237+
238+
Assert.assertNotNull(testBean.getDemoServiceFromAncestor());
239+
Assert.assertNotNull(testBean.getDemoServiceFromParent());
240+
Assert.assertNotNull(testBean.getDemoService());
241+
242+
Assert.assertEquals(testBean.getDemoServiceFromAncestor(), testBean.getDemoServiceFromParent());
243+
Assert.assertEquals(testBean.getDemoService(), testBean.getDemoServiceFromParent());
244+
245+
Assert.assertEquals(testBean.getDemoServiceWithArray(), testBean.getDemoServiceWithArrayShouldBeSame());
246+
247+
context.close();
248+
249+
}
250+
200251
private static class AncestorBean {
201252

202253

@@ -239,6 +290,19 @@ static class TestBean extends ParentBean {
239290

240291
private DemoService demoService;
241292

293+
@Reference(version = "1.2", url = "dubbo://127.0.0.1:12345")
294+
private DemoService demoServiceShouldBeSame;
295+
296+
@Reference(version = "1.2", url = "dubbo://127.0.0.1:12345", async = true)
297+
private DemoService demoServiceShouldNotBeSame;
298+
299+
300+
@Reference(version = "1.2", url = "dubbo://127.0.0.1:12345", parameters = { "key1", "value1"})
301+
private DemoService demoServiceWithArray;
302+
303+
@Reference(version = "1.2", url = "dubbo://127.0.0.1:12345", parameters = { "key1", "value1"})
304+
private DemoService demoServiceWithArrayShouldBeSame;
305+
242306
@Autowired
243307
private ApplicationContext applicationContext;
244308

@@ -250,6 +314,22 @@ public DemoService getDemoService() {
250314
public void setDemoService(DemoService demoService) {
251315
this.demoService = demoService;
252316
}
317+
318+
public DemoService getDemoServiceShouldNotBeSame() {
319+
return demoServiceShouldNotBeSame;
320+
}
321+
322+
public DemoService getDemoServiceShouldBeSame() {
323+
return demoServiceShouldBeSame;
324+
}
325+
326+
public DemoService getDemoServiceWithArray() {
327+
return demoServiceWithArray;
328+
}
329+
330+
public DemoService getDemoServiceWithArrayShouldBeSame() {
331+
return demoServiceWithArrayShouldBeSame;
332+
}
253333
}
254334

255335
}

0 commit comments

Comments
 (0)