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 extends Annotation>[] 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 extends Annotation>... annotationTypes) {
+ Assert.notEmpty(annotationTypes, "The argument of annotations' types must not empty");
+ this.annotationTypes = annotationTypes;
+ }
+
+ private static Collection combine(Collection extends T>... elements) {
+ List allElements = new ArrayList();
+ for (Collection extends T> e : elements) {
+ allElements.addAll(e);
+ }
+ return allElements;
+ }
+
+ /**
+ * Annotation type
+ *
+ * @return non-null
+ * @deprecated 2.7.3, uses {@link #getAnnotationTypes()}
+ */
+ @Deprecated
+ public final Class extends Annotation> getAnnotationType() {
+ return annotationTypes[0];
+ }
+
+ protected final Class extends Annotation>[] 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 extends Annotation> 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 extends Annotation> 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 extends Annotation> 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