From d4543ecb97f7c20329487ea4509f17469e5e496a Mon Sep 17 00:00:00 2001 From: SuperZ1999 <34301918+SuperZ1999@users.noreply.github.com> Date: Sat, 30 Sep 2023 20:09:02 +0800 Subject: [PATCH] [ISSUE #321] Support spring 6.x and native-image (#322) * [ISSUE #315] Support spring 6.x * Support native-image --- nacos-spring-context-aot/pom.xml | 38 ++ ...nnotationBeanRegistrationAotProcessor.java | 76 +++ .../nacos-spring-context/resource-config.json | 15 + .../resources/META-INF/spring/aot.factories | 2 + .../AbstractAnnotationBeanPostProcessor.java | 642 ++++++++++++++++++ ...otationNacosInjectedBeanPostProcessor.java | 1 - .../context/annotation/EnableNacos.java | 2 +- .../annotation/EnableNacosAotProcessor.java | 96 +++ .../annotation/config/EnableNacosConfig.java | 2 +- .../config/EnableNacosConfigAotProcessor.java | 95 +++ ...NacosValueAnnotationBeanPostProcessor.java | 2 +- .../discovery/EnableNacosDiscovery.java | 2 +- .../EnableNacosDiscoveryAotProcessor.java | 92 +++ .../env/NacosPropertySourcePostProcessor.java | 51 +- .../nacos/spring/util/aot/AotDetector.java | 44 ++ .../nacos/spring/util/aot/NativeDetector.java | 33 + ...stractAnnotationBeanPostProcessorTest.java | 52 ++ .../NacosPropertySourceBuilderTest.java | 39 ++ ...sValueAnnotationBeanPostProcessorTest.java | 64 ++ ...osPropertySourceXmlBeanDefinitionTest.java | 36 + .../properties/config/DataBinderTest.java | 79 +++ .../DelegatingNamingMaintainServiceTest.java | 40 ++ .../factory/DelegatingNamingServiceTest.java | 43 ++ .../spring/util/ConfigParseUtilsTest.java | 8 + .../nacos/spring/util/NacosUtilsTest.java | 24 + .../nacos/spring/util/ObjectUtilsTest.java | 25 +- .../PropertiesPlaceholderResolverTest.java | 26 +- .../util/config/NacosConfigLoaderTest.java | 39 ++ pom.xml | 8 +- 29 files changed, 1649 insertions(+), 27 deletions(-) create mode 100644 nacos-spring-context-aot/pom.xml create mode 100644 nacos-spring-context-aot/src/main/java/com/alibaba/nacos/spring/aot/NacosAnnotationBeanRegistrationAotProcessor.java create mode 100644 nacos-spring-context-aot/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-spring-context/resource-config.json create mode 100644 nacos-spring-context-aot/src/main/resources/META-INF/spring/aot.factories 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/main/java/com/alibaba/nacos/spring/context/annotation/EnableNacosAotProcessor.java create mode 100644 nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/config/EnableNacosConfigAotProcessor.java create mode 100644 nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/discovery/EnableNacosDiscoveryAotProcessor.java create mode 100644 nacos-spring-context/src/main/java/com/alibaba/nacos/spring/util/aot/AotDetector.java create mode 100644 nacos-spring-context/src/main/java/com/alibaba/nacos/spring/util/aot/NativeDetector.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-aot/pom.xml b/nacos-spring-context-aot/pom.xml new file mode 100644 index 00000000..ff733abd --- /dev/null +++ b/nacos-spring-context-aot/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + com.alibaba.nacos + nacos-spring-parent + ${revision} + ../pom.xml + + + com.alibaba.nacos + nacos-spring-context-aot + ${revision} + Alibaba Nacos :: Spring :: Context :: Aot + jar + + + + org.springframework + spring-beans + ${spring6.framework.version} + + + org.springframework + spring-core + ${spring6.framework.version} + + + com.alibaba.nacos + nacos-spring-context + ${revision} + true + + + + \ No newline at end of file diff --git a/nacos-spring-context-aot/src/main/java/com/alibaba/nacos/spring/aot/NacosAnnotationBeanRegistrationAotProcessor.java b/nacos-spring-context-aot/src/main/java/com/alibaba/nacos/spring/aot/NacosAnnotationBeanRegistrationAotProcessor.java new file mode 100644 index 00000000..fec26582 --- /dev/null +++ b/nacos-spring-context-aot/src/main/java/com/alibaba/nacos/spring/aot/NacosAnnotationBeanRegistrationAotProcessor.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.nacos.spring.aot; + +import com.alibaba.nacos.api.annotation.NacosInjected; +import com.alibaba.nacos.api.config.annotation.NacosValue; +import org.springframework.aot.generate.GenerationContext; +import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; +import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor; +import org.springframework.beans.factory.aot.BeanRegistrationCode; +import org.springframework.beans.factory.support.RegisteredBean; +import org.springframework.util.ReflectionUtils; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +/** + * {@link NacosInjected} and {@link NacosValue} AotProcessor + * The fields annotated with {@link NacosInjected} or {@link NacosValue} must be added to the reflect-config.json + * @author SuperZ1999 + */ +public class NacosAnnotationBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor { + @Override + public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) { + Class beanClass = registeredBean.getBeanClass(); + List fields = new ArrayList<>(); + ReflectionUtils.doWithFields(beanClass, field -> { + NacosInjected injectedAnnotation = field.getDeclaredAnnotation(NacosInjected.class); + NacosValue nacosValueAnnotation = field.getDeclaredAnnotation(NacosValue.class); + if (injectedAnnotation != null || nacosValueAnnotation != null) { + fields.add(field); + } + }); + if (fields.isEmpty()) { + return null; + } + return new AotContribution(fields); + } + + private static class AotContribution implements BeanRegistrationAotContribution { + private final List fields; + + public AotContribution() { + this.fields = new ArrayList<>(); + } + + public AotContribution(List fields) { + this.fields = fields; + } + + @Override + public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) { + for (Field field : fields) { + generationContext.getRuntimeHints().reflection().registerField(field); + } + } + } +} \ No newline at end of file diff --git a/nacos-spring-context-aot/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-spring-context/resource-config.json b/nacos-spring-context-aot/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-spring-context/resource-config.json new file mode 100644 index 00000000..38e68942 --- /dev/null +++ b/nacos-spring-context-aot/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-spring-context/resource-config.json @@ -0,0 +1,15 @@ +{ + "resources": { + "includes": [ + { + "pattern": "\\QMETA-INF/spring.handlers\\E" + }, + { + "pattern": "\\QMETA-INF/spring.schemas\\E" + }, + { + "pattern": "\\QMETA-INF/schemas/nacos.xsd\\E" + } + ] + } +} \ No newline at end of file diff --git a/nacos-spring-context-aot/src/main/resources/META-INF/spring/aot.factories b/nacos-spring-context-aot/src/main/resources/META-INF/spring/aot.factories new file mode 100644 index 00000000..3ae26c63 --- /dev/null +++ b/nacos-spring-context-aot/src/main/resources/META-INF/spring/aot.factories @@ -0,0 +1,2 @@ +org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\ +com.alibaba.nacos.spring.aot.NacosAnnotationBeanRegistrationAotProcessor \ No newline at end of file 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/EnableNacos.java b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/EnableNacos.java index ff23b22a..501438d3 100644 --- a/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/EnableNacos.java +++ b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/EnableNacos.java @@ -39,7 +39,7 @@ @Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented -@Import(NacosBeanDefinitionRegistrar.class) +@Import({NacosBeanDefinitionRegistrar.class, EnableNacosAotProcessor.class}) public @interface EnableNacos { /** diff --git a/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/EnableNacosAotProcessor.java b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/EnableNacosAotProcessor.java new file mode 100644 index 00000000..cee19fd3 --- /dev/null +++ b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/EnableNacosAotProcessor.java @@ -0,0 +1,96 @@ +/* + * + * * 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; + +import com.alibaba.nacos.spring.util.aot.AotDetector; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.env.Environment; +import org.springframework.core.type.AnnotationMetadata; + +import java.util.Collection; +import java.util.Map; + +import static com.alibaba.nacos.spring.util.NacosBeanUtils.*; + +/** + * {@link EnableNacos} AotProcessor + * Except for the operation of registering BeanDefinition, all other operations in {@link NacosBeanDefinitionRegistrar} must be done here + * because spring will not call {@link NacosBeanDefinitionRegistrar#registerBeanDefinitions} in AOT. + * @author SuperZ1999 + */ +public class EnableNacosAotProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, BeanFactoryAware { + private Environment environment; + + private BeanFactory beanFactory; + + @Override + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { + if (!AotDetector.useGeneratedArtifacts()) { + return; + } + DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) this.beanFactory; + Map beansWithAnnotation = beanFactory.getBeansWithAnnotation(EnableNacos.class); + Object[] beans = beansWithAnnotation.values().toArray(); + if (beans.length != 0) { + // only handle the first one + Class aClass = beans[0].getClass(); + if (aClass.getAnnotation(EnableNacos.class) == null) { + // cglib proxy object + aClass = aClass.getSuperclass(); + } + AnnotationMetadata annotationMetadata = AnnotationMetadata.introspect(aClass); + AnnotationAttributes attributes = AnnotationAttributes + .fromMap(annotationMetadata + .getAnnotationAttributes(EnableNacos.class.getName())); + + // Register Global Nacos Properties Bean + registerGlobalNacosProperties(attributes, registry, environment, + GLOBAL_NACOS_PROPERTIES_BEAN_NAME); + } + + registerNacosConfigListenerExecutor(registry, environment); + // Invoke NacosPropertySourcePostProcessor immediately + // in order to enhance the precedence of @NacosPropertySource process + invokeNacosPropertySourcePostProcessor(this.beanFactory); + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + + } + + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } +} \ No newline at end of file diff --git a/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/config/EnableNacosConfig.java b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/config/EnableNacosConfig.java index ea2c59c0..f6ca8169 100644 --- a/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/config/EnableNacosConfig.java +++ b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/config/EnableNacosConfig.java @@ -54,7 +54,7 @@ @Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented -@Import(NacosConfigBeanDefinitionRegistrar.class) +@Import({NacosConfigBeanDefinitionRegistrar.class, EnableNacosConfigAotProcessor.class}) public @interface EnableNacosConfig { /** diff --git a/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/config/EnableNacosConfigAotProcessor.java b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/config/EnableNacosConfigAotProcessor.java new file mode 100644 index 00000000..6a75e0e8 --- /dev/null +++ b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/config/EnableNacosConfigAotProcessor.java @@ -0,0 +1,95 @@ +/* + * + * * 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 com.alibaba.nacos.spring.util.aot.AotDetector; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.env.Environment; +import org.springframework.core.type.AnnotationMetadata; + +import java.util.Map; + +import static com.alibaba.nacos.spring.util.NacosBeanUtils.*; + +/** + * {@link EnableNacosConfig} AotProcessor + * Except for the operation of registering BeanDefinition, all other operations in {@link NacosConfigBeanDefinitionRegistrar} must be done here + * because spring will not call {@link NacosConfigBeanDefinitionRegistrar#registerBeanDefinitions} in AOT. + * @author SuperZ1999 + */ +public class EnableNacosConfigAotProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, BeanFactoryAware { + private Environment environment; + + private BeanFactory beanFactory; + + @Override + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { + if (!AotDetector.useGeneratedArtifacts()) { + return; + } + DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) this.beanFactory; + Map beansWithAnnotation = beanFactory.getBeansWithAnnotation(EnableNacosConfig.class); + Object[] beans = beansWithAnnotation.values().toArray(); + if (beans.length != 0) { + // only handle the first one + Class aClass = beans[0].getClass(); + if (aClass.getAnnotation(EnableNacosConfig.class) == null) { + // cglib proxy object + aClass = aClass.getSuperclass(); + } + AnnotationMetadata annotationMetadata = AnnotationMetadata.introspect(aClass); + AnnotationAttributes attributes = AnnotationAttributes + .fromMap(annotationMetadata + .getAnnotationAttributes(EnableNacosConfig.class.getName())); + + // Register Global Nacos Properties Bean + registerGlobalNacosProperties(attributes, registry, environment, + CONFIG_GLOBAL_NACOS_PROPERTIES_BEAN_NAME); + } + + registerNacosConfigListenerExecutor(registry, environment); + // Invoke NacosPropertySourcePostProcessor immediately + // in order to enhance the precedence of @NacosPropertySource process + invokeNacosPropertySourcePostProcessor(this.beanFactory); + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + + } + + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } +} \ No newline at end of file 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/main/java/com/alibaba/nacos/spring/context/annotation/discovery/EnableNacosDiscovery.java b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/discovery/EnableNacosDiscovery.java index e6b2f0ce..92379084 100644 --- a/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/discovery/EnableNacosDiscovery.java +++ b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/discovery/EnableNacosDiscovery.java @@ -49,7 +49,7 @@ @Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented -@Import(NacosDiscoveryBeanDefinitionRegistrar.class) +@Import({NacosDiscoveryBeanDefinitionRegistrar.class, EnableNacosDiscoveryAotProcessor.class}) public @interface EnableNacosDiscovery { /** * The prefix of property name of Nacos discovery diff --git a/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/discovery/EnableNacosDiscoveryAotProcessor.java b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/discovery/EnableNacosDiscoveryAotProcessor.java new file mode 100644 index 00000000..7dbce164 --- /dev/null +++ b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/context/annotation/discovery/EnableNacosDiscoveryAotProcessor.java @@ -0,0 +1,92 @@ +/* + * + * * 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.discovery; + +import com.alibaba.nacos.spring.util.aot.AotDetector; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.env.Environment; +import org.springframework.core.type.AnnotationMetadata; + +import java.util.Map; + +import static com.alibaba.nacos.spring.util.NacosBeanUtils.*; + +/** + * {@link EnableNacosDiscovery} AotProcessor + * Except for the operation of registering BeanDefinition, all other operations in {@link NacosDiscoveryBeanDefinitionRegistrar} must be done here + * because spring will not call {@link NacosDiscoveryBeanDefinitionRegistrar#registerBeanDefinitions} in AOT. + * @author SuperZ1999 + */ +public class EnableNacosDiscoveryAotProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, BeanFactoryAware { + private Environment environment; + + private BeanFactory beanFactory; + + @Override + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { + if (!AotDetector.useGeneratedArtifacts()) { + return; + } + DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) this.beanFactory; + Map beansWithAnnotation = beanFactory.getBeansWithAnnotation(EnableNacosDiscovery.class); + Object[] beans = beansWithAnnotation.values().toArray(); + if (beans.length != 0) { + // only handle the first one + Class aClass = beans[0].getClass(); + if (aClass.getAnnotation(EnableNacosDiscovery.class) == null) { + // cglib proxy object + aClass = aClass.getSuperclass(); + } + AnnotationMetadata annotationMetadata = AnnotationMetadata.introspect(aClass); + AnnotationAttributes attributes = AnnotationAttributes + .fromMap(annotationMetadata + .getAnnotationAttributes(EnableNacosDiscovery.class.getName())); + + // Register Global Nacos Properties Bean + registerGlobalNacosProperties(attributes, registry, environment, + DISCOVERY_GLOBAL_NACOS_PROPERTIES_BEAN_NAME); + registerGlobalNacosProperties(attributes, registry, environment, + MAINTAIN_GLOBAL_NACOS_PROPERTIES_BEAN_NAME); + } + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + + } + + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } +} \ No newline at end of file diff --git a/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/core/env/NacosPropertySourcePostProcessor.java b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/core/env/NacosPropertySourcePostProcessor.java index e57f6ffe..0856be79 100644 --- a/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/core/env/NacosPropertySourcePostProcessor.java +++ b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/core/env/NacosPropertySourcePostProcessor.java @@ -21,16 +21,12 @@ import static com.alibaba.nacos.spring.util.NacosUtils.DEFAULT_STRING_ATTRIBUTE_VALUE; import static org.springframework.util.ObjectUtils.nullSafeEquals; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Properties; -import java.util.Set; +import java.util.*; +import com.alibaba.nacos.spring.util.aot.AotDetector; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; @@ -162,6 +158,14 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) NacosPropertySourcePostProcessor.beanFactory = beanFactory; this.configServiceBeanBuilder = getConfigServiceBeanBuilder(beanFactory); + if (AotDetector.useGeneratedArtifacts()) { + Map beansWithAnnotation = beanFactory.getBeansWithAnnotation( + com.alibaba.nacos.spring.context.annotation.config.NacosPropertySource.class); + for (Map.Entry entry : beansWithAnnotation.entrySet()) { + processPropertySourceForAot(entry.getKey(), entry.getValue()); + } + } + String[] beanNames = beanFactory.getBeanDefinitionNames(); for (String beanName : beanNames) { @@ -171,18 +175,45 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) } private void processPropertySource(String beanName, - ConfigurableListableBeanFactory beanFactory) { - + ConfigurableListableBeanFactory beanFactory) { if (processedBeanNames.contains(beanName)) { return; } BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); + doProcessPropertySource(beanName, beanDefinition); + + processedBeanNames.add(beanName); + } + + private void processPropertySourceForAot(String beanName, Object bean) { + if (processedBeanNames.contains(beanName)) { + return; + } + + BeanDefinition beanDefinition = null; + Class aClass = bean.getClass(); + com.alibaba.nacos.spring.context.annotation.config.NacosPropertySource[] annotations + = aClass.getSuperclass().getAnnotationsByType( + com.alibaba.nacos.spring.context.annotation.config.NacosPropertySource.class); + if (annotations.length != 0) { + beanDefinition = new AnnotatedGenericBeanDefinition(aClass.getSuperclass()); + } + annotations = aClass.getAnnotationsByType(com.alibaba.nacos.spring.context.annotation.config.NacosPropertySource.class); + if (annotations.length != 0) { + beanDefinition = new AnnotatedGenericBeanDefinition(aClass); + } + + doProcessPropertySource(beanName, beanDefinition); + + processedBeanNames.add(beanName); + } + + private void doProcessPropertySource(String beanName, BeanDefinition beanDefinition) { // Build multiple instance if possible List nacosPropertySources = buildNacosPropertySources( beanName, beanDefinition); - // Add Orderly for (NacosPropertySource nacosPropertySource : nacosPropertySources) { addNacosPropertySource(nacosPropertySource); diff --git a/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/util/aot/AotDetector.java b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/util/aot/AotDetector.java new file mode 100644 index 00000000..ea1f1f00 --- /dev/null +++ b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/util/aot/AotDetector.java @@ -0,0 +1,44 @@ +/* + * + * * 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.aot; + +import org.springframework.core.SpringProperties; + +public abstract class AotDetector { + + /** + * System property that indicates the application should run with AOT + * generated artifacts. If such optimizations are not available, it is + * recommended to throw an exception rather than fall back to the regular + * runtime behavior. + */ + public static final String AOT_ENABLED = "spring.aot.enabled"; + + /** + * Determine whether AOT optimizations must be considered at runtime. This + * is mandatory in a native image but can be triggered on the JVM using + * the {@value #AOT_ENABLED} Spring property. + * @return whether AOT optimizations must be considered + */ + public static boolean useGeneratedArtifacts() { + return (NativeDetector.inNativeImage() || SpringProperties.getFlag(AOT_ENABLED)); + } + +} \ No newline at end of file diff --git a/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/util/aot/NativeDetector.java b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/util/aot/NativeDetector.java new file mode 100644 index 00000000..5e1cd7ce --- /dev/null +++ b/nacos-spring-context/src/main/java/com/alibaba/nacos/spring/util/aot/NativeDetector.java @@ -0,0 +1,33 @@ +/* + * + * * 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.aot; + +public abstract class NativeDetector { + + // See https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java + private static final boolean imageCode = (System.getProperty("org.graalvm.nativeimage.imagecode") != null); + + /** + * Returns {@code true} if invoked in the context of image building or during image runtime, else {@code false}. + */ + public static boolean inNativeImage() { + return imageCode; + } +} \ No newline at end of file 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..1ae2539c --- /dev/null +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/beans/factory/annotation/AbstractAnnotationBeanPostProcessorTest.java @@ -0,0 +1,52 @@ +/* + * + * * 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; + +/** + * {@link AbstractAnnotationBeanPostProcessor} Test + * @author SuperZ1999 + * @date 2023/9/28 + */ +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..e2311422 --- /dev/null +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/annotation/config/NacosPropertySourceBuilderTest.java @@ -0,0 +1,39 @@ +/* + * + * * 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; + +/** + * {@link NacosPropertySourceBuilder} Test + * @author SuperZ1999 + * @date 2023/9/28 + */ +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..c49ae054 --- /dev/null +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/annotation/config/NacosValueAnnotationBeanPostProcessorTest.java @@ -0,0 +1,64 @@ +/* + * + * * 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; + +/** + * {@link NacosValueAnnotationBeanPostProcessor} Test + * @author SuperZ1999 + * @date 2023/9/28 + */ +@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..80bee121 --- /dev/null +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/config/xml/NacosPropertySourceXmlBeanDefinitionTest.java @@ -0,0 +1,36 @@ +/* + * + * * 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; + +/** + * {@link NacosPropertySourceXmlBeanDefinition} Test + * @author SuperZ1999 + * @date 2023/9/28 + */ +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..fa78900a --- /dev/null +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/context/properties/config/DataBinderTest.java @@ -0,0 +1,79 @@ +/* + * + * * 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; + +/** + * {@link DataBinder} Test + * @author SuperZ1999 + * @date 2023/9/28 + */ +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..79a4118c --- /dev/null +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/factory/DelegatingNamingMaintainServiceTest.java @@ -0,0 +1,40 @@ +/* + * + * * 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; + +/** + * {@link DelegatingNamingMaintainService} Test + * @author SuperZ1999 + * @date 2023/9/28 + */ +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..391086b2 --- /dev/null +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/factory/DelegatingNamingServiceTest.java @@ -0,0 +1,43 @@ +/* + * + * * 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; + +/** + * {@link DelegatingNamingService} Test + * @author SuperZ1999 + * @date 2023/9/28 + */ +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..a724b556 --- /dev/null +++ b/nacos-spring-context/src/test/java/com/alibaba/nacos/spring/util/config/NacosConfigLoaderTest.java @@ -0,0 +1,39 @@ +/* + * + * * 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; + +/** + * {@link NacosConfigLoader} Test + * @author SuperZ1999 + * @date 2023/9/28 + */ +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..770ccdd9 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,7 @@ nacos-spring-context nacos-spring-samples + nacos-spring-context-aot @@ -75,13 +76,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 @@ -92,6 +93,7 @@ 2.2.1 5.2.9.RELEASE + 6.0.8 1.29 3.0.1