Avoid double proxying for @Resource @Lazy fallback autowiring
Includes refactored @Resource resolver for AOT with lazy resolution support. Closes gh-31447 See gh-29614
This commit is contained in:
parent
6bb9775309
commit
6dcba4de2c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
|
@ -375,6 +375,16 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether this dependency supports lazy resolution,
|
||||
* e.g. through extra proxying. The default is {@code true}.
|
||||
* @since 6.1.2
|
||||
* @see org.springframework.beans.factory.support.AutowireCandidateResolver#getLazyResolutionProxyIfNecessary
|
||||
*/
|
||||
public boolean supportsLazyResolution() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
|
|
|
@ -1343,15 +1343,15 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
|
||||
return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
|
||||
}
|
||||
else {
|
||||
else if (descriptor.supportsLazyResolution()) {
|
||||
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
|
||||
descriptor, requestingBeanName);
|
||||
if (result == null) {
|
||||
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
|
||||
}
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
|
||||
|
|
|
@ -62,10 +62,7 @@ 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.RootBeanDefinition;
|
||||
import org.springframework.context.aot.ResourceFieldValueResolver;
|
||||
import org.springframework.context.aot.ResourceMethodArgumentResolver;
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.javapoet.ClassName;
|
||||
|
@ -501,16 +498,9 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
|||
return element.lookupType;
|
||||
}
|
||||
@Override
|
||||
public boolean isStatic() {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public Object getTarget() {
|
||||
return getResource(element, requestingBeanName);
|
||||
}
|
||||
@Override
|
||||
public void releaseTarget(Object target) {
|
||||
}
|
||||
};
|
||||
|
||||
ProxyFactory pf = new ProxyFactory();
|
||||
|
@ -655,12 +645,23 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
|||
*/
|
||||
public final DependencyDescriptor getDependencyDescriptor() {
|
||||
if (this.isField) {
|
||||
return new LookupDependencyDescriptor((Field) this.member, this.lookupType);
|
||||
return new ResourceElementResolver.LookupDependencyDescriptor(
|
||||
(Field) this.member, this.lookupType, isLazyLookup());
|
||||
}
|
||||
else {
|
||||
return new LookupDependencyDescriptor((Method) this.member, this.lookupType);
|
||||
return new ResourceElementResolver.LookupDependencyDescriptor(
|
||||
(Method) this.member, this.lookupType, isLazyLookup());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether this dependency is marked for lazy lookup.
|
||||
* The default is {@code false}.
|
||||
* @since 6.1.2
|
||||
*/
|
||||
boolean isLazyLookup() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -707,6 +708,11 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
|||
return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
|
||||
getResource(this, requestingBeanName));
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isLazyLookup() {
|
||||
return this.lazyLookup;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -753,6 +759,11 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
|||
return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
|
||||
getResource(this, requestingBeanName));
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isLazyLookup() {
|
||||
return this.lazyLookup;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -812,30 +823,6 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extension of the DependencyDescriptor class,
|
||||
* overriding the dependency type with the specified resource type.
|
||||
*/
|
||||
private static class LookupDependencyDescriptor extends DependencyDescriptor {
|
||||
|
||||
private final Class<?> lookupType;
|
||||
|
||||
public LookupDependencyDescriptor(Field field, Class<?> lookupType) {
|
||||
super(field, true);
|
||||
this.lookupType = lookupType;
|
||||
}
|
||||
|
||||
public LookupDependencyDescriptor(Method method, Class<?> lookupType) {
|
||||
super(new MethodParameter(method, 0), true);
|
||||
this.lookupType = lookupType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getDependencyType() {
|
||||
return this.lookupType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link BeanRegistrationAotContribution} to inject resources on fields and methods.
|
||||
*/
|
||||
|
@ -924,11 +911,11 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
|||
|
||||
private CodeBlock generateFieldResolverCode(Field field, LookupElement lookupElement) {
|
||||
if (lookupElement.isDefaultName) {
|
||||
return CodeBlock.of("$T.$L($S)", ResourceFieldValueResolver.class,
|
||||
return CodeBlock.of("$T.$L($S)", ResourceElementResolver.class,
|
||||
"forField", field.getName());
|
||||
}
|
||||
else {
|
||||
return CodeBlock.of("$T.$L($S, $S)", ResourceFieldValueResolver.class,
|
||||
return CodeBlock.of("$T.$L($S, $S)", ResourceElementResolver.class,
|
||||
"forField", field.getName(), lookupElement.getName());
|
||||
}
|
||||
}
|
||||
|
@ -940,7 +927,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
|||
AccessControl accessControl = AccessControl.forMember(method);
|
||||
if (!accessControl.isAccessibleFrom(targetClassName)) {
|
||||
hints.reflection().registerMethod(method, ExecutableMode.INVOKE);
|
||||
return CodeBlock.of("$L.resolveAndInvoke($L, $L)", resolver,
|
||||
return CodeBlock.of("$L.resolveAndSet($L, $L)", resolver,
|
||||
REGISTERED_BEAN_PARAMETER, INSTANCE_PARAMETER);
|
||||
}
|
||||
hints.reflection().registerMethod(method, ExecutableMode.INTROSPECT);
|
||||
|
@ -951,11 +938,11 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
|||
|
||||
private CodeBlock generateMethodResolverCode(Method method, LookupElement lookupElement) {
|
||||
if (lookupElement.isDefaultName) {
|
||||
return CodeBlock.of("$T.$L($S, $T.class)", ResourceMethodArgumentResolver.class,
|
||||
return CodeBlock.of("$T.$L($S, $T.class)", ResourceElementResolver.class,
|
||||
"forMethod", method.getName(), lookupElement.getLookupType());
|
||||
}
|
||||
else {
|
||||
return CodeBlock.of("$T.$L($S, $T.class, $S)", ResourceMethodArgumentResolver.class,
|
||||
return CodeBlock.of("$T.$L($S, $T.class, $S)", ResourceElementResolver.class,
|
||||
"forMethod", method.getName(), lookupElement.getLookupType(), lookupElement.getName());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,330 @@
|
|||
/*
|
||||
* Copyright 2002-2023 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.annotation;
|
||||
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.aop.TargetSource;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Resolver for the injection of named beans on a field or method element,
|
||||
* following the rules of the {@link jakarta.annotation.Resource} annotation
|
||||
* but without any JNDI support. This is primarily intended for AOT processing.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Juergen Hoeller
|
||||
* @since 6.1.2
|
||||
* @see CommonAnnotationBeanPostProcessor
|
||||
* @see jakarta.annotation.Resource
|
||||
*/
|
||||
public abstract class ResourceElementResolver {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final boolean defaultName;
|
||||
|
||||
|
||||
ResourceElementResolver(String name, boolean defaultName) {
|
||||
this.name = name;
|
||||
this.defaultName = defaultName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link ResourceFieldResolver} for the specified field.
|
||||
* @param fieldName the field name
|
||||
* @return a new {@link ResourceFieldResolver} instance
|
||||
*/
|
||||
public static ResourceElementResolver forField(String fieldName) {
|
||||
return new ResourceFieldResolver(fieldName, true, fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ResourceFieldResolver} for the specified field and resource name.
|
||||
* @param fieldName the field name
|
||||
* @param resourceName the resource name
|
||||
* @return a new {@link ResourceFieldResolver} instance
|
||||
*/
|
||||
public static ResourceElementResolver forField(String fieldName, String resourceName) {
|
||||
return new ResourceFieldResolver(resourceName, false, fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ResourceMethodResolver} for the specified method
|
||||
* using a resource name that infers from the method name.
|
||||
* @param methodName the method name
|
||||
* @param parameterType the parameter type.
|
||||
* @return a new {@link ResourceMethodResolver} instance
|
||||
*/
|
||||
public static ResourceElementResolver forMethod(String methodName, Class<?> parameterType) {
|
||||
return new ResourceMethodResolver(defaultResourceNameForMethod(methodName), true,
|
||||
methodName, parameterType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ResourceMethodResolver} for the specified method
|
||||
* and resource name.
|
||||
* @param methodName the method name
|
||||
* @param parameterType the parameter type
|
||||
* @param resourceName the resource name
|
||||
* @return a new {@link ResourceMethodResolver} instance
|
||||
*/
|
||||
public static ResourceElementResolver forMethod(String methodName, Class<?> parameterType, String resourceName) {
|
||||
return new ResourceMethodResolver(resourceName, false, methodName, parameterType);
|
||||
}
|
||||
|
||||
private static String defaultResourceNameForMethod(String methodName) {
|
||||
if (methodName.startsWith("set") && methodName.length() > 3) {
|
||||
return StringUtils.uncapitalizeAsProperty(methodName.substring(3));
|
||||
}
|
||||
return methodName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolve the value for the specified registered bean.
|
||||
* @param registeredBean the registered bean
|
||||
* @return the resolved field or method parameter value
|
||||
*/
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T resolve(RegisteredBean registeredBean) {
|
||||
Assert.notNull(registeredBean, "'registeredBean' must not be null");
|
||||
return (T) (isLazyLookup(registeredBean) ? buildLazyResourceProxy(registeredBean) :
|
||||
resolveValue(registeredBean));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the value for the specified registered bean and set it using reflection.
|
||||
* @param registeredBean the registered bean
|
||||
* @param instance the bean instance
|
||||
*/
|
||||
public abstract void resolveAndSet(RegisteredBean registeredBean, Object instance);
|
||||
|
||||
/**
|
||||
* Create a suitable {@link DependencyDescriptor} for the specified bean.
|
||||
* @param registeredBean the registered bean
|
||||
* @return a descriptor for that bean
|
||||
*/
|
||||
abstract DependencyDescriptor createDependencyDescriptor(RegisteredBean registeredBean);
|
||||
|
||||
abstract Class<?> getLookupType(RegisteredBean registeredBean);
|
||||
|
||||
abstract AnnotatedElement getAnnotatedElement(RegisteredBean registeredBean);
|
||||
|
||||
boolean isLazyLookup(RegisteredBean registeredBean) {
|
||||
AnnotatedElement ae = getAnnotatedElement(registeredBean);
|
||||
Lazy lazy = ae.getAnnotation(Lazy.class);
|
||||
return (lazy != null && lazy.value());
|
||||
}
|
||||
|
||||
private Object buildLazyResourceProxy(RegisteredBean registeredBean) {
|
||||
Class<?> lookupType = getLookupType(registeredBean);
|
||||
|
||||
TargetSource ts = new TargetSource() {
|
||||
@Override
|
||||
public Class<?> getTargetClass() {
|
||||
return lookupType;
|
||||
}
|
||||
@Override
|
||||
public Object getTarget() {
|
||||
return resolveValue(registeredBean);
|
||||
}
|
||||
};
|
||||
|
||||
ProxyFactory pf = new ProxyFactory();
|
||||
pf.setTargetSource(ts);
|
||||
if (lookupType.isInterface()) {
|
||||
pf.addInterface(lookupType);
|
||||
}
|
||||
return pf.getProxy(registeredBean.getBeanFactory().getBeanClassLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the value to inject for this instance.
|
||||
* @param registeredBean the bean registration
|
||||
* @return the value to inject
|
||||
*/
|
||||
private Object resolveValue(RegisteredBean registeredBean) {
|
||||
ConfigurableListableBeanFactory factory = registeredBean.getBeanFactory();
|
||||
|
||||
Object resource;
|
||||
Set<String> autowiredBeanNames;
|
||||
DependencyDescriptor descriptor = createDependencyDescriptor(registeredBean);
|
||||
if (this.defaultName && !factory.containsBean(this.name)) {
|
||||
autowiredBeanNames = new LinkedHashSet<>();
|
||||
resource = factory.resolveDependency(descriptor, registeredBean.getBeanName(), autowiredBeanNames, null);
|
||||
if (resource == null) {
|
||||
throw new NoSuchBeanDefinitionException(descriptor.getDependencyType(), "No resolvable resource object");
|
||||
}
|
||||
}
|
||||
else {
|
||||
resource = factory.resolveBeanByName(this.name, descriptor);
|
||||
autowiredBeanNames = Collections.singleton(this.name);
|
||||
}
|
||||
|
||||
for (String autowiredBeanName : autowiredBeanNames) {
|
||||
if (factory.containsBean(autowiredBeanName)) {
|
||||
factory.registerDependentBean(autowiredBeanName, registeredBean.getBeanName());
|
||||
}
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
|
||||
private static final class ResourceFieldResolver extends ResourceElementResolver {
|
||||
|
||||
private final String fieldName;
|
||||
|
||||
public ResourceFieldResolver(String name, boolean defaultName, String fieldName) {
|
||||
super(name, defaultName);
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveAndSet(RegisteredBean registeredBean, Object instance) {
|
||||
Assert.notNull(registeredBean, "'registeredBean' must not be null");
|
||||
Assert.notNull(instance, "'instance' must not be null");
|
||||
Field field = getField(registeredBean);
|
||||
Object resolved = resolve(registeredBean);
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
ReflectionUtils.setField(field, instance, resolved);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DependencyDescriptor createDependencyDescriptor(RegisteredBean registeredBean) {
|
||||
Field field = getField(registeredBean);
|
||||
return new LookupDependencyDescriptor(field, field.getType(), isLazyLookup(registeredBean));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> getLookupType(RegisteredBean registeredBean) {
|
||||
return getField(registeredBean).getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AnnotatedElement getAnnotatedElement(RegisteredBean registeredBean) {
|
||||
return getField(registeredBean);
|
||||
}
|
||||
|
||||
private Field getField(RegisteredBean registeredBean) {
|
||||
Field field = ReflectionUtils.findField(registeredBean.getBeanClass(), this.fieldName);
|
||||
Assert.notNull(field,
|
||||
() -> "No field '" + this.fieldName + "' found on " + registeredBean.getBeanClass().getName());
|
||||
return field;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class ResourceMethodResolver extends ResourceElementResolver {
|
||||
|
||||
private final String methodName;
|
||||
|
||||
private final Class<?> lookupType;
|
||||
|
||||
private ResourceMethodResolver(String name, boolean defaultName, String methodName, Class<?> lookupType) {
|
||||
super(name, defaultName);
|
||||
this.methodName = methodName;
|
||||
this.lookupType = lookupType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveAndSet(RegisteredBean registeredBean, Object instance) {
|
||||
Assert.notNull(registeredBean, "'registeredBean' must not be null");
|
||||
Assert.notNull(instance, "'instance' must not be null");
|
||||
Method method = getMethod(registeredBean);
|
||||
Object resolved = resolve(registeredBean);
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
ReflectionUtils.invokeMethod(method, instance, resolved);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DependencyDescriptor createDependencyDescriptor(RegisteredBean registeredBean) {
|
||||
return new LookupDependencyDescriptor(
|
||||
getMethod(registeredBean), this.lookupType, isLazyLookup(registeredBean));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> getLookupType(RegisteredBean bean) {
|
||||
return this.lookupType;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AnnotatedElement getAnnotatedElement(RegisteredBean registeredBean) {
|
||||
return getMethod(registeredBean);
|
||||
}
|
||||
|
||||
private Method getMethod(RegisteredBean registeredBean) {
|
||||
Method method = ReflectionUtils.findMethod(registeredBean.getBeanClass(), this.methodName, this.lookupType);
|
||||
Assert.notNull(method,
|
||||
() -> "Method '%s' with parameter type '%s' declared on %s could not be found.".formatted(
|
||||
this.methodName, this.lookupType.getName(), registeredBean.getBeanClass().getName()));
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extension of the DependencyDescriptor class,
|
||||
* overriding the dependency type with the specified resource type.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
static class LookupDependencyDescriptor extends DependencyDescriptor {
|
||||
|
||||
private final Class<?> lookupType;
|
||||
|
||||
private final boolean lazyLookup;
|
||||
|
||||
public LookupDependencyDescriptor(Field field, Class<?> lookupType, boolean lazyLookup) {
|
||||
super(field, true);
|
||||
this.lookupType = lookupType;
|
||||
this.lazyLookup = lazyLookup;
|
||||
}
|
||||
|
||||
public LookupDependencyDescriptor(Method method, Class<?> lookupType, boolean lazyLookup) {
|
||||
super(new MethodParameter(method, 0), true);
|
||||
this.lookupType = lookupType;
|
||||
this.lazyLookup = lazyLookup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getDependencyType() {
|
||||
return this.lookupType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLazyResolution() {
|
||||
return !this.lazyLookup;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,138 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2023 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.aot;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class for resolvers that support injection of named beans on
|
||||
* an {@link Element}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.1
|
||||
* @see Resource
|
||||
*/
|
||||
public abstract class ResourceElementResolver {
|
||||
|
||||
protected final String name;
|
||||
|
||||
protected final boolean defaultName;
|
||||
|
||||
protected ResourceElementResolver(String name, boolean defaultName) {
|
||||
this.name = name;
|
||||
this.defaultName = defaultName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the field value for the specified registered bean.
|
||||
* @param registeredBean the registered bean
|
||||
* @return the resolved field value
|
||||
*/
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T resolve(RegisteredBean registeredBean) {
|
||||
return (T) resolveObject(registeredBean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the field value for the specified registered bean.
|
||||
* @param registeredBean the registered bean
|
||||
* @return the resolved field value
|
||||
*/
|
||||
public Object resolveObject(RegisteredBean registeredBean) {
|
||||
Assert.notNull(registeredBean, "'registeredBean' must not be null");
|
||||
return resolveValue(registeredBean);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a suitable {@link DependencyDescriptor} for the specified bean.
|
||||
* @param bean the registered bean
|
||||
* @return a descriptor for that bean
|
||||
*/
|
||||
protected abstract DependencyDescriptor createDependencyDescriptor(RegisteredBean bean);
|
||||
|
||||
/**
|
||||
* Resolve the value to inject for this instance.
|
||||
* @param bean the bean registration
|
||||
* @return the value to inject
|
||||
*/
|
||||
protected Object resolveValue(RegisteredBean bean) {
|
||||
ConfigurableListableBeanFactory factory = bean.getBeanFactory();
|
||||
|
||||
Object resource;
|
||||
Set<String> autowiredBeanNames;
|
||||
DependencyDescriptor descriptor = createDependencyDescriptor(bean);
|
||||
if (this.defaultName && !factory.containsBean(this.name)) {
|
||||
autowiredBeanNames = new LinkedHashSet<>();
|
||||
resource = factory.resolveDependency(descriptor, bean.getBeanName(), autowiredBeanNames, null);
|
||||
if (resource == null) {
|
||||
throw new NoSuchBeanDefinitionException(descriptor.getDependencyType(), "No resolvable resource object");
|
||||
}
|
||||
}
|
||||
else {
|
||||
resource = factory.resolveBeanByName(this.name, descriptor);
|
||||
autowiredBeanNames = Collections.singleton(this.name);
|
||||
}
|
||||
|
||||
for (String autowiredBeanName : autowiredBeanNames) {
|
||||
if (factory.containsBean(autowiredBeanName)) {
|
||||
factory.registerDependentBean(autowiredBeanName, bean.getBeanName());
|
||||
}
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
protected static class LookupDependencyDescriptor extends DependencyDescriptor {
|
||||
|
||||
private final Class<?> lookupType;
|
||||
|
||||
public LookupDependencyDescriptor(Field field, Class<?> lookupType) {
|
||||
super(field, true);
|
||||
this.lookupType = lookupType;
|
||||
}
|
||||
|
||||
public LookupDependencyDescriptor(Method method, Class<?> lookupType) {
|
||||
super(new MethodParameter(method, 0), true);
|
||||
this.lookupType = lookupType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getDependencyType() {
|
||||
return this.lookupType;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2023 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.aot;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Resolver used to support injection of named beans on fields. Typically used in
|
||||
* AOT-processed applications as a targeted alternative to the
|
||||
* {@link org.springframework.context.annotation.CommonAnnotationBeanPostProcessor}.
|
||||
*
|
||||
* <p>When resolving arguments in a native image, the {@link Field} being used must
|
||||
* be marked with an {@link ExecutableMode#INTROSPECT introspection} hint so
|
||||
* that field annotations can be read. Full {@link ExecutableMode#INVOKE
|
||||
* invocation} hints are only required if the
|
||||
* {@link #resolveAndSet(RegisteredBean, Object)} method of this class is being
|
||||
* used (typically to support private fields).
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.1
|
||||
*/
|
||||
public final class ResourceFieldValueResolver extends ResourceElementResolver {
|
||||
|
||||
private final String fieldName;
|
||||
|
||||
public ResourceFieldValueResolver(String name, boolean defaultName, String fieldName) {
|
||||
super(name, defaultName);
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link ResourceFieldValueResolver} for the specified field.
|
||||
* @param fieldName the field name
|
||||
* @return a new {@link ResourceFieldValueResolver} instance
|
||||
*/
|
||||
public static ResourceFieldValueResolver forField(String fieldName) {
|
||||
return new ResourceFieldValueResolver(fieldName, true, fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ResourceFieldValueResolver} for the specified field and
|
||||
* resource name.
|
||||
* @param fieldName the field name
|
||||
* @param resourceName the resource name
|
||||
* @return a new {@link ResourceFieldValueResolver} instance
|
||||
*/
|
||||
public static ResourceFieldValueResolver forField(String fieldName, String resourceName) {
|
||||
return new ResourceFieldValueResolver(resourceName, false, fieldName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DependencyDescriptor createDependencyDescriptor(RegisteredBean bean) {
|
||||
Field field = getField(bean);
|
||||
return new LookupDependencyDescriptor(field, field.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the field value for the specified registered bean and set it
|
||||
* using reflection.
|
||||
* @param registeredBean the registered bean
|
||||
* @param instance the bean instance
|
||||
*/
|
||||
public void resolveAndSet(RegisteredBean registeredBean, Object instance) {
|
||||
Assert.notNull(registeredBean, "'registeredBean' must not be null");
|
||||
Assert.notNull(instance, "'instance' must not be null");
|
||||
Field field = getField(registeredBean);
|
||||
Object resolved = resolveValue(registeredBean);
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
ReflectionUtils.setField(field, instance, resolved);
|
||||
}
|
||||
|
||||
private Field getField(RegisteredBean registeredBean) {
|
||||
Field field = ReflectionUtils.findField(registeredBean.getBeanClass(),
|
||||
this.fieldName);
|
||||
Assert.notNull(field, () -> "No field '" + this.fieldName + "' found on "
|
||||
+ registeredBean.getBeanClass().getName());
|
||||
return field;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2023 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.aot;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Resolver used to support injection of named beans to methods. Typically used in
|
||||
* AOT-processed applications as a targeted alternative to the
|
||||
* {@link org.springframework.context.annotation.CommonAnnotationBeanPostProcessor}.
|
||||
*
|
||||
* <p>When resolving arguments in a native image, the {@link Method} being used
|
||||
* must be marked with an {@link ExecutableMode#INTROSPECT introspection} hint
|
||||
* so that field annotations can be read. Full {@link ExecutableMode#INVOKE
|
||||
* invocation} hints are only required if the
|
||||
* {@link #resolveAndInvoke(RegisteredBean, Object)} method of this class is
|
||||
* being used (typically to support private methods).
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.1
|
||||
*/
|
||||
public final class ResourceMethodArgumentResolver extends ResourceElementResolver {
|
||||
|
||||
private final String methodName;
|
||||
|
||||
private final Class<?> lookupType;
|
||||
|
||||
private ResourceMethodArgumentResolver(String name, boolean defaultName,
|
||||
String methodName, Class<?> lookupType) {
|
||||
super(name, defaultName);
|
||||
this.methodName = methodName;
|
||||
this.lookupType = lookupType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link ResourceMethodArgumentResolver} for the specified method
|
||||
* using a resource name that infers from the method name.
|
||||
* @param methodName the method name
|
||||
* @param parameterType the parameter type.
|
||||
* @return a new {@link ResourceMethodArgumentResolver} instance
|
||||
*/
|
||||
public static ResourceMethodArgumentResolver forMethod(String methodName, Class<?> parameterType) {
|
||||
return new ResourceMethodArgumentResolver(defaultResourceName(methodName), true,
|
||||
methodName, parameterType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ResourceMethodArgumentResolver} for the specified method
|
||||
* and resource name.
|
||||
* @param methodName the method name
|
||||
* @param parameterType the parameter type
|
||||
* @param resourceName the resource name
|
||||
* @return a new {@link ResourceMethodArgumentResolver} instance
|
||||
*/
|
||||
public static ResourceMethodArgumentResolver forMethod(String methodName, Class<?> parameterType, String resourceName) {
|
||||
return new ResourceMethodArgumentResolver(resourceName, false, methodName, parameterType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DependencyDescriptor createDependencyDescriptor(RegisteredBean bean) {
|
||||
return new LookupDependencyDescriptor(getMethod(bean), this.lookupType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the method argument for the specified registered bean and invoke
|
||||
* the method using reflection.
|
||||
* @param registeredBean the registered bean
|
||||
* @param instance the bean instance
|
||||
*/
|
||||
public void resolveAndInvoke(RegisteredBean registeredBean, Object instance) {
|
||||
Assert.notNull(registeredBean, "'registeredBean' must not be null");
|
||||
Assert.notNull(instance, "'instance' must not be null");
|
||||
Method method = getMethod(registeredBean);
|
||||
Object resolved = resolveValue(registeredBean);
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
ReflectionUtils.invokeMethod(method, instance, resolved);
|
||||
}
|
||||
|
||||
private Method getMethod(RegisteredBean registeredBean) {
|
||||
Method method = ReflectionUtils.findMethod(registeredBean.getBeanClass(),
|
||||
this.methodName, this.lookupType);
|
||||
Assert.notNull(method, () ->
|
||||
"Method '%s' with parameter type '%s' declared on %s could not be found.".formatted(
|
||||
this.methodName, this.lookupType.getName(),
|
||||
registeredBean.getBeanClass().getName()));
|
||||
return method;
|
||||
}
|
||||
|
||||
private static String defaultResourceName(String methodName) {
|
||||
if (methodName.startsWith("set") && methodName.length() > 3) {
|
||||
return StringUtils.uncapitalizeAsProperty(methodName.substring(3));
|
||||
}
|
||||
return methodName;
|
||||
}
|
||||
|
||||
}
|
|
@ -142,7 +142,7 @@ class CommonAnnotationBeanRegistrationAotContributionTests {
|
|||
postProcessor.apply(registeredBean, instance);
|
||||
assertThat(instance).extracting("one").isEqualTo("1");
|
||||
assertThat(getSourceFile(compiled, PrivateMethodResourceSample.class))
|
||||
.contains("resolveAndInvoke(");
|
||||
.contains("resolveAndSet(");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -160,7 +160,7 @@ class CommonAnnotationBeanRegistrationAotContributionTests {
|
|||
postProcessor.apply(registeredBean, instance);
|
||||
assertThat(instance).extracting("text").isEqualTo("1");
|
||||
assertThat(getSourceFile(compiled, PrivateMethodResourceWithCustomNameSample.class))
|
||||
.contains("resolveAndInvoke(");
|
||||
.contains("resolveAndSet(");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -198,7 +198,7 @@ class CommonAnnotationBeanRegistrationAotContributionTests {
|
|||
postProcessor.apply(registeredBean, instance);
|
||||
assertThat(instance).extracting("one").isEqualTo("1");
|
||||
assertThat(getSourceFile(compiled, PackagePrivateMethodResourceFromParentSample.class))
|
||||
.contains("resolveAndInvoke(");
|
||||
.contains("resolveAndSet(");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.aot;
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -30,11 +30,11 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* Tests for {@link ResourceFieldValueResolver}.
|
||||
* Tests for {@code ResourceFieldValueResolver}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class ResourceFieldValueResolverTests {
|
||||
class ResourceElementResolverFieldTests {
|
||||
|
||||
private final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
|
||||
|
@ -43,8 +43,7 @@ class ResourceFieldValueResolverTests {
|
|||
void resolveWhenFieldIsMissingThrowsException() {
|
||||
RegisteredBean registeredBean = registerTestBean(this.beanFactory);
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> ResourceFieldValueResolver.forField("missing")
|
||||
.resolve(registeredBean))
|
||||
.isThrownBy(() -> ResourceElementResolver.forField("missing").resolve(registeredBean))
|
||||
.withMessage("No field 'missing' found on " + TestBean.class.getName());
|
||||
}
|
||||
|
||||
|
@ -53,7 +52,7 @@ class ResourceFieldValueResolverTests {
|
|||
this.beanFactory.registerSingleton("one", "1");
|
||||
this.beanFactory.registerSingleton("two", "2");
|
||||
RegisteredBean registeredBean = registerTestBean(this.beanFactory);
|
||||
Object resolved = ResourceFieldValueResolver.forField("one")
|
||||
Object resolved = ResourceElementResolver.forField("one")
|
||||
.resolve(registeredBean);
|
||||
assertThat(resolved).isEqualTo("1");
|
||||
}
|
||||
|
@ -63,8 +62,7 @@ class ResourceFieldValueResolverTests {
|
|||
this.beanFactory.registerSingleton("one", "1");
|
||||
this.beanFactory.registerSingleton("two", "2");
|
||||
RegisteredBean registeredBean = registerTestBean(this.beanFactory);
|
||||
Object resolved = ResourceFieldValueResolver.forField("test", "two")
|
||||
.resolve(registeredBean);
|
||||
Object resolved = ResourceElementResolver.forField("test", "two").resolve(registeredBean);
|
||||
assertThat(resolved).isEqualTo("2");
|
||||
}
|
||||
|
||||
|
@ -72,8 +70,7 @@ class ResourceFieldValueResolverTests {
|
|||
void resolveWheNoMatchFallbackOnType() {
|
||||
this.beanFactory.registerSingleton("two", "2");
|
||||
RegisteredBean registeredBean = registerTestBean(this.beanFactory);
|
||||
Object resolved = ResourceFieldValueResolver.forField("one")
|
||||
.resolve(registeredBean);
|
||||
Object resolved = ResourceElementResolver.forField("one").resolve(registeredBean);
|
||||
assertThat(resolved).isEqualTo("2");
|
||||
}
|
||||
|
||||
|
@ -82,9 +79,8 @@ class ResourceFieldValueResolverTests {
|
|||
this.beanFactory.registerSingleton("one", "1");
|
||||
this.beanFactory.registerSingleton("two", "2");
|
||||
RegisteredBean registeredBean = registerTestBean(this.beanFactory);
|
||||
assertThatThrownBy(() -> ResourceFieldValueResolver.forField("test")
|
||||
.resolve(registeredBean)
|
||||
).isInstanceOf(NoUniqueBeanDefinitionException.class)
|
||||
assertThatThrownBy(() -> ResourceElementResolver.forField("test").resolve(registeredBean))
|
||||
.isInstanceOf(NoUniqueBeanDefinitionException.class)
|
||||
.hasMessageContaining(String.class.getName())
|
||||
.hasMessageContaining("one").hasMessageContaining("two");
|
||||
}
|
||||
|
@ -92,9 +88,8 @@ class ResourceFieldValueResolverTests {
|
|||
@Test
|
||||
void resolveWhenNoCandidateMatchingTypeThrowsException() {
|
||||
RegisteredBean registeredBean = registerTestBean(this.beanFactory);
|
||||
assertThatThrownBy(() -> ResourceFieldValueResolver.forField("test")
|
||||
.resolve(registeredBean)
|
||||
).isInstanceOf(NoSuchBeanDefinitionException.class)
|
||||
assertThatThrownBy(() -> ResourceElementResolver.forField("test").resolve(registeredBean))
|
||||
.isInstanceOf(NoSuchBeanDefinitionException.class)
|
||||
.hasMessageContaining(String.class.getName());
|
||||
}
|
||||
|
||||
|
@ -102,9 +97,8 @@ class ResourceFieldValueResolverTests {
|
|||
void resolveWhenInvalidMatchingTypeThrowsException() {
|
||||
this.beanFactory.registerSingleton("count", "counter");
|
||||
RegisteredBean registeredBean = registerTestBean(this.beanFactory);
|
||||
assertThatThrownBy(() -> ResourceFieldValueResolver.forField("count")
|
||||
.resolve(registeredBean)
|
||||
).isInstanceOf(BeanNotOfRequiredTypeException.class)
|
||||
assertThatThrownBy(() -> ResourceElementResolver.forField("count").resolve(registeredBean))
|
||||
.isInstanceOf(BeanNotOfRequiredTypeException.class)
|
||||
.hasMessageContaining(Integer.class.getName())
|
||||
.hasMessageContaining(String.class.getName());
|
||||
}
|
||||
|
@ -114,8 +108,7 @@ class ResourceFieldValueResolverTests {
|
|||
this.beanFactory.registerSingleton("one", "1");
|
||||
RegisteredBean registeredBean = registerTestBean(this.beanFactory);
|
||||
TestBean testBean = new TestBean();
|
||||
ResourceFieldValueResolver.forField("one").resolveAndSet(registeredBean,
|
||||
testBean);
|
||||
ResourceElementResolver.forField("one").resolveAndSet(registeredBean, testBean);
|
||||
assertThat(testBean.one).isEqualTo("1");
|
||||
}
|
||||
|
||||
|
@ -123,16 +116,16 @@ class ResourceFieldValueResolverTests {
|
|||
void resolveRegistersDependantBeans() {
|
||||
this.beanFactory.registerSingleton("one", "1");
|
||||
RegisteredBean registeredBean = registerTestBean(this.beanFactory);
|
||||
ResourceFieldValueResolver.forField("one").resolve(registeredBean);
|
||||
ResourceElementResolver.forField("one").resolve(registeredBean);
|
||||
assertThat(this.beanFactory.getDependentBeans("one")).containsExactly("testBean");
|
||||
}
|
||||
|
||||
private RegisteredBean registerTestBean(DefaultListableBeanFactory beanFactory) {
|
||||
beanFactory.registerBeanDefinition("testBean",
|
||||
new RootBeanDefinition(TestBean.class));
|
||||
beanFactory.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
|
||||
return RegisteredBean.of(beanFactory, "testBean");
|
||||
}
|
||||
|
||||
|
||||
static class TestBean {
|
||||
|
||||
String one;
|
||||
|
@ -140,7 +133,6 @@ class ResourceFieldValueResolverTests {
|
|||
String test;
|
||||
|
||||
Integer count;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.aot;
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
|
@ -32,18 +32,19 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* Tests for {@link ResourceMethodArgumentResolver}/
|
||||
* Tests for {@code ResourceMethodArgumentResolver}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class ResourceMethodArgumentResolverTests {
|
||||
class ResourceElementResolverMethodTests {
|
||||
|
||||
private final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
|
||||
|
||||
@Test
|
||||
void resolveWhenMethodIsMissingThrowsException() {
|
||||
RegisteredBean registeredBean = registerTestBean(this.beanFactory);
|
||||
ResourceMethodArgumentResolver resolver = ResourceMethodArgumentResolver.forMethod("missing", InputStream.class);
|
||||
ResourceElementResolver resolver = ResourceElementResolver.forMethod("missing", InputStream.class);
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> resolver.resolve(registeredBean))
|
||||
.withMessage("Method 'missing' with parameter type 'java.io.InputStream' declared on %s could not be found.",
|
||||
|
@ -55,8 +56,7 @@ class ResourceMethodArgumentResolverTests {
|
|||
this.beanFactory.registerSingleton("one", "1");
|
||||
this.beanFactory.registerSingleton("two", "2");
|
||||
RegisteredBean registeredBean = registerTestBean(this.beanFactory);
|
||||
ResourceMethodArgumentResolver resolver = ResourceMethodArgumentResolver
|
||||
.forMethod("setOne", String.class);
|
||||
ResourceElementResolver resolver = ResourceElementResolver.forMethod("setOne", String.class);
|
||||
Object resolved = resolver.resolve(registeredBean);
|
||||
assertThat(resolved).isEqualTo("1");
|
||||
}
|
||||
|
@ -66,8 +66,7 @@ class ResourceMethodArgumentResolverTests {
|
|||
this.beanFactory.registerSingleton("one", "1");
|
||||
this.beanFactory.registerSingleton("two", "2");
|
||||
RegisteredBean registeredBean = registerTestBean(this.beanFactory);
|
||||
Object resolved = ResourceMethodArgumentResolver.forMethod("setTest", String.class, "two")
|
||||
.resolve(registeredBean);
|
||||
Object resolved = ResourceElementResolver.forMethod("setTest", String.class, "two").resolve(registeredBean);
|
||||
assertThat(resolved).isEqualTo("2");
|
||||
}
|
||||
|
||||
|
@ -75,8 +74,7 @@ class ResourceMethodArgumentResolverTests {
|
|||
void resolveWheNoMatchFallbackOnType() {
|
||||
this.beanFactory.registerSingleton("two", "2");
|
||||
RegisteredBean registeredBean = registerTestBean(this.beanFactory);
|
||||
Object resolved = ResourceMethodArgumentResolver.forMethod("setOne", String.class)
|
||||
.resolve(registeredBean);
|
||||
Object resolved = ResourceElementResolver.forMethod("setOne", String.class).resolve(registeredBean);
|
||||
assertThat(resolved).isEqualTo("2");
|
||||
}
|
||||
|
||||
|
@ -85,9 +83,8 @@ class ResourceMethodArgumentResolverTests {
|
|||
this.beanFactory.registerSingleton("one", "1");
|
||||
this.beanFactory.registerSingleton("two", "2");
|
||||
RegisteredBean registeredBean = registerTestBean(this.beanFactory);
|
||||
assertThatThrownBy(() -> ResourceMethodArgumentResolver.forMethod("setTest", String.class)
|
||||
.resolve(registeredBean)
|
||||
).isInstanceOf(NoUniqueBeanDefinitionException.class)
|
||||
assertThatThrownBy(() -> ResourceElementResolver.forMethod("setTest", String.class).resolve(registeredBean))
|
||||
.isInstanceOf(NoUniqueBeanDefinitionException.class)
|
||||
.hasMessageContaining(String.class.getName())
|
||||
.hasMessageContaining("one").hasMessageContaining("two");
|
||||
}
|
||||
|
@ -95,9 +92,8 @@ class ResourceMethodArgumentResolverTests {
|
|||
@Test
|
||||
void resolveWhenNoCandidateMatchingTypeThrowsException() {
|
||||
RegisteredBean registeredBean = registerTestBean(this.beanFactory);
|
||||
assertThatThrownBy(() -> ResourceMethodArgumentResolver.forMethod("setTest", String.class)
|
||||
.resolve(registeredBean)
|
||||
).isInstanceOf(NoSuchBeanDefinitionException.class)
|
||||
assertThatThrownBy(() -> ResourceElementResolver.forMethod("setTest", String.class).resolve(registeredBean))
|
||||
.isInstanceOf(NoSuchBeanDefinitionException.class)
|
||||
.hasMessageContaining(String.class.getName());
|
||||
}
|
||||
|
||||
|
@ -105,9 +101,8 @@ class ResourceMethodArgumentResolverTests {
|
|||
void resolveWhenInvalidMatchingTypeThrowsException() {
|
||||
this.beanFactory.registerSingleton("count", "counter");
|
||||
RegisteredBean registeredBean = registerTestBean(this.beanFactory);
|
||||
assertThatThrownBy(() -> ResourceMethodArgumentResolver.forMethod("setCount", Integer.class)
|
||||
.resolve(registeredBean)
|
||||
).isInstanceOf(BeanNotOfRequiredTypeException.class)
|
||||
assertThatThrownBy(() -> ResourceElementResolver.forMethod("setCount", Integer.class).resolve(registeredBean))
|
||||
.isInstanceOf(BeanNotOfRequiredTypeException.class)
|
||||
.hasMessageContaining(Integer.class.getName())
|
||||
.hasMessageContaining(String.class.getName());
|
||||
}
|
||||
|
@ -117,8 +112,7 @@ class ResourceMethodArgumentResolverTests {
|
|||
this.beanFactory.registerSingleton("one", "1");
|
||||
RegisteredBean registeredBean = registerTestBean(this.beanFactory);
|
||||
TestBean testBean = new TestBean();
|
||||
ResourceMethodArgumentResolver.forMethod("setOne", String.class)
|
||||
.resolveAndInvoke(registeredBean, testBean);
|
||||
ResourceElementResolver.forMethod("setOne", String.class).resolveAndSet(registeredBean, testBean);
|
||||
assertThat(testBean.one).isEqualTo("1");
|
||||
}
|
||||
|
||||
|
@ -126,13 +120,12 @@ class ResourceMethodArgumentResolverTests {
|
|||
void resolveRegistersDependantBeans() {
|
||||
this.beanFactory.registerSingleton("one", "1");
|
||||
RegisteredBean registeredBean = registerTestBean(this.beanFactory);
|
||||
ResourceMethodArgumentResolver.forMethod("setOne", String.class).resolve(registeredBean);
|
||||
ResourceElementResolver.forMethod("setOne", String.class).resolve(registeredBean);
|
||||
assertThat(this.beanFactory.getDependentBeans("one")).containsExactly("testBean");
|
||||
}
|
||||
|
||||
private RegisteredBean registerTestBean(DefaultListableBeanFactory beanFactory) {
|
||||
beanFactory.registerBeanDefinition("testBean",
|
||||
new RootBeanDefinition(TestBean.class));
|
||||
beanFactory.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
|
||||
return RegisteredBean.of(beanFactory, "testBean");
|
||||
}
|
||||
|
||||
|
@ -156,7 +149,6 @@ class ResourceMethodArgumentResolverTests {
|
|||
public void setCount(Integer count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -24,7 +24,6 @@ import java.util.function.BiConsumer;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
|
@ -253,7 +252,6 @@ class ApplicationContextAotGeneratorTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Disabled("gh-31447")
|
||||
void processAheadOfTimeWhenHasLazyResourceAutowiringOnField() {
|
||||
testResourceAutowiringComponent(LazyResourceFieldComponent.class, (bean, generationContext) -> {
|
||||
Environment environment = bean.getEnvironment();
|
||||
|
|
Loading…
Reference in New Issue