Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Dubbo -fix annotation bug] Fix @Reference bug #2649

Merged
merged 2 commits into from
Oct 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -35,15 +37,18 @@
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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -193,7 +201,7 @@ private ReferenceInjectionMetadata buildReferenceMetadata(final Class<?> beanCla

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)) {
Expand Down Expand Up @@ -402,11 +410,7 @@ private ReferenceBean<?> buildReferenceBean(Reference reference, Class<?> refere
*/
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();

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

}

/**
* 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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,12 @@ public void testGetReferenceBeans() {

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

Expand All @@ -130,7 +135,10 @@ public void testGetInjectedFieldReferenceBeanMap() {
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()) {

Expand Down Expand Up @@ -197,6 +205,49 @@ public void testModuleInfo() {
}
}

@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 {


Expand Down Expand Up @@ -239,6 +290,19 @@ static class TestBean extends ParentBean {

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;

Expand All @@ -250,6 +314,22 @@ public DemoService getDemoService() {
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;
}
}

}