Support service annotation work with java config bean (#8011)

This commit is contained in:
Gong Dewei 2021-06-11 00:07:12 +08:00 committed by GitHub
parent c1e3a01d8c
commit f07640fc74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 523 additions and 183 deletions

View File

@ -35,7 +35,7 @@ import java.lang.annotation.Target;
@Deprecated
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Target({ElementType.TYPE, ElementType.METHOD})
@Inherited
public @interface Service {

View File

@ -25,9 +25,11 @@ import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
@ -479,4 +481,56 @@ public interface AnnotationUtils {
Method method = findMethod(annotationType, attributeName);
return (T) (method == null ? null : method.getDefaultValue());
}
/**
* Filter default value of Annotation type
* @param annotationType annotation type from {@link Annotation#annotationType()}
* @param attributes
* @return
*/
static Map<String, Object> filterDefaultValues(Class<? extends Annotation> annotationType, Map<String, Object> attributes) {
Map<String, Object> filteredAttributes = new LinkedHashMap<>(attributes.size());
attributes.forEach((key,val) -> {
if (!Objects.deepEquals(val, getDefaultValue(annotationType, key))) {
filteredAttributes.put(key, val);
}
});
return filteredAttributes;
}
/**
* Filter default value of Annotation type
* @param annotation
* @param attributes
* @return
*/
static Map<String, Object> filterDefaultValues(Annotation annotation, Map<String, Object> attributes) {
return filterDefaultValues(annotation.annotationType(), attributes);
}
/**
* Get attributes of annotation
* @param annotation
* @return
*/
static Map<String, Object> getAttributes(Annotation annotation, boolean filterDefaultValue) {
Class<?> annotationType = annotation.annotationType();
Method[] methods = annotationType.getMethods();
Map<String, Object> attributes = new LinkedHashMap<>(methods.length);
for (Method method : methods) {
try {
if (method.getDeclaringClass() == Annotation.class) {
continue;
}
String name = method.getName();
Object value = method.invoke(annotation);
if (!filterDefaultValue || !Objects.deepEquals(value, method.getDefaultValue())) {
attributes.put(name, value);
}
} catch (Exception e) {
throw new IllegalStateException("get attribute value of annotation failed: " + method, e);
}
}
return attributes;
}
}

View File

@ -86,7 +86,7 @@ public abstract class AbstractConfig implements Serializable {
protected final AtomicBoolean refreshed = new AtomicBoolean(false);
/**
* Is default or not
* Is default config or not
*/
protected Boolean isDefault;
@ -606,6 +606,36 @@ public abstract class AbstractConfig implements Serializable {
return refreshed.get();
}
/**
* FIXME check @Parameter(required=true) and any conditions that need to match.
*/
@Parameter(excluded = true, attribute = false)
public boolean isValid() {
return true;
}
@Parameter(excluded = true, attribute = false)
public Boolean isDefault() {
return isDefault;
}
public void setDefault(Boolean isDefault) {
this.isDefault = isDefault;
}
/**
* Add {@link AbstractConfig instance} into {@link ConfigManager}
* <p>
* Current method will invoked by Spring or Java EE container automatically, or should be triggered manually.
*
* @see ConfigManager#addConfig(AbstractConfig)
* @since 2.7.5
*/
@PostConstruct
public void addIntoConfigManager() {
ApplicationModel.getConfigManager().addConfig(this);
}
@Override
public String toString() {
try {
@ -658,15 +688,6 @@ public abstract class AbstractConfig implements Serializable {
return fieldNamesCache.computeIfAbsent(configClass, ReflectUtils::getAllFieldNames);
}
/**
* FIXME check @Parameter(required=true) and any conditions that need to match.
*/
@Parameter(excluded = true, attribute = false)
public boolean isValid() {
return true;
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != this.getClass()) {
@ -708,19 +729,6 @@ public abstract class AbstractConfig implements Serializable {
return true;
}
/**
* Add {@link AbstractConfig instance} into {@link ConfigManager}
* <p>
* Current method will invoked by Spring or Java EE container automatically, or should be triggered manually.
*
* @see ConfigManager#addConfig(AbstractConfig)
* @since 2.7.5
*/
@PostConstruct
public void addIntoConfigManager() {
ApplicationModel.getConfigManager().addConfig(this);
}
@Override
public int hashCode() {
int hashCode = 1;
@ -748,11 +756,4 @@ public abstract class AbstractConfig implements Serializable {
return hashCode;
}
public Boolean isDefault() {
return isDefault;
}
public void setDefault(Boolean isDefault) {
this.isDefault = isDefault;
}
}

View File

@ -159,12 +159,6 @@ public class ProviderConfig extends AbstractServiceConfig {
this.protocols = new ArrayList<>(Arrays.asList(new ProtocolConfig(protocol)));
}
@Override
@Parameter(excluded = true)
public Boolean isDefault() {
return isDefault;
}
@Parameter(excluded = true)
public String getHost() {
return host;

View File

@ -27,13 +27,40 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Class-level annotation used for declaring Dubbo service
* Class-level annotation used for declaring Dubbo service.
* <p/>
* <b>1. Using with java config bean:</b>
* <p/>
* <b>This usage is recommended</b>.<br/>
* It is more flexible on bean methods than on implementation classes, and is more compatible with Spring.
* <pre>
* &#64;Configuration
* class ProviderConfiguration {
*
* &#64;Bean
* &#64;DubboService(group="demo")
* public DemoService demoServiceImpl() {
* return new DemoServiceImpl();
* }
* }
* </pre>
*
* <b>2. Using on implementation class of service: </b>
* <pre>
* &#64;DubboService(group="demo")
* public class DemoServiceImpl implements DemoService {
* ...
* }
* </pre>
*
* This usage causes the implementation class to rely on the Dubbo module.
*
*
* @since 2.7.7
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Target({ElementType.TYPE, ElementType.METHOD})
@Inherited
public @interface DubboService {

View File

@ -33,7 +33,7 @@ import java.lang.annotation.Target;
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Target({ElementType.TYPE, ElementType.METHOD})
@Inherited
@Deprecated
public @interface Service {

View File

@ -676,16 +676,11 @@ public class ConfigManager extends LifecycleAdapter implements FrameworkExt {
}
static <C extends AbstractConfig> Boolean isDefaultConfig(C config) {
// If no isDefault() method, it is considered that each can be used as default
if (!ReflectUtils.hasMethod(config.getClass(), "isDefault")) {
return true;
}
Boolean isDefault = ReflectUtils.getProperty(config, "isDefault");
return isDefault;
return config.isDefault();
}
static <C extends AbstractConfig> List<C> getDefaultConfigs(Map<String, C> configsMap) {
// find isDefault() == TRUE
// find isDefault() == true
List<C> list = configsMap.values()
.stream()
.filter(c -> TRUE.equals(ConfigManager.isDefaultConfig(c)))
@ -700,6 +695,8 @@ public class ConfigManager extends LifecycleAdapter implements FrameworkExt {
.filter(c -> ConfigManager.isDefaultConfig(c) == null)
.collect(Collectors.toList());
return list;
// exclude isDefault() == false
}
}

View File

@ -31,9 +31,11 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import static java.util.Arrays.asList;
import static org.apache.dubbo.common.utils.AnnotationUtils.excludedType;
import static org.apache.dubbo.common.utils.AnnotationUtils.filterDefaultValues;
import static org.apache.dubbo.common.utils.AnnotationUtils.findAnnotation;
import static org.apache.dubbo.common.utils.AnnotationUtils.findMetaAnnotation;
import static org.apache.dubbo.common.utils.AnnotationUtils.findMetaAnnotations;
@ -41,7 +43,9 @@ import static org.apache.dubbo.common.utils.AnnotationUtils.getAllDeclaredAnnota
import static org.apache.dubbo.common.utils.AnnotationUtils.getAllMetaAnnotations;
import static org.apache.dubbo.common.utils.AnnotationUtils.getAnnotation;
import static org.apache.dubbo.common.utils.AnnotationUtils.getAttribute;
import static org.apache.dubbo.common.utils.AnnotationUtils.getAttributes;
import static org.apache.dubbo.common.utils.AnnotationUtils.getDeclaredAnnotations;
import static org.apache.dubbo.common.utils.AnnotationUtils.getDefaultValue;
import static org.apache.dubbo.common.utils.AnnotationUtils.getMetaAnnotations;
import static org.apache.dubbo.common.utils.AnnotationUtils.getValue;
import static org.apache.dubbo.common.utils.AnnotationUtils.isAnnotationPresent;
@ -100,6 +104,26 @@ public class AnnotationUtilsTest {
assertEquals(false, getAttribute(annotation, "deprecated"));
}
@Test
public void testGetAttributesMap() {
Annotation annotation = A.class.getAnnotation(Service.class);
Map<String, Object> attributes = getAttributes(annotation, false);
assertEquals("java.lang.CharSequence", attributes.get("interfaceName"));
assertEquals(CharSequence.class, attributes.get("interfaceClass"));
assertEquals("", attributes.get("group"));
assertEquals(getDefaultValue(annotation, "export"), attributes.get("export"));
Map<String, Object> filteredAttributes = filterDefaultValues(annotation, attributes);
assertEquals(2, filteredAttributes.size());
assertEquals("java.lang.CharSequence", filteredAttributes.get("interfaceName"));
assertEquals(CharSequence.class, filteredAttributes.get("interfaceClass"));
assertFalse(filteredAttributes.containsKey("group"));
assertFalse(filteredAttributes.containsKey("export"));
Map<String, Object> nonDefaultAttributes = getAttributes(annotation, true);
assertEquals(nonDefaultAttributes, filteredAttributes);
}
@Test
public void testGetValue() {
Adaptive adaptive = A.class.getAnnotation(Adaptive.class);

View File

@ -28,6 +28,8 @@
<properties>
<skip_maven_deploy>false</skip_maven_deploy>
<spring-boot.version>2.3.1.RELEASE</spring-boot.version>
<!-- Uncomment spring_version property to check Spring 4.x compatibility -->
<!-- <spring_version>4.3.30.RELEASE</spring_version> -->
</properties>
<dependencies>
<dependency>

View File

@ -31,4 +31,15 @@ public interface Constants {
*/
String REFERENCE_SOURCES = "referenceSources";
/**
* The name of an attribute that can be
* {@link org.springframework.core.AttributeAccessor#setAttribute set} on a
* {@link org.springframework.beans.factory.config.BeanDefinition} so that
* factory beans can signal their object type when it can't be deduced from
* the factory bean class.
* <p/>
* From FactoryBean.OBJECT_TYPE_ATTRIBUTE of Spring 5.2.
*/
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
}

View File

@ -100,7 +100,7 @@ public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean
* @return {@link ServiceBean}'s name
* @since 2.6.5
*/
@Parameter(excluded = true)
@Parameter(excluded = true, attribute = false)
public String getBeanName() {
return this.beanName;
}

View File

@ -241,7 +241,7 @@ public abstract class AbstractAnnotationBeanPostProcessor extends
private AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass);
Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass);
return new AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
return new AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
}
protected AnnotatedInjectionMetadata findInjectionMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
@ -249,11 +249,11 @@ public abstract class AbstractAnnotationBeanPostProcessor extends
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (needsRefreshInjectionMetadata(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (needsRefreshInjectionMetadata(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
@ -270,6 +270,11 @@ public abstract class AbstractAnnotationBeanPostProcessor extends
return metadata;
}
// Use custom check method to compatible with Spring 4.x
private boolean needsRefreshInjectionMetadata(AnnotatedInjectionMetadata metadata, Class<?> clazz) {
return (metadata == null || metadata.needsRefresh(clazz));
}
@Override
public int getOrder() {
return order;
@ -402,7 +407,7 @@ public abstract class AbstractAnnotationBeanPostProcessor extends
/**
* {@link Annotation Annotated} {@link InjectionMetadata} implementation
*/
protected class AnnotatedInjectionMetadata extends InjectionMetadata {
protected static class AnnotatedInjectionMetadata extends InjectionMetadata {
private Class<?> targetClass;
private final Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements;
@ -425,7 +430,7 @@ public abstract class AbstractAnnotationBeanPostProcessor extends
return methodElements;
}
@Override
//@Override // since Spring 5.2.4
protected boolean needsRefresh(Class<?> clazz) {
if (this.targetClass == clazz) {
return false;

View File

@ -34,7 +34,6 @@ 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;
@ -47,7 +46,6 @@ 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.core.annotation.MergedAnnotations;
import org.springframework.core.type.MethodMetadata;
import java.beans.PropertyDescriptor;
@ -63,6 +61,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static com.alibaba.spring.util.AnnotationUtils.getAttribute;
import static org.apache.dubbo.common.utils.AnnotationUtils.filterDefaultValues;
import static org.springframework.util.StringUtils.hasText;
/**
@ -193,8 +192,8 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
// Extract beanClass from generic return type of java-config bean method: ReferenceBean<DemoService>
// see org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBeanFromMethod
Class beanClass = getBeanFactory().getType(beanName);
MethodMetadata factoryMethodMetadata = beanDefinition.getFactoryMethodMetadata();
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, " +
@ -203,14 +202,14 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
// get dubbo reference annotation attributes
Map<String, Object> 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 (factoryMethodMetadata.isAnnotated(annotationType.getName())) {
// Since Spring 5.2
// return factoryMethodMetadata.getAnnotations().get(annotationType).filterDefaultValues().asMap();
// Compatible with Spring 4.x
annotationAttributes = factoryMethodMetadata.getAnnotationAttributes(annotationType.getName());
annotationAttributes = filterDefaultValues(annotationType, annotationAttributes);
}
}
@ -226,7 +225,6 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
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. " +
@ -468,7 +466,7 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
beanDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, id+"_decorated"));
// signal object type since Spring 5.2
beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, interfaceClass);
beanDefinition.setAttribute(Constants.OBJECT_TYPE_ATTRIBUTE, interfaceClass);
beanDefinitionRegistry.registerBeanDefinition(referenceBeanName, beanDefinition);
logger.info("Register dubbo reference bean: "+referenceBeanName+" = "+referenceKey+" at "+member);

View File

@ -16,22 +16,25 @@
*/
package org.apache.dubbo.config.spring.beans.factory.annotation;
import com.alibaba.spring.util.AnnotationUtils;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.ArrayUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.config.Constants;
import org.apache.dubbo.config.MethodConfig;
import org.apache.dubbo.config.annotation.DubboService;
import org.apache.dubbo.config.annotation.Method;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.config.spring.ServiceBean;
import org.apache.dubbo.config.spring.context.DubboBootstrapApplicationListener;
import org.apache.dubbo.config.spring.context.annotation.DubboClassPathBeanDefinitionScanner;
import org.apache.dubbo.config.spring.schema.AnnotationBeanDefinitionParser;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
@ -43,49 +46,59 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.CollectionUtils;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import static com.alibaba.spring.util.BeanRegistrar.registerInfrastructureBean;
import static com.alibaba.spring.util.ObjectUtils.of;
import static java.util.Arrays.asList;
import static org.apache.dubbo.common.utils.AnnotationUtils.filterDefaultValues;
import static org.apache.dubbo.config.spring.beans.factory.annotation.ServiceBeanNameBuilder.create;
import static org.apache.dubbo.config.spring.util.DubboAnnotationUtils.resolveServiceInterfaceClass;
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
import static org.springframework.context.annotation.AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR;
import static org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation;
import static org.springframework.core.annotation.AnnotationUtils.getAnnotationAttributes;
import static org.springframework.util.ClassUtils.resolveClassName;
/**
* {@link BeanFactoryPostProcessor} used for processing of {@link Service @Service} annotated classes. it's also the
* infrastructure class of XML {@link BeanDefinitionParser} on &lt;dubbbo:annotation /&gt;
* A {@link BeanFactoryPostProcessor} used for processing of {@link Service @Service} annotated classes and annotated bean in java config classes.
* It's also the infrastructure class of XML {@link BeanDefinitionParser} on &lt;dubbbo:annotation /&gt;
*
*
* @see AnnotationBeanDefinitionParser
* @see BeanDefinitionRegistryPostProcessor
* @since 2.7.7
*/
public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware,
ResourceLoaderAware, BeanClassLoaderAware {
ResourceLoaderAware, BeanClassLoaderAware, ApplicationContextAware {
public static final String BEAN_NAME = "dubboServiceAnnotationPostProcessor";
private final static List<Class<? extends Annotation>> serviceAnnotationTypes = asList(
// @since 2.7.7 Add the @DubboService , the issue : https://github.com/apache/dubbo/issues/6007
@ -107,6 +120,11 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
private ClassLoader classLoader;
private BeanDefinitionRegistry registry;
private ServicePackagesHolder servicePackagesHolder;
public ServiceAnnotationPostProcessor(String... packagesToScan) {
this(asList(packagesToScan));
}
@ -121,14 +139,12 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// @since 2.7.5
registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME, DubboBootstrapApplicationListener.class);
this.registry = registry;
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
registerServiceBeans(resolvedPackagesToScan, registry);
scanServiceBeans(resolvedPackagesToScan, registry);
} else {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
@ -138,27 +154,35 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
}
/**
* Registers Beans whose classes was annotated {@link Service}
* Scan and registers service beans whose classes was annotated {@link Service}
*
* @param packagesToScan The base packages to scan
* @param registry {@link BeanDefinitionRegistry}
*/
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
private void scanServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
// refactor @since 2.7.7
serviceAnnotationTypes.forEach(annotationType -> {
for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
});
}
ScanExcludeFilter scanExcludeFilter = new ScanExcludeFilter();
scanner.addExcludeFilter(scanExcludeFilter);
for (String packageToScan : packagesToScan) {
// avoid duplicated scans
if (servicePackagesHolder.isPackageScanned(packageToScan)) {
if (logger.isInfoEnabled()) {
logger.info("Ignore package who has already bean scanned: " + packageToScan);
}
continue;
}
// Registers @Service Bean first
scanner.scan(packageToScan);
@ -167,28 +191,27 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
if (logger.isInfoEnabled()) {
List<String> serviceClasses = new ArrayList<>(beanDefinitionHolders.size());
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
serviceClasses.add(beanDefinitionHolder.getBeanDefinition().getBeanClassName());
}
logger.info("Found " + beanDefinitionHolders.size() + " classes annotated by Dubbo @Service under package [" + packageToScan + "]: " + serviceClasses);
}
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
registerServiceBean(beanDefinitionHolder, registry, scanner);
processScannedBeanDefinition(beanDefinitionHolder, registry, scanner);
servicePackagesHolder.addScannedClass(beanDefinitionHolder.getBeanDefinition().getBeanClassName());
}
if (logger.isInfoEnabled()) {
logger.info(beanDefinitionHolders.size() + " annotated Dubbo's @Service Components { " +
beanDefinitionHolders +
" } were scanned under package[" + packageToScan + "]");
}
} else {
if (logger.isWarnEnabled()) {
logger.warn("No Spring Bean annotating Dubbo's @Service was found under package["
+ packageToScan + "]");
logger.warn("No class annotated by Dubbo @Service was found under package ["
+ packageToScan + "], ignore re-scanned classes: " + scanExcludeFilter.getExcludedCount());
}
}
servicePackagesHolder.addScannedPackage(packageToScan);
}
}
/**
@ -270,17 +293,15 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
* @see ServiceBean
* @see BeanDefinition
*/
private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
DubboClassPathBeanDefinitionScanner scanner) {
private void processScannedBeanDefinition(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
DubboClassPathBeanDefinitionScanner scanner) {
Class<?> beanClass = resolveClass(beanDefinitionHolder);
Annotation service = findServiceAnnotation(beanClass);
/**
* The {@link AnnotationAttributes} of @Service annotation
*/
AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
// The attributes of @Service annotation
Map<String, Object> serviceAnnotationAttributes = AnnotationUtils.getAttributes(service, true);
Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
@ -290,26 +311,9 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(beanName, service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
buildServiceBeanDefinition(serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
registry.registerBeanDefinition(beanName, serviceBeanDefinition);
if (logger.isInfoEnabled()) {
logger.info("The BeanDefinition[" + serviceBeanDefinition +
"] of ServiceBean has been registered with name : " + beanName);
}
} else {
//TODO throw exception
if (logger.isWarnEnabled()) {
logger.warn("The Duplicated BeanDefinition[" + serviceBeanDefinition +
"] of ServiceBean[ bean name : " + beanName +
"] was be found , Did @DubboComponentScan scan to same package in many times?");
}
}
registerServiceBeanDefinition(beanName, serviceBeanDefinition, interfaceClass);
}
@ -337,10 +341,10 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
* @return ServiceBean@interfaceClassName#annotatedServiceBeanName
* @since 2.7.3
*/
private String generateServiceBeanName(AnnotationAttributes serviceAnnotationAttributes, Class<?> interfaceClass) {
private String generateServiceBeanName(Map<String, Object> serviceAnnotationAttributes, Class<?> interfaceClass) {
ServiceBeanNameBuilder builder = create(interfaceClass, environment)
.group(serviceAnnotationAttributes.getString("group"))
.version(serviceAnnotationAttributes.getString("version"));
.group((String) serviceAnnotationAttributes.get("group"))
.version((String) serviceAnnotationAttributes.get("version"));
return builder.build();
}
@ -375,18 +379,15 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
* Build the {@link AbstractBeanDefinition Bean Definition}
*
*
* @param beanName
* @param serviceAnnotation
* @param serviceAnnotationAttributes
* @param interfaceClass
* @param annotatedServiceBeanName
* @param refServiceBeanName
* @return
* @since 2.7.3
*/
private AbstractBeanDefinition buildServiceBeanDefinition(String beanName, Annotation serviceAnnotation,
AnnotationAttributes serviceAnnotationAttributes,
private AbstractBeanDefinition buildServiceBeanDefinition(Map<String, Object> serviceAnnotationAttributes,
Class<?> interfaceClass,
String annotatedServiceBeanName) {
String refServiceBeanName) {
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
@ -397,16 +398,16 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
"interface", "interfaceName", "parameters");
propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotation, environment, ignoreAttributeNames));
propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotationAttributes, environment, ignoreAttributeNames));
//set config id, for ConfigManager cache key
//builder.addPropertyValue("id", beanName);
// References "ref" property to annotated-@Service Bean
addPropertyReference(builder, "ref", annotatedServiceBeanName);
addPropertyReference(builder, "ref", refServiceBeanName);
// Set interface
builder.addPropertyValue("interface", interfaceClass.getName());
// Convert parameters into map
builder.addPropertyValue("parameters", convertParameters(serviceAnnotationAttributes.getStringArray("parameters")));
builder.addPropertyValue("parameters", convertParameters((String[]) serviceAnnotationAttributes.get("parameters")));
// Add methods parameters
List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
if (!methodConfigs.isEmpty()) {
@ -414,20 +415,20 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
}
// convert provider to providerIds
String providerConfigId = serviceAnnotationAttributes.getString("provider");
String providerConfigId = (String) serviceAnnotationAttributes.get("provider");
if (StringUtils.hasText(providerConfigId)) {
addPropertyValue(builder, "providerIds", providerConfigId);
}
// Convert registry[] to registryIds
String[] registryConfigIds = serviceAnnotationAttributes.getStringArray("registry");
String[] registryConfigIds = (String[]) serviceAnnotationAttributes.get("registry");
if (registryConfigIds != null && registryConfigIds.length > 0) {
resolveStringArray(registryConfigIds);
builder.addPropertyValue("registryIds", StringUtils.join(registryConfigIds, ','));
}
// Convert protocol[] to protocolIds
String[] protocolConfigIds = serviceAnnotationAttributes.getStringArray("protocol");
String[] protocolConfigIds = (String[]) serviceAnnotationAttributes.get("protocol");
if (protocolConfigIds != null && protocolConfigIds.length > 0) {
resolveStringArray(protocolConfigIds);
builder.addPropertyValue("protocolIds", StringUtils.join(protocolConfigIds, ','));
@ -435,19 +436,19 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
// TODO Could we ignore these attributes: applicatin/monitor/module ? Use global config
// monitor reference
String monitorConfigId = serviceAnnotationAttributes.getString("monitor");
String monitorConfigId = (String) serviceAnnotationAttributes.get("monitor");
if (StringUtils.hasText(monitorConfigId)) {
addPropertyReference(builder, "monitor", monitorConfigId);
}
// application reference
String applicationConfigId = serviceAnnotationAttributes.getString("application");
String applicationConfigId = (String) serviceAnnotationAttributes.get("application");
if (StringUtils.hasText(applicationConfigId)) {
addPropertyReference(builder, "application", applicationConfigId);
}
// module reference
String moduleConfigId = serviceAnnotationAttributes.getString("module");
String moduleConfigId = (String) serviceAnnotationAttributes.get("module");
if (StringUtils.hasText(moduleConfigId)) {
addPropertyReference(builder, "module", moduleConfigId);
}
@ -501,7 +502,99 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] beanNames = beanFactory.getBeanDefinitionNames();
for (String beanName : beanNames) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
Map<String, Object> annotationAttributes = getServiceAnnotationAttributes(beanDefinition);
if (annotationAttributes != null) {
// process @DubboService at java-config @bean method
processAnnotatedBeanDefinition(beanName, (AnnotatedBeanDefinition) beanDefinition, annotationAttributes);
}
}
}
/**
* Get dubbo service annotation class at java-config @bean method
* @return return service annotation attributes map if found, or return null if not found.
*/
private Map<String, Object> getServiceAnnotationAttributes(BeanDefinition beanDefinition) {
if (beanDefinition instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition;
MethodMetadata factoryMethodMetadata = annotatedBeanDefinition.getFactoryMethodMetadata();
if (factoryMethodMetadata != null) {
// try all dubbo service annotation types
for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
if (factoryMethodMetadata.isAnnotated(annotationType.getName())) {
// Since Spring 5.2
// return factoryMethodMetadata.getAnnotations().get(annotationType).filterDefaultValues().asMap();
// Compatible with Spring 4.x
Map<String, Object> annotationAttributes = factoryMethodMetadata.getAnnotationAttributes(annotationType.getName());
return filterDefaultValues(annotationType, annotationAttributes);
}
}
}
}
return null;
}
/**
* process @DubboService at java-config @bean method
* <pre class="code">
* &#064;Configuration
* public class ProviderConfig {
*
* &#064;Bean
* &#064;DubboService(group="demo", version="1.2.3")
* public DemoService demoService() {
* return new DemoServiceImpl();
* }
*
* }
* </pre>
* @param refServiceBeanName
* @param refServiceBeanDefinition
* @param attributes
*/
private void processAnnotatedBeanDefinition(String refServiceBeanName, AnnotatedBeanDefinition refServiceBeanDefinition, Map<String, Object> attributes) {
Map<String, Object> serviceAnnotationAttributes = new LinkedHashMap<>(attributes);
// get bean class from return type
String returnTypeName = refServiceBeanDefinition.getFactoryMethodMetadata().getReturnTypeName();
Class<?> beanClass = resolveClassName(returnTypeName, classLoader);
Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
// ServiceBean Bean name
String serviceBeanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
AbstractBeanDefinition serviceBeanDefinition = buildServiceBeanDefinition(serviceAnnotationAttributes, interfaceClass, refServiceBeanName);
// set id
serviceBeanDefinition.getPropertyValues().add(Constants.ID, serviceBeanName);
registerServiceBeanDefinition(serviceBeanName, serviceBeanDefinition, interfaceClass);
}
private void registerServiceBeanDefinition(String serviceBeanName, AbstractBeanDefinition serviceBeanDefinition, Class<?> interfaceClass) {
// check service bean
if (registry.containsBeanDefinition(serviceBeanName)) {
BeanDefinition existingDefinition = registry.getBeanDefinition(serviceBeanName);
if (existingDefinition.equals(serviceBeanDefinition)) {
// exist equipment bean definition
return;
}
String msg = "Found duplicated BeanDefinition of service interface [" + interfaceClass.getName() + "] with bean name [" + serviceBeanName +
"], existing definition [ " + existingDefinition + "], new definition [" + serviceBeanDefinition + "]";
logger.error(msg);
throw new BeanDefinitionStoreException(serviceBeanDefinition.getResourceDescription(), serviceBeanName, msg);
}
registry.registerBeanDefinition(serviceBeanName, serviceBeanDefinition);
if (logger.isInfoEnabled()) {
logger.info("Register ServiceBean[" + serviceBeanName + "]: " + serviceBeanDefinition);
}
}
@Override
@ -518,4 +611,28 @@ public class ServiceAnnotationPostProcessor implements BeanDefinitionRegistryPos
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.servicePackagesHolder = applicationContext.getBean(ServicePackagesHolder.class);
}
private class ScanExcludeFilter implements TypeFilter {
private int excludedCount;
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
String className = metadataReader.getClassMetadata().getClassName();
boolean excluded = servicePackagesHolder.isClassScanned(className);
if (excluded) {
excludedCount ++;
}
return excluded;
}
public int getExcludedCount() {
return excludedCount;
}
}
}

View File

@ -0,0 +1,86 @@
/*
* 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.beans.factory.annotation;
import java.util.HashSet;
import java.util.Set;
/**
* A temp holder for scanned packages of service.
*/
public class ServicePackagesHolder {
public static final String BEAN_NAME = "dubboServicePackagesHolder";
private final Set<String> scannedPackages = new HashSet<>();
private final Set<String> scannedClasses = new HashSet<>();
public void addScannedPackage(String apackage) {
apackage = normalizePackage(apackage);
synchronized (scannedPackages) {
scannedPackages.add(apackage);
}
}
public boolean isPackageScanned(String packageName) {
packageName = normalizePackage(packageName);
synchronized (scannedPackages) {
if (scannedPackages.contains(packageName)) {
return true;
}
for (String scannedPackage : scannedPackages) {
if (isSubPackage(packageName, scannedPackage)) {
return true;
}
}
}
return false;
}
public void addScannedClass(String className) {
synchronized (scannedClasses) {
scannedClasses.add(className);
}
}
public boolean isClassScanned(String className) {
synchronized (scannedClasses) {
return scannedClasses.contains(className);
}
}
/**
* Whether test package is sub package of parent package
* @param testPkg
* @param parent
* @return
*/
private boolean isSubPackage(String testPkg, String parent) {
// child pkg startsWith parent pkg
return testPkg.startsWith(parent);
}
private String normalizePackage(String apackage) {
if (!apackage.endsWith(".")) {
apackage += ".";
}
return apackage;
}
}

View File

@ -54,7 +54,7 @@ public class DubboInfraBeanRegisterPostProcessor implements BeanDefinitionRegist
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
DubboBeanUtils.registerBeansIfNotExists(registry);
// register ConfigManager
// register ConfigManager singleton
beanFactory.registerSingleton(ConfigManager.BEAN_NAME, ApplicationModel.getConfigManager());
}

View File

@ -53,12 +53,12 @@ public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistra
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// @since 2.7.6 Register the common beans
registerCommonBeans(registry);
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
registerServiceAnnotationPostProcessor(packagesToScan, registry);
// @since 2.7.6 Register the common beans
registerCommonBeans(registry);
}
/**

View File

@ -29,10 +29,10 @@ import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.ProviderConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.spring.Constants;
import org.apache.dubbo.config.spring.ReferenceBean;
import org.apache.dubbo.config.spring.ServiceBean;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
@ -278,7 +278,7 @@ public class DubboBeanDefinitionParser implements BeanDefinitionParser {
beanDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, beanName+"_decorated"));
// signal object type since Spring 5.2
beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, interfaceClass);
beanDefinition.setAttribute(Constants.OBJECT_TYPE_ATTRIBUTE, interfaceClass);
//mark property value as optional
List<PropertyValue> propertyValues = beanDefinition.getPropertyValues().getPropertyValueList();

View File

@ -19,10 +19,11 @@ package org.apache.dubbo.config.spring.util;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import java.util.Map;
import static com.alibaba.spring.util.AnnotationUtils.getAttribute;
import static org.springframework.util.ClassUtils.getAllInterfacesForClass;
import static org.springframework.util.ClassUtils.resolveClassName;
@ -59,14 +60,14 @@ public class DubboAnnotationUtils {
}
/**
* Resolve the interface name from {@link AnnotationAttributes}
* Resolve the interface name from annotation attributes
*
* @param attributes {@link AnnotationAttributes} instance, may be {@link Service @Service} or {@link Reference @Reference}
* @param defaultInterfaceClass the default {@link Class class} of interface
* @param attributes annotation attributes instance, may be {@link Service @Service} or {@link Reference @Reference}
* @param defaultInterfaceClass the default class of interface
* @return the interface name if found
* @throws IllegalStateException if interface name was not found
*/
public static String resolveInterfaceName(AnnotationAttributes attributes, Class<?> defaultInterfaceClass) {
public static String resolveInterfaceName(Map<String, Object> attributes, Class<?> defaultInterfaceClass) {
Boolean generic = getAttribute(attributes, "generic");
if (generic != null && generic) {
// it's a generic reference
@ -79,22 +80,22 @@ public class DubboAnnotationUtils {
}
/**
* Resolve the {@link Class class} of Dubbo Service interface from the specified
* {@link AnnotationAttributes annotation attributes} and annotated {@link Class class}.
* Resolve the interface class of Dubbo Service annotation from the specified
* annotation attributes and annotated class.
*
* @param attributes {@link AnnotationAttributes annotation attributes}
* @param defaultInterfaceClass the annotated {@link Class class}.
* @return the {@link Class class} of Dubbo Service interface
* @param attributes annotation attributes
* @param defaultInterfaceClass the annotated class.
* @return the class of Dubbo Service interface
* @throws IllegalArgumentException if can't resolved
*/
public static Class<?> resolveServiceInterfaceClass(AnnotationAttributes attributes, Class<?> defaultInterfaceClass)
public static Class<?> resolveServiceInterfaceClass(Map<String, Object> attributes, Class<?> defaultInterfaceClass)
throws IllegalArgumentException {
ClassLoader classLoader = defaultInterfaceClass != null ? defaultInterfaceClass.getClassLoader() : Thread.currentThread().getContextClassLoader();
Class<?> interfaceClass = getAttribute(attributes, "interfaceClass");
if (void.class.equals(interfaceClass)) { // default or set void.class for purpose.
if (interfaceClass == null || void.class.equals(interfaceClass)) { // default or set void.class for purpose.
interfaceClass = null;

View File

@ -18,6 +18,7 @@ package org.apache.dubbo.config.spring.util;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.dubbo.config.spring.beans.factory.annotation.ServicePackagesHolder;
import org.apache.dubbo.config.spring.context.DubboConfigInitializationPostProcessor;
import org.apache.dubbo.config.spring.reference.ReferenceBeanManager;
import org.apache.dubbo.config.spring.beans.factory.annotation.DubboConfigAliasPostProcessor;
@ -58,12 +59,15 @@ public interface DubboBeanUtils {
*/
static void registerCommonBeans(BeanDefinitionRegistry registry) {
registerInfrastructureBean(registry, ServicePackagesHolder.BEAN_NAME, ServicePackagesHolder.class);
registerInfrastructureBean(registry, ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class);
// Since 2.5.7 Register @Reference Annotation Bean Processor as an infrastructure Bean
registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
ReferenceAnnotationBeanPostProcessor.class);
// TODO Whether DubboConfigAliasPostProcessor can be removed ?
// Since 2.7.4 [Feature] https://github.com/apache/dubbo/issues/5093
registerInfrastructureBean(registry, DubboConfigAliasPostProcessor.BEAN_NAME,
DubboConfigAliasPostProcessor.class);

View File

@ -22,10 +22,12 @@ import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.apache.dubbo.config.bootstrap.DubboBootstrap;
import org.apache.dubbo.config.context.ConfigManager;
import org.apache.dubbo.config.spring.api.DemoService;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.apache.dubbo.config.spring.impl.DemoServiceImpl;
import org.apache.dubbo.rpc.Constants;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.junit.jupiter.api.AfterEach;
@ -38,7 +40,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class JavaConfigBeanTest {
@ -70,15 +71,12 @@ public class JavaConfigBeanTest {
SysProps.setProperty("dubbo.protocol.port", "2346");
String registryAddress = "zookeeper://127.0.0.1:2181";
SysProps.setProperty("dubbo.registry.address", registryAddress);
SysProps.setProperty("dubbo.provider.group", "test");
// test destroy flag
// MockServiceDiscovery mockServiceDiscovery = (MockServiceDiscovery) ExtensionLoader.getExtensionLoader(ServiceDiscovery.class).getExtension("mock");
// Assertions.assertEquals(false, mockServiceDiscovery.isDestroySucceed());
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(
TestConfiguration.class, ReferenceConfiguration.class);
AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext(
TestConfiguration.class, ConsumerConfiguration.class, ProviderConfiguration.class);
try {
applicationContext.start();
consumerContext.start();
ConfigManager configManager = ApplicationModel.getConfigManager();
ApplicationConfig application = configManager.getApplication().get();
@ -101,7 +99,7 @@ public class JavaConfigBeanTest {
Assertions.assertEquals(false, consumerConfig.isCheck());
Assertions.assertEquals(2, consumerConfig.getRetries());
Map<String, ReferenceBean> referenceBeanMap = applicationContext.getBeansOfType(ReferenceBean.class);
Map<String, ReferenceBean> referenceBeanMap = consumerContext.getBeansOfType(ReferenceBean.class);
Assertions.assertEquals(1, referenceBeanMap.size());
ReferenceBean referenceBean = referenceBeanMap.get("&demoService");
Assertions.assertNotNull(referenceBean);
@ -112,12 +110,14 @@ public class JavaConfigBeanTest {
// consumer cannot override reference's attribute
Assertions.assertEquals(5, referenceConfig.getRetries());
} finally {
applicationContext.close();
}
DemoService referProxy = (DemoService) referenceConfig.get();
Assertions.assertTrue( referProxy instanceof DemoService);
String result = referProxy.sayName("dubbo");
Assertions.assertEquals("say:dubbo", result);
// test destroy flag
//Assertions.assertEquals(true, mockServiceDiscovery.isDestroySucceed());
} finally {
consumerContext.close();
}
}
@ -160,7 +160,7 @@ public class JavaConfigBeanTest {
}
@Configuration
static class ReferenceConfiguration {
static class ConsumerConfiguration {
@Bean
@DubboReference(scope = Constants.SCOPE_LOCAL, retries = 5)
@ -169,4 +169,14 @@ public class JavaConfigBeanTest {
}
}
@Configuration
static class ProviderConfiguration {
@Bean
@DubboService(group = "demo")
public DemoService demoServiceImpl() {
return new DemoServiceImpl();
}
}
}

View File

@ -19,6 +19,7 @@ package org.apache.dubbo.config.spring.beans.factory.annotation;
import org.apache.dubbo.config.bootstrap.DubboBootstrap;
import org.apache.dubbo.config.spring.ServiceBean;
import org.apache.dubbo.config.spring.api.HelloService;
import org.apache.dubbo.config.spring.context.annotation.DubboComponentScan;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
@ -45,7 +46,8 @@ import static org.springframework.test.annotation.DirtiesContext.ClassMode.AFTER
@ContextConfiguration(
classes = {
ServiceAnnotationTestConfiguration.class,
ServiceAnnotationPostProcessorTest.class
ServiceAnnotationPostProcessorTest.class,
ServiceAnnotationPostProcessorTest.DuplicatedScanConfig.class
})
@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD)
@TestPropertySource(properties = {
@ -81,7 +83,7 @@ public class ServiceAnnotationPostProcessorTest {
Map<String, ServiceAnnotationPostProcessor> beanPostProcessorsMap =
beanFactory.getBeansOfType(ServiceAnnotationPostProcessor.class);
Assertions.assertEquals(1, beanPostProcessorsMap.size());
Assertions.assertEquals(2, beanPostProcessorsMap.size());
}
@ -98,4 +100,9 @@ public class ServiceAnnotationPostProcessorTest {
}
@DubboComponentScan({"org.apache.dubbo.config.spring.context.annotation", "${provider.package}"})
static class DuplicatedScanConfig {
}
}

View File

@ -18,6 +18,7 @@ package org.apache.dubbo.config.spring.extension;
import org.apache.dubbo.common.extension.ExtensionFactory;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.config.bootstrap.DubboBootstrap;
import org.apache.dubbo.config.spring.api.DemoService;
import org.apache.dubbo.config.spring.api.HelloService;
import org.apache.dubbo.config.spring.impl.DemoServiceImpl;
@ -41,6 +42,8 @@ public class SpringExtensionFactoryTest {
@BeforeEach
public void init() {
DubboBootstrap.reset();
// init SpringExtensionFactory
ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getExtension("spring");
@ -56,6 +59,13 @@ public class SpringExtensionFactoryTest {
SpringExtensionFactory.addApplicationContext(context2);
}
@AfterEach
public void destroy() {
context1.close();
context2.close();
SpringExtensionFactory.clearContexts();
}
@Test
public void testGetExtensionBySPI() {
Protocol protocol = springExtensionFactory.getExtension(Protocol.class, "protocol");
@ -70,13 +80,6 @@ public class SpringExtensionFactoryTest {
Assertions.assertNotNull(hello);
}
@AfterEach
public void destroy() {
SpringExtensionFactory.clearContexts();
context1.close();
context2.close();
}
@Bean("bean1")
public DemoService bean1() {
return new DemoServiceImpl();

View File

@ -89,6 +89,7 @@
<properties>
<!-- Common libs -->
<!-- <spring_version>4.3.30.RELEASE</spring_version> -->
<spring_version>5.2.8.RELEASE</spring_version>
<javassist_version>3.23.1-GA</javassist_version>
<eclipse_collections_version>10.4.0</eclipse_collections_version>

View File

@ -62,7 +62,7 @@ public abstract class AbstractRequestAnnotationParameterProcessor extends Abstra
private boolean isDefaultValue(Annotation annotation, String attributeName, Object attributeValue) {
String defaultValue = AnnotationUtils.getDefaultValue(annotation, attributeName);
return Objects.equals(attributeValue, defaultValue);
return Objects.deepEquals(attributeValue, defaultValue);
}
protected boolean isEmpty(String str) {

View File

@ -31,7 +31,6 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
@ -71,7 +70,6 @@ public class DubboAutoConfiguration implements ApplicationContextAware, BeanDefi
*/
@ConditionalOnProperty(prefix = DUBBO_SCAN_PREFIX, name = BASE_PACKAGES_PROPERTY_NAME)
@ConditionalOnBean(name = BASE_PACKAGES_BEAN_NAME)
@ConditionalOnMissingBean
@Bean
public ServiceAnnotationPostProcessor serviceAnnotationBeanProcessor(@Qualifier(BASE_PACKAGES_BEAN_NAME)
Set<String> packagesToScan) {