Infer proxy on `@Lazy`-annotated injection points

This commit makes use of the new `getLazyResolutionProxyClass` on
`AutowireCandidateResolver` to detect if a injection point requires
a proxy.

Closes gh-28980
This commit is contained in:
Stephane Nicoll 2022-08-22 19:59:21 +02:00
parent e5f9bb76b1
commit 455715899d
7 changed files with 387 additions and 4 deletions

View File

@ -25,6 +25,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member; import java.lang.reflect.Member;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -70,6 +71,8 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor; import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory; import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
import org.springframework.beans.factory.support.AutowireCandidateResolver;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.LookupOverride; import org.springframework.beans.factory.support.LookupOverride;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.beans.factory.support.RegisteredBean;
@ -289,7 +292,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
InjectionMetadata metadata = findInjectionMetadata(beanName, beanClass, beanDefinition); InjectionMetadata metadata = findInjectionMetadata(beanName, beanClass, beanDefinition);
Collection<AutowiredElement> autowiredElements = getAutowiredElements(metadata); Collection<AutowiredElement> autowiredElements = getAutowiredElements(metadata);
if (!ObjectUtils.isEmpty(autowiredElements)) { if (!ObjectUtils.isEmpty(autowiredElements)) {
return new AotContribution(beanClass, autowiredElements); return new AotContribution(beanClass, autowiredElements, getAutowireCandidateResolver());
} }
return null; return null;
} }
@ -300,6 +303,14 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
return (Collection) metadata.getInjectedElements(); return (Collection) metadata.getInjectedElements();
} }
@Nullable
private AutowireCandidateResolver getAutowireCandidateResolver() {
if (this.beanFactory instanceof DefaultListableBeanFactory lbf) {
return lbf.getAutowireCandidateResolver();
}
return null;
}
private InjectionMetadata findInjectionMetadata(String beanName, Class<?> beanType, RootBeanDefinition beanDefinition) { private InjectionMetadata findInjectionMetadata(String beanName, Class<?> beanType, RootBeanDefinition beanDefinition) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null); InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition); metadata.checkConfigMembers(beanDefinition);
@ -914,10 +925,15 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
private final Collection<AutowiredElement> autowiredElements; private final Collection<AutowiredElement> autowiredElements;
@Nullable
private final AutowireCandidateResolver candidateResolver;
AotContribution(Class<?> target, Collection<AutowiredElement> autowiredElements,
@Nullable AutowireCandidateResolver candidateResolver) {
AotContribution(Class<?> target, Collection<AutowiredElement> autowiredElements) {
this.target = target; this.target = target;
this.autowiredElements = autowiredElements; this.autowiredElements = autowiredElements;
this.candidateResolver = candidateResolver;
} }
@ -940,6 +956,10 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
}); });
beanRegistrationCode.addInstancePostProcessor( beanRegistrationCode.addInstancePostProcessor(
MethodReference.ofStatic(generatedClass.getName(), generateMethod.getName())); MethodReference.ofStatic(generatedClass.getName(), generateMethod.getName()));
if (this.candidateResolver != null) {
registerHints(generationContext.getRuntimeHints());
}
} }
private CodeBlock generateMethodCode(RuntimeHints hints) { private CodeBlock generateMethodCode(RuntimeHints hints) {
@ -1023,6 +1043,35 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
return code.build(); return code.build();
} }
private void registerHints(RuntimeHints runtimeHints) {
this.autowiredElements.forEach(autowiredElement -> {
boolean required = autowiredElement.required;
Member member = autowiredElement.getMember();
if (member instanceof Field field) {
DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(
field, required);
registerProxyIfNecessary(runtimeHints, dependencyDescriptor);
}
if (member instanceof Method method) {
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
MethodParameter methodParam = new MethodParameter(method, i);
DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(
methodParam, required);
registerProxyIfNecessary(runtimeHints, dependencyDescriptor);
}
}
});
}
private void registerProxyIfNecessary(RuntimeHints runtimeHints, DependencyDescriptor dependencyDescriptor) {
Class<?> proxyType = this.candidateResolver
.getLazyResolutionProxyClass(dependencyDescriptor, null);
if (proxyType != null && Proxy.isProxyClass(proxyType)) {
runtimeHints.proxies().registerJdkProxy(proxyType.getInterfaces());
}
}
} }
} }

View File

@ -16,7 +16,10 @@
package org.springframework.beans.factory.aot; package org.springframework.beans.factory.aot;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable; import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List; import java.util.List;
import javax.lang.model.element.Modifier; import javax.lang.model.element.Modifier;
@ -26,8 +29,13 @@ import org.springframework.aot.generate.GeneratedMethod;
import org.springframework.aot.generate.GeneratedMethods; import org.springframework.aot.generate.GeneratedMethods;
import org.springframework.aot.generate.GenerationContext; import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.generate.MethodReference; import org.springframework.aot.generate.MethodReference;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.AutowireCandidateResolver;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.core.MethodParameter;
import org.springframework.javapoet.ClassName; import org.springframework.javapoet.ClassName;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -83,6 +91,7 @@ class BeanDefinitionMethodGenerator {
MethodReference generateBeanDefinitionMethod(GenerationContext generationContext, MethodReference generateBeanDefinitionMethod(GenerationContext generationContext,
BeanRegistrationsCode beanRegistrationsCode) { BeanRegistrationsCode beanRegistrationsCode) {
registerRuntimeHintsIfNecessary(generationContext.getRuntimeHints());
BeanRegistrationCodeFragments codeFragments = getCodeFragments(generationContext, BeanRegistrationCodeFragments codeFragments = getCodeFragments(generationContext,
beanRegistrationsCode); beanRegistrationsCode);
Class<?> target = codeFragments.getTarget(this.registeredBean, Class<?> target = codeFragments.getTarget(this.registeredBean,
@ -166,4 +175,54 @@ class BeanDefinitionMethodGenerator {
return StringUtils.uncapitalize(beanName); return StringUtils.uncapitalize(beanName);
} }
private void registerRuntimeHintsIfNecessary(RuntimeHints runtimeHints) {
if (this.registeredBean.getBeanFactory() instanceof DefaultListableBeanFactory dlbf) {
ProxyRuntimeHintsRegistrar registrar = new ProxyRuntimeHintsRegistrar(dlbf.getAutowireCandidateResolver());
if (this.constructorOrFactoryMethod instanceof Method method) {
registrar.registerRuntimeHints(runtimeHints, method);
}
else if (this.constructorOrFactoryMethod instanceof Constructor<?> constructor) {
registrar.registerRuntimeHints(runtimeHints, constructor);
}
}
}
private static class ProxyRuntimeHintsRegistrar {
private final AutowireCandidateResolver candidateResolver;
public ProxyRuntimeHintsRegistrar(AutowireCandidateResolver candidateResolver) {
this.candidateResolver = candidateResolver;
}
public void registerRuntimeHints(RuntimeHints runtimeHints, Method method) {
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
MethodParameter methodParam = new MethodParameter(method, i);
DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(
methodParam, true);
registerProxyIfNecessary(runtimeHints, dependencyDescriptor);
}
}
public void registerRuntimeHints(RuntimeHints runtimeHints, Constructor<?> constructor) {
Class<?>[] parameterTypes = constructor.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
MethodParameter methodParam = new MethodParameter(constructor, i);
DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(
methodParam, true);
registerProxyIfNecessary(runtimeHints, dependencyDescriptor);
}
}
private void registerProxyIfNecessary(RuntimeHints runtimeHints, DependencyDescriptor dependencyDescriptor) {
Class<?> proxyType = this.candidateResolver
.getLazyResolutionProxyClass(dependencyDescriptor, null);
if (proxyType != null && Proxy.isProxyClass(proxyType)) {
runtimeHints.proxies().registerJdkProxy(proxyType.getInterfaces());
}
}
}
} }

View File

@ -17,12 +17,15 @@
package org.springframework.context.aot; package org.springframework.context.aot;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Proxy;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.aot.generate.GeneratedFiles.Kind; import org.springframework.aot.generate.GeneratedFiles.Kind;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.TypeReference; import org.springframework.aot.hint.TypeReference;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
import org.springframework.aot.test.generator.compile.Compiled; import org.springframework.aot.test.generator.compile.Compiled;
@ -44,11 +47,18 @@ import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.AnnotationConfigUtils; import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor; import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.context.support.GenericApplicationContext; import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.testfixture.context.generator.SimpleComponent; import org.springframework.context.testfixture.context.generator.SimpleComponent;
import org.springframework.context.testfixture.context.generator.annotation.AutowiredComponent; import org.springframework.context.testfixture.context.generator.annotation.AutowiredComponent;
import org.springframework.context.testfixture.context.generator.annotation.CglibConfiguration; import org.springframework.context.testfixture.context.generator.annotation.CglibConfiguration;
import org.springframework.context.testfixture.context.generator.annotation.InitDestroyComponent; import org.springframework.context.testfixture.context.generator.annotation.InitDestroyComponent;
import org.springframework.context.testfixture.context.generator.annotation.LazyAutowiredFieldComponent;
import org.springframework.context.testfixture.context.generator.annotation.LazyAutowiredMethodComponent;
import org.springframework.context.testfixture.context.generator.annotation.LazyConstructorArgumentComponent;
import org.springframework.context.testfixture.context.generator.annotation.LazyFactoryMethodArgumentComponent;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.testfixture.aot.generate.TestGenerationContext; import org.springframework.core.testfixture.aot.generate.TestGenerationContext;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -93,6 +103,87 @@ class ApplicationContextAotGeneratorTests {
}); });
} }
@Test
void processAheadOfTimeWhenHasLazyAutowiringOnField() {
testAutowiredComponent(LazyAutowiredFieldComponent.class, (bean, generationContext) -> {
Environment environment = bean.getEnvironment();
assertThat(environment).isInstanceOf(Proxy.class);
ResourceLoader resourceLoader = bean.getResourceLoader();
assertThat(resourceLoader).isNotInstanceOf(Proxy.class);
RuntimeHints runtimeHints = generationContext.getRuntimeHints();
assertThat(runtimeHints.proxies().jdkProxies()).singleElement().satisfies(proxyHint ->
assertThat(proxyHint.getProxiedInterfaces()).isEqualTo(TypeReference.listOf(
environment.getClass().getInterfaces())));
});
}
@Test
void processAheadOfTimeWhenHasLazyAutowiringOnMethod() {
testAutowiredComponent(LazyAutowiredMethodComponent.class, (bean, generationContext) -> {
Environment environment = bean.getEnvironment();
assertThat(environment).isNotInstanceOf(Proxy.class);
ResourceLoader resourceLoader = bean.getResourceLoader();
assertThat(resourceLoader).isInstanceOf(Proxy.class);
RuntimeHints runtimeHints = generationContext.getRuntimeHints();
assertThat(runtimeHints.proxies().jdkProxies()).singleElement().satisfies(proxyHint ->
assertThat(proxyHint.getProxiedInterfaces()).isEqualTo(TypeReference.listOf(
resourceLoader.getClass().getInterfaces())));
});
}
@Test
void processAheadOfTimeWhenHasLazyAutowiringOnConstructor() {
testAutowiredComponent(LazyConstructorArgumentComponent.class, (bean, generationContext) -> {
Environment environment = bean.getEnvironment();
assertThat(environment).isInstanceOf(Proxy.class);
ResourceLoader resourceLoader = bean.getResourceLoader();
assertThat(resourceLoader).isNotInstanceOf(Proxy.class);
RuntimeHints runtimeHints = generationContext.getRuntimeHints();
assertThat(runtimeHints.proxies().jdkProxies()).singleElement().satisfies(proxyHint ->
assertThat(proxyHint.getProxiedInterfaces()).isEqualTo(TypeReference.listOf(
environment.getClass().getInterfaces())));
});
}
@Test
void processAheadOfTimeWhenHasLazyAutowiringOnFactoryMethod() {
RootBeanDefinition bd = new RootBeanDefinition(LazyFactoryMethodArgumentComponent.class);
bd.setFactoryMethodName("of");
testAutowiredComponent(LazyFactoryMethodArgumentComponent.class, bd, (bean, generationContext) -> {
Environment environment = bean.getEnvironment();
assertThat(environment).isInstanceOf(Proxy.class);
ResourceLoader resourceLoader = bean.getResourceLoader();
assertThat(resourceLoader).isNotInstanceOf(Proxy.class);
RuntimeHints runtimeHints = generationContext.getRuntimeHints();
assertThat(runtimeHints.proxies().jdkProxies()).singleElement().satisfies(proxyHint ->
assertThat(proxyHint.getProxiedInterfaces()).isEqualTo(TypeReference.listOf(
environment.getClass().getInterfaces())));
});
}
private <T> void testAutowiredComponent(Class<T> type, BiConsumer<T, GenerationContext> assertions) {
testAutowiredComponent(type, new RootBeanDefinition(type), assertions);
}
private <T> void testAutowiredComponent(Class<T> type, RootBeanDefinition beanDefinition,
BiConsumer<T, GenerationContext> assertions) {
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.getDefaultListableBeanFactory().setAutowireCandidateResolver(
new ContextAnnotationAutowireCandidateResolver());
applicationContext.registerBeanDefinition(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME,
BeanDefinitionBuilder
.rootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
applicationContext.registerBeanDefinition("testComponent", beanDefinition);
TestGenerationContext generationContext = processAheadOfTime(applicationContext);
testCompiledResult(generationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBeanDefinitionNames()).containsOnly("testComponent");
assertions.accept(freshApplicationContext.getBean("testComponent", type), generationContext);
});
}
@Test @Test
void processAheadOfTimeWhenHasInitDestroyMethods() { void processAheadOfTimeWhenHasInitDestroyMethods() {
GenericApplicationContext applicationContext = new GenericApplicationContext(); GenericApplicationContext applicationContext = new GenericApplicationContext();
@ -189,10 +280,14 @@ class ApplicationContextAotGeneratorTests {
return generationContext; return generationContext;
} }
@SuppressWarnings({ "rawtypes", "unchecked" })
private void testCompiledResult(GenericApplicationContext applicationContext, private void testCompiledResult(GenericApplicationContext applicationContext,
BiConsumer<ApplicationContextInitializer<GenericApplicationContext>, Compiled> result) { BiConsumer<ApplicationContextInitializer<GenericApplicationContext>, Compiled> result) {
TestGenerationContext generationContext = processAheadOfTime(applicationContext); testCompiledResult(processAheadOfTime(applicationContext), result);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void testCompiledResult(TestGenerationContext generationContext,
BiConsumer<ApplicationContextInitializer<GenericApplicationContext>, Compiled> result) {
TestCompiler.forSystem().withFiles(generationContext.getGeneratedFiles()).compile(compiled -> TestCompiler.forSystem().withFiles(generationContext.getGeneratedFiles()).compile(compiled ->
result.accept(compiled.getInstance(ApplicationContextInitializer.class), compiled)); result.accept(compiled.getInstance(ApplicationContextInitializer.class), compiled));
} }

View File

@ -0,0 +1,41 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.testfixture.context.generator.annotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
public class LazyAutowiredFieldComponent {
@Lazy
@Autowired
private Environment environment;
@Autowired
private ResourceLoader resourceLoader;
public Environment getEnvironment() {
return this.environment;
}
public ResourceLoader getResourceLoader() {
return this.resourceLoader;
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.testfixture.context.generator.annotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
public class LazyAutowiredMethodComponent {
private Environment environment;
private ResourceLoader resourceLoader;
@Autowired
public void setEnvironment(Environment environment) {
this.environment = environment;
}
public Environment getEnvironment() {
return this.environment;
}
@Autowired
public void setResourceLoader(@Lazy ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public ResourceLoader getResourceLoader() {
return this.resourceLoader;
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.testfixture.context.generator.annotation;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
/**
*
* @author Stephane Nicoll
*/
public class LazyConstructorArgumentComponent {
private final Environment environment;
private final ResourceLoader resourceLoader;
public LazyConstructorArgumentComponent(@Lazy Environment environment, ResourceLoader resourceLoader) {
this.environment = environment;
this.resourceLoader = resourceLoader;
}
public Environment getEnvironment() {
return this.environment;
}
public ResourceLoader getResourceLoader() {
return this.resourceLoader;
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.testfixture.context.generator.annotation;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
public class LazyFactoryMethodArgumentComponent {
private final Environment environment;
private final ResourceLoader resourceLoader;
LazyFactoryMethodArgumentComponent(Environment environment, ResourceLoader resourceLoader) {
this.environment = environment;
this.resourceLoader = resourceLoader;
}
public static LazyFactoryMethodArgumentComponent of(@Lazy Environment environment, ResourceLoader resourceLoader) {
return new LazyFactoryMethodArgumentComponent(environment, resourceLoader);
}
public Environment getEnvironment() {
return this.environment;
}
public ResourceLoader getResourceLoader() {
return this.resourceLoader;
}
}