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.BeanFactoryAware;
 | 
			
		||||
import org.springframework.beans.factory.BeanFactoryUtils;
 | 
			
		||||
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
 | 
			
		||||
import org.springframework.beans.factory.CannotLoadBeanClassException;
 | 
			
		||||
import org.springframework.beans.factory.FactoryBean;
 | 
			
		||||
import org.springframework.beans.factory.InjectionPoint;
 | 
			
		||||
| 
						 | 
				
			
			@ -1043,7 +1044,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
 | 
			
		|||
			Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
 | 
			
		||||
			if (matchingBeans.isEmpty()) {
 | 
			
		||||
				if (descriptor.isRequired()) {
 | 
			
		||||
					raiseNoSuchBeanDefinitionException(type, descriptor.getResolvableType().toString(), descriptor);
 | 
			
		||||
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType().toString(), descriptor);
 | 
			
		||||
				}
 | 
			
		||||
				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(
 | 
			
		||||
			Class<?> type, String dependencyDescription, DependencyDescriptor descriptor)
 | 
			
		||||
			throws NoSuchBeanDefinitionException {
 | 
			
		||||
	private void raiseNoMatchingBeanFound(
 | 
			
		||||
			Class<?> type, String dependencyDescription, DependencyDescriptor descriptor) throws BeansException {
 | 
			
		||||
 | 
			
		||||
		checkBeanNotOfRequiredType(type, descriptor);
 | 
			
		||||
 | 
			
		||||
		throw new NoSuchBeanDefinitionException(type, dependencyDescription,
 | 
			
		||||
				"expected at least 1 bean which qualifies as autowire candidate for this dependency. " +
 | 
			
		||||
				"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) {
 | 
			
		||||
		DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) {
 | 
			
		||||
			@Override
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,6 +33,8 @@ import org.springframework.aop.framework.Advised;
 | 
			
		|||
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
 | 
			
		||||
import org.springframework.aop.support.AopUtils;
 | 
			
		||||
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.context.annotation.AdviceMode;
 | 
			
		||||
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.core.Ordered;
 | 
			
		||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.springframework.util.ReflectionUtils;
 | 
			
		||||
 | 
			
		||||
import static org.hamcrest.CoreMatchers.anyOf;
 | 
			
		||||
| 
						 | 
				
			
			@ -81,6 +84,36 @@ public class EnableAsyncTests {
 | 
			
		|||
		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
 | 
			
		||||
	public void withAsyncBeanWithExecutorQualifiedByName() throws ExecutionException, InterruptedException {
 | 
			
		||||
		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 {
 | 
			
		||||
 | 
			
		||||
		private final AsyncBean asyncBean;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue