Consistent throwing of BeanNotOfRequiredTypeException even for existing proxy
Issue: SPR-14504
This commit is contained in:
parent
2a3e62ddfc
commit
4a85309a04
|
|
@ -52,6 +52,7 @@ import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
import org.springframework.beans.factory.BeanFactoryAware;
|
import org.springframework.beans.factory.BeanFactoryAware;
|
||||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||||
|
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
|
||||||
import org.springframework.beans.factory.CannotLoadBeanClassException;
|
import org.springframework.beans.factory.CannotLoadBeanClassException;
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
import org.springframework.beans.factory.InjectionPoint;
|
import org.springframework.beans.factory.InjectionPoint;
|
||||||
|
|
@ -1043,7 +1044,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||||
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
|
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
|
||||||
if (matchingBeans.isEmpty()) {
|
if (matchingBeans.isEmpty()) {
|
||||||
if (descriptor.isRequired()) {
|
if (descriptor.isRequired()) {
|
||||||
raiseNoSuchBeanDefinitionException(type, descriptor.getResolvableType().toString(), descriptor);
|
raiseNoMatchingBeanFound(type, descriptor.getResolvableType().toString(), descriptor);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -1382,17 +1383,46 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raise a NoSuchBeanDefinitionException for an unresolvable dependency.
|
* Raise a NoSuchBeanDefinitionException or BeanNotOfRequiredTypeException
|
||||||
|
* for an unresolvable dependency.
|
||||||
*/
|
*/
|
||||||
private void raiseNoSuchBeanDefinitionException(
|
private void raiseNoMatchingBeanFound(
|
||||||
Class<?> type, String dependencyDescription, DependencyDescriptor descriptor)
|
Class<?> type, String dependencyDescription, DependencyDescriptor descriptor) throws BeansException {
|
||||||
throws NoSuchBeanDefinitionException {
|
|
||||||
|
checkBeanNotOfRequiredType(type, descriptor);
|
||||||
|
|
||||||
throw new NoSuchBeanDefinitionException(type, dependencyDescription,
|
throw new NoSuchBeanDefinitionException(type, dependencyDescription,
|
||||||
"expected at least 1 bean which qualifies as autowire candidate for this dependency. " +
|
"expected at least 1 bean which qualifies as autowire candidate for this dependency. " +
|
||||||
"Dependency annotations: " + ObjectUtils.nullSafeToString(descriptor.getAnnotations()));
|
"Dependency annotations: " + ObjectUtils.nullSafeToString(descriptor.getAnnotations()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raise a BeanNotOfRequiredTypeException for an unresolvable dependency, if applicable,
|
||||||
|
* i.e. if the target type of the bean would match but an exposed proxy doesn't.
|
||||||
|
*/
|
||||||
|
private void checkBeanNotOfRequiredType(Class<?> type, DependencyDescriptor descriptor) {
|
||||||
|
for (String beanName : this.beanDefinitionNames) {
|
||||||
|
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
|
||||||
|
Class<?> targetType = mbd.getTargetType();
|
||||||
|
if (targetType != null && type.isAssignableFrom(targetType) &&
|
||||||
|
isAutowireCandidate(beanName, mbd, descriptor, getAutowireCandidateResolver())) {
|
||||||
|
// Probably a poxy interfering with target type match -> throw meaningful exception.
|
||||||
|
Object beanInstance = getSingleton(beanName, false);
|
||||||
|
Class<?> beanType = (beanInstance != null ? beanInstance.getClass() : predictBeanType(beanName, mbd));
|
||||||
|
if (type != beanType) {
|
||||||
|
throw new BeanNotOfRequiredTypeException(beanName, type, beanType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getParentBeanFactory() instanceof DefaultListableBeanFactory) {
|
||||||
|
((DefaultListableBeanFactory) getParentBeanFactory()).checkBeanNotOfRequiredType(type, descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an {@link Optional} wrapper for the specified dependency.
|
||||||
|
*/
|
||||||
private Optional<?> createOptionalDependency(DependencyDescriptor descriptor, String beanName, final Object... args) {
|
private Optional<?> createOptionalDependency(DependencyDescriptor descriptor, String beanName, final Object... args) {
|
||||||
DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) {
|
DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ import org.springframework.aop.framework.Advised;
|
||||||
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
||||||
import org.springframework.aop.support.AopUtils;
|
import org.springframework.aop.support.AopUtils;
|
||||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||||
|
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
|
||||||
|
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.context.annotation.AdviceMode;
|
import org.springframework.context.annotation.AdviceMode;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
|
|
@ -41,6 +43,7 @@ import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.anyOf;
|
import static org.hamcrest.CoreMatchers.anyOf;
|
||||||
|
|
@ -81,6 +84,36 @@ public class EnableAsyncTests {
|
||||||
asyncBean.work();
|
asyncBean.work();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void properExceptionForExistingProxyDependencyMismatch() {
|
||||||
|
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||||
|
ctx.register(AsyncConfig.class, AsyncBeanWithInterface.class, AsyncBeanUser.class);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ctx.refresh();
|
||||||
|
fail("Should have thrown UnsatisfiedDependencyException");
|
||||||
|
}
|
||||||
|
catch (UnsatisfiedDependencyException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
assertTrue(ex.getCause() instanceof BeanNotOfRequiredTypeException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void properExceptionForResolvedProxyDependencyMismatch() {
|
||||||
|
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||||
|
ctx.register(AsyncConfig.class, AsyncBeanUser.class, AsyncBeanWithInterface.class);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ctx.refresh();
|
||||||
|
fail("Should have thrown UnsatisfiedDependencyException");
|
||||||
|
}
|
||||||
|
catch (UnsatisfiedDependencyException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
assertTrue(ex.getCause() instanceof BeanNotOfRequiredTypeException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void withAsyncBeanWithExecutorQualifiedByName() throws ExecutionException, InterruptedException {
|
public void withAsyncBeanWithExecutorQualifiedByName() throws ExecutionException, InterruptedException {
|
||||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||||
|
|
@ -214,6 +247,15 @@ public class EnableAsyncTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Component("asyncBean")
|
||||||
|
static class AsyncBeanWithInterface extends AsyncBean implements Runnable {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static class AsyncBeanUser {
|
static class AsyncBeanUser {
|
||||||
|
|
||||||
private final AsyncBean asyncBean;
|
private final AsyncBean asyncBean;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue