diff --git a/dependencies-bom/pom.xml b/dependencies-bom/pom.xml index e9a3f80b2c5..6f06c22a7a5 100644 --- a/dependencies-bom/pom.xml +++ b/dependencies-bom/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 @@ -29,8 +30,8 @@ https://github.com/apache/incubator-dubbo scm:git:https://github.com/apache/incubator-dubbo.git scm:git:https://github.com/apache/incubator-dubbo.git - HEAD - + HEAD + Development List @@ -101,6 +102,9 @@ 2.0 3.0.19.Final 8.5.31 + + + 1.0.1 1.7.25 1.2 @@ -300,6 +304,14 @@ tomcat-embed-logging-juli ${tomcat_embed_version} + + + + com.alibaba.spring + spring-context-support + ${alibaba_spring_context_support_version} + + org.slf4j diff --git a/dubbo-config/dubbo-config-spring/pom.xml b/dubbo-config/dubbo-config-spring/pom.xml index a186182c49a..ff528b05195 100644 --- a/dubbo-config/dubbo-config-spring/pom.xml +++ b/dubbo-config/dubbo-config-spring/pom.xml @@ -56,6 +56,10 @@ javax.servlet-api provided + + com.alibaba.spring + spring-context-support + com.alibaba dubbo-registry-default diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/ServiceBean.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/ServiceBean.java index b38684b2c75..d03f4c1a31b 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/ServiceBean.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/ServiceBean.java @@ -16,23 +16,16 @@ */ package com.alibaba.dubbo.config.spring; -import com.alibaba.dubbo.config.ApplicationConfig; -import com.alibaba.dubbo.config.ModuleConfig; -import com.alibaba.dubbo.config.MonitorConfig; -import com.alibaba.dubbo.config.ProtocolConfig; -import com.alibaba.dubbo.config.ProviderConfig; -import com.alibaba.dubbo.config.RegistryConfig; -import com.alibaba.dubbo.config.ServiceConfig; +import com.alibaba.dubbo.config.*; import com.alibaba.dubbo.config.annotation.Service; +import com.alibaba.dubbo.config.spring.context.event.ServiceBeanExportedEvent; import com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.ApplicationListener; +import org.springframework.context.*; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.support.AbstractApplicationContext; @@ -46,7 +39,9 @@ * * @export */ -public class ServiceBean extends ServiceConfig implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener, BeanNameAware { +public class ServiceBean extends ServiceConfig implements InitializingBean, DisposableBean, + ApplicationContextAware, ApplicationListener, BeanNameAware, + ApplicationEventPublisherAware { private static final long serialVersionUID = 213195494150089726L; @@ -60,6 +55,8 @@ public class ServiceBean extends ServiceConfig implements InitializingBean private transient boolean supportedApplicationListener; + private ApplicationEventPublisher applicationEventPublisher; + public ServiceBean() { super(); this.service = null; @@ -265,6 +262,34 @@ && getInterface() != null && getInterface().length() > 0 } } + /** + * Get the name of {@link ServiceBean} + * + * @return {@link ServiceBean}'s name + * @since 2.6.4 + */ + public String getBeanName() { + return this.beanName; + } + + /** + * @since 2.6.4 + */ + @Override + public void export() { + super.export(); + // Publish ServiceBeanExportedEvent + publishExportEvent(); + } + + /** + * @since 2.6.4 + */ + private void publishExportEvent() { + ServiceBeanExportedEvent exportEvent = new ServiceBeanExportedEvent(this); + applicationEventPublisher.publishEvent(exportEvent); + } + @Override public void destroy() throws Exception { // This will only be called for singleton scope bean, and expected to be called by spring shutdown hook when BeanFactory/ApplicationContext destroys. @@ -280,4 +305,13 @@ protected Class getServiceClass(T ref) { } return super.getServiceClass(ref); } + + /** + * @param applicationEventPublisher + * @since 2.6.4 + */ + @Override + public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { + this.applicationEventPublisher = applicationEventPublisher; + } } diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationConfigBeanBuilder.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationConfigBeanBuilder.java index fc8d3f46105..2f58a6ec3b7 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationConfigBeanBuilder.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/AbstractAnnotationConfigBeanBuilder.java @@ -34,6 +34,7 @@ /** * Abstract Configurable {@link Annotation} Bean Builder + * * @since 2.5.7 */ abstract class AbstractAnnotationConfigBeanBuilder { @@ -76,7 +77,7 @@ public final B build() throws Exception { configureBean(bean); if (logger.isInfoEnabled()) { - logger.info(bean + " has been built."); + logger.info("The bean[type:" + bean.getClass().getSimpleName() + "] has been built."); } return bean; diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java index cfd719668ae..37debea5e19 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java @@ -18,488 +18,233 @@ import com.alibaba.dubbo.config.annotation.Reference; import com.alibaba.dubbo.config.spring.ReferenceBean; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.BeanUtils; +import com.alibaba.dubbo.config.spring.ServiceBean; +import com.alibaba.dubbo.config.spring.context.event.ServiceBeanExportedEvent; +import com.alibaba.spring.beans.factory.annotation.AnnotationInjectedBeanPostProcessor; 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 org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; -import java.beans.PropertyDescriptor; import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; +import java.lang.reflect.Proxy; import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; +import java.util.Collections; 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 */ -public class ReferenceAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter - implements MergedBeanDefinitionPostProcessor, PriorityOrdered, ApplicationContextAware, BeanClassLoaderAware, - DisposableBean { +public class ReferenceAnnotationBeanPostProcessor extends AnnotationInjectedBeanPostProcessor + implements ApplicationContextAware, ApplicationListener { /** * The bean name of {@link ReferenceAnnotationBeanPostProcessor} */ public static final String BEAN_NAME = "referenceAnnotationBeanPostProcessor"; - private final Log logger = LogFactory.getLog(getClass()); - - private ApplicationContext applicationContext; + /** + * Cache size + */ + private static final int CACHE_SIZE = Integer.getInteger(BEAN_NAME + ".cache.size", 32); - private ClassLoader classLoader; + private final ConcurrentMap> referenceBeanCache = + new ConcurrentHashMap>(CACHE_SIZE); - private final ConcurrentMap injectionMetadataCache = - new ConcurrentHashMap(256); + private final ConcurrentHashMap localReferenceBeanInvocationHandlerCache = + new ConcurrentHashMap(CACHE_SIZE); - private final ConcurrentMap> referenceBeansCache = - new ConcurrentHashMap>(); + private final ConcurrentMap> injectedFieldReferenceBeanCache = + new ConcurrentHashMap>(CACHE_SIZE); - @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; - } + private final ConcurrentMap> injectedMethodReferenceBeanCache = + new ConcurrentHashMap>(CACHE_SIZE); + private ApplicationContext applicationContext; /** - * Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated {@link Reference @Reference} fields + * Gets all beans of {@link ReferenceBean} * - * @param beanClass The {@link Class} of Bean - * @return non-null {@link List} + * @return non-null read-only {@link Collection} + * @since 2.5.9 */ - 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; - + public Collection> getReferenceBeans() { + return referenceBeanCache.values(); } /** - * Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated {@link Reference @Reference} methods + * Get {@link ReferenceBean} {@link Map} in injected field. * - * @param beanClass The {@link Class} of Bean - * @return non-null {@link List} + * @return non-null {@link Map} + * @since 2.5.11 */ - 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; - + public Map> getInjectedFieldReferenceBeanMap() { + return Collections.unmodifiableMap(injectedFieldReferenceBeanCache); } - /** - * @param beanClass - * @return + * Get {@link ReferenceBean} {@link Map} in injected method. + * + * @return non-null {@link Map} + * @since 2.5.11 */ - 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; + public Map> getInjectedMethodReferenceBeanMap() { + return Collections.unmodifiableMap(injectedMethodReferenceBeanCache); } @Override - public void destroy() throws Exception { + protected Object doGetInjectedBean(Reference reference, Object bean, String beanName, Class injectedType, + InjectionMetadata.InjectedElement injectedElement) throws Exception { - for (ReferenceBean referenceBean : referenceBeansCache.values()) { - if (logger.isInfoEnabled()) { - logger.info(referenceBean + " was destroying!"); - } - referenceBean.destroy(); - } + String referencedBeanName = buildReferencedBeanName(reference, injectedType); - injectionMetadataCache.clear(); - referenceBeansCache.clear(); + ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referencedBeanName, reference, injectedType, getClassLoader()); - if (logger.isInfoEnabled()) { - logger.info(getClass() + " was destroying!"); - } + cacheInjectedReferenceBean(referenceBean, injectedElement); - } + Object proxy = buildProxy(referencedBeanName, referenceBean, injectedType); - @Override - public void setBeanClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader; + return proxy; } - - /** - * Gets all beans of {@link ReferenceBean} - * - * @return non-null {@link Collection} - * @since 2.5.9 - */ - public Collection> getReferenceBeans() { - return this.referenceBeansCache.values(); + private Object buildProxy(String referencedBeanName, ReferenceBean referenceBean, Class injectedType) { + InvocationHandler handler = buildInvocationHandler(referencedBeanName, referenceBean); + Object proxy = Proxy.newProxyInstance(getClassLoader(), new Class[]{injectedType}, handler); + return proxy; } + private InvocationHandler buildInvocationHandler(String referencedBeanName, ReferenceBean referenceBean) { - /** - * {@link Reference} {@link InjectionMetadata} implementation - * - * @since 2.5.11 - */ - private static class ReferenceInjectionMetadata extends InjectionMetadata { - - private final Collection fieldElements; + ReferenceBeanInvocationHandler handler = localReferenceBeanInvocationHandlerCache.get(referencedBeanName); - private final Collection methodElements; - - - public ReferenceInjectionMetadata(Class targetClass, Collection fieldElements, - Collection methodElements) { - super(targetClass, combine(fieldElements, methodElements)); - this.fieldElements = fieldElements; - this.methodElements = methodElements; + if (handler == null) { + handler = new ReferenceBeanInvocationHandler(referenceBean); } - private static Collection combine(Collection... elements) { - List allElements = new ArrayList(); - for (Collection e : elements) { - allElements.addAll(e); - } - return allElements; - } - - public Collection getFieldElements() { - return fieldElements; + if (applicationContext.containsBean(referencedBeanName)) { // Is local @Service Bean or not ? + // ReferenceBeanInvocationHandler's initialization has to wait for current local @Service Bean has been exported. + localReferenceBeanInvocationHandlerCache.put(referencedBeanName, handler); + } else { + // Remote Reference Bean should initialize immediately + handler.init(); } - public Collection getMethodElements() { - return methodElements; - } + return handler; } - /** - * {@link Reference} {@link Method} {@link InjectionMetadata.InjectedElement} - */ - private class ReferenceMethodElement extends InjectionMetadata.InjectedElement { + private static class ReferenceBeanInvocationHandler implements InvocationHandler { - private final Method method; + private final ReferenceBean referenceBean; - private final Reference reference; + private Object bean; - private volatile ReferenceBean referenceBean; - - protected ReferenceMethodElement(Method method, PropertyDescriptor pd, Reference reference) { - super(method, pd); - this.method = method; - this.reference = reference; + private ReferenceBeanInvocationHandler(ReferenceBean referenceBean) { + this.referenceBean = referenceBean; } @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()); - + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return method.invoke(bean, args); } - } - - /** - * {@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; + private void init() { + this.bean = referenceBean.get(); } + } - @Override - protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable { - - Class referenceClass = field.getType(); + @Override + protected String buildInjectedObjectCacheKey(Reference reference, Object bean, String beanName, + Class injectedType, InjectionMetadata.InjectedElement injectedElement) { - referenceBean = buildReferenceBean(reference, referenceClass); + String key = buildReferencedBeanName(reference, injectedType) + + "#source=" + (injectedElement.getMember()); - ReflectionUtils.makeAccessible(field); + return key; + } - field.set(bean, referenceBean.getObject()); + private String buildReferencedBeanName(Reference reference, Class injectedType) { - } + ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(reference, injectedType, getEnvironment()); + return getEnvironment().resolvePlaceholders(builder.build()); } - private ReferenceBean buildReferenceBean(Reference reference, Class referenceClass) throws Exception { - - String referenceBeanCacheKey = generateReferenceBeanCacheKey(reference, referenceClass); + private ReferenceBean buildReferenceBeanIfAbsent(String referencedBeanName, Reference reference, + Class referencedType, ClassLoader classLoader) + throws Exception { - ReferenceBean referenceBean = referenceBeansCache.get(referenceBeanCacheKey); + ReferenceBean referenceBean = referenceBeanCache.get(referencedBeanName); if (referenceBean == null) { - ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder .create(reference, classLoader, applicationContext) - .interfaceClass(referenceClass); - + .interfaceClass(referencedType); referenceBean = beanBuilder.build(); - - referenceBeansCache.putIfAbsent(referenceBeanCacheKey, referenceBean); - + referenceBeanCache.put(referencedBeanName, 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."); + private void cacheInjectedReferenceBean(ReferenceBean referenceBean, + InjectionMetadata.InjectedElement injectedElement) { + if (injectedElement.getMember() instanceof Field) { + injectedFieldReferenceBeanCache.put(injectedElement, referenceBean); + } else if (injectedElement.getMember() instanceof Method) { + injectedMethodReferenceBeanCache.put(injectedElement, referenceBean); } - - return interfaceName; - } + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } - /** - * 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); - - } - + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof ServiceBeanExportedEvent) { + onServiceBeanExportEvent((ServiceBeanExportedEvent) event); + } else if (event instanceof ContextRefreshedEvent) { + onContextRefreshedEvent((ContextRefreshedEvent) event); } - - 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); - - } + private void onServiceBeanExportEvent(ServiceBeanExportedEvent event) { + ServiceBean serviceBean = event.getServiceBean(); + initReferenceBeanInvocationHandler(serviceBean); + } + private void initReferenceBeanInvocationHandler(ServiceBean serviceBean) { + String serviceBeanName = serviceBean.getBeanName(); + // Remove ServiceBean when it's exported + ReferenceBeanInvocationHandler handler = localReferenceBeanInvocationHandlerCache.remove(serviceBeanName); + // Initialize + if (handler != null) { + handler.init(); } - - 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); + private void onContextRefreshedEvent(ContextRefreshedEvent event) { } + @Override + public void destroy() throws Exception { + super.destroy(); + this.referenceBeanCache.clear(); + this.localReferenceBeanInvocationHandlerCache.clear(); + this.injectedFieldReferenceBeanCache.clear(); + this.injectedMethodReferenceBeanCache.clear(); + } } diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilder.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilder.java index 5d9418fbc8a..883a56a1879 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilder.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilder.java @@ -16,21 +16,23 @@ */ package com.alibaba.dubbo.config.spring.beans.factory.annotation; +import com.alibaba.dubbo.common.utils.CollectionUtils; import com.alibaba.dubbo.config.ConsumerConfig; import com.alibaba.dubbo.config.annotation.Reference; import com.alibaba.dubbo.config.spring.ReferenceBean; -import com.alibaba.dubbo.config.spring.convert.converter.StringArrayToMapConverter; -import com.alibaba.dubbo.config.spring.convert.converter.StringArrayToStringConverter; +import org.springframework.beans.propertyeditors.StringTrimmerEditor; import org.springframework.context.ApplicationContext; -import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.support.DefaultConversionService; 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 com.alibaba.dubbo.config.spring.util.BeanFactoryUtils.getOptionalBean; import static com.alibaba.dubbo.config.spring.util.ObjectUtils.of; +import static org.springframework.util.StringUtils.commaDelimitedListToStringArray; /** * {@link ReferenceBean} Builder @@ -39,6 +41,8 @@ */ class ReferenceBeanBuilder extends AbstractAnnotationConfigBeanBuilder { + // 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); @@ -93,20 +97,30 @@ protected ReferenceBean doBuild() { protected void preConfigureBean(Reference reference, ReferenceBean referenceBean) { Assert.notNull(interfaceClass, "The interface class must set first!"); DataBinder dataBinder = new DataBinder(referenceBean); - // Set ConversionService - dataBinder.setConversionService(getConversionService()); - // Ignore those fields - String[] ignoreAttributeNames = of("application", "module", "consumer", "monitor", "registry"); -// dataBinder.setDisallowedFields(ignoreAttributeNames); + // 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() { + + 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(), ignoreAttributeNames)); - } + dataBinder.bind(new AnnotationPropertyValuesAdapter(reference, applicationContext.getEnvironment(), IGNORE_FIELD_NAMES)); - private ConversionService getConversionService() { - DefaultConversionService conversionService = new DefaultConversionService(); - conversionService.addConverter(new StringArrayToStringConverter()); - conversionService.addConverter(new StringArrayToMapConverter()); - return conversionService; } diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessor.java index 35604650906..f92173d6c1a 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessor.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessor.java @@ -24,17 +24,8 @@ 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.beans.factory.config.*; +import org.springframework.beans.factory.support.*; import org.springframework.context.EnvironmentAware; import org.springframework.context.ResourceLoaderAware; import org.springframework.context.annotation.AnnotationBeanNameGenerator; @@ -44,17 +35,9 @@ 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.LinkedHashSet; -import java.util.List; -import java.util.Set; +import org.springframework.util.*; + +import java.util.*; import static com.alibaba.dubbo.config.spring.util.ObjectUtils.of; import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition; @@ -71,8 +54,6 @@ public class ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, ResourceLoaderAware, BeanClassLoaderAware { - private static final String SEPARATOR = ":"; - private final Logger logger = LoggerFactory.getLogger(getClass()); private final Set packagesToScan; @@ -262,7 +243,7 @@ private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, Bean registry.registerBeanDefinition(beanName, serviceBeanDefinition); if (logger.isInfoEnabled()) { - logger.warn("The BeanDefinition[" + serviceBeanDefinition + + logger.info("The BeanDefinition[" + serviceBeanDefinition + "] of ServiceBean has been registered with name : " + beanName); } @@ -271,7 +252,7 @@ private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, Bean if (logger.isWarnEnabled()) { logger.warn("The Duplicated BeanDefinition[" + serviceBeanDefinition + "] of ServiceBean[ bean name : " + beanName + - "] was be found , Did @DubboComponentScan scan to same package in many times?"); + "] was be found!"); } } @@ -289,27 +270,9 @@ private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, Bean */ 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); - } + ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(service, interfaceClass, environment); - return beanNameBuilder.toString(); + return builder.build(); } diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilder.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilder.java new file mode 100644 index 00000000000..14556eab140 --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilder.java @@ -0,0 +1,112 @@ +/* + * 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 com.alibaba.dubbo.config.spring.beans.factory.annotation; + +import com.alibaba.dubbo.config.annotation.Reference; +import com.alibaba.dubbo.config.annotation.Service; +import com.alibaba.dubbo.config.spring.ReferenceBean; +import com.alibaba.dubbo.config.spring.ServiceBean; +import org.springframework.core.env.Environment; +import org.springframework.util.StringUtils; + +import static com.alibaba.dubbo.config.spring.util.AnnotationUtils.resolveInterfaceName; + +/** + * Dubbo {@link Service @Service} Bean Builder + * + * @author Mercy + * @see Service + * @see Reference + * @see ServiceBean + * @see ReferenceBean + * @since 2.6.4 + */ +class ServiceBeanNameBuilder { + + private static final String SEPARATOR = ":"; + + private final String interfaceClassName; + + private final Environment environment; + + // Optional + private String version; + + private String group; + + private ServiceBeanNameBuilder(String interfaceClassName, Environment environment) { + this.interfaceClassName = interfaceClassName; + this.environment = environment; + } + + private ServiceBeanNameBuilder(Class interfaceClass, Environment environment) { + this(interfaceClass.getName(), environment); + } + + private ServiceBeanNameBuilder(Service service, Class interfaceClass, Environment environment) { + this(resolveInterfaceName(service, interfaceClass), environment); + this.group(service.group()); + this.version(service.version()); + } + + private ServiceBeanNameBuilder(Reference reference, Class interfaceClass, Environment environment) { + this(resolveInterfaceName(reference, interfaceClass), environment); + this.group(reference.group()); + this.version(reference.version()); + } + + public static ServiceBeanNameBuilder create(Class interfaceClass, Environment environment) { + return new ServiceBeanNameBuilder(interfaceClass, environment); + } + + public static ServiceBeanNameBuilder create(Service service, Class interfaceClass, Environment environment) { + return new ServiceBeanNameBuilder(service, interfaceClass, environment); + } + + public static ServiceBeanNameBuilder create(Reference reference, Class interfaceClass, Environment environment) { + return new ServiceBeanNameBuilder(reference, interfaceClass, environment); + } + + private static void append(StringBuilder builder, String value) { + if (StringUtils.hasText(value)) { + builder.append(value).append(SEPARATOR); + } + } + + public ServiceBeanNameBuilder group(String group) { + this.group = group; + return this; + } + + public ServiceBeanNameBuilder version(String version) { + this.version = version; + return this; + } + + public String build() { + StringBuilder beanNameBuilder = new StringBuilder("ServiceBean").append(SEPARATOR); + // Required + append(beanNameBuilder, interfaceClassName); + // Optional + append(beanNameBuilder, version); + append(beanNameBuilder, group); + // Build and remove last ":" + String rawBeanName = beanNameBuilder.substring(0, beanNameBuilder.length() - 1); + // Resolve placeholders + return environment.resolvePlaceholders(rawBeanName); + } +} diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/context/annotation/DubboConfigBindingRegistrar.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/context/annotation/DubboConfigBindingRegistrar.java index 6936787cc67..354a6c33d0f 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/context/annotation/DubboConfigBindingRegistrar.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/context/annotation/DubboConfigBindingRegistrar.java @@ -40,8 +40,8 @@ import java.util.Map; import java.util.Set; -import static com.alibaba.dubbo.config.spring.util.PropertySourcesUtils.getSubProperties; -import static com.alibaba.dubbo.config.spring.util.PropertySourcesUtils.normalizePrefix; +import static com.alibaba.spring.util.PropertySourcesUtils.getSubProperties; +import static com.alibaba.spring.util.PropertySourcesUtils.normalizePrefix; import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition; import static org.springframework.beans.factory.support.BeanDefinitionReaderUtils.registerWithGeneratedName; @@ -85,7 +85,7 @@ private void registerDubboConfigBeans(String prefix, boolean multiple, BeanDefinitionRegistry registry) { - Map properties = getSubProperties(environment.getPropertySources(), prefix); + Map properties = getSubProperties(environment.getPropertySources(), prefix); if (CollectionUtils.isEmpty(properties)) { if (log.isDebugEnabled()) { @@ -157,7 +157,7 @@ public void setEnvironment(Environment environment) { } - private Set resolveMultipleBeanNames(Map properties) { + private Set resolveMultipleBeanNames(Map properties) { Set beanNames = new LinkedHashSet(); @@ -178,10 +178,10 @@ private Set resolveMultipleBeanNames(Map properties) { } - private String resolveSingleBeanName(Map properties, Class configClass, + private String resolveSingleBeanName(Map properties, Class configClass, BeanDefinitionRegistry registry) { - String beanName = properties.get("id"); + String beanName = (String) properties.get("id"); if (!StringUtils.hasText(beanName)) { BeanDefinitionBuilder builder = rootBeanDefinition(configClass); diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/context/event/ServiceBeanExportedEvent.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/context/event/ServiceBeanExportedEvent.java new file mode 100644 index 00000000000..3642fd16966 --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/context/event/ServiceBeanExportedEvent.java @@ -0,0 +1,51 @@ +/* + * 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 com.alibaba.dubbo.config.spring.context.event; + +import com.alibaba.dubbo.config.spring.ServiceBean; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; + +/** + * A {@link ApplicationEvent} after {@link ServiceBean} {@link ServiceBean#export() export} invocation + * + * @author Mercy + * @see ApplicationEvent + * @see ApplicationListener + * @see ServiceBean + * @since 2.6.4 + */ +public class ServiceBeanExportedEvent extends ApplicationEvent { + + /** + * Create a new ApplicationEvent. + * + * @param serviceBean {@link ServiceBean} bean + */ + public ServiceBeanExportedEvent(ServiceBean serviceBean) { + super(serviceBean); + } + + /** + * Get {@link ServiceBean} instance + * + * @return non-null + */ + public ServiceBean getServiceBean() { + return (ServiceBean) super.getSource(); + } +} diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/context/properties/DefaultDubboConfigBinder.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/context/properties/DefaultDubboConfigBinder.java index 52cc87ca4f5..129d1730ef2 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/context/properties/DefaultDubboConfigBinder.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/context/properties/DefaultDubboConfigBinder.java @@ -22,7 +22,7 @@ import java.util.Map; -import static com.alibaba.dubbo.config.spring.util.PropertySourcesUtils.getSubProperties; +import static com.alibaba.spring.util.PropertySourcesUtils.getSubProperties; /** * Default {@link DubboConfigBinder} implementation based on Spring {@link DataBinder} @@ -36,7 +36,7 @@ public void bind(String prefix, C dubboConfig) { dataBinder.setIgnoreInvalidFields(isIgnoreInvalidFields()); dataBinder.setIgnoreUnknownFields(isIgnoreUnknownFields()); // Get properties under specified prefix from PropertySources - Map properties = getSubProperties(getPropertySources(), prefix); + Map properties = getSubProperties(getPropertySources(), prefix); // Convert Map to MutablePropertyValues MutablePropertyValues propertyValues = new MutablePropertyValues(properties); // Bind diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/convert/converter/StringArrayToMapConverter.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/convert/converter/StringArrayToMapConverter.java deleted file mode 100644 index 56c6d4c4ccd..00000000000 --- a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/convert/converter/StringArrayToMapConverter.java +++ /dev/null @@ -1,38 +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 com.alibaba.dubbo.config.spring.convert.converter; - -import com.alibaba.dubbo.common.utils.CollectionUtils; -import org.springframework.core.convert.converter.Converter; -import org.springframework.util.ObjectUtils; - -import java.util.Map; - -/** - * {@link String}[] to {@link Map} {@link Converter} - * - * @see CollectionUtils#toStringMap(String[]) - * @since 2.5.11 - */ -public class StringArrayToMapConverter implements Converter> { - - @Override - public Map convert(String[] source) { - return ObjectUtils.isEmpty(source) ? null : CollectionUtils.toStringMap(source); - } - -} diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/convert/converter/StringArrayToStringConverter.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/convert/converter/StringArrayToStringConverter.java deleted file mode 100644 index 23e948b0644..00000000000 --- a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/convert/converter/StringArrayToStringConverter.java +++ /dev/null @@ -1,37 +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 com.alibaba.dubbo.config.spring.convert.converter; - -import org.springframework.core.convert.converter.Converter; -import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; - - -/** - * String[] to String {@ConditionalGenericConverter} - * - * @see StringUtils#arrayToCommaDelimitedString(Object[]) - * @since 2.5.11 - */ -public class StringArrayToStringConverter implements Converter { - - @Override - public String convert(String[] source) { - return ObjectUtils.isEmpty(source) ? null : StringUtils.arrayToCommaDelimitedString(source); - } - -} diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/util/AnnotationUtils.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/util/AnnotationUtils.java index aa15e567eb0..87f82d3063d 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/util/AnnotationUtils.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/util/AnnotationUtils.java @@ -16,6 +16,8 @@ */ package com.alibaba.dubbo.config.spring.util; +import com.alibaba.dubbo.config.annotation.Reference; +import com.alibaba.dubbo.config.annotation.Service; import org.springframework.core.env.PropertyResolver; import java.lang.annotation.Annotation; @@ -86,4 +88,44 @@ public static Map getAttributes(Annotation annotation, PropertyR } + public static String resolveInterfaceName(Service service, Class defaultInterfaceClass) + throws IllegalStateException { + + String interfaceName; + if (!"".equals(service.interfaceName())) { + interfaceName = service.interfaceName(); + } else if (!void.class.equals(service.interfaceClass())) { + interfaceName = service.interfaceClass().getName(); + } else if (defaultInterfaceClass.isInterface()) { + interfaceName = defaultInterfaceClass.getName(); + } else { + throw new IllegalStateException( + "The @Service undefined interfaceClass or interfaceName, and the type " + + defaultInterfaceClass.getName() + " is not a interface."); + } + + return interfaceName; + + } + + public static String resolveInterfaceName(Reference reference, Class defaultInterfaceClass) + throws IllegalStateException { + + String interfaceName; + if (!"".equals(reference.interfaceName())) { + interfaceName = reference.interfaceName(); + } else if (!void.class.equals(reference.interfaceClass())) { + interfaceName = reference.interfaceClass().getName(); + } else if (defaultInterfaceClass.isInterface()) { + interfaceName = defaultInterfaceClass.getName(); + } else { + throw new IllegalStateException( + "The @Reference undefined interfaceClass or interfaceName, and the type " + + defaultInterfaceClass.getName() + " is not a interface."); + } + + return interfaceName; + + } + } diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/util/PropertySourcesUtils.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/util/PropertySourcesUtils.java deleted file mode 100644 index 5b183e1f2f7..00000000000 --- a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/util/PropertySourcesUtils.java +++ /dev/null @@ -1,75 +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 com.alibaba.dubbo.config.spring.util; - -import org.springframework.core.env.EnumerablePropertySource; -import org.springframework.core.env.PropertySource; -import org.springframework.core.env.PropertySources; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Properties; - -/** - * {@link PropertySources} Utilities - * - * @see PropertySources - * @since 2.5.8 - */ -public abstract class PropertySourcesUtils { - - /** - * Get Sub {@link Properties} - * - * @param propertySources {@link PropertySource} Iterable - * @param prefix the prefix of property name - * @return Map - * @see Properties - */ - public static Map getSubProperties(Iterable> propertySources, String prefix) { - - Map subProperties = new LinkedHashMap(); - - String normalizedPrefix = normalizePrefix(prefix); - - for (PropertySource source : propertySources) { - if (source instanceof EnumerablePropertySource) { - for (String name : ((EnumerablePropertySource) source).getPropertyNames()) { - if (name.startsWith(normalizedPrefix)) { - String subName = name.substring(normalizedPrefix.length()); - Object value = source.getProperty(name); - subProperties.put(subName, String.valueOf(value)); - } - } - } - } - - return subProperties; - - } - - /** - * Normalize the prefix - * - * @param prefix the prefix - * @return the prefix - */ - public static String normalizePrefix(String prefix) { - return prefix.endsWith(".") ? prefix : prefix + "."; - } - -} diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java index a1b5807cbf9..eba43b2a599 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java @@ -19,26 +19,23 @@ import com.alibaba.dubbo.config.annotation.Reference; import com.alibaba.dubbo.config.spring.ReferenceBean; import com.alibaba.dubbo.config.spring.api.DemoService; -import com.alibaba.dubbo.config.spring.context.annotation.DubboComponentScan; -import org.junit.After; import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; +import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.InjectionMetadata; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.annotation.ImportResource; +import org.springframework.context.annotation.Bean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; import java.util.Collection; import java.util.Map; import static com.alibaba.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor.BEAN_NAME; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; /** @@ -46,50 +43,48 @@ * * @since 2.5.7 */ +@RunWith(SpringRunner.class) +@ContextConfiguration( + classes = { + ServiceAnnotationTestConfiguration.class, + ReferenceAnnotationBeanPostProcessorTest.class + }) +@TestPropertySource(properties = { + "packagesToScan = com.alibaba.dubbo.config.spring.context.annotation.provider", + "consumer.version = ${demo.service.version}", + "consumer.url = dubbo://127.0.0.1:12345", +}) public class ReferenceAnnotationBeanPostProcessorTest { - private ConfigurableApplicationContext providerApplicationContext; - - @BeforeClass - public static void prepare() { - System.setProperty("provider.version", "1.2"); - System.setProperty("package1", "com.alibaba.dubbo.config.spring.annotation.provider"); - System.setProperty("packagesToScan", "${package1}"); - System.setProperty("consumer.version", "1.2"); - System.setProperty("consumer.url", "dubbo://127.0.0.1:12345"); + @Bean + public TestBean testBean() { + return new TestBean(); } - @Before - public void init() { - // Starts Provider - providerApplicationContext = new AnnotationConfigApplicationContext(ServiceAnnotationBeanPostProcessorTest.TestConfiguration.class); + @Bean(BEAN_NAME) + public ReferenceAnnotationBeanPostProcessor referenceAnnotationBeanPostProcessor() { + return new ReferenceAnnotationBeanPostProcessor(); } - @After - public void destroy() { - // Shutdowns Provider - providerApplicationContext.close(); - } + @Autowired + private ConfigurableApplicationContext context; @Test public void test() throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestBean.class); - TestBean testBean = context.getBean(TestBean.class); + DemoService demoService = testBean.getDemoService(); + + Assert.assertEquals("Hello,Mercy", demoService.sayName("Mercy")); + Assert.assertNotNull(testBean.getDemoServiceFromAncestor()); Assert.assertNotNull(testBean.getDemoServiceFromParent()); Assert.assertNotNull(testBean.getDemoService()); - Assert.assertEquals(testBean.getDemoServiceFromAncestor(), testBean.getDemoServiceFromParent()); - Assert.assertEquals(testBean.getDemoService(), testBean.getDemoServiceFromParent()); - - DemoService demoService = testBean.getDemoService(); - - Assert.assertEquals("annotation:Mercy", demoService.sayName("Mercy")); - - context.close(); + Assert.assertEquals("Hello,Mercy", testBean.getDemoServiceFromAncestor().sayName("Mercy")); + Assert.assertEquals("Hello,Mercy", testBean.getDemoServiceFromParent().sayName("Mercy")); + Assert.assertEquals("Hello,Mercy", testBean.getDemoService().sayName("Mercy")); } @@ -99,8 +94,6 @@ public void test() throws Exception { @Test public void testGetReferenceBeans() { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestBean.class); - ReferenceAnnotationBeanPostProcessor beanPostProcessor = context.getBean(BEAN_NAME, ReferenceAnnotationBeanPostProcessor.class); @@ -112,17 +105,13 @@ public void testGetReferenceBeans() { TestBean testBean = context.getBean(TestBean.class); - Assert.assertEquals(referenceBean.get(), testBean.getDemoServiceFromAncestor()); - Assert.assertEquals(referenceBean.get(), testBean.getDemoServiceFromParent()); - Assert.assertEquals(referenceBean.get(), testBean.getDemoService()); + Assert.assertNotNull(referenceBean.get()); } @Test public void testGetInjectedFieldReferenceBeanMap() { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestBean.class); - ReferenceAnnotationBeanPostProcessor beanPostProcessor = context.getBean(BEAN_NAME, ReferenceAnnotationBeanPostProcessor.class); @@ -136,12 +125,12 @@ public void testGetInjectedFieldReferenceBeanMap() { InjectionMetadata.InjectedElement injectedElement = entry.getKey(); - Assert.assertEquals("com.alibaba.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor$ReferenceFieldElement", + Assert.assertEquals("com.alibaba.spring.beans.factory.annotation.AnnotationInjectedBeanPostProcessor$AnnotatedFieldElement", injectedElement.getClass().getName()); ReferenceBean referenceBean = entry.getValue(); - Assert.assertEquals("1.2", referenceBean.getVersion()); + Assert.assertEquals("2.5.7", referenceBean.getVersion()); Assert.assertEquals("dubbo://127.0.0.1:12345", referenceBean.getUrl()); } @@ -151,8 +140,6 @@ public void testGetInjectedFieldReferenceBeanMap() { @Test public void testGetInjectedMethodReferenceBeanMap() { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestBean.class); - ReferenceAnnotationBeanPostProcessor beanPostProcessor = context.getBean(BEAN_NAME, ReferenceAnnotationBeanPostProcessor.class); @@ -166,36 +153,35 @@ public void testGetInjectedMethodReferenceBeanMap() { InjectionMetadata.InjectedElement injectedElement = entry.getKey(); - Assert.assertEquals("com.alibaba.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor$ReferenceMethodElement", + Assert.assertEquals("com.alibaba.spring.beans.factory.annotation.AnnotationInjectedBeanPostProcessor$AnnotatedMethodElement", injectedElement.getClass().getName()); ReferenceBean referenceBean = entry.getValue(); - Assert.assertEquals("1.2", referenceBean.getVersion()); + Assert.assertEquals("2.5.7", referenceBean.getVersion()); Assert.assertEquals("dubbo://127.0.0.1:12345", referenceBean.getUrl()); } } - @Test - public void testModuleInfo() { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestBean.class); - - ReferenceAnnotationBeanPostProcessor beanPostProcessor = context.getBean(BEAN_NAME, - ReferenceAnnotationBeanPostProcessor.class); - - - Map> referenceBeanMap = - beanPostProcessor.getInjectedMethodReferenceBeanMap(); - - for (Map.Entry> entry : referenceBeanMap.entrySet()) { - ReferenceBean referenceBean = entry.getValue(); - - assertThat(referenceBean.getModule().getName(),is("defaultModule")); - assertThat(referenceBean.getMonitor(), not(nullValue())); - } - } +// @Test +// public void testModuleInfo() { +// +// ReferenceAnnotationBeanPostProcessor beanPostProcessor = context.getBean(BEAN_NAME, +// ReferenceAnnotationBeanPostProcessor.class); +// +// +// Map> referenceBeanMap = +// beanPostProcessor.getInjectedMethodReferenceBeanMap(); +// +// for (Map.Entry> entry : referenceBeanMap.entrySet()) { +// ReferenceBean referenceBean = entry.getValue(); +// +// assertThat(referenceBean.getModule().getName(), is("defaultModule")); +// assertThat(referenceBean.getMonitor(), not(nullValue())); +// } +// } private static class AncestorBean { @@ -209,7 +195,7 @@ public DemoService getDemoServiceFromAncestor() { return demoServiceFromAncestor; } - @Reference(version = "1.2", url = "dubbo://127.0.0.1:12345") + @Reference(version = "2.5.7", url = "dubbo://127.0.0.1:12345") public void setDemoServiceFromAncestor(DemoService demoServiceFromAncestor) { this.demoServiceFromAncestor = demoServiceFromAncestor; } @@ -233,8 +219,6 @@ public DemoService getDemoServiceFromParent() { } - @ImportResource("META-INF/spring/dubbo-annotation-consumer.xml") - @DubboComponentScan(basePackageClasses = ReferenceAnnotationBeanPostProcessorTest.class) static class TestBean extends ParentBean { private DemoService demoService; @@ -246,7 +230,7 @@ public DemoService getDemoService() { return demoService; } - @Reference(version = "1.2", url = "dubbo://127.0.0.1:12345") + @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/com/alibaba/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilderTest.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilderTest.java new file mode 100644 index 00000000000..54b1b9741ea --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilderTest.java @@ -0,0 +1,124 @@ +/* + * 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 com.alibaba.dubbo.config.spring.beans.factory.annotation; + +import com.alibaba.dubbo.config.annotation.Reference; +import com.alibaba.dubbo.config.spring.ReferenceBean; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.springframework.core.annotation.AnnotationUtils.findAnnotation; +import static org.springframework.util.ReflectionUtils.findField; + +/** + * {@link ReferenceBeanBuilder} Test + * + * @author Mercy + * @see ReferenceBeanBuilder + * @see Reference + * @since 2.6.4 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = ReferenceBeanBuilderTest.class) +public class ReferenceBeanBuilderTest { + + @Reference( + interfaceClass = CharSequence.class, + interfaceName = "java.lang.CharSequence", + version = "1.0.0", group = "TEST_GROUP", url = "dubbo://localhost:12345", + client = "client", generic = true, injvm = true, + check = false, init = false, lazy = true, + stubevent = true, reconnect = "reconnect", sticky = true, + proxy = "javassist", stub = "java.lang.CharSequence", cluster = "failover", + connections = 3, callbacks = 1, onconnect = "onconnect", ondisconnect = "ondisconnect", + owner = "owner", layer = "layer", retries = 1, + loadbalance = "random", async = true, actives = 3, + sent = true, mock = "mock", validation = "validation", + timeout = 3, cache = "cache", filter = {"echo", "generic", "accesslog"}, + listener = {"deprecated"}, parameters = {"n1=v1 ", "n2 = v2 ", " n3 = v3 "}, + application = "application", + module = "module", consumer = "consumer", monitor = "monitor", registry = {"registry"} + ) + private static final Object TEST_FIELD = new Object(); + + @Autowired + private ApplicationContext context; + + @Test + public void testBuild() throws Exception { + Reference reference = findAnnotation(findField(getClass(), "TEST_FIELD"), Reference.class); + ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder.create(reference, context.getClassLoader(), context); + beanBuilder.interfaceClass(CharSequence.class); + ReferenceBean referenceBean = beanBuilder.build(); + Assert.assertEquals(CharSequence.class, referenceBean.getInterfaceClass()); + Assert.assertEquals("1.0.0", referenceBean.getVersion()); + Assert.assertEquals("TEST_GROUP", referenceBean.getGroup()); + Assert.assertEquals("dubbo://localhost:12345", referenceBean.getUrl()); + Assert.assertEquals("client", referenceBean.getClient()); + Assert.assertEquals(true, referenceBean.isGeneric()); + Assert.assertEquals(true, referenceBean.isInjvm()); + Assert.assertEquals(false, referenceBean.isCheck()); + Assert.assertEquals(null, referenceBean.isInit()); + Assert.assertEquals(true, referenceBean.getLazy()); + Assert.assertEquals(true, referenceBean.getStubevent()); + Assert.assertEquals("reconnect", referenceBean.getReconnect()); + Assert.assertEquals(true, referenceBean.getSticky()); + Assert.assertEquals("javassist", referenceBean.getProxy()); + Assert.assertEquals("java.lang.CharSequence", referenceBean.getStub()); + Assert.assertEquals("failover", referenceBean.getCluster()); + Assert.assertEquals(Integer.valueOf(3), referenceBean.getConnections()); + Assert.assertEquals(Integer.valueOf(1), referenceBean.getCallbacks()); + Assert.assertEquals("onconnect", referenceBean.getOnconnect()); + Assert.assertEquals("ondisconnect", referenceBean.getOndisconnect()); + Assert.assertEquals("owner", referenceBean.getOwner()); + Assert.assertEquals("layer", referenceBean.getLayer()); + Assert.assertEquals(Integer.valueOf(1), referenceBean.getRetries()); + Assert.assertEquals("random", referenceBean.getLoadbalance()); + Assert.assertEquals(true, referenceBean.isAsync()); + Assert.assertEquals(Integer.valueOf(3), referenceBean.getActives()); + Assert.assertEquals(true, referenceBean.getSent()); + Assert.assertEquals("mock", referenceBean.getMock()); + Assert.assertEquals("validation", referenceBean.getValidation()); + Assert.assertEquals(Integer.valueOf(3), referenceBean.getTimeout()); + Assert.assertEquals("cache", referenceBean.getCache()); + Assert.assertEquals("echo,generic,accesslog", referenceBean.getFilter()); + Assert.assertEquals("deprecated", referenceBean.getListener()); + + // parameters + Map parameters = new HashMap(); + parameters.put("n1", "v1"); + parameters.put("n2", "v2"); + parameters.put("n3", "v3"); + Assert.assertEquals(parameters, referenceBean.getParameters()); + + // Asserts Null fields + Assert.assertNull(referenceBean.getApplication()); + Assert.assertNull(referenceBean.getModule()); + Assert.assertNull(referenceBean.getConsumer()); + Assert.assertNull(referenceBean.getMonitor()); + Assert.assertEquals(Collections.emptyList(), referenceBean.getRegistries()); + } +} diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessorTest.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessorTest.java index c0964418144..6133c48aaba 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessorTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessorTest.java @@ -25,9 +25,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.ImportResource; -import org.springframework.context.annotation.PropertySource; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; @@ -41,17 +38,25 @@ */ @RunWith(SpringRunner.class) @ContextConfiguration( - classes = {ServiceAnnotationBeanPostProcessorTest.TestConfiguration.class}) + classes = { + ServiceAnnotationTestConfiguration.class, + ServiceAnnotationBeanPostProcessorTest.class + }) @TestPropertySource(properties = { - "package1 = com.alibaba.dubbo.config.spring.context.annotation", - "packagesToScan = ${package1}", - "provider.version = 1.2" + "provider.package = com.alibaba.dubbo.config.spring.context.annotation.provider", + "packagesToScan = ${provider.package}", }) public class ServiceAnnotationBeanPostProcessorTest { @Autowired private ConfigurableListableBeanFactory beanFactory; + @Bean + public ServiceAnnotationBeanPostProcessor serviceAnnotationBeanPostProcessor2 + (@Value("${packagesToScan}") String... packagesToScan) { + return new ServiceAnnotationBeanPostProcessor(packagesToScan); + } + @Test public void test() { @@ -61,38 +66,16 @@ public void test() { Map serviceBeansMap = beanFactory.getBeansOfType(ServiceBean.class); - Assert.assertEquals(3, serviceBeansMap.size()); + Assert.assertEquals(2, serviceBeansMap.size()); Map beanPostProcessorsMap = beanFactory.getBeansOfType(ServiceAnnotationBeanPostProcessor.class); - Assert.assertEquals(4, beanPostProcessorsMap.size()); + Assert.assertEquals(2, beanPostProcessorsMap.size()); - Assert.assertTrue(beanPostProcessorsMap.containsKey("doubleServiceAnnotationBeanPostProcessor")); - Assert.assertTrue(beanPostProcessorsMap.containsKey("emptyServiceAnnotationBeanPostProcessor")); Assert.assertTrue(beanPostProcessorsMap.containsKey("serviceAnnotationBeanPostProcessor")); Assert.assertTrue(beanPostProcessorsMap.containsKey("serviceAnnotationBeanPostProcessor2")); } - @ImportResource("META-INF/spring/dubbo-annotation-provider.xml") - @PropertySource("META-INF/default.properties") - @ComponentScan("com.alibaba.dubbo.config.spring.context.annotation.provider") - public static class TestConfiguration { - - @Bean - public ServiceAnnotationBeanPostProcessor serviceAnnotationBeanPostProcessor - (@Value("${packagesToScan}") String... packagesToScan) { - return new ServiceAnnotationBeanPostProcessor(packagesToScan); - } - - @Bean - public ServiceAnnotationBeanPostProcessor serviceAnnotationBeanPostProcessor2 - (@Value("${packagesToScan}") String... packagesToScan) { - return new ServiceAnnotationBeanPostProcessor(packagesToScan); - } - - - } - } diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationTestConfiguration.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationTestConfiguration.java new file mode 100644 index 00000000000..c2f59239041 --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationTestConfiguration.java @@ -0,0 +1,115 @@ +/* + * 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 com.alibaba.dubbo.config.spring.beans.factory.annotation; + +import com.alibaba.dubbo.config.ApplicationConfig; +import com.alibaba.dubbo.config.ProtocolConfig; +import com.alibaba.dubbo.config.RegistryConfig; +import com.alibaba.dubbo.config.annotation.Service; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.PropertySource; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionException; +import org.springframework.transaction.TransactionStatus; + +/** + * {@link Service} Bean + * + * @author Mercy + * @since 2.6.4 + */ +@PropertySource("META-INF/default.properties") +public class ServiceAnnotationTestConfiguration { + + /** + * Current application configuration, to replace XML config: + * + * <dubbo:application name="dubbo-annotation-provider"/> + * + * + * @return {@link ApplicationConfig} Bean + */ + @Bean("dubbo-annotation-provider") + public ApplicationConfig applicationConfig() { + ApplicationConfig applicationConfig = new ApplicationConfig(); + applicationConfig.setName("dubbo-annotation-provider"); + return applicationConfig; + } + + /** + * Current registry center configuration, to replace XML config: + * + * <dubbo:registry id="my-registry" address="N/A"/> + * + * + * @return {@link RegistryConfig} Bean + */ + @Bean("my-registry") + public RegistryConfig registryConfig() { + RegistryConfig registryConfig = new RegistryConfig(); + registryConfig.setAddress("N/A"); + return registryConfig; + } + + /** + * Current protocol configuration, to replace XML config: + * + * <dubbo:protocol name="dubbo" port="12345"/> + * + * + * @return {@link ProtocolConfig} Bean + */ + @Bean("dubbo") + public ProtocolConfig protocolConfig() { + ProtocolConfig protocolConfig = new ProtocolConfig(); + protocolConfig.setName("dubbo"); + protocolConfig.setPort(12345); + return protocolConfig; + } + + @Primary + @Bean + public PlatformTransactionManager platformTransactionManager() { + return new PlatformTransactionManager() { + + @Override + public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { + return null; + } + + @Override + public void commit(TransactionStatus status) throws TransactionException { + + } + + @Override + public void rollback(TransactionStatus status) throws TransactionException { + + } + }; + } + + @Bean + public ServiceAnnotationBeanPostProcessor serviceAnnotationBeanPostProcessor + (@Value("${packagesToScan}") String... packagesToScan) { + return new ServiceAnnotationBeanPostProcessor(packagesToScan); + } + +} \ No newline at end of file diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilderTest.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilderTest.java new file mode 100644 index 00000000000..83363691e45 --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilderTest.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 com.alibaba.dubbo.config.spring.beans.factory.annotation; + +import com.alibaba.dubbo.config.annotation.Reference; +import com.alibaba.dubbo.config.annotation.Service; +import com.alibaba.dubbo.config.spring.api.DemoService; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.mock.env.MockEnvironment; +import org.springframework.util.ReflectionUtils; + +import static com.alibaba.dubbo.config.spring.beans.factory.annotation.ServiceBeanNameBuilderTest.GROUP; +import static com.alibaba.dubbo.config.spring.beans.factory.annotation.ServiceBeanNameBuilderTest.VERSION; + +/** + * {@link ServiceBeanNameBuilder} Test + * + * @author Mercy + * @see ServiceBeanNameBuilder + * @since 2.6.4 + */ +@Service(interfaceClass = DemoService.class, group = GROUP, version = VERSION, + application = "application", module = "module", registry = {"1", "2", "3"}) +public class ServiceBeanNameBuilderTest { + + @Reference(interfaceClass = DemoService.class, group = "DUBBO", version = "1.0.0", + application = "application", module = "module", registry = {"1", "2", "3"}) + static final Class INTERFACE_CLASS = DemoService.class; + + static final String GROUP = "DUBBO"; + + static final String VERSION = "1.0.0"; + + static final String BEAN_NAME = "ServiceBean:com.alibaba.dubbo.config.spring.api.DemoService:1.0.0:DUBBO"; + + private MockEnvironment environment = new MockEnvironment(); + + @Test + public void testRequiredAttributes() { + ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(INTERFACE_CLASS, environment); + Assert.assertEquals("ServiceBean:com.alibaba.dubbo.config.spring.api.DemoService", builder.build()); + } + + @Test + public void testServiceAnnotation() { + Service service = AnnotationUtils.getAnnotation(ServiceBeanNameBuilderTest.class, Service.class); + ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(service, INTERFACE_CLASS, environment); + Assert.assertEquals(BEAN_NAME, + builder.build()); + } + + @Test + public void testReferenceAnnotation() { + Reference reference = AnnotationUtils.getAnnotation(ReflectionUtils.findField(ServiceBeanNameBuilderTest.class, "INTERFACE_CLASS"), Reference.class); + ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(reference, INTERFACE_CLASS, environment); + Assert.assertEquals(BEAN_NAME, + builder.build()); + } + +} diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/context/annotation/EnableDubboTest.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/context/annotation/EnableDubboTest.java index c5018da2522..4f71ce148fd 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/context/annotation/EnableDubboTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/context/annotation/EnableDubboTest.java @@ -23,12 +23,14 @@ import com.alibaba.dubbo.config.spring.context.annotation.provider.DemoServiceImpl; import org.junit.Assert; import org.junit.Test; +import org.junit.runner.RunWith; import org.springframework.aop.support.AopUtils; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Primary; -import org.springframework.context.annotation.PropertySource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.*; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionException; @@ -43,17 +45,21 @@ * * @since 2.5.8 */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = {EnableDubboTest.class}) +@TestPropertySource(locations = "classpath:/META-INF/dubbb-provider.properties", + properties = "demo.service.version = 2.5.7") +@EnableDubbo(scanBasePackages = "com.alibaba.dubbo.config.spring.context.annotation.provider") +@ComponentScan(basePackages = "com.alibaba.dubbo.config.spring.context.annotation.provider") +@EnableTransactionManagement public class EnableDubboTest { + @Autowired + private ApplicationContext providerContext; + @Test public void test() { - AnnotationConfigApplicationContext providerContext = new AnnotationConfigApplicationContext(); - - providerContext.register(TestProviderConfiguration.class); - - providerContext.refresh(); - DemoService demoService = providerContext.getBean(DemoService.class); String value = demoService.sayName("Mercy"); @@ -68,11 +74,7 @@ public void test() { // Test @Transactional is present or not Assert.assertNotNull(findAnnotation(beanClass, Transactional.class)); - AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext(); - - consumerContext.register(TestConsumerConfiguration.class); - - consumerContext.refresh(); + AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext(TestConsumerConfiguration.class); TestConsumerConfiguration consumerConfiguration = consumerContext.getBean(TestConsumerConfiguration.class); @@ -127,10 +129,6 @@ public void test() { // Test multiple binding Assert.assertEquals("N/A", registryConfig.getAddress()); - providerContext.close(); - consumerContext.close(); - - } @EnableDubbo(scanBasePackages = "com.alibaba.dubbo.config.spring.context.annotation.provider") diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/context/annotation/provider/DemoServiceImpl.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/context/annotation/provider/DemoServiceImpl.java index 9961aa27a85..96a6b1af737 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/context/annotation/provider/DemoServiceImpl.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/context/annotation/provider/DemoServiceImpl.java @@ -28,7 +28,7 @@ * @since 2.5.8 */ @com.alibaba.dubbo.config.annotation.Service( - version = "2.5.7", + version = "${demo.service.version}", application = "${demo.service.application}", protocol = "${demo.service.protocol}", registry = "${demo.service.registry}" diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/convert/converter/StringArrayToMapConverterTest.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/convert/converter/StringArrayToMapConverterTest.java deleted file mode 100644 index 51be7c3f2f2..00000000000 --- a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/convert/converter/StringArrayToMapConverterTest.java +++ /dev/null @@ -1,52 +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 com.alibaba.dubbo.config.spring.convert.converter; - -import org.junit.Assert; -import org.junit.Test; - -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * {@link StringArrayToMapConverter} Test - */ -public class StringArrayToMapConverterTest { - - @Test - public void testConvert() { - - StringArrayToMapConverter converter = new StringArrayToMapConverter(); - - Map value = converter.convert(new String[]{"Hello", "World"}); - - Map expected = new LinkedHashMap(); - - expected.put("Hello", "World"); - - Assert.assertEquals(expected, value); - - value = converter.convert(new String[]{}); - - Assert.assertNull(value); - - value = converter.convert(null); - - Assert.assertNull(value); - - } -} diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/convert/converter/StringArrayToStringConverterTest.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/convert/converter/StringArrayToStringConverterTest.java deleted file mode 100644 index 67e82479235..00000000000 --- a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/convert/converter/StringArrayToStringConverterTest.java +++ /dev/null @@ -1,46 +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 com.alibaba.dubbo.config.spring.convert.converter; - -import org.junit.Assert; -import org.junit.Test; - -/** - * {@link StringArrayToStringConverter} Test - */ -public class StringArrayToStringConverterTest { - - @Test - public void testConvert() { - - StringArrayToStringConverter converter = new StringArrayToStringConverter(); - - String value = converter.convert(new String[]{"Hello", "World"}); - - Assert.assertEquals("Hello,World", value); - - value = converter.convert(new String[]{}); - - Assert.assertNull(value); - - value = converter.convert(null); - - Assert.assertNull(value); - - } - -} diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/util/PropertySourcesUtilsTest.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/util/PropertySourcesUtilsTest.java deleted file mode 100644 index 5dd526f7bcf..00000000000 --- a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/util/PropertySourcesUtilsTest.java +++ /dev/null @@ -1,74 +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 com.alibaba.dubbo.config.spring.util; - -import org.junit.Assert; -import org.junit.Test; -import org.springframework.core.env.MapPropertySource; -import org.springframework.core.env.MutablePropertySources; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * {@link PropertySourcesUtils} Test - * - * @see PropertySourcesUtils - * @since 2.5.8 - */ -public class PropertySourcesUtilsTest { - - @Test - public void testGetSubProperties() { - - MutablePropertySources propertySources = new MutablePropertySources(); - - Map source = new HashMap(); - - MapPropertySource propertySource = new MapPropertySource("test", source); - - propertySources.addFirst(propertySource); - - String KEY_PREFIX = "user"; - String KEY_NAME = "name"; - String KEY_AGE = "age"; - Map result = PropertySourcesUtils.getSubProperties(propertySources, KEY_PREFIX); - - Assert.assertEquals(Collections.emptyMap(), result); - - source.put(KEY_PREFIX + "." + KEY_NAME, "Mercy"); - source.put(KEY_PREFIX + "." + KEY_AGE, 31); - - Map expected = new HashMap(); - expected.put(KEY_NAME, "Mercy"); - expected.put(KEY_AGE, "31"); - - result = PropertySourcesUtils.getSubProperties(propertySources, KEY_PREFIX); - Assert.assertEquals(expected, result); - - result = PropertySourcesUtils.getSubProperties(propertySources, ""); - - Assert.assertEquals(Collections.emptyMap(), result); - - result = PropertySourcesUtils.getSubProperties(propertySources, "no-exists"); - - Assert.assertEquals(Collections.emptyMap(), result); - - } - -} diff --git a/pom.xml b/pom.xml index 69aa46ede32..43a160c9b80 100644 --- a/pom.xml +++ b/pom.xml @@ -115,7 +115,7 @@ 2.8.2 3.6.0 3.0.1 - 3.0.0 + 3.0.1 6.1.26 3.0.0 0.8.1