From e2e015348bbc3133b09129b40e2167fddf3f7ba9 Mon Sep 17 00:00:00 2001 From: SuperZ1999 Date: Thu, 28 Sep 2023 16:17:02 +0800 Subject: [PATCH] [ISSUE #315] Support spring 6.x --- .../AbstractAnnotationBeanPostProcessor.java | 642 ++++++++++++++++++ ...otationNacosInjectedBeanPostProcessor.java | 1 - ...NacosValueAnnotationBeanPostProcessor.java | 2 +- ...stractAnnotationBeanPostProcessorTest.java | 47 ++ .../NacosPropertySourceBuilderTest.java | 34 + ...sValueAnnotationBeanPostProcessorTest.java | 59 ++ ...osPropertySourceXmlBeanDefinitionTest.java | 31 + .../properties/config/DataBinderTest.java | 74 ++ .../DelegatingNamingMaintainServiceTest.java | 35 + .../factory/DelegatingNamingServiceTest.java | 38 ++ .../spring/util/ConfigParseUtilsTest.java | 8 + .../nacos/spring/util/NacosUtilsTest.java | 24 + .../nacos/spring/util/ObjectUtilsTest.java | 25 +- .../PropertiesPlaceholderResolverTest.java | 26 +- .../util/config/NacosConfigLoaderTest.java | 34 + pom.xml | 6 +- 16 files changed, 1072 insertions(+), 14 deletions(-) create mode 100644 nacos-spring-context/src/main/java/com/alibaba/nacos/spring/beans/factory/annotation/AbstractAnnotationBeanPostProcessor.java create mode 100644 nacos-spring-context/src/test/java/com/alibaba/nacos/spring/beans/factory/annotation/AbstractAnnotationBeanPostProcessorTest.java create mode 100644 nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/annotation/config/NacosPropertySourceBuilderTest.java create mode 100644 nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/annotation/config/NacosValueAnnotationBeanPostProcessorTest.java create mode 100644 nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/config/xml/NacosPropertySourceXmlBeanDefinitionTest.java create mode 100644 nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/properties/config/DataBinderTest.java create mode 100644 nacos-spring-context/src/test/java/com/alibaba/nacos/spring/factory/DelegatingNamingMaintainServiceTest.java create mode 100644 nacos-spring-context/src/test/java/com/alibaba/nacos/spring/factory/DelegatingNamingServiceTest.java create mode 100644 nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/config/NacosConfigLoaderTest.java diff --git a/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/beans/factory/annotation/AbstractAnnotationBeanPostProcessor.java b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/beans/factory/annotation/AbstractAnnotationBeanPostProcessor.java new file mode 100644 index 00000000..cf09a96b --- /dev/null +++ b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/beans/factory/annotation/AbstractAnnotationBeanPostProcessor.java @@ -0,0 +1,642 @@ +/* + * 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.nacos.spring.beans.factory.annotation; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.PropertyValues; +import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; +import org.springframework.beans.factory.annotation.InjectionMetadata; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; +import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.Ordered; +import org.springframework.core.PriorityOrdered; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.env.Environment; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.StringUtils; + +import java.beans.PropertyDescriptor; +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import static com.alibaba.spring.util.AnnotationUtils.getAnnotationAttributes; +import static java.util.Collections.unmodifiableMap; +import static org.springframework.aop.support.AopUtils.getTargetClass; +import static org.springframework.core.BridgeMethodResolver.findBridgedMethod; +import static org.springframework.core.BridgeMethodResolver.isVisibilityBridgeMethodPair; +import static org.springframework.core.GenericTypeResolver.resolveTypeArgument; + +/** + * Abstract common {@link BeanPostProcessor} implementation for customized annotation that annotated injected-object. + * + * @author Mercy + * @since 1.0.3 + */ +@SuppressWarnings("unchecked") +public abstract class AbstractAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, PriorityOrdered, + BeanFactoryAware, BeanClassLoaderAware, EnvironmentAware, DisposableBean { + + private final static int CACHE_SIZE = Integer.getInteger("", 32); + + private final Log logger = LogFactory.getLog(getClass()); + + private final Class[] annotationTypes; + + private final ConcurrentMap injectionMetadataCache = + new ConcurrentHashMap(CACHE_SIZE); + + private final ConcurrentMap injectedObjectsCache = new ConcurrentHashMap(CACHE_SIZE); + + private ConfigurableListableBeanFactory beanFactory; + + private Environment environment; + + private ClassLoader classLoader; + + /** + * make sure higher priority than {@link AutowiredAnnotationBeanPostProcessor} + */ + private int order = Ordered.LOWEST_PRECEDENCE - 3; + + /** + * whether to turn Class references into Strings (for + * compatibility with {@link org.springframework.core.type.AnnotationMetadata} or to + * preserve them as Class references + * + * @since 1.0.11 + */ + private boolean classValuesAsString = true; + + /** + * whether to turn nested Annotation instances into + * {@link AnnotationAttributes} maps (for compatibility with + * {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as + * Annotation instances + * + * @since 1.0.11 + */ + private boolean nestedAnnotationsAsMap = true; + + /** + * whether ignore default value or not + * + * @since 1.0.11 + */ + private boolean ignoreDefaultValue = true; + + /** + * whether try merged annotation or not + * + * @since 1.0.11 + */ + private boolean tryMergedAnnotation = true; + + /** + * @param annotationTypes the multiple types of {@link Annotation annotations} + */ + public AbstractAnnotationBeanPostProcessor(Class... annotationTypes) { + Assert.notEmpty(annotationTypes, "The argument of annotations' types must not empty"); + this.annotationTypes = annotationTypes; + } + + private static Collection combine(Collection... elements) { + List allElements = new ArrayList(); + for (Collection e : elements) { + allElements.addAll(e); + } + return allElements; + } + + /** + * Annotation type + * + * @return non-null + * @deprecated 2.7.3, uses {@link #getAnnotationTypes()} + */ + @Deprecated + public final Class getAnnotationType() { + return annotationTypes[0]; + } + + protected final Class[] getAnnotationTypes() { + return annotationTypes; + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory, + "AnnotationInjectedBeanPostProcessor requires a ConfigurableListableBeanFactory"); + this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; + } + + @Override + public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeanCreationException { + + InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs); + try { + metadata.inject(bean, beanName, pvs); + } catch (BeanCreationException ex) { + throw ex; + } catch (Throwable ex) { + throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getSimpleName() + + " dependencies is failed", ex); + } + return pvs; + } + + + /** + * Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated fields + * + * @param beanClass The {@link Class} of Bean + * @return non-null {@link List} + */ + private List findFieldAnnotationMetadata(final Class beanClass) { + + final List elements = new LinkedList(); + + ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() { + @Override + public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { + + for (Class annotationType : getAnnotationTypes()) { + + AnnotationAttributes attributes = doGetAnnotationAttributes(field, annotationType); + + if (attributes != null) { + + if (Modifier.isStatic(field.getModifiers())) { + if (logger.isWarnEnabled()) { + logger.warn("@" + annotationType.getName() + " is not supported on static fields: " + field); + } + return; + } + + elements.add(new AnnotatedFieldElement(field, attributes)); + } + } + } + }); + + return elements; + + } + + /** + * Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated methods + * + * @param beanClass The {@link Class} of Bean + * @return non-null {@link List} + */ + private List findAnnotatedMethodMetadata(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; + } + + + for (Class annotationType : getAnnotationTypes()) { + + AnnotationAttributes attributes = doGetAnnotationAttributes(bridgedMethod, annotationType); + + if (attributes != null && method.equals(ClassUtils.getMostSpecificMethod(method, beanClass))) { + if (Modifier.isStatic(method.getModifiers())) { + if (logger.isWarnEnabled()) { + logger.warn("@" + annotationType.getName() + " annotation is not supported on static methods: " + method); + } + return; + } + if (method.getParameterTypes().length == 0) { + if (logger.isWarnEnabled()) { + logger.warn("@" + annotationType.getName() + " annotation should only be used on methods with parameters: " + + method); + } + } + PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, beanClass); + elements.add(new AnnotatedMethodElement(method, pd, attributes)); + } + } + } + }); + + return elements; + } + + /** + * Get {@link AnnotationAttributes} + * + * @param annotatedElement {@link AnnotatedElement the annotated element} + * @param annotationType the {@link Class tyoe} pf {@link Annotation annotation} + * @return if annotatedElement can't be found in annotatedElement, return null + * @since 1.0.11 + */ + protected AnnotationAttributes doGetAnnotationAttributes(AnnotatedElement annotatedElement, + Class annotationType) { + return getAnnotationAttributes(annotatedElement, annotationType, getEnvironment(), + classValuesAsString, nestedAnnotationsAsMap, ignoreDefaultValue, tryMergedAnnotation); + } + + private AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class beanClass) { + Collection fieldElements = findFieldAnnotationMetadata(beanClass); + Collection methodElements = findAnnotatedMethodMetadata(beanClass); + return new AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements); + } + + private InjectionMetadata findInjectionMetadata(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. + AnnotatedInjectionMetadata 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 = buildAnnotatedMetadata(clazz); + this.injectionMetadataCache.put(cacheKey, metadata); + } catch (NoClassDefFoundError err) { + throw new IllegalStateException("Failed to introspect object class [" + clazz.getName() + + "] for annotation metadata: could not find class that it depends on", err); + } + } + } + } + return metadata; + } + + @Override + public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { + if (beanType != null) { + InjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null); + metadata.checkConfigMembers(beanDefinition); + } + } + + @Override + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + @Override + public void destroy() throws Exception { + + for (Object object : injectedObjectsCache.values()) { + if (logger.isInfoEnabled()) { + logger.info(object + " was destroying!"); + } + + if (object instanceof DisposableBean) { + ((DisposableBean) object).destroy(); + } + } + + injectionMetadataCache.clear(); + injectedObjectsCache.clear(); + + if (logger.isInfoEnabled()) { + logger.info(getClass() + " was destroying!"); + } + + } + + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + protected Environment getEnvironment() { + return environment; + } + + protected ClassLoader getClassLoader() { + return classLoader; + } + + protected ConfigurableListableBeanFactory getBeanFactory() { + return beanFactory; + } + + /** + * Gets all injected-objects. + * + * @return non-null {@link Collection} + */ + protected Collection getInjectedObjects() { + return this.injectedObjectsCache.values(); + } + + /** + * Get injected-object from specified {@link AnnotationAttributes annotation attributes} and Bean Class + * + * @param attributes {@link AnnotationAttributes the annotation attributes} + * @param bean Current bean that will be injected + * @param beanName Current bean name that will be injected + * @param injectedType the type of injected-object + * @param injectedElement {@link InjectionMetadata.InjectedElement} + * @return An injected object + * @throws Exception If getting is failed + */ + protected Object getInjectedObject(AnnotationAttributes attributes, Object bean, String beanName, Class injectedType, + InjectionMetadata.InjectedElement injectedElement) throws Exception { + + String cacheKey = buildInjectedObjectCacheKey(attributes, bean, beanName, injectedType, injectedElement); + + Object injectedObject = injectedObjectsCache.get(cacheKey); + + if (injectedObject == null) { + injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement); + // Customized inject-object if necessary + injectedObjectsCache.putIfAbsent(cacheKey, injectedObject); + } + + return injectedObject; + + } + + /** + * Subclass must implement this method to get injected-object. The context objects could help this method if + * necessary : + *
    + *
  • {@link #getBeanFactory() BeanFactory}
  • + *
  • {@link #getClassLoader() ClassLoader}
  • + *
  • {@link #getEnvironment() Environment}
  • + *
+ * + * @param attributes {@link AnnotationAttributes the annotation attributes} + * @param bean Current bean that will be injected + * @param beanName Current bean name that will be injected + * @param injectedType the type of injected-object + * @param injectedElement {@link InjectionMetadata.InjectedElement} + * @return The injected object + * @throws Exception If resolving an injected object is failed. + */ + protected abstract Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class injectedType, + InjectionMetadata.InjectedElement injectedElement) throws Exception; + + /** + * Build a cache key for injected-object. The context objects could help this method if + * necessary : + *
    + *
  • {@link #getBeanFactory() BeanFactory}
  • + *
  • {@link #getClassLoader() ClassLoader}
  • + *
  • {@link #getEnvironment() Environment}
  • + *
+ * + * @param attributes {@link AnnotationAttributes the annotation attributes} + * @param bean Current bean that will be injected + * @param beanName Current bean name that will be injected + * @param injectedType the type of injected-object + * @param injectedElement {@link InjectionMetadata.InjectedElement} + * @return Bean cache key + */ + protected abstract String buildInjectedObjectCacheKey(AnnotationAttributes attributes, Object bean, String beanName, + Class injectedType, + InjectionMetadata.InjectedElement injectedElement); + + /** + * Get {@link Map} in injected field. + * + * @return non-null ready-only {@link Map} + */ + protected Map getInjectedFieldObjectsMap() { + + Map injectedElementBeanMap = + new LinkedHashMap(); + + for (AnnotatedInjectionMetadata metadata : injectionMetadataCache.values()) { + + Collection fieldElements = metadata.getFieldElements(); + + for (AnnotatedFieldElement fieldElement : fieldElements) { + + injectedElementBeanMap.put(fieldElement, fieldElement.bean); + + } + + } + + return unmodifiableMap(injectedElementBeanMap); + + } + + /** + * Get {@link Map} in injected method. + * + * @return non-null {@link Map} + */ + protected Map getInjectedMethodObjectsMap() { + + Map injectedElementBeanMap = + new LinkedHashMap(); + + for (AnnotatedInjectionMetadata metadata : injectionMetadataCache.values()) { + + Collection methodElements = metadata.getMethodElements(); + + for (AnnotatedMethodElement methodElement : methodElements) { + + injectedElementBeanMap.put(methodElement, methodElement.object); + + } + + } + + return unmodifiableMap(injectedElementBeanMap); + + } + + /** + * @param classValuesAsString whether to turn Class references into Strings (for + * compatibility with {@link org.springframework.core.type.AnnotationMetadata} or to + * preserve them as Class references + * @since 1.0.11 + */ + public void setClassValuesAsString(boolean classValuesAsString) { + this.classValuesAsString = classValuesAsString; + } + + /** + * @param nestedAnnotationsAsMap whether to turn nested Annotation instances into + * {@link AnnotationAttributes} maps (for compatibility with + * {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as + * Annotation instances + * @since 1.0.11 + */ + public void setNestedAnnotationsAsMap(boolean nestedAnnotationsAsMap) { + this.nestedAnnotationsAsMap = nestedAnnotationsAsMap; + } + + /** + * @param ignoreDefaultValue whether ignore default value or not + * @since 1.0.11 + */ + public void setIgnoreDefaultValue(boolean ignoreDefaultValue) { + this.ignoreDefaultValue = ignoreDefaultValue; + } + + /** + * @param tryMergedAnnotation whether try merged annotation or not + * @since 1.0.11 + */ + public void setTryMergedAnnotation(boolean tryMergedAnnotation) { + this.tryMergedAnnotation = tryMergedAnnotation; + } + + /** + * {@link Annotation Annotated} {@link InjectionMetadata} implementation + */ + private class AnnotatedInjectionMetadata extends InjectionMetadata { + + private final Collection fieldElements; + + private final Collection methodElements; + + public AnnotatedInjectionMetadata(Class targetClass, Collection fieldElements, + Collection methodElements) { + super(targetClass, combine(fieldElements, methodElements)); + this.fieldElements = fieldElements; + this.methodElements = methodElements; + } + + public Collection getFieldElements() { + return fieldElements; + } + + public Collection getMethodElements() { + return methodElements; + } + } + + /** + * {@link Annotation Annotated} {@link Method} {@link InjectionMetadata.InjectedElement} + */ + private class AnnotatedMethodElement extends InjectionMetadata.InjectedElement { + + private final Method method; + + private final AnnotationAttributes attributes; + + private volatile Object object; + + protected AnnotatedMethodElement(Method method, PropertyDescriptor pd, AnnotationAttributes attributes) { + super(method, pd); + this.method = method; + this.attributes = attributes; + } + + @Override + protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable { + + Class injectedType = pd.getPropertyType(); + + Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this); + + ReflectionUtils.makeAccessible(method); + + method.invoke(bean, injectedObject); + + } + + } + + /** + * {@link Annotation Annotated} {@link Field} {@link InjectionMetadata.InjectedElement} + */ + public class AnnotatedFieldElement extends InjectionMetadata.InjectedElement { + + private final Field field; + + private final AnnotationAttributes attributes; + + private volatile Object bean; + + protected AnnotatedFieldElement(Field field, AnnotationAttributes attributes) { + super(field, null); + this.field = field; + this.attributes = attributes; + } + + @Override + protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable { + + Class injectedType = resolveInjectedType(bean, field); + + Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this); + + ReflectionUtils.makeAccessible(field); + + field.set(bean, injectedObject); + + } + + private Class resolveInjectedType(Object bean, Field field) { + Type genericType = field.getGenericType(); + if (genericType instanceof Class) { // Just a normal Class + return field.getType(); + } else { // GenericType + return resolveTypeArgument(getTargetClass(bean), field.getDeclaringClass()); + } + } + } +} \ No newline at end of file diff --git a/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/beans/factory/annotation/AnnotationNacosInjectedBeanPostProcessor.java b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/beans/factory/annotation/AnnotationNacosInjectedBeanPostProcessor.java index d889cd1c..5f1326b8 100644 --- a/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/beans/factory/annotation/AnnotationNacosInjectedBeanPostProcessor.java +++ b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/beans/factory/annotation/AnnotationNacosInjectedBeanPostProcessor.java @@ -34,7 +34,6 @@ import com.alibaba.nacos.api.annotation.NacosInjected; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.naming.NamingService; -import com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor; import com.alibaba.spring.beans.factory.annotation.AnnotationInjectedBeanPostProcessor; import com.alibaba.spring.util.BeanUtils; diff --git a/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/config/NacosValueAnnotationBeanPostProcessor.java b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/config/NacosValueAnnotationBeanPostProcessor.java index 8a701868..20bca764 100644 --- a/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/config/NacosValueAnnotationBeanPostProcessor.java +++ b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/config/NacosValueAnnotationBeanPostProcessor.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Map; +import com.alibaba.nacos.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; @@ -48,7 +49,6 @@ import com.alibaba.nacos.api.config.annotation.NacosValue; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.spring.context.event.config.NacosConfigReceivedEvent; -import com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor; /** * Injected {@link NacosValue} diff --git a/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/beans/factory/annotation/AbstractAnnotationBeanPostProcessorTest.java b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/beans/factory/annotation/AbstractAnnotationBeanPostProcessorTest.java new file mode 100644 index 00000000..50e84297 --- /dev/null +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/beans/factory/annotation/AbstractAnnotationBeanPostProcessorTest.java @@ -0,0 +1,47 @@ +/* + * + * * 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.nacos.spring.beans.factory.annotation; + +import org.junit.Assert; +import org.junit.Test; + +import java.beans.IntrospectionException; +import java.beans.PropertyDescriptor; + +public class AbstractAnnotationBeanPostProcessorTest { + @Test + public void testPropertyDescriptor() throws IntrospectionException { + PropertyDescriptor pd = new PropertyDescriptor("name", Student.class); + Class type = pd.getPropertyType(); + Assert.assertEquals(type, String.class); + } + + static class Student { + String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +} \ No newline at end of file diff --git a/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/annotation/config/NacosPropertySourceBuilderTest.java b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/annotation/config/NacosPropertySourceBuilderTest.java new file mode 100644 index 00000000..11e2835c --- /dev/null +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/annotation/config/NacosPropertySourceBuilderTest.java @@ -0,0 +1,34 @@ +/* + * + * * 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.nacos.spring.context.annotation.config; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.core.env.MapPropertySource; + +import java.util.HashMap; + +public class NacosPropertySourceBuilderTest { + @Test + public void test() { + MapPropertySource propertySource = new MapPropertySource("test", new HashMap<>()); + Assert.assertNotNull(propertySource); + } +} \ No newline at end of file diff --git a/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/annotation/config/NacosValueAnnotationBeanPostProcessorTest.java b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/annotation/config/NacosValueAnnotationBeanPostProcessorTest.java new file mode 100644 index 00000000..87de3c90 --- /dev/null +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/annotation/config/NacosValueAnnotationBeanPostProcessorTest.java @@ -0,0 +1,59 @@ +/* + * + * * 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.nacos.spring.context.annotation.config; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.TypeConverter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.core.MethodParameter; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.lang.reflect.Method; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = NacosValueAnnotationBeanPostProcessorTest.class) +public class NacosValueAnnotationBeanPostProcessorTest { + + @Autowired + private ConfigurableListableBeanFactory beanFactory; + + @Bean + public NacosValueAnnotationBeanPostProcessor nacosValueAnnotationBeanPostProcessor() { + return new NacosValueAnnotationBeanPostProcessor(); + } + + @Test + public void testConvertIfNecessary() throws NoSuchMethodException { + TypeConverter converter = beanFactory.getTypeConverter(); + Method method = NacosValueAnnotationBeanPostProcessorTest.class.getMethod("testMethodParameter", Integer.class); + Integer integer = converter.convertIfNecessary("12", Integer.class, new MethodParameter(method, 0)); + System.out.println(integer); + Assert.assertEquals(integer, Integer.valueOf(12)); + } + + public void testMethodParameter(Integer i) { + System.out.println(i); + } +} \ No newline at end of file diff --git a/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/config/xml/NacosPropertySourceXmlBeanDefinitionTest.java b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/config/xml/NacosPropertySourceXmlBeanDefinitionTest.java new file mode 100644 index 00000000..a0b6f45d --- /dev/null +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/config/xml/NacosPropertySourceXmlBeanDefinitionTest.java @@ -0,0 +1,31 @@ +/* + * + * * 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.nacos.spring.context.config.xml; + +import org.junit.Assert; +import org.junit.Test; + +public class NacosPropertySourceXmlBeanDefinitionTest { + @Test + public void test() { + NacosPropertySourceXmlBeanDefinition beanDefinition = new NacosPropertySourceXmlBeanDefinition(); + Assert.assertNotNull(beanDefinition); + } +} \ No newline at end of file diff --git a/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/properties/config/DataBinderTest.java b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/properties/config/DataBinderTest.java new file mode 100644 index 00000000..ac08551a --- /dev/null +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/properties/config/DataBinderTest.java @@ -0,0 +1,74 @@ +/* + * + * * Licensed to the Apache Software Foundation (ASF) under one or more + * * contributor license agreements. See the NOTICE file distributed with + * * this work for additional information regarding copyright ownership. + * * The ASF licenses this file to You under the Apache License, Version 2.0 + * * (the "License"); you may not use this file except in compliance with + * * the License. You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package com.alibaba.nacos.spring.context.properties.config; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.beans.MutablePropertyValues; +import org.springframework.validation.BindException; +import org.springframework.validation.DataBinder; + +public class DataBinderTest { + @Test + public void test() throws BindException { + People people = new People(); + MutablePropertyValues propertyValues = new MutablePropertyValues(); + propertyValues.add("name", "SuperZ1999"); + propertyValues.add("age", 24); + DataBinder dataBinder = new DataBinder(people); + dataBinder.setAutoGrowNestedPaths(false); + dataBinder.setIgnoreInvalidFields(false); + dataBinder.setIgnoreUnknownFields(true); + dataBinder.bind(propertyValues); + dataBinder.close(); + + Assert.assertEquals("SuperZ1999", people.getName()); + Assert.assertEquals(24, people.getAge()); + } + + static class People { + private String name; + private int age; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + @Override + public String toString() { + return "People{" + + "name='" + name + '\'' + + ", age=" + age + + '}'; + } + } +} \ No newline at end of file diff --git a/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/factory/DelegatingNamingMaintainServiceTest.java b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/factory/DelegatingNamingMaintainServiceTest.java new file mode 100644 index 00000000..a16aac84 --- /dev/null +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/factory/DelegatingNamingMaintainServiceTest.java @@ -0,0 +1,35 @@ +/* + * + * * 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.nacos.spring.factory; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.pojo.Service; +import com.alibaba.nacos.client.naming.NacosNamingMaintainService; +import org.junit.Assert; +import org.junit.Test; + +public class DelegatingNamingMaintainServiceTest { + @Test + public void testNamingMaintainService() throws NacosException { + NacosNamingMaintainService namingMaintainService = new NacosNamingMaintainService("127.0.0.1:8848"); + Service service = namingMaintainService.queryService("example"); + Assert.assertNotNull(service); + } +} \ No newline at end of file diff --git a/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/factory/DelegatingNamingServiceTest.java b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/factory/DelegatingNamingServiceTest.java new file mode 100644 index 00000000..28c211a8 --- /dev/null +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/factory/DelegatingNamingServiceTest.java @@ -0,0 +1,38 @@ +/* + * + * * 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.nacos.spring.factory; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.client.naming.NacosNamingService; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +public class DelegatingNamingServiceTest { + @Test + public void testNamingService() throws NacosException { + NacosNamingService nacosNamingService = new NacosNamingService("127.0.0.1:8848"); + List instances = nacosNamingService.getAllInstances("example"); + System.out.println(instances); + Assert.assertNotNull(instances); + } +} \ No newline at end of file diff --git a/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/ConfigParseUtilsTest.java b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/ConfigParseUtilsTest.java index b16f08d1..fcd84221 100644 --- a/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/ConfigParseUtilsTest.java +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/ConfigParseUtilsTest.java @@ -26,6 +26,7 @@ import com.alibaba.nacos.spring.util.parse.DefaultPropertiesConfigParse; import com.alibaba.nacos.spring.util.parse.DefaultYamlConfigParse; +import org.springframework.core.io.ByteArrayResource; /** * @author liaochuntao @@ -83,6 +84,13 @@ public void testPropertiesParser() { System.out.println(p); } + @Test + public void testResource() { + ByteArrayResource resource = new ByteArrayResource(new byte[]{1, 1, 1, 1, 1, 1}); + String description = resource.getDescription(); + Assert.assertNotNull(description); + } + @Test public void testYamlParser() { final String yaml = "students:\n" + " - {name: lct-1,num: 12}\n" diff --git a/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/NacosUtilsTest.java b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/NacosUtilsTest.java index 6efc2726..9a4b95dc 100644 --- a/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/NacosUtilsTest.java +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/NacosUtilsTest.java @@ -20,6 +20,12 @@ import org.junit.Assert; import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.MutablePropertyValues; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.ReflectionUtils; import com.alibaba.nacos.api.annotation.NacosInjected; @@ -31,6 +37,8 @@ * @author Mercy * @since 0.1.0 */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = NacosUtilsTest.class) public class NacosUtilsTest { @NacosInjected @@ -39,6 +47,9 @@ public class NacosUtilsTest { @NacosInjected(properties = @NacosProperties(serverAddr = "test")) private Object object2 = new Object(); + @Autowired + private ConfigurableBeanFactory beanFactory; + @Test public void testIsDefault() { @@ -57,4 +68,17 @@ private void testIsDefault(String fieldName, boolean expectedValue) { Assert.assertEquals(expectedValue, NacosUtils.isDefault(nacosProperties)); } + + @Test + public void testReadFromBeanFactory() { + Object o = NacosUtils.readFromBeanFactory("test", beanFactory); + Assert.assertEquals("test", o); + } + + @Test + public void testMutablePropertyValues() { + MutablePropertyValues propertyValues = new MutablePropertyValues(); + propertyValues.add("testKey", "testValue"); + Assert.assertEquals("testValue", propertyValues.get("testKey")); + } } diff --git a/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/ObjectUtilsTest.java b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/ObjectUtilsTest.java index ed882403..336340e9 100644 --- a/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/ObjectUtilsTest.java +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/ObjectUtilsTest.java @@ -1,37 +1,56 @@ package com.alibaba.nacos.spring.util; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import com.alibaba.nacos.api.config.annotation.NacosIgnore; import org.junit.Assert; import org.junit.Test; public class ObjectUtilsTest { - @Test public void test_map_clean() { TestObj obj = new TestObj(); Map map = new HashMap(); map.put("key", "value"); obj.setMap(map); + List list = new ArrayList<>(); + list.add("element"); + obj.setList(list); + Assert.assertNotNull(map.get("key")); Assert.assertNotNull(obj.map); + Assert.assertNotNull(obj.list); ObjectUtils.cleanMapOrCollectionField(obj); map = obj.map; Assert.assertNull(map); + + Assert.assertNull(obj.map); + Assert.assertNotNull(obj.list); } private static class TestObj { private Map map; + @NacosIgnore + private List list; + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } + public Map getMap() { return map; } - public void setMap(Map map) { this.map = map; } } - } diff --git a/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/PropertiesPlaceholderResolverTest.java b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/PropertiesPlaceholderResolverTest.java index e84a1dbe..6cbd4490 100644 --- a/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/PropertiesPlaceholderResolverTest.java +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/PropertiesPlaceholderResolverTest.java @@ -16,14 +16,15 @@ */ package com.alibaba.nacos.spring.util; +import java.lang.annotation.Annotation; import java.util.HashMap; import java.util.Map; import java.util.Properties; +import com.alibaba.nacos.api.annotation.NacosProperties; import org.junit.Assert; import org.junit.Test; import org.springframework.mock.env.MockEnvironment; - /** * {@link PropertiesPlaceholderResolver} Test * @@ -31,26 +32,39 @@ * @since 0.1.0 */ public class PropertiesPlaceholderResolverTest { - @Test public void testResolve() { - MockEnvironment environment = new MockEnvironment(); - PropertiesPlaceholderResolver resolver = new PropertiesPlaceholderResolver( environment); + testMapResolve(environment, resolver); + testAnnotationResolve(environment, resolver); + } + + private void testMapResolve(MockEnvironment environment, PropertiesPlaceholderResolver resolver) { Map properties = new HashMap(); properties.put("my.name", "${my.name}"); properties.put("my.age", 18); - environment.setProperty("my.name", "mercyblitz"); environment.setProperty("my.age", "18"); - Properties resolvedProperties = resolver.resolve(properties); Assert.assertEquals(resolvedProperties.get("my.name"), "mercyblitz"); Assert.assertNull(resolvedProperties.get("my.age")); + } + + private void testAnnotationResolve(MockEnvironment environment, PropertiesPlaceholderResolver resolver) { + environment.setProperty("nacos.username:", "SuperZ1999"); + + Annotation[] annotations = TestAnnotation.class.getAnnotations(); + Properties resolvedProperties = resolver.resolve(annotations[0]); + + Assert.assertEquals(resolvedProperties.get("username"), "SuperZ1999"); + } + + @NacosProperties + @interface TestAnnotation { } diff --git a/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/config/NacosConfigLoaderTest.java b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/config/NacosConfigLoaderTest.java new file mode 100644 index 00000000..5e785c45 --- /dev/null +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/config/NacosConfigLoaderTest.java @@ -0,0 +1,34 @@ +/* + * + * * 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.nacos.spring.util.config; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.mock.env.MockEnvironment; + +public class NacosConfigLoaderTest { + @Test + public void testNacosConfigLoader() { + MockEnvironment environment = new MockEnvironment(); + NacosConfigLoader nacosConfigLoader = new NacosConfigLoader(environment); + Integer convert = environment.getConversionService().convert("12", Integer.class); + Assert.assertEquals(Integer.valueOf(12), convert); + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 501b7ceb..3ea86d2a 100644 --- a/pom.xml +++ b/pom.xml @@ -75,13 +75,13 @@ - 2.0.0 + 2.1.0 -Dfile.encoding=UTF-8 UTF-8 UTF-8 - 1.6 - 1.6 + 1.8 + 1.8 UTF-8 jacoco