diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java index 3bab26134e3..52204acb4dd 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java @@ -366,7 +366,7 @@ private static Object realize0(Object pojo, Class type, Type genericType, fin history.put(pojo, dest); for (Object obj : src) { Type keyType = getGenericClassByIndex(genericType, 0); - Class keyClazz = obj.getClass(); + Class keyClazz = obj == null ? null : obj.getClass(); if (keyType instanceof Class) { keyClazz = (Class) keyType; } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java index 3595efb99f6..f0ab173b4f8 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java @@ -37,6 +37,7 @@ import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; +import java.util.HashSet; import java.util.Map; import java.util.UUID; @@ -682,6 +683,24 @@ public void testDateTimeTimestamp() throws Exception { assertEquals(dateTimeStr, new SimpleDateFormat(dateFormat[0]).format(timestamp)); } + @Test + public void testRealizeCollectionWithNullElement() { + LinkedList listStr = new LinkedList<>(); + listStr.add("arrayValue"); + listStr.add(null); + HashSet setStr = new HashSet<>(); + setStr.add("setValue"); + setStr.add(null); + + Object listResult = PojoUtils.realize(listStr, LinkedList.class); + assertEquals(LinkedList.class, listResult.getClass()); + assertEquals(listResult, listStr); + + Object setResult = PojoUtils.realize(setStr, HashSet.class); + assertEquals(HashSet.class, setResult.getClass()); + assertEquals(setResult, setStr); + } + public enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY } diff --git a/dubbo-compatible/src/main/java/com/alibaba/dubbo/config/spring/context/annotation/EnableDubbo.java b/dubbo-compatible/src/main/java/com/alibaba/dubbo/config/spring/context/annotation/EnableDubbo.java index 1310d1a2032..d5227ed7e97 100644 --- a/dubbo-compatible/src/main/java/com/alibaba/dubbo/config/spring/context/annotation/EnableDubbo.java +++ b/dubbo-compatible/src/main/java/com/alibaba/dubbo/config/spring/context/annotation/EnableDubbo.java @@ -18,7 +18,7 @@ package com.alibaba.dubbo.config.spring.context.annotation; import org.apache.dubbo.config.AbstractConfig; -import org.apache.dubbo.config.spring.context.annotation.CompatibleDubboComponentScan; +import org.apache.dubbo.config.spring.context.annotation.DubboComponentScan; import org.apache.dubbo.config.spring.context.annotation.EnableDubboConfig; import org.springframework.core.annotation.AliasFor; @@ -36,7 +36,7 @@ @Inherited @Documented @EnableDubboConfig -@CompatibleDubboComponentScan +@DubboComponentScan public @interface EnableDubbo { /** @@ -46,9 +46,9 @@ * package names. * * @return the base packages to scan - * @see CompatibleDubboComponentScan#basePackages() + * @see DubboComponentScan#basePackages() */ - @AliasFor(annotation = CompatibleDubboComponentScan.class, attribute = "basePackages") + @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default {}; /** @@ -57,9 +57,9 @@ * scanned. * * @return classes from the base packages to scan - * @see CompatibleDubboComponentScan#basePackageClasses + * @see DubboComponentScan#basePackageClasses */ - @AliasFor(annotation = CompatibleDubboComponentScan.class, attribute = "basePackageClasses") + @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackageClasses") Class[] scanBasePackageClasses() default {}; diff --git a/dubbo-compatible/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/CompatibleReferenceAnnotationBeanPostProcessor.java b/dubbo-compatible/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/CompatibleReferenceAnnotationBeanPostProcessor.java deleted file mode 100644 index d6826a5f819..00000000000 --- a/dubbo-compatible/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/CompatibleReferenceAnnotationBeanPostProcessor.java +++ /dev/null @@ -1,508 +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.beans.factory.annotation; - -import org.apache.dubbo.config.spring.ReferenceBean; - -import com.alibaba.dubbo.config.annotation.Reference; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.BeanUtils; -import org.springframework.beans.BeansException; -import org.springframework.beans.PropertyValues; -import org.springframework.beans.factory.BeanClassLoaderAware; -import org.springframework.beans.factory.BeanCreationException; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.annotation.InjectionMetadata; -import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; -import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.context.ApplicationContext; -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.ReflectionUtils; -import org.springframework.util.StringUtils; - -import java.beans.PropertyDescriptor; -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.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import static org.springframework.core.BridgeMethodResolver.findBridgedMethod; -import static org.springframework.core.BridgeMethodResolver.isVisibilityBridgeMethodPair; -import static org.springframework.core.annotation.AnnotationUtils.findAnnotation; -import static org.springframework.core.annotation.AnnotationUtils.getAnnotation; - -/** - * {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation - * that Consumer service {@link Reference} annotated fields - * - * @since 2.5.7 deprecated since 2.7.0 - */ -@Deprecated -public class CompatibleReferenceAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter - implements MergedBeanDefinitionPostProcessor, PriorityOrdered, ApplicationContextAware, BeanClassLoaderAware, - DisposableBean { - - /** - * The bean name of {@link CompatibleReferenceAnnotationBeanPostProcessor} - */ - public static final String BEAN_NAME = "compatibleReferenceAnnotationBeanPostProcessor"; - - private final Log logger = LogFactory.getLog(getClass()); - - private ApplicationContext applicationContext; - - private ClassLoader classLoader; - - private final ConcurrentMap injectionMetadataCache = - new ConcurrentHashMap(256); - - private final ConcurrentMap> referenceBeansCache = - new ConcurrentHashMap>(); - - @Override - public PropertyValues postProcessPropertyValues( - PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException { - - InjectionMetadata metadata = findReferenceMetadata(beanName, bean.getClass(), pvs); - try { - metadata.inject(bean, beanName, pvs); - } catch (BeanCreationException ex) { - throw ex; - } catch (Throwable ex) { - throw new BeanCreationException(beanName, "Injection of @Reference dependencies failed", ex); - } - return pvs; - } - - - /** - * Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated {@link Reference @Reference} fields - * - * @param beanClass The {@link Class} of Bean - * @return non-null {@link List} - */ - private List findFieldReferenceMetadata(final Class beanClass) { - - final List elements = new LinkedList(); - - ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() { - @Override - public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { - - Reference reference = getAnnotation(field, Reference.class); - - if (reference != null) { - - if (Modifier.isStatic(field.getModifiers())) { - if (logger.isWarnEnabled()) { - logger.warn("@Reference annotation is not supported on static fields: " + field); - } - return; - } - - elements.add(new ReferenceFieldElement(field, reference)); - } - - } - }); - - return elements; - - } - - /** - * Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated {@link Reference @Reference} methods - * - * @param beanClass The {@link Class} of Bean - * @return non-null {@link List} - */ - private List findMethodReferenceMetadata(final Class beanClass) { - - final List elements = new LinkedList(); - - ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() { - @Override - public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { - - Method bridgedMethod = findBridgedMethod(method); - - if (!isVisibilityBridgeMethodPair(method, bridgedMethod)) { - return; - } - - Reference reference = findAnnotation(bridgedMethod, Reference.class); - - if (reference != null && method.equals(ClassUtils.getMostSpecificMethod(method, beanClass))) { - if (Modifier.isStatic(method.getModifiers())) { - if (logger.isWarnEnabled()) { - logger.warn("@Reference annotation is not supported on static methods: " + method); - } - return; - } - if (method.getParameterTypes().length == 0) { - if (logger.isWarnEnabled()) { - logger.warn("@Reference annotation should only be used on methods with parameters: " + - method); - } - } - PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, beanClass); - elements.add(new ReferenceMethodElement(method, pd, reference)); - } - } - }); - - return elements; - - } - - - /** - * @param beanClass - * @return - */ - private ReferenceInjectionMetadata buildReferenceMetadata(final Class beanClass) { - Collection fieldElements = findFieldReferenceMetadata(beanClass); - Collection methodElements = findMethodReferenceMetadata(beanClass); - return new ReferenceInjectionMetadata(beanClass, fieldElements, methodElements); - - } - - 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()); - // Quick check on the concurrent map first, with minimal locking. - ReferenceInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey); - if (InjectionMetadata.needsRefresh(metadata, clazz)) { - synchronized (this.injectionMetadataCache) { - metadata = this.injectionMetadataCache.get(cacheKey); - if (InjectionMetadata.needsRefresh(metadata, clazz)) { - if (metadata != null) { - metadata.clear(pvs); - } - try { - metadata = buildReferenceMetadata(clazz); - this.injectionMetadataCache.put(cacheKey, metadata); - } catch (NoClassDefFoundError err) { - throw new IllegalStateException("Failed to introspect bean class [" + clazz.getName() + - "] for reference metadata: could not find class that it depends on", err); - } - } - } - } - return metadata; - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; - } - - @Override - public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { - if (beanType != null) { - InjectionMetadata metadata = findReferenceMetadata(beanName, beanType, null); - metadata.checkConfigMembers(beanDefinition); - } - } - - @Override - public int getOrder() { - return LOWEST_PRECEDENCE; - } - - @Override - public void destroy() throws Exception { - - for (ReferenceBean referenceBean : referenceBeansCache.values()) { - if (logger.isInfoEnabled()) { - logger.info(referenceBean + " was destroying!"); - } - referenceBean.destroy(); - } - - injectionMetadataCache.clear(); - referenceBeansCache.clear(); - - if (logger.isInfoEnabled()) { - logger.info(getClass() + " was destroying!"); - } - - } - - @Override - public void setBeanClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader; - } - - - /** - * Gets all beans of {@link ReferenceBean} - * - * @return non-null {@link Collection} - * @since 2.5.9 - */ - public Collection> getReferenceBeans() { - return this.referenceBeansCache.values(); - } - - - /** - * {@link Reference} {@link InjectionMetadata} implementation - * - * @since 2.5.11 - */ - private static class ReferenceInjectionMetadata extends InjectionMetadata { - - private final Collection fieldElements; - - private final Collection methodElements; - - - public ReferenceInjectionMetadata(Class targetClass, Collection fieldElements, - Collection methodElements) { - super(targetClass, combine(fieldElements, methodElements)); - this.fieldElements = fieldElements; - this.methodElements = methodElements; - } - - private static Collection combine(Collection... elements) { - List allElements = new ArrayList(); - for (Collection e : elements) { - allElements.addAll(e); - } - return allElements; - } - - public Collection getFieldElements() { - return fieldElements; - } - - public Collection getMethodElements() { - return methodElements; - } - } - - /** - * {@link Reference} {@link Method} {@link InjectionMetadata.InjectedElement} - */ - private class ReferenceMethodElement extends InjectionMetadata.InjectedElement { - - private final Method method; - - private final Reference reference; - - private volatile ReferenceBean referenceBean; - - protected ReferenceMethodElement(Method method, PropertyDescriptor pd, Reference reference) { - super(method, pd); - this.method = method; - this.reference = reference; - } - - @Override - protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable { - - Class referenceClass = pd.getPropertyType(); - - referenceBean = buildReferenceBean(reference, referenceClass); - - ReflectionUtils.makeAccessible(method); - - method.invoke(bean, referenceBean.getObject()); - - } - - } - - /** - * {@link Reference} {@link Field} {@link InjectionMetadata.InjectedElement} - */ - private class ReferenceFieldElement extends InjectionMetadata.InjectedElement { - - private final Field field; - - private final Reference reference; - - private volatile ReferenceBean referenceBean; - - protected ReferenceFieldElement(Field field, Reference reference) { - super(field, null); - this.field = field; - this.reference = reference; - } - - @Override - protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable { - - Class referenceClass = field.getType(); - - referenceBean = buildReferenceBean(reference, referenceClass); - - ReflectionUtils.makeAccessible(field); - - field.set(bean, referenceBean.getObject()); - - } - - } - - private ReferenceBean buildReferenceBean(Reference reference, Class referenceClass) throws Exception { - - String referenceBeanCacheKey = generateReferenceBeanCacheKey(reference, referenceClass); - - ReferenceBean referenceBean = referenceBeansCache.get(referenceBeanCacheKey); - - if (referenceBean == null) { - - CompatibleReferenceBeanBuilder beanBuilder = CompatibleReferenceBeanBuilder - .create(reference, classLoader, applicationContext) - .interfaceClass(referenceClass); - - referenceBean = beanBuilder.build(); - - referenceBeansCache.putIfAbsent(referenceBeanCacheKey, referenceBean); - - } - - return referenceBean; - - } - - - /** - * Generate a cache key of {@link ReferenceBean} - * - * @param reference {@link Reference} - * @param beanClass {@link Class} - * @return - */ - private String generateReferenceBeanCacheKey(Reference reference, Class beanClass) { - - String interfaceName = resolveInterfaceName(reference, beanClass); - - String key = reference.url() + "/" + interfaceName + - "/" + reference.version() + - "/" + reference.group(); - - Environment environment = applicationContext.getEnvironment(); - - key = environment.resolvePlaceholders(key); - - return key; - - } - - private static String resolveInterfaceName(Reference reference, Class beanClass) - throws IllegalStateException { - - String interfaceName; - if (!"".equals(reference.interfaceName())) { - interfaceName = reference.interfaceName(); - } else if (!void.class.equals(reference.interfaceClass())) { - interfaceName = reference.interfaceClass().getName(); - } else if (beanClass.isInterface()) { - interfaceName = beanClass.getName(); - } else { - throw new IllegalStateException( - "The @Reference undefined interfaceClass or interfaceName, and the property type " - + beanClass.getName() + " is not a interface."); - } - - return interfaceName; - - } - - - /** - * Get {@link ReferenceBean} {@link Map} in injected field. - * - * @return non-null {@link Map} - * @since 2.5.11 - */ - public Map> getInjectedFieldReferenceBeanMap() { - - Map> injectedElementReferenceBeanMap = - new LinkedHashMap>(); - - for (ReferenceInjectionMetadata metadata : injectionMetadataCache.values()) { - - Collection fieldElements = metadata.getFieldElements(); - - for (ReferenceFieldElement fieldElement : fieldElements) { - - injectedElementReferenceBeanMap.put(fieldElement, fieldElement.referenceBean); - - } - - } - - return injectedElementReferenceBeanMap; - - } - - /** - * Get {@link ReferenceBean} {@link Map} in injected method. - * - * @return non-null {@link Map} - * @since 2.5.11 - */ - public Map> getInjectedMethodReferenceBeanMap() { - - Map> injectedElementReferenceBeanMap = - new LinkedHashMap>(); - - for (ReferenceInjectionMetadata metadata : injectionMetadataCache.values()) { - - Collection methodElements = metadata.getMethodElements(); - - for (ReferenceMethodElement methodElement : methodElements) { - - injectedElementReferenceBeanMap.put(methodElement, methodElement.referenceBean); - - } - - } - - return injectedElementReferenceBeanMap; - - } - - private T getFieldValue(Object object, String fieldName, Class fieldType) { - - Field field = ReflectionUtils.findField(object.getClass(), fieldName, fieldType); - - ReflectionUtils.makeAccessible(field); - - return (T) ReflectionUtils.getField(field, object); - - } - - -} diff --git a/dubbo-compatible/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/CompatibleReferenceBeanBuilder.java b/dubbo-compatible/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/CompatibleReferenceBeanBuilder.java deleted file mode 100644 index 16013b37e2f..00000000000 --- a/dubbo-compatible/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/CompatibleReferenceBeanBuilder.java +++ /dev/null @@ -1,167 +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.beans.factory.annotation; - -import com.alibaba.dubbo.config.annotation.Reference; -import org.apache.dubbo.common.utils.CollectionUtils; -import org.apache.dubbo.config.ConsumerConfig; -import org.apache.dubbo.config.spring.ReferenceBean; -import org.springframework.beans.propertyeditors.StringTrimmerEditor; -import org.springframework.context.ApplicationContext; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; -import org.springframework.validation.DataBinder; - -import java.beans.PropertyEditorSupport; -import java.util.Map; - -import static org.apache.dubbo.config.spring.util.BeanFactoryUtils.getOptionalBean; -import static org.apache.dubbo.config.spring.util.ObjectUtils.of; -import static org.springframework.util.StringUtils.commaDelimitedListToStringArray; - -/** - * {@link ReferenceBean} Builder - * - * @since 2.5.7 deprecated since 2.7.0 - */ -@Deprecated -class CompatibleReferenceBeanBuilder extends AbstractAnnotationConfigBeanBuilder { - - - // Ignore those fields - static final String[] IGNORE_FIELD_NAMES = of("application", "module", "consumer", "monitor", "registry"); - - private CompatibleReferenceBeanBuilder(Reference annotation, ClassLoader classLoader, ApplicationContext applicationContext) { - super(annotation, classLoader, applicationContext); - } - - private void configureInterface(Reference reference, ReferenceBean referenceBean) { - - Class interfaceClass = reference.interfaceClass(); - - if (void.class.equals(interfaceClass)) { - - interfaceClass = null; - - String interfaceClassName = reference.interfaceName(); - - if (StringUtils.hasText(interfaceClassName)) { - if (ClassUtils.isPresent(interfaceClassName, classLoader)) { - interfaceClass = ClassUtils.resolveClassName(interfaceClassName, classLoader); - } - } - - } - - if (interfaceClass == null) { - interfaceClass = this.interfaceClass; - } - - Assert.isTrue(interfaceClass.isInterface(), - "The class of field or method that was annotated @Reference is not an interface!"); - - referenceBean.setInterface(interfaceClass); - - } - - - private void configureConsumerConfig(Reference reference, ReferenceBean referenceBean) { - - String consumerBeanName = reference.consumer(); - - ConsumerConfig consumerConfig = getOptionalBean(applicationContext, consumerBeanName, ConsumerConfig.class); - - referenceBean.setConsumer(consumerConfig); - - } - - @Override - protected ReferenceBean doBuild() { - return new ReferenceBean(); - } - - @Override - protected void preConfigureBean(Reference reference, ReferenceBean referenceBean) { - Assert.notNull(interfaceClass, "The interface class must set first!"); - DataBinder dataBinder = new DataBinder(referenceBean); - // Register CustomEditors for special fields - dataBinder.registerCustomEditor(String.class, "filter", new StringTrimmerEditor(true)); - dataBinder.registerCustomEditor(String.class, "listener", new StringTrimmerEditor(true)); - dataBinder.registerCustomEditor(Map.class, "parameters", new PropertyEditorSupport() { - @Override - public void setAsText(String text) throws java.lang.IllegalArgumentException { - // Trim all whitespace - String content = StringUtils.trimAllWhitespace(text); - if (!StringUtils.hasText(content)) { // No content , ignore directly - return; - } - // replace "=" to "," - content = StringUtils.replace(content, "=", ","); - // replace ":" to "," - content = StringUtils.replace(content, ":", ","); - // String[] to Map - Map parameters = CollectionUtils.toStringMap(commaDelimitedListToStringArray(content)); - setValue(parameters); - } - }); - - // Bind annotation attributes - dataBinder.bind(new AnnotationPropertyValuesAdapter(reference, applicationContext.getEnvironment(), IGNORE_FIELD_NAMES)); - - } - - - @Override - protected String resolveModuleConfigBeanName(Reference annotation) { - return annotation.module(); - } - - @Override - protected String resolveApplicationConfigBeanName(Reference annotation) { - return annotation.application(); - } - - @Override - protected String[] resolveRegistryConfigBeanNames(Reference annotation) { - return annotation.registry(); - } - - @Override - protected String resolveMonitorConfigBeanName(Reference annotation) { - return annotation.monitor(); - } - - @Override - protected void postConfigureBean(Reference annotation, ReferenceBean bean) throws Exception { - - bean.setApplicationContext(applicationContext); - - configureInterface(annotation, bean); - - configureConsumerConfig(annotation, bean); - - bean.afterPropertiesSet(); - - } - - public static CompatibleReferenceBeanBuilder create(Reference annotation, ClassLoader classLoader, - ApplicationContext applicationContext) { - return new CompatibleReferenceBeanBuilder(annotation, classLoader, applicationContext); - } - -} diff --git a/dubbo-compatible/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/CompatibleServiceAnnotationBeanPostProcessor.java b/dubbo-compatible/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/CompatibleServiceAnnotationBeanPostProcessor.java deleted file mode 100644 index 3d0370c7cc2..00000000000 --- a/dubbo-compatible/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/CompatibleServiceAnnotationBeanPostProcessor.java +++ /dev/null @@ -1,525 +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.beans.factory.annotation; - -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.config.spring.ServiceBean; -import org.apache.dubbo.config.spring.context.annotation.DubboClassPathBeanDefinitionScanner; - -import com.alibaba.dubbo.config.annotation.Service; -import org.springframework.beans.BeansException; -import org.springframework.beans.MutablePropertyValues; -import org.springframework.beans.factory.BeanClassLoaderAware; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.config.RuntimeBeanReference; -import org.springframework.beans.factory.config.SingletonBeanRegistry; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -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.support.ManagedList; -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.env.Environment; -import org.springframework.core.io.ResourceLoader; -import org.springframework.core.type.filter.AnnotationTypeFilter; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.CollectionUtils; -import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; - -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static org.apache.dubbo.config.spring.util.ObjectUtils.of; -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.AnnotationUtils.findAnnotation; -import static org.springframework.util.ClassUtils.resolveClassName; - -/** - * {@link Service} Annotation - * {@link BeanDefinitionRegistryPostProcessor Bean Definition Registry Post Processor} - * - * @since 2.5.8 deprecated since 2.7.0 - */ -@Deprecated -public class CompatibleServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, - ResourceLoaderAware, BeanClassLoaderAware { - - private static final String SEPARATOR = ":"; - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final Set packagesToScan; - - private Environment environment; - - private ResourceLoader resourceLoader; - - private ClassLoader classLoader; - - public CompatibleServiceAnnotationBeanPostProcessor(String... packagesToScan) { - this(Arrays.asList(packagesToScan)); - } - - public CompatibleServiceAnnotationBeanPostProcessor(Collection packagesToScan) { - this(new LinkedHashSet(packagesToScan)); - } - - public CompatibleServiceAnnotationBeanPostProcessor(Set packagesToScan) { - this.packagesToScan = packagesToScan; - } - - @Override - public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { - - Set resolvedPackagesToScan = resolvePackagesToScan(packagesToScan); - - if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) { - registerServiceBeans(resolvedPackagesToScan, registry); - } else { - if (logger.isWarnEnabled()) { - logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!"); - } - } - - } - - - /** - * Registers Beans whose classes was annotated {@link Service} - * - * @param packagesToScan The base packages to scan - * @param registry {@link BeanDefinitionRegistry} - */ - private void registerServiceBeans(Set packagesToScan, BeanDefinitionRegistry registry) { - - DubboClassPathBeanDefinitionScanner scanner = - new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader); - - BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry); - - scanner.setBeanNameGenerator(beanNameGenerator); - - scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class)); - - for (String packageToScan : packagesToScan) { - - // Registers @Service Bean first - scanner.scan(packageToScan); - - // Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not. - Set beanDefinitionHolders = - findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator); - - if (!CollectionUtils.isEmpty(beanDefinitionHolders)) { - - for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) { - registerServiceBean(beanDefinitionHolder, registry, scanner); - } - - 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 + "]"); - } - - } - - } - - } - - /** - * It'd better to use BeanNameGenerator instance that should reference - * {@link ConfigurationClassPostProcessor#componentScanBeanNameGenerator}, - * thus it maybe a potential problem on bean name generation. - * - * @param registry {@link BeanDefinitionRegistry} - * @return {@link BeanNameGenerator} instance - * @see SingletonBeanRegistry - * @see AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR - * @see ConfigurationClassPostProcessor#processConfigBeanDefinitions - * @since 2.5.8 - */ - private BeanNameGenerator resolveBeanNameGenerator(BeanDefinitionRegistry registry) { - - BeanNameGenerator beanNameGenerator = null; - - if (registry instanceof SingletonBeanRegistry) { - SingletonBeanRegistry singletonBeanRegistry = SingletonBeanRegistry.class.cast(registry); - beanNameGenerator = (BeanNameGenerator) singletonBeanRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR); - } - - if (beanNameGenerator == null) { - - if (logger.isInfoEnabled()) { - - logger.info("BeanNameGenerator bean can't be found in BeanFactory with name [" - + CONFIGURATION_BEAN_NAME_GENERATOR + "]"); - logger.info("BeanNameGenerator will be a instance of " + - AnnotationBeanNameGenerator.class.getName() + - " , it maybe a potential problem on bean name generation."); - } - - beanNameGenerator = new AnnotationBeanNameGenerator(); - - } - - return beanNameGenerator; - - } - - /** - * Finds a {@link Set} of {@link BeanDefinitionHolder BeanDefinitionHolders} whose bean type annotated - * {@link Service} Annotation. - * - * @param scanner {@link ClassPathBeanDefinitionScanner} - * @param packageToScan package to scan - * @param registry {@link BeanDefinitionRegistry} - * @return non-null - * @since 2.5.8 - */ - private Set findServiceBeanDefinitionHolders( - ClassPathBeanDefinitionScanner scanner, String packageToScan, BeanDefinitionRegistry registry, - BeanNameGenerator beanNameGenerator) { - - Set beanDefinitions = scanner.findCandidateComponents(packageToScan); - - Set beanDefinitionHolders = new LinkedHashSet(beanDefinitions.size()); - - for (BeanDefinition beanDefinition : beanDefinitions) { - - String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry); - BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, beanName); - beanDefinitionHolders.add(beanDefinitionHolder); - - } - - return beanDefinitionHolders; - - } - - /** - * Registers {@link ServiceBean} from new annotated {@link Service} {@link BeanDefinition} - * - * @param beanDefinitionHolder - * @param registry - * @param scanner - * @see ServiceBean - * @see BeanDefinition - */ - private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry, - DubboClassPathBeanDefinitionScanner scanner) { - - Class beanClass = resolveClass(beanDefinitionHolder); - - Service service = findAnnotation(beanClass, Service.class); - - Class interfaceClass = resolveServiceInterfaceClass(beanClass, service); - - String annotatedServiceBeanName = beanDefinitionHolder.getBeanName(); - - AbstractBeanDefinition serviceBeanDefinition = - buildServiceBeanDefinition(service, interfaceClass, annotatedServiceBeanName); - - // ServiceBean Bean name - String beanName = generateServiceBeanName(service, interfaceClass, annotatedServiceBeanName); - - if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean - registry.registerBeanDefinition(beanName, serviceBeanDefinition); - - if (logger.isInfoEnabled()) { - logger.warn("The BeanDefinition[" + serviceBeanDefinition + - "] of ServiceBean has been registered with name : " + beanName); - } - - } else { - - if (logger.isWarnEnabled()) { - logger.warn("The Duplicated BeanDefinition[" + serviceBeanDefinition + - "] of ServiceBean[ bean name : " + beanName + - "] was be found , Did @CompatibleDubboComponentScan scan to same package in many times?"); - } - - } - - } - - /** - * Generates the bean name of {@link ServiceBean} - * - * @param service - * @param interfaceClass the class of interface annotated {@link Service} - * @param annotatedServiceBeanName the bean name of annotated {@link Service} - * @return ServiceBean@interfaceClassName#annotatedServiceBeanName - * @since 2.5.9 - */ - private String generateServiceBeanName(Service service, Class interfaceClass, String annotatedServiceBeanName) { - - StringBuilder beanNameBuilder = new StringBuilder(ServiceBean.class.getSimpleName()); - - beanNameBuilder.append(SEPARATOR).append(annotatedServiceBeanName); - - String interfaceClassName = interfaceClass.getName(); - - beanNameBuilder.append(SEPARATOR).append(interfaceClassName); - - String version = service.version(); - - if (StringUtils.hasText(version)) { - beanNameBuilder.append(SEPARATOR).append(version); - } - - String group = service.group(); - - if (StringUtils.hasText(group)) { - beanNameBuilder.append(SEPARATOR).append(group); - } - - return beanNameBuilder.toString(); - - } - - private Class resolveServiceInterfaceClass(Class annotatedServiceBeanClass, Service service) { - - Class interfaceClass = service.interfaceClass(); - - if (void.class.equals(interfaceClass)) { - - interfaceClass = null; - - String interfaceClassName = service.interfaceName(); - - if (StringUtils.hasText(interfaceClassName)) { - if (ClassUtils.isPresent(interfaceClassName, classLoader)) { - interfaceClass = resolveClassName(interfaceClassName, classLoader); - } - } - - } - - if (interfaceClass == null) { - - Class[] allInterfaces = annotatedServiceBeanClass.getInterfaces(); - - if (allInterfaces.length > 0) { - interfaceClass = allInterfaces[0]; - } - - } - - Assert.notNull(interfaceClass, - "@Service interfaceClass() or interfaceName() or interface class must be present!"); - - Assert.isTrue(interfaceClass.isInterface(), - "The type that was annotated @Service is not an interface!"); - - return interfaceClass; - } - - private Class resolveClass(BeanDefinitionHolder beanDefinitionHolder) { - - BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition(); - - return resolveClass(beanDefinition); - - } - - private Class resolveClass(BeanDefinition beanDefinition) { - - String beanClassName = beanDefinition.getBeanClassName(); - - return resolveClassName(beanClassName, classLoader); - - } - - private Set resolvePackagesToScan(Set packagesToScan) { - Set resolvedPackagesToScan = new LinkedHashSet(packagesToScan.size()); - for (String packageToScan : packagesToScan) { - if (StringUtils.hasText(packageToScan)) { - String resolvedPackageToScan = environment.resolvePlaceholders(packageToScan.trim()); - resolvedPackagesToScan.add(resolvedPackageToScan); - } - } - return resolvedPackagesToScan; - } - - private AbstractBeanDefinition buildServiceBeanDefinition(Service service, Class interfaceClass, - String annotatedServiceBeanName) { - - BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class); - - AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); - - MutablePropertyValues propertyValues = beanDefinition.getPropertyValues(); - - String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol", - "interface", "parameters"); - - propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(service, environment, ignoreAttributeNames)); - - // References "ref" property to annotated-@Service Bean - addPropertyReference(builder, "ref", annotatedServiceBeanName); - // Set interface - builder.addPropertyValue("interface", interfaceClass.getName()); - // Convert parameters into map - builder.addPropertyValue("parameters", convertParameters(service.parameters())); - - /** - * Add {@link org.apache.dubbo.config.ProviderConfig} Bean reference - */ - String providerConfigBeanName = service.provider(); - if (StringUtils.hasText(providerConfigBeanName)) { - addPropertyReference(builder, "provider", providerConfigBeanName); - } - - /** - * Add {@link org.apache.dubbo.config.MonitorConfig} Bean reference - */ - String monitorConfigBeanName = service.monitor(); - if (StringUtils.hasText(monitorConfigBeanName)) { - addPropertyReference(builder, "monitor", monitorConfigBeanName); - } - - /** - * Add {@link org.apache.dubbo.config.ApplicationConfig} Bean reference - */ - String applicationConfigBeanName = service.application(); - if (StringUtils.hasText(applicationConfigBeanName)) { - addPropertyReference(builder, "application", applicationConfigBeanName); - } - - /** - * Add {@link org.apache.dubbo.config.ModuleConfig} Bean reference - */ - String moduleConfigBeanName = service.module(); - if (StringUtils.hasText(moduleConfigBeanName)) { - addPropertyReference(builder, "module", moduleConfigBeanName); - } - - - /** - * Add {@link org.apache.dubbo.config.RegistryConfig} Bean reference - */ - String[] registryConfigBeanNames = service.registry(); - - List registryRuntimeBeanReferences = toRuntimeBeanReferences(registryConfigBeanNames); - - if (!registryRuntimeBeanReferences.isEmpty()) { - builder.addPropertyValue("registries", registryRuntimeBeanReferences); - } - - /** - * Add {@link org.apache.dubbo.config.ProtocolConfig} Bean reference - */ - String[] protocolConfigBeanNames = service.protocol(); - - List protocolRuntimeBeanReferences = toRuntimeBeanReferences(protocolConfigBeanNames); - - if (!protocolRuntimeBeanReferences.isEmpty()) { - builder.addPropertyValue("protocols", protocolRuntimeBeanReferences); - } - - return builder.getBeanDefinition(); - - } - - - private ManagedList toRuntimeBeanReferences(String... beanNames) { - - ManagedList runtimeBeanReferences = new ManagedList(); - - if (!ObjectUtils.isEmpty(beanNames)) { - - for (String beanName : beanNames) { - - String resolvedBeanName = environment.resolvePlaceholders(beanName); - - runtimeBeanReferences.add(new RuntimeBeanReference(resolvedBeanName)); - } - - } - - return runtimeBeanReferences; - - } - - private void addPropertyReference(BeanDefinitionBuilder builder, String propertyName, String beanName) { - String resolvedBeanName = environment.resolvePlaceholders(beanName); - builder.addPropertyReference(propertyName, resolvedBeanName); - } - - private Map convertParameters(String[] parameters) { - if (ArrayUtils.isEmpty(parameters)) { - return null; - } - - if (parameters.length % 2 != 0) { - throw new IllegalArgumentException("parameter attribute must be paired with key followed by value"); - } - - Map map = new HashMap<>(); - for (int i = 0; i < parameters.length; i += 2) { - map.put(parameters[i], parameters[i + 1]); - } - return map; - } - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - - } - - @Override - public void setEnvironment(Environment environment) { - this.environment = environment; - } - - @Override - public void setResourceLoader(ResourceLoader resourceLoader) { - this.resourceLoader = resourceLoader; - } - - @Override - public void setBeanClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader; - } -} diff --git a/dubbo-compatible/src/main/java/org/apache/dubbo/config/spring/context/annotation/CompatibleDubboComponentScan.java b/dubbo-compatible/src/main/java/org/apache/dubbo/config/spring/context/annotation/CompatibleDubboComponentScan.java deleted file mode 100644 index 4b8e229d2f2..00000000000 --- a/dubbo-compatible/src/main/java/org/apache/dubbo/config/spring/context/annotation/CompatibleDubboComponentScan.java +++ /dev/null @@ -1,66 +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.context.annotation; - -import org.springframework.context.annotation.Import; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @since 2.5.7 deprecated since 2.7.0 - */ -@Deprecated -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Import(CompatibleDubboComponentScanRegistrar.class) -public @interface CompatibleDubboComponentScan { - - /** - * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation - * declarations e.g.: {@code @CompatibleDubboComponentScan("org.my.pkg")} instead of - * {@code @CompatibleDubboComponentScan(basePackages="org.my.pkg")}. - * - * @return the base packages to scan - */ - String[] value() default {}; - - /** - * Base packages to scan for annotated @Service classes. {@link #value()} is an - * alias for (and mutually exclusive with) this attribute. - *

- * Use {@link #basePackageClasses()} for a type-safe alternative to String-based - * package names. - * - * @return the base packages to scan - */ - String[] basePackages() default {}; - - /** - * Type-safe alternative to {@link #basePackages()} for specifying the packages to - * scan for annotated @Service classes. The package of each class specified will be - * scanned. - * - * @return classes from the base packages to scan - */ - Class[] basePackageClasses() default {}; - -} diff --git a/dubbo-compatible/src/main/java/org/apache/dubbo/config/spring/context/annotation/CompatibleDubboComponentScanRegistrar.java b/dubbo-compatible/src/main/java/org/apache/dubbo/config/spring/context/annotation/CompatibleDubboComponentScanRegistrar.java deleted file mode 100644 index 089bdda8cdf..00000000000 --- a/dubbo-compatible/src/main/java/org/apache/dubbo/config/spring/context/annotation/CompatibleDubboComponentScanRegistrar.java +++ /dev/null @@ -1,110 +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.context.annotation; - -import org.apache.dubbo.config.spring.beans.factory.annotation.CompatibleReferenceAnnotationBeanPostProcessor; -import org.apache.dubbo.config.spring.beans.factory.annotation.CompatibleServiceAnnotationBeanPostProcessor; -import org.apache.dubbo.config.spring.util.BeanRegistrar; - -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; -import org.springframework.core.annotation.AnnotationAttributes; -import org.springframework.core.type.AnnotationMetadata; -import org.springframework.util.ClassUtils; - -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; - -import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition; - -/** - * Dubbo Bean Registrar - * @see ImportBeanDefinitionRegistrar - * @see CompatibleServiceAnnotationBeanPostProcessor - * @see CompatibleReferenceAnnotationBeanPostProcessor - * @since 2.5.7 deprecated since 2.7.0 - */ -@Deprecated -public class CompatibleDubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar { - - @Override - public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { - - Set packagesToScan = getPackagesToScan(importingClassMetadata); - - registerServiceAnnotationBeanPostProcessor(packagesToScan, registry); - - registerReferenceAnnotationBeanPostProcessor(registry); - - } - - /** - * Registers {@link CompatibleServiceAnnotationBeanPostProcessor} - * - * @param packagesToScan packages to scan without resolving placeholders - * @param registry {@link BeanDefinitionRegistry} - * @since 2.5.8 deprecated since 2.7.0 - */ - private void registerServiceAnnotationBeanPostProcessor(Set packagesToScan, BeanDefinitionRegistry registry) { - - BeanDefinitionBuilder builder = rootBeanDefinition(CompatibleServiceAnnotationBeanPostProcessor.class); - builder.addConstructorArgValue(packagesToScan); - builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); - BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry); - - } - - /** - * Registers {@link CompatibleReferenceAnnotationBeanPostProcessor} into {@link BeanFactory} - * - * @param registry {@link BeanDefinitionRegistry} - */ - private void registerReferenceAnnotationBeanPostProcessor(BeanDefinitionRegistry registry) { - - // Register @Reference Annotation Bean Processor - BeanRegistrar.registerInfrastructureBean(registry, - CompatibleReferenceAnnotationBeanPostProcessor.BEAN_NAME, CompatibleReferenceAnnotationBeanPostProcessor.class); - - } - - private Set getPackagesToScan(AnnotationMetadata metadata) { - AnnotationAttributes attributes = AnnotationAttributes.fromMap( - metadata.getAnnotationAttributes(CompatibleDubboComponentScan.class.getName())); - String[] basePackages = attributes.getStringArray("basePackages"); - Class[] basePackageClasses = attributes.getClassArray("basePackageClasses"); - String[] value = attributes.getStringArray("value"); - // Appends value array attributes - Set packagesToScan = new LinkedHashSet(Arrays.asList(value)); - packagesToScan.addAll(Arrays.asList(basePackages)); - for (Class basePackageClass : basePackageClasses) { - packagesToScan.add(ClassUtils.getPackageName(basePackageClass)); - } - if (packagesToScan.isEmpty()) { - return Collections.singleton(ClassUtils.getPackageName(metadata.getClassName())); - } - return packagesToScan; - } - -} diff --git a/dubbo-compatible/src/main/java/org/apache/dubbo/config/spring/schema/CompatibleAnnotationBeanDefinitionParser.java b/dubbo-compatible/src/main/java/org/apache/dubbo/config/spring/schema/CompatibleAnnotationBeanDefinitionParser.java deleted file mode 100644 index 1172591190a..00000000000 --- a/dubbo-compatible/src/main/java/org/apache/dubbo/config/spring/schema/CompatibleAnnotationBeanDefinitionParser.java +++ /dev/null @@ -1,93 +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.schema; - -import org.apache.dubbo.config.spring.beans.factory.annotation.CompatibleReferenceAnnotationBeanPostProcessor; -import org.apache.dubbo.config.spring.beans.factory.annotation.CompatibleServiceAnnotationBeanPostProcessor; -import org.apache.dubbo.config.spring.util.BeanRegistrar; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; -import org.springframework.beans.factory.xml.BeanDefinitionParser; -import org.springframework.beans.factory.xml.ParserContext; -import org.w3c.dom.Element; - -import static org.springframework.util.StringUtils.commaDelimitedListToStringArray; -import static org.springframework.util.StringUtils.trimArrayElements; - -/** - * {@link BeanDefinitionParser} - * - * @see CompatibleServiceAnnotationBeanPostProcessor - * @see CompatibleReferenceAnnotationBeanPostProcessor - * @since 2.5.9 deprecated since 2.7.0 - */ -@Deprecated -public class CompatibleAnnotationBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { - - /** - * parse - * - * <dubbo:annotation package="" /> - * - * - * @param element - * @param parserContext - * @param builder - */ - @Override - protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { - - String packageToScan = element.getAttribute("package"); - - String[] packagesToScan = trimArrayElements(commaDelimitedListToStringArray(packageToScan)); - - builder.addConstructorArgValue(packagesToScan); - - builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - - // Registers CompatibleReferenceAnnotationBeanPostProcessor - registerReferenceAnnotationBeanPostProcessor(parserContext.getRegistry()); - - } - - @Override - protected boolean shouldGenerateIdAsFallback() { - return true; - } - - /** - * Registers {@link CompatibleReferenceAnnotationBeanPostProcessor} into {@link BeanFactory} - * - * @param registry {@link BeanDefinitionRegistry} - */ - private void registerReferenceAnnotationBeanPostProcessor(BeanDefinitionRegistry registry) { - - // Register @Reference Annotation Bean Processor - BeanRegistrar.registerInfrastructureBean(registry, - CompatibleReferenceAnnotationBeanPostProcessor.BEAN_NAME, CompatibleReferenceAnnotationBeanPostProcessor.class); - - } - - @Override - protected Class getBeanClass(Element element) { - return CompatibleServiceAnnotationBeanPostProcessor.class; - } - -} diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationConfigBeanBuilder.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationConfigBeanBuilder.java index 7f7afd98d66..25da5d9ee71 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationConfigBeanBuilder.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationConfigBeanBuilder.java @@ -35,8 +35,11 @@ /** * Abstract Configurable {@link Annotation} Bean Builder + * * @since 2.5.7 + * @deprecated use {@link AnnotatedInterfaceConfigBeanBuilder} */ +@Deprecated abstract class AbstractAnnotationConfigBeanBuilder { protected final Log logger = LogFactory.getLog(getClass()); diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotatedInterfaceConfigBeanBuilder.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotatedInterfaceConfigBeanBuilder.java new file mode 100644 index 00000000000..32a37ba9346 --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotatedInterfaceConfigBeanBuilder.java @@ -0,0 +1,215 @@ +/* + * 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 org.apache.dubbo.config.AbstractInterfaceConfig; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.ModuleConfig; +import org.apache.dubbo.config.MonitorConfig; +import org.apache.dubbo.config.RegistryConfig; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.util.Assert; + +import java.lang.annotation.Annotation; +import java.util.List; + +import static org.apache.dubbo.config.spring.util.BeanFactoryUtils.getBeans; +import static org.apache.dubbo.config.spring.util.BeanFactoryUtils.getOptionalBean; + +/** + * An Abstract Builder to build {@link AbstractInterfaceConfig Interface Config} Bean that annotated + * some {@link Annotation annotation}. + * + * @see ReferenceBeanBuilder + * @see AbstractInterfaceConfig + * @see AnnotationAttributes + * @since 2.7.3 + */ +public abstract class AnnotatedInterfaceConfigBeanBuilder { + + protected final Log logger = LogFactory.getLog(getClass()); + + protected final AnnotationAttributes attributes; + + protected final ApplicationContext applicationContext; + + protected final ClassLoader classLoader; + + protected Object configBean; + + protected Class interfaceClass; + + protected AnnotatedInterfaceConfigBeanBuilder(AnnotationAttributes attributes, ApplicationContext applicationContext) { + Assert.notNull(attributes, "The Annotation attributes must not be null!"); + Assert.notNull(applicationContext, "The ApplicationContext must not be null!"); + this.attributes = attributes; + this.applicationContext = applicationContext; + this.classLoader = applicationContext.getClassLoader() != null ? + applicationContext.getClassLoader() : Thread.currentThread().getContextClassLoader(); + } + + /** + * Build {@link C} + * + * @return non-null + * @throws Exception + */ + public final C build() throws Exception { + + checkDependencies(); + + C configBean = doBuild(); + + configureBean(configBean); + + if (logger.isInfoEnabled()) { + logger.info("The configBean[type:" + configBean.getClass().getSimpleName() + "] has been built."); + } + + return configBean; + + } + + private void checkDependencies() { + + } + + /** + * Builds {@link C Bean} + * + * @return {@link C Bean} + */ + protected abstract C doBuild(); + + + protected void configureBean(C configBean) throws Exception { + + preConfigureBean(attributes, configBean); + + configureRegistryConfigs(configBean); + + configureMonitorConfig(configBean); + + configureApplicationConfig(configBean); + + configureModuleConfig(configBean); + + postConfigureBean(attributes, configBean); + + } + + protected abstract void preConfigureBean(AnnotationAttributes attributes, C configBean) throws Exception; + + + private void configureRegistryConfigs(C configBean) { + + String[] registryConfigBeanIds = resolveRegistryConfigBeanNames(attributes); + + List registryConfigs = getBeans(applicationContext, registryConfigBeanIds, RegistryConfig.class); + + configBean.setRegistries(registryConfigs); + + } + + private void configureMonitorConfig(C configBean) { + + String monitorBeanName = resolveMonitorConfigBeanName(attributes); + + MonitorConfig monitorConfig = getOptionalBean(applicationContext, monitorBeanName, MonitorConfig.class); + + configBean.setMonitor(monitorConfig); + + } + + private void configureApplicationConfig(C configBean) { + + String applicationConfigBeanName = resolveApplicationConfigBeanName(attributes); + + ApplicationConfig applicationConfig = + getOptionalBean(applicationContext, applicationConfigBeanName, ApplicationConfig.class); + + configBean.setApplication(applicationConfig); + + } + + private void configureModuleConfig(C configBean) { + + String moduleConfigBeanName = resolveModuleConfigBeanName(attributes); + + ModuleConfig moduleConfig = + getOptionalBean(applicationContext, moduleConfigBeanName, ModuleConfig.class); + + configBean.setModule(moduleConfig); + + } + + /** + * Resolves the configBean name of {@link ModuleConfig} + * + * @param attributes {@link AnnotationAttributes} + * @return + */ + protected abstract String resolveModuleConfigBeanName(AnnotationAttributes attributes); + + /** + * Resolves the configBean name of {@link ApplicationConfig} + * + * @param attributes {@link AnnotationAttributes} + * @return + */ + protected abstract String resolveApplicationConfigBeanName(AnnotationAttributes attributes); + + + /** + * Resolves the configBean ids of {@link RegistryConfig} + * + * @param attributes {@link AnnotationAttributes} + * @return non-empty array + */ + protected abstract String[] resolveRegistryConfigBeanNames(AnnotationAttributes attributes); + + /** + * Resolves the configBean name of {@link MonitorConfig} + * + * @param attributes {@link AnnotationAttributes} + * @return + */ + protected abstract String resolveMonitorConfigBeanName(AnnotationAttributes attributes); + + /** + * Configures Bean + * + * @param attributes + * @param configBean + */ + protected abstract void postConfigureBean(AnnotationAttributes attributes, C configBean) throws Exception; + + + public > T configBean(Object configBean) { + this.configBean = configBean; + return (T) this; + } + + public > T interfaceClass(Class interfaceClass) { + this.interfaceClass = interfaceClass; + return (T) this; + } +} diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationInjectedBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationInjectedBeanPostProcessor.java index 53ff695da07..de56d292fcd 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationInjectedBeanPostProcessor.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationInjectedBeanPostProcessor.java @@ -35,6 +35,7 @@ import org.springframework.context.EnvironmentAware; 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; import org.springframework.util.ClassUtils; @@ -56,20 +57,20 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import static org.apache.dubbo.config.spring.util.ClassUtils.resolveGenericType; +import static org.apache.dubbo.config.spring.util.AnnotationUtils.getMergedAttributes; import static org.springframework.core.BridgeMethodResolver.findBridgedMethod; import static org.springframework.core.BridgeMethodResolver.isVisibilityBridgeMethodPair; -import static org.springframework.core.annotation.AnnotationUtils.findAnnotation; -import static org.springframework.core.annotation.AnnotationUtils.getAnnotation; +import static org.springframework.util.CollectionUtils.isEmpty; /** * Abstract generic {@link BeanPostProcessor} implementation for customized annotation that annotated injected-object. - * + *

* The source code is cloned from https://github.com/alibaba/spring-context-support/blob/1.0.2/src/main/java/com/alibaba/spring/beans/factory/annotation/AnnotationInjectedBeanPostProcessor.java * + * @revision 2.7.3 Uses {@link AnnotationAttributes} instead of {@link Annotation} * @since 2.6.6 */ -public abstract class AnnotationInjectedBeanPostProcessor extends +public abstract class AnnotationInjectedBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware, BeanClassLoaderAware, EnvironmentAware, DisposableBean { @@ -77,7 +78,7 @@ public abstract class AnnotationInjectedBeanPostProcessor private final Log logger = LogFactory.getLog(getClass()); - private final Class annotationType; + private final Class[] annotationTypes; private final ConcurrentMap injectionMetadataCache = new ConcurrentHashMap(CACHE_SIZE); @@ -92,8 +93,13 @@ public abstract class AnnotationInjectedBeanPostProcessor private int order = Ordered.LOWEST_PRECEDENCE; - public AnnotationInjectedBeanPostProcessor() { - this.annotationType = resolveGenericType(getClass()); + /** + * @param annotationTypes the multiple types of {@link Annotation annotations} + * @since 2.7.3 + */ + public AnnotationInjectedBeanPostProcessor(Class... annotationTypes) { + Assert.notEmpty(annotationTypes, "The argument of annotations' types must not empty"); + this.annotationTypes = annotationTypes; } @SafeVarargs @@ -109,9 +115,15 @@ private static Collection combine(Collection... elements) { * Annotation type * * @return non-null + * @deprecated 2.7.3, uses {@link #getAnnotationTypes()} */ - public final Class getAnnotationType() { - return annotationType; + @Deprecated + public final Class getAnnotationType() { + return annotationTypes[0]; + } + + protected final Class[] getAnnotationTypes() { + return annotationTypes; } @Override @@ -131,7 +143,7 @@ public PropertyValues postProcessPropertyValues( } catch (BeanCreationException ex) { throw ex; } catch (Throwable ex) { - throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getName() + throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getSimpleName() + " dependencies is failed", ex); } return pvs; @@ -139,7 +151,7 @@ public PropertyValues postProcessPropertyValues( /** - * Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated {@link A} fields + * Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated fields * * @param beanClass The {@link Class} of Bean * @return non-null {@link List} @@ -150,20 +162,22 @@ private List findFiel ReflectionUtils.doWithFields(beanClass, field -> { - A annotation = getAnnotation(field, getAnnotationType()); + for (Class annotationType : getAnnotationTypes()) { + + AnnotationAttributes attributes = getMergedAttributes(field, annotationType, getEnvironment(), true); - if (annotation != null) { + if (!isEmpty(attributes)) { - if (Modifier.isStatic(field.getModifiers())) { - if (logger.isWarnEnabled()) { - logger.warn("@" + getAnnotationType().getName() + " is not supported on static fields: " + field); + if (Modifier.isStatic(field.getModifiers())) { + if (logger.isWarnEnabled()) { + logger.warn("@" + annotationType.getName() + " is not supported on static fields: " + field); + } + return; } - return; - } - elements.add(new AnnotatedFieldElement(field, annotation)); + elements.add(new AnnotatedFieldElement(field, attributes)); + } } - }); return elements; @@ -171,7 +185,7 @@ private List findFiel } /** - * Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated {@link A @A} methods + * Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated methods * * @param beanClass The {@link Class} of Bean * @return non-null {@link List} @@ -188,23 +202,27 @@ private List findAnn return; } - A annotation = findAnnotation(bridgedMethod, getAnnotationType()); - if (annotation != null && method.equals(ClassUtils.getMostSpecificMethod(method, beanClass))) { - if (Modifier.isStatic(method.getModifiers())) { - if (logger.isWarnEnabled()) { - logger.warn("@" + getAnnotationType().getSimpleName() + " annotation is not supported on static methods: " + method); + for (Class annotationType : getAnnotationTypes()) { + + AnnotationAttributes attributes = getMergedAttributes(bridgedMethod, annotationType, getEnvironment(), true); + + if (!isEmpty(attributes) && 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; } - return; - } - if (method.getParameterTypes().length == 0) { - if (logger.isWarnEnabled()) { - logger.warn("@" + getAnnotationType().getSimpleName() + " annotation should only be used on methods with parameters: " + - method); + if (method.getParameterTypes().length == 0) { + if (logger.isWarnEnabled()) { + logger.warn("@" + annotationType.getName() + " annotation should only be used on methods with parameters: " + + method); + } } + PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, beanClass); + elements.add(new AnnotatedMethodElement(method, pd, attributes)); } - PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, beanClass); - elements.add(new AnnotatedMethodElement(method, pd, annotation)); } }); @@ -316,9 +334,9 @@ protected Collection getInjectedObjects() { } /** - * Get injected-object from specified {@link A annotation} and Bean Class + * Get injected-object from specified {@link AnnotationAttributes annotation attributes} and Bean Class * - * @param annotation {@link A annotation} + * @param attributes {@link AnnotationAttributes the annotation attributes} * @param bean Current bean that will be injected * @param beanName Current bean name that will be injected * @param injectedType the type of injected-object @@ -326,15 +344,15 @@ protected Collection getInjectedObjects() { * @return An injected object * @throws Exception If getting is failed */ - protected Object getInjectedObject(A annotation, Object bean, String beanName, Class injectedType, + protected Object getInjectedObject(AnnotationAttributes attributes, Object bean, String beanName, Class injectedType, InjectionMetadata.InjectedElement injectedElement) throws Exception { - String cacheKey = buildInjectedObjectCacheKey(annotation, bean, beanName, injectedType, injectedElement); + String cacheKey = buildInjectedObjectCacheKey(attributes, bean, beanName, injectedType, injectedElement); Object injectedObject = injectedObjectsCache.get(cacheKey); if (injectedObject == null) { - injectedObject = doGetInjectedBean(annotation, bean, beanName, injectedType, injectedElement); + injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement); // Customized inject-object if necessary injectedObjectsCache.putIfAbsent(cacheKey, injectedObject); } @@ -352,7 +370,7 @@ protected Object getInjectedObject(A annotation, Object bean, String beanName, C *
  • {@link #getEnvironment() Environment}
  • * * - * @param annotation {@link A annotation} + * @param attributes {@link AnnotationAttributes the annotation attributes} * @param bean Current bean that will be injected * @param beanName Current bean name that will be injected * @param injectedType the type of injected-object @@ -360,7 +378,7 @@ protected Object getInjectedObject(A annotation, Object bean, String beanName, C * @return The injected object * @throws Exception If resolving an injected object is failed. */ - protected abstract Object doGetInjectedBean(A annotation, Object bean, String beanName, Class injectedType, + protected abstract Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class injectedType, InjectionMetadata.InjectedElement injectedElement) throws Exception; /** @@ -372,14 +390,14 @@ protected abstract Object doGetInjectedBean(A annotation, Object bean, String be *
  • {@link #getEnvironment() Environment}
  • * * - * @param annotation {@link A annotation} + * @param attributes {@link AnnotationAttributes the annotation attributes} * @param bean Current bean that will be injected * @param beanName Current bean name that will be injected * @param injectedType the type of injected-object * @param injectedElement {@link InjectionMetadata.InjectedElement} * @return Bean cache key */ - protected abstract String buildInjectedObjectCacheKey(A annotation, Object bean, String beanName, + protected abstract String buildInjectedObjectCacheKey(AnnotationAttributes attributes, Object bean, String beanName, Class injectedType, InjectionMetadata.InjectedElement injectedElement); @@ -436,7 +454,7 @@ protected Map getInjectedMethodObject } /** - * {@link A} {@link InjectionMetadata} implementation + * {@link Annotation Annotated} {@link InjectionMetadata} implementation */ private class AnnotatedInjectionMetadata extends InjectionMetadata { @@ -461,20 +479,20 @@ public Collection ge } /** - * {@link A} {@link Method} {@link InjectionMetadata.InjectedElement} + * {@link Annotation Annotated} {@link Method} {@link InjectionMetadata.InjectedElement} */ private class AnnotatedMethodElement extends InjectionMetadata.InjectedElement { private final Method method; - private final A annotation; + private final AnnotationAttributes attributes; private volatile Object object; - protected AnnotatedMethodElement(Method method, PropertyDescriptor pd, A annotation) { + protected AnnotatedMethodElement(Method method, PropertyDescriptor pd, AnnotationAttributes attributes) { super(method, pd); this.method = method; - this.annotation = annotation; + this.attributes = attributes; } @Override @@ -482,7 +500,7 @@ protected void inject(Object bean, String beanName, PropertyValues pvs) throws T Class injectedType = pd.getPropertyType(); - Object injectedObject = getInjectedObject(annotation, bean, beanName, injectedType, this); + Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this); ReflectionUtils.makeAccessible(method); @@ -493,20 +511,20 @@ protected void inject(Object bean, String beanName, PropertyValues pvs) throws T } /** - * {@link A} {@link Field} {@link InjectionMetadata.InjectedElement} + * {@link Annotation Annotated} {@link Field} {@link InjectionMetadata.InjectedElement} */ public class AnnotatedFieldElement extends InjectionMetadata.InjectedElement { private final Field field; - private final A annotation; + private final AnnotationAttributes attributes; private volatile Object bean; - protected AnnotatedFieldElement(Field field, A annotation) { + protected AnnotatedFieldElement(Field field, AnnotationAttributes attributes) { super(field, null); this.field = field; - this.annotation = annotation; + this.attributes = attributes; } @Override @@ -514,7 +532,7 @@ protected void inject(Object bean, String beanName, PropertyValues pvs) throws T Class injectedType = field.getType(); - Object injectedObject = getInjectedObject(annotation, bean, beanName, injectedType, this); + Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this); ReflectionUtils.makeAccessible(field); diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapter.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapter.java index b051d35ab0b..5566c392018 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapter.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapter.java @@ -22,8 +22,10 @@ import org.springframework.core.env.PropertyResolver; import java.lang.annotation.Annotation; +import java.util.Map; import static org.apache.dubbo.config.spring.util.AnnotationUtils.getAttributes; +import static org.apache.dubbo.config.spring.util.AnnotationUtils.resolvePlaceholders; /** * {@link Annotation} {@link PropertyValues} Adapter @@ -34,35 +36,26 @@ */ class AnnotationPropertyValuesAdapter implements PropertyValues { - private final Annotation annotation; - - private final PropertyResolver propertyResolver; - - private final boolean ignoreDefaultValue; - private final PropertyValues delegate; - public AnnotationPropertyValuesAdapter(Annotation annotation, PropertyResolver propertyResolver, boolean ignoreDefaultValue, String... ignoreAttributeNames) { - this.annotation = annotation; - this.propertyResolver = propertyResolver; - this.ignoreDefaultValue = ignoreDefaultValue; - this.delegate = adapt(annotation, ignoreDefaultValue, ignoreAttributeNames); + /** + * @param attributes + * @param propertyResolver + * @param ignoreAttributeNames + * @since 2.7.3 + */ + public AnnotationPropertyValuesAdapter(Map attributes, PropertyResolver propertyResolver, + String... ignoreAttributeNames) { + this.delegate = new MutablePropertyValues(resolvePlaceholders(attributes, propertyResolver, ignoreAttributeNames)); } - public AnnotationPropertyValuesAdapter(Annotation annotation, PropertyResolver propertyResolver, String... ignoreAttributeNames) { - this(annotation, propertyResolver, true, ignoreAttributeNames); + public AnnotationPropertyValuesAdapter(Annotation annotation, PropertyResolver propertyResolver, + boolean ignoreDefaultValue, String... ignoreAttributeNames) { + this.delegate = new MutablePropertyValues(getAttributes(annotation, propertyResolver, ignoreDefaultValue, ignoreAttributeNames)); } - private PropertyValues adapt(Annotation annotation, boolean ignoreDefaultValue, String... ignoreAttributeNames) { - return new MutablePropertyValues(getAttributes(annotation, propertyResolver, ignoreDefaultValue, ignoreAttributeNames)); - } - - public Annotation getAnnotation() { - return annotation; - } - - public boolean isIgnoreDefaultValue() { - return ignoreDefaultValue; + public AnnotationPropertyValuesAdapter(Annotation annotation, PropertyResolver propertyResolver, String... ignoreAttributeNames) { + this(annotation, propertyResolver, true, ignoreAttributeNames); } @Override 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 f7cc7b797e6..d477c582eba 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 @@ -29,6 +29,7 @@ import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.core.annotation.AnnotationAttributes; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; @@ -49,8 +50,8 @@ * * @since 2.5.7 */ -public class ReferenceAnnotationBeanPostProcessor extends AnnotationInjectedBeanPostProcessor - implements ApplicationContextAware, ApplicationListener { +public class ReferenceAnnotationBeanPostProcessor extends AnnotationInjectedBeanPostProcessor implements + ApplicationContextAware, ApplicationListener { /** * The bean name of {@link ReferenceAnnotationBeanPostProcessor} @@ -76,6 +77,13 @@ public class ReferenceAnnotationBeanPostProcessor extends AnnotationInjectedBean private ApplicationContext applicationContext; + /** + * To support the legacy annotation that is @com.alibaba.dubbo.config.annotation.Reference since 2.7.3 + */ + public ReferenceAnnotationBeanPostProcessor() { + super(Reference.class, com.alibaba.dubbo.config.annotation.Reference.class); + } + /** * Gets all beans of {@link ReferenceBean} * @@ -107,12 +115,12 @@ public Map> getInjectedMetho } @Override - protected Object doGetInjectedBean(Reference reference, Object bean, String beanName, Class injectedType, + protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class injectedType, InjectionMetadata.InjectedElement injectedElement) throws Exception { - String referencedBeanName = buildReferencedBeanName(reference, injectedType); + String referencedBeanName = buildReferencedBeanName(attributes, injectedType); - ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referencedBeanName, reference, injectedType, getClassLoader()); + ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referencedBeanName, attributes, injectedType, getClassLoader()); cacheInjectedReferenceBean(referenceBean, injectedElement); @@ -175,22 +183,22 @@ private void init() { } @Override - protected String buildInjectedObjectCacheKey(Reference reference, Object bean, String beanName, + protected String buildInjectedObjectCacheKey(AnnotationAttributes attributes, Object bean, String beanName, Class injectedType, InjectionMetadata.InjectedElement injectedElement) { - return buildReferencedBeanName(reference, injectedType) + + return buildReferencedBeanName(attributes, injectedType) + "#source=" + (injectedElement.getMember()) + - "#attributes=" + AnnotationUtils.getAttributes(reference, getEnvironment(), true); + "#attributes=" + AnnotationUtils.resolvePlaceholders(attributes, getEnvironment()); } - private String buildReferencedBeanName(Reference reference, Class injectedType) { + private String buildReferencedBeanName(AnnotationAttributes attributes, Class injectedType) { - ServiceBeanNameBuilder serviceBeanNameBuilder = create(reference, injectedType, getEnvironment()); + ServiceBeanNameBuilder serviceBeanNameBuilder = create(attributes, injectedType, getEnvironment()); return serviceBeanNameBuilder.build(); } - private ReferenceBean buildReferenceBeanIfAbsent(String referencedBeanName, Reference reference, + private ReferenceBean buildReferenceBeanIfAbsent(String referencedBeanName, AnnotationAttributes attributes, Class referencedType, ClassLoader classLoader) throws Exception { @@ -198,7 +206,7 @@ private ReferenceBean buildReferenceBeanIfAbsent(String referencedBeanName, Refe if (referenceBean == null) { ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder - .create(reference, classLoader, applicationContext) + .create(attributes, applicationContext) .interfaceClass(referencedType); referenceBean = beanBuilder.build(); referenceBeanCache.put(referencedBeanName, referenceBean); diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilder.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilder.java index d4aef6a37ec..2054473587d 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilder.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilder.java @@ -25,8 +25,8 @@ import org.springframework.beans.propertyeditors.StringTrimmerEditor; import org.springframework.context.ApplicationContext; +import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; import org.springframework.validation.DataBinder; @@ -34,8 +34,12 @@ import java.util.List; import java.util.Map; +import static org.apache.dubbo.config.spring.util.AnnotationUtils.getAttribute; +import static org.apache.dubbo.config.spring.util.AnnotationUtils.getAttributes; +import static org.apache.dubbo.config.spring.util.AnnotationUtils.resolveServiceInterfaceClass; import static org.apache.dubbo.config.spring.util.BeanFactoryUtils.getOptionalBean; import static org.apache.dubbo.config.spring.util.ObjectUtils.of; +import static org.springframework.core.annotation.AnnotationAttributes.fromMap; import static org.springframework.util.StringUtils.commaDelimitedListToStringArray; /** @@ -43,48 +47,30 @@ * * @since 2.5.7 */ -class ReferenceBeanBuilder extends AbstractAnnotationConfigBeanBuilder { +class ReferenceBeanBuilder extends AnnotatedInterfaceConfigBeanBuilder { // Ignore those fields static final String[] IGNORE_FIELD_NAMES = of("application", "module", "consumer", "monitor", "registry"); - private ReferenceBeanBuilder(Reference annotation, ClassLoader classLoader, ApplicationContext applicationContext) { - super(annotation, classLoader, applicationContext); + private ReferenceBeanBuilder(AnnotationAttributes attributes, ApplicationContext applicationContext) { + super(attributes, applicationContext); } - private void configureInterface(Reference reference, ReferenceBean referenceBean) { + private void configureInterface(AnnotationAttributes attributes, ReferenceBean referenceBean) { - Class interfaceClass = reference.interfaceClass(); + Class serviceInterfaceClass = resolveServiceInterfaceClass(attributes, interfaceClass); - if (void.class.equals(interfaceClass)) { - - interfaceClass = null; - - String interfaceClassName = reference.interfaceName(); - - if (StringUtils.hasText(interfaceClassName)) { - if (ClassUtils.isPresent(interfaceClassName, classLoader)) { - interfaceClass = ClassUtils.resolveClassName(interfaceClassName, classLoader); - } - } - - } - - if (interfaceClass == null) { - interfaceClass = this.interfaceClass; - } - - Assert.isTrue(interfaceClass.isInterface(), + Assert.isTrue(serviceInterfaceClass.isInterface(), "The class of field or method that was annotated @Reference is not an interface!"); - referenceBean.setInterface(interfaceClass); + referenceBean.setInterface(serviceInterfaceClass); } - private void configureConsumerConfig(Reference reference, ReferenceBean referenceBean) { + private void configureConsumerConfig(AnnotationAttributes attributes, ReferenceBean referenceBean) { - String consumerBeanName = reference.consumer(); + String consumerBeanName = getAttribute(attributes, "consumer"); ConsumerConfig consumerConfig = getOptionalBean(applicationContext, consumerBeanName, ConsumerConfig.class); @@ -92,10 +78,10 @@ private void configureConsumerConfig(Reference reference, ReferenceBean refer } - void configureMethodConfig(Reference reference, ReferenceBean referenceBean){ - Method[] methods = reference.methods(); + void configureMethodConfig(AnnotationAttributes attributes, ReferenceBean referenceBean) { + Method[] methods = (Method[]) attributes.get("methods"); List methodConfigs = MethodConfig.constructMethodConfig(methods); - if(!methodConfigs.isEmpty()){ + if (!methodConfigs.isEmpty()) { referenceBean.setMethods(methodConfigs); } } @@ -106,7 +92,7 @@ protected ReferenceBean doBuild() { } @Override - protected void preConfigureBean(Reference reference, ReferenceBean referenceBean) { + protected void preConfigureBean(AnnotationAttributes attributes, ReferenceBean referenceBean) { Assert.notNull(interfaceClass, "The interface class must set first!"); DataBinder dataBinder = new DataBinder(referenceBean); // Register CustomEditors for special fields @@ -131,49 +117,53 @@ public void setAsText(String text) throws java.lang.IllegalArgumentException { }); // Bind annotation attributes - dataBinder.bind(new AnnotationPropertyValuesAdapter(reference, applicationContext.getEnvironment(), IGNORE_FIELD_NAMES)); + dataBinder.bind(new AnnotationPropertyValuesAdapter(attributes, applicationContext.getEnvironment(), IGNORE_FIELD_NAMES)); } @Override - protected String resolveModuleConfigBeanName(Reference annotation) { - return annotation.module(); + protected String resolveModuleConfigBeanName(AnnotationAttributes attributes) { + return getAttribute(attributes, "module"); } @Override - protected String resolveApplicationConfigBeanName(Reference annotation) { - return annotation.application(); + protected String resolveApplicationConfigBeanName(AnnotationAttributes attributes) { + return getAttribute(attributes, "application"); } @Override - protected String[] resolveRegistryConfigBeanNames(Reference annotation) { - return annotation.registry(); + protected String[] resolveRegistryConfigBeanNames(AnnotationAttributes attributes) { + return getAttribute(attributes, "registry"); } @Override - protected String resolveMonitorConfigBeanName(Reference annotation) { - return annotation.monitor(); + protected String resolveMonitorConfigBeanName(AnnotationAttributes attributes) { + return getAttribute(attributes, "monitor"); } @Override - protected void postConfigureBean(Reference annotation, ReferenceBean bean) throws Exception { + protected void postConfigureBean(AnnotationAttributes attributes, ReferenceBean bean) throws Exception { bean.setApplicationContext(applicationContext); - configureInterface(annotation, bean); + configureInterface(attributes, bean); - configureConsumerConfig(annotation, bean); + configureConsumerConfig(attributes, bean); - configureMethodConfig(annotation, bean); + configureMethodConfig(attributes, bean); bean.afterPropertiesSet(); } - public static ReferenceBeanBuilder create(Reference annotation, ClassLoader classLoader, + @Deprecated + public static ReferenceBeanBuilder create(Reference reference, ClassLoader classLoader, ApplicationContext applicationContext) { - return new ReferenceBeanBuilder(annotation, classLoader, applicationContext); + return create(fromMap(getAttributes(reference, applicationContext.getEnvironment(), true)), applicationContext); } + public static ReferenceBeanBuilder create(AnnotationAttributes attributes, ApplicationContext applicationContext) { + return new ReferenceBeanBuilder(attributes, applicationContext); + } } diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessor.java index 0edc5f333ba..06deb57fdd9 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessor.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessor.java @@ -65,7 +65,7 @@ import static org.apache.dubbo.config.spring.util.ObjectUtils.of; 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.AnnotationUtils.findAnnotation; +import static org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation; import static org.springframework.core.annotation.AnnotationUtils.getAnnotationAttributes; import static org.springframework.util.ClassUtils.resolveClassName; @@ -306,9 +306,9 @@ private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, Bean * @since 2.7.3 */ private Annotation findServiceAnnotation(Class beanClass) { - Annotation service = findAnnotation(beanClass, Service.class); + Annotation service = findMergedAnnotation(beanClass, Service.class); if (service == null) { - service = findAnnotation(beanClass, com.alibaba.dubbo.config.annotation.Service.class); + service = findMergedAnnotation(beanClass, com.alibaba.dubbo.config.annotation.Service.class); } return service; } diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilder.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilder.java index 390e0b6cf1f..8aef87f16d8 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilder.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilder.java @@ -25,6 +25,7 @@ import org.springframework.core.env.Environment; import org.springframework.util.StringUtils; +import static org.apache.dubbo.config.spring.util.AnnotationUtils.getAttribute; import static org.apache.dubbo.config.spring.util.AnnotationUtils.resolveInterfaceName; import static org.springframework.core.annotation.AnnotationUtils.getAnnotationAttributes; @@ -62,8 +63,8 @@ private ServiceBeanNameBuilder(String interfaceClassName, Environment environmen private ServiceBeanNameBuilder(AnnotationAttributes attributes, Class defaultInterfaceClass, Environment environment) { this(resolveInterfaceName(attributes, defaultInterfaceClass), environment); - this.group(attributes.getString("group")); - this.version(attributes.getString("version")); + this.group(getAttribute(attributes,"group")); + this.version(getAttribute(attributes,"version")); } /** diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/AnnotatedBeanDefinitionRegistryUtils.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/AnnotatedBeanDefinitionRegistryUtils.java index bf065f8b085..c46ca8adea7 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/AnnotatedBeanDefinitionRegistryUtils.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/AnnotatedBeanDefinitionRegistryUtils.java @@ -18,18 +18,27 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; +import org.springframework.core.type.AnnotationMetadata; import org.springframework.util.ObjectUtils; import java.lang.annotation.Annotation; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Objects; + +import static java.lang.String.format; +import static java.util.Arrays.asList; +import static org.springframework.util.ClassUtils.resolveClassName; /** * Annotated {@link BeanDefinition} Utilities *

    * The source code is cloned from https://github.com/alibaba/spring-context-support/blob/1.0.2/src/main/java/com/alibaba/spring/util/AnnotatedBeanDefinitionRegistryUtils.java + * * @since 2.6.6 */ public abstract class AnnotatedBeanDefinitionRegistryUtils { @@ -37,10 +46,47 @@ public abstract class AnnotatedBeanDefinitionRegistryUtils { private static final Log logger = LogFactory.getLog(AnnotatedBeanDefinitionRegistryUtils.class); /** - * Register Beans + * Is present bean that was registered by the specified {@link Annotation annotated} {@link Class class} + * + * @param registry {@link BeanDefinitionRegistry} + * @param annotatedClass the {@link Annotation annotated} {@link Class class} + * @return if present, return true, or false + * @since 2.7.3 + */ + public static boolean isPresentBean(BeanDefinitionRegistry registry, Class annotatedClass) { + + boolean present = false; + + String[] beanNames = registry.getBeanDefinitionNames(); + + ClassLoader classLoader = annotatedClass.getClassLoader(); + + for (String beanName : beanNames) { + BeanDefinition beanDefinition = registry.getBeanDefinition(beanName); + if (beanDefinition instanceof AnnotatedBeanDefinition) { + AnnotationMetadata annotationMetadata = ((AnnotatedBeanDefinition) beanDefinition).getMetadata(); + String className = annotationMetadata.getClassName(); + Class targetClass = resolveClassName(className, classLoader); + present = Objects.equals(targetClass, annotatedClass); + if (present) { + if (logger.isDebugEnabled()) { + logger.debug(format("The annotatedClass[class : %s , bean name : %s] was present in registry[%s]", + className, beanName, registry)); + } + break; + } + } + } + + return present; + } + + /** + * Register Beans if not present in {@link BeanDefinitionRegistry registry} * * @param registry {@link BeanDefinitionRegistry} * @param annotatedClasses {@link Annotation annotation} class + * @revision 2.7.3 {@link #isPresentBean(BeanDefinitionRegistry, Class)} */ public static void registerBeans(BeanDefinitionRegistry registry, Class... annotatedClasses) { @@ -50,10 +96,20 @@ public static void registerBeans(BeanDefinitionRegistry registry, Class... an boolean debugEnabled = logger.isDebugEnabled(); + // Remove all annotated-classes that have been registered + Iterator> iterator = new ArrayList<>(asList(annotatedClasses)).iterator(); + + while (iterator.hasNext()) { + Class annotatedClass = iterator.next(); + if (isPresentBean(registry, annotatedClass)) { + iterator.remove(); + } + } + AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(registry); if (debugEnabled) { - logger.debug(registry.getClass().getSimpleName() + " will register annotated classes : " + Arrays.asList(annotatedClasses) + " ."); + logger.debug(registry.getClass().getSimpleName() + " will register annotated classes : " + asList(annotatedClasses) + " ."); } reader.register(annotatedClasses); diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/AnnotationUtils.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/AnnotationUtils.java index 600c1b72280..3b308d30e70 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/AnnotationUtils.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/AnnotationUtils.java @@ -30,22 +30,26 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.Collections; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Set; import static java.lang.String.valueOf; +import static java.util.Collections.emptyMap; +import static java.util.Collections.unmodifiableMap; +import static org.springframework.core.annotation.AnnotatedElementUtils.getMergedAnnotation; +import static org.springframework.core.annotation.AnnotationAttributes.fromMap; import static org.springframework.core.annotation.AnnotationUtils.findAnnotation; import static org.springframework.core.annotation.AnnotationUtils.getAnnotationAttributes; import static org.springframework.core.annotation.AnnotationUtils.getDefaultValue; import static org.springframework.util.ClassUtils.getAllInterfacesForClass; import static org.springframework.util.ClassUtils.resolveClassName; -import static org.springframework.util.CollectionUtils.arrayToList; +import static org.springframework.util.CollectionUtils.isEmpty; +import static org.springframework.util.ObjectUtils.containsElement; import static org.springframework.util.ObjectUtils.nullSafeEquals; import static org.springframework.util.StringUtils.hasText; import static org.springframework.util.StringUtils.trimWhitespace; @@ -89,46 +93,43 @@ public static String resolveInterfaceName(Service service, Class defaultInter * @throws IllegalStateException if interface name was not found */ public static String resolveInterfaceName(AnnotationAttributes attributes, Class defaultInterfaceClass) { + return resolveServiceInterfaceClass(attributes, defaultInterfaceClass).getName(); + } - String interfaceName = null; - - Class interfaceClass = attributes.getClass("interfaceClass"); - if (interfaceClass != null && !void.class.equals(interfaceClass)) { - interfaceName = interfaceClass.getName(); - } else if ((!hasText(interfaceName = attributes.getString("interfaceName")))) { - interfaceName = defaultInterfaceClass.isInterface() ? defaultInterfaceClass.getName() : null; - } - - if (!hasText(interfaceName)) { - throw new IllegalStateException( - "The @Service undefined interfaceClass or interfaceName, and the type " - + defaultInterfaceClass.getName() + " is not a interface."); - } - - return interfaceName; + /** + * Get the attribute value + * + * @param attributes {@link AnnotationAttributes the annotation attributes} + * @param name the name of attribute + * @param the type of attribute value + * @return the attribute value if found + * @since 2.7.3 + */ + public static T getAttribute(AnnotationAttributes attributes, String name) { + return (T) attributes.get(name); } /** * Resolve the {@link Class class} of Dubbo Service interface from the specified * {@link AnnotationAttributes annotation attributes} and annotated {@link Class class}. * - * @param attributes {@link AnnotationAttributes annotation attributes} - * @param annotatedClass the annotated {@link Class class}. + * @param attributes {@link AnnotationAttributes annotation attributes} + * @param defaultInterfaceClass the annotated {@link Class class}. * @return the {@link Class class} of Dubbo Service interface * @throws IllegalArgumentException if can't resolved */ - public static Class resolveServiceInterfaceClass(AnnotationAttributes attributes, Class annotatedClass) + public static Class resolveServiceInterfaceClass(AnnotationAttributes attributes, Class defaultInterfaceClass) throws IllegalArgumentException { - ClassLoader classLoader = annotatedClass.getClassLoader(); + ClassLoader classLoader = defaultInterfaceClass != null ? defaultInterfaceClass.getClassLoader() : Thread.currentThread().getContextClassLoader(); - Class interfaceClass = attributes.getClass("interfaceClass"); + Class interfaceClass = getAttribute(attributes, "interfaceClass"); if (void.class.equals(interfaceClass)) { // default or set void.class for purpose. interfaceClass = null; - String interfaceClassName = attributes.getString("interfaceName"); + String interfaceClassName = getAttribute(attributes, "interfaceName"); if (hasText(interfaceClassName)) { if (ClassUtils.isPresent(interfaceClassName, classLoader)) { @@ -138,10 +139,10 @@ public static Class resolveServiceInterfaceClass(AnnotationAttributes attribu } - if (interfaceClass == null) { + if (interfaceClass == null && defaultInterfaceClass != null) { // Find all interfaces from the annotated class // To resolve an issue : https://github.com/apache/dubbo/issues/3251 - Class[] allInterfaces = getAllInterfacesForClass(annotatedClass); + Class[] allInterfaces = getAllInterfacesForClass(defaultInterfaceClass); if (allInterfaces.length > 0) { interfaceClass = allInterfaces[0]; @@ -153,7 +154,7 @@ public static Class resolveServiceInterfaceClass(AnnotationAttributes attribu "@Service interfaceClass() or interfaceName() or interface class must be present!"); Assert.isTrue(interfaceClass.isInterface(), - "The type that was annotated @Service is not an interface!"); + "The annotated type must be an interface!"); return interfaceClass; } @@ -292,7 +293,7 @@ public static Map> findAnnotations(M } - return Collections.unmodifiableMap(annotationsMap); + return unmodifiableMap(annotationsMap); } @@ -304,7 +305,9 @@ public static Map> findAnnotations(M * @param ignoreAttributeNames the attribute names of annotation should be ignored * @return non-null * @since 2.6.6 + * @deprecated */ + @Deprecated public static Map getAttributes(Annotation annotation, boolean ignoreDefaultValue, String... ignoreAttributeNames) { return getAttributes(annotation, null, ignoreDefaultValue, ignoreAttributeNames); @@ -323,11 +326,13 @@ public static Map getAttributes(Annotation annotation, boolean i public static Map getAttributes(Annotation annotation, PropertyResolver propertyResolver, boolean ignoreDefaultValue, String... ignoreAttributeNames) { - Set ignoreAttributeNamesSet = new HashSet(arrayToList(ignoreAttributeNames)); + if (annotation == null) { + return emptyMap(); + } Map attributes = getAnnotationAttributes(annotation); - Map actualAttributes = new LinkedHashMap(); + Map actualAttributes = new LinkedHashMap<>(); for (Map.Entry entry : attributes.entrySet()) { @@ -339,11 +344,6 @@ public static Map getAttributes(Annotation annotation, PropertyR continue; } - // ignore attribute name - if (ignoreAttributeNamesSet.contains(attributeName)) { - continue; - } - /** * @since 2.7.1 * ignore annotation member @@ -354,6 +354,42 @@ public static Map getAttributes(Annotation annotation, PropertyR if (attributeValue.getClass().isArray() && attributeValue.getClass().getComponentType().isAnnotation()) { continue; } + actualAttributes.put(attributeName, attributeValue); + } + + + return resolvePlaceholders(actualAttributes, propertyResolver, ignoreAttributeNames); + } + + /** + * Resolve the placeholders from the specified annotation attributes + * + * @param sourceAnnotationAttributes the source of annotation attributes + * @param propertyResolver {@link PropertyResolver} + * @param ignoreAttributeNames the attribute names to be ignored + * @return a new resolved annotation attributes , non-null and read-only + * @since 2.7.3 + */ + public static Map resolvePlaceholders(Map sourceAnnotationAttributes, + PropertyResolver propertyResolver, + String... ignoreAttributeNames) { + + if (isEmpty(sourceAnnotationAttributes)) { + return emptyMap(); + } + + Map resolvedAnnotationAttributes = new LinkedHashMap<>(); + + for (Map.Entry entry : sourceAnnotationAttributes.entrySet()) { + + String attributeName = entry.getKey(); + + // ignore attribute name to skip + if (containsElement(ignoreAttributeNames, attributeName)) { + continue; + } + + Object attributeValue = entry.getValue(); if (attributeValue instanceof String) { attributeValue = resolvePlaceholders(valueOf(attributeValue), propertyResolver); @@ -364,9 +400,32 @@ public static Map getAttributes(Annotation annotation, PropertyR } attributeValue = values; } - actualAttributes.put(attributeName, attributeValue); + + resolvedAnnotationAttributes.put(attributeName, attributeValue); } - return actualAttributes; + + return unmodifiableMap(resolvedAnnotationAttributes); + } + + /** + * Get {@link AnnotationAttributes the annotation attributes} after merging and resolving the placeholders + * + * @param annotatedElement {@link AnnotatedElement the annotated element} + * @param annotationType the {@link Class tyoe} pf {@link Annotation annotation} + * @param propertyResolver {@link PropertyResolver} instance, e.g {@link Environment} + * @param ignoreDefaultValue whether ignore default value or not + * @param ignoreAttributeNames the attribute names of annotation should be ignored + * @return non-null + * @since 2.7.3 + */ + public static AnnotationAttributes getMergedAttributes(AnnotatedElement annotatedElement, + Class annotationType, + PropertyResolver propertyResolver, + boolean ignoreDefaultValue, + String... ignoreAttributeNames) { + Annotation annotation = getMergedAnnotation(annotatedElement, annotationType); + return fromMap(getAttributes(annotation, propertyResolver, ignoreDefaultValue, ignoreAttributeNames)); + } private static String resolvePlaceholders(String attributeValue, PropertyResolver propertyResolver) { diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/BeanFactoryUtils.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/BeanFactoryUtils.java index 71fb7676f88..558986efabe 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/BeanFactoryUtils.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/BeanFactoryUtils.java @@ -29,9 +29,11 @@ import java.util.List; import java.util.Map; +import static java.util.Collections.emptyList; import static org.springframework.beans.factory.BeanFactoryUtils.beanNamesForTypeIncludingAncestors; import static org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors; import static org.springframework.util.ObjectUtils.containsElement; +import static org.springframework.util.ObjectUtils.isEmpty; /** * {@link BeanFactory} Utilities class @@ -104,6 +106,10 @@ public static T getOptionalBean(ListableBeanFactory beanFactory, String bean */ public static List getBeans(ListableBeanFactory beanFactory, String[] beanNames, Class beanType) { + if (isEmpty(beanNames)) { + return emptyList(); + } + String[] allBeanNames = beanNamesForTypeIncludingAncestors(beanFactory, beanType); List beans = new ArrayList(beanNames.length); diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/annotation/merged/MergedReference.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/annotation/merged/MergedReference.java new file mode 100644 index 00000000000..9d3407782dc --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/annotation/merged/MergedReference.java @@ -0,0 +1,41 @@ +/* + * 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.annotation.merged; + + +import org.apache.dubbo.config.annotation.Reference; + +import org.springframework.core.annotation.AliasFor; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Reference +public @interface MergedReference { + + @AliasFor(annotation = Reference.class, attribute = "group") + String group() default "dubbo"; + + @AliasFor(annotation = Reference.class, attribute = "version") + String version() default "1.0.0"; +} diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/annotation/merged/MergedService.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/annotation/merged/MergedService.java new file mode 100644 index 00000000000..cfe124362cf --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/annotation/merged/MergedService.java @@ -0,0 +1,41 @@ +/* + * 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.annotation.merged; + + +import org.apache.dubbo.config.annotation.Service; + +import org.springframework.core.annotation.AliasFor; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +@Service +public @interface MergedService { + + @AliasFor(annotation = Service.class, attribute = "group") + String group() default "dubbo"; + + @AliasFor(annotation = Service.class, attribute = "version") + String version() default "1.0.0"; +} diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/MergedAnnotationTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/MergedAnnotationTest.java new file mode 100644 index 00000000000..48c04ebfbf6 --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/MergedAnnotationTest.java @@ -0,0 +1,76 @@ +/* + * 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 org.apache.dubbo.config.annotation.Reference; +import org.apache.dubbo.config.annotation.Service; +import org.apache.dubbo.config.spring.annotation.merged.MergedReference; +import org.apache.dubbo.config.spring.annotation.merged.MergedService; +import org.apache.dubbo.config.spring.api.DemoService; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.util.ReflectionUtils; + +import java.lang.reflect.Field; + +public class MergedAnnotationTest { + + @Test + public void testMergedReference() { + Field field = ReflectionUtils.findField(MergedAnnotationTest.TestBean1.class, "demoService"); + Reference reference = AnnotatedElementUtils.getMergedAnnotation(field, Reference.class); + Assert.assertEquals("dubbo", reference.group()); + Assert.assertEquals("1.0.0", reference.version()); + + Field field2 = ReflectionUtils.findField(MergedAnnotationTest.TestBean2.class, "demoService"); + Reference reference2 = AnnotatedElementUtils.getMergedAnnotation(field2, Reference.class); + Assert.assertEquals("group", reference2.group()); + Assert.assertEquals("2.0", reference2.version()); + } + + @Test + public void testMergedService() { + Service service1 = AnnotatedElementUtils.getMergedAnnotation(MergedAnnotationTest.DemoServiceImpl1.class, Service.class); + Assert.assertEquals("dubbo", service1.group()); + Assert.assertEquals("1.0.0", service1.version()); + + Service service2 = AnnotatedElementUtils.getMergedAnnotation(MergedAnnotationTest.DemoServiceImpl2.class, Service.class); + Assert.assertEquals("group", service2.group()); + Assert.assertEquals("2.0", service2.version()); + } + + @MergedService + public static class DemoServiceImpl1 { + } + + @MergedService(group = "group", version = "2.0") + public static class DemoServiceImpl2 { + } + + private static class TestBean1 { + @MergedReference + private DemoService demoService; + } + + private static class TestBean2 { + @MergedReference(group = "group", version = "2.0") + private DemoService demoService; + } + +} 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 6f626784db3..1ee01dcd952 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 @@ -229,7 +229,7 @@ public DemoService getDemoService() { return demoService; } - @Reference(version = "2.5.7", url = "dubbo://127.0.0.1:12345") + @com.alibaba.dubbo.config.annotation.Reference(version = "2.5.7", url = "dubbo://127.0.0.1:12345") public void setDemoService(DemoService demoService) { this.demoService = demoService; } diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/context/annotation/provider/DefaultHelloService.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/context/annotation/provider/DefaultHelloService.java index b484d26a931..8277bc9b4e8 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/context/annotation/provider/DefaultHelloService.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/context/annotation/provider/DefaultHelloService.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.config.spring.context.context.annotation.provider; +import org.apache.dubbo.config.spring.annotation.merged.MergedService; import org.apache.dubbo.config.spring.api.HelloService; import org.springframework.stereotype.Service; @@ -27,7 +28,8 @@ * @since TODO */ @Service -@org.apache.dubbo.config.annotation.Service +//@org.apache.dubbo.config.annotation.Service +@MergedService public class DefaultHelloService implements HelloService { @Override diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/util/AnnotatedBeanDefinitionRegistryUtilsTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/util/AnnotatedBeanDefinitionRegistryUtilsTest.java new file mode 100644 index 00000000000..05c4a78a7bc --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/util/AnnotatedBeanDefinitionRegistryUtilsTest.java @@ -0,0 +1,74 @@ +/* + * 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.util; + +import org.junit.Assert; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.stereotype.Service; + +import static org.apache.dubbo.config.spring.util.AnnotatedBeanDefinitionRegistryUtils.isPresentBean; +import static org.apache.dubbo.config.spring.util.AnnotatedBeanDefinitionRegistryUtils.registerBeans; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link AnnotatedBeanDefinitionRegistryUtils} Test + * + * @since 2.7.3 + */ +public class AnnotatedBeanDefinitionRegistryUtilsTest { + + private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + + @AfterEach + public void destroy() { + context.close(); + } + + @Test + public void testIsPresentBean() { + + assertFalse(isPresentBean(context, A.class)); + + context.register(A.class); + + for (int i = 0; i < 9; i++) { + assertTrue(isPresentBean(context, A.class)); + } + + } + + @Test + public void testRegisterBeans() { + + registerBeans(context, A.class, A.class); + + context.refresh(); + + A a = context.getBean(A.class); + + Assert.assertNotNull(a); + } + + + @Service + static class A { + + } +}