Avoid JDK proxy against CGLIB Factory interface and assert required type when resolving dependency
Issue: SPR-14478
This commit is contained in:
		
							parent
							
								
									938b56c0fe
								
							
						
					
					
						commit
						0e3f0bd9d0
					
				| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Copyright 2002-2014 the original author or authors.
 | 
					 * Copyright 2002-2016 the original author or authors.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 * you may not use this file except in compliance with the License.
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
| 
						 | 
					@ -139,7 +139,8 @@ public class ProxyProcessorSupport extends ProxyConfig implements Ordered, BeanC
 | 
				
			||||||
	 * @return whether the given interface is an internal language interface
 | 
						 * @return whether the given interface is an internal language interface
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	protected boolean isInternalLanguageInterface(Class<?> ifc) {
 | 
						protected boolean isInternalLanguageInterface(Class<?> ifc) {
 | 
				
			||||||
		return ifc.getName().equals("groovy.lang.GroovyObject");
 | 
							return (ifc.getName().equals("groovy.lang.GroovyObject") ||
 | 
				
			||||||
 | 
									ifc.getName().endsWith(".cglib.proxy.Factory"));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -577,7 +577,8 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
 | 
				
			||||||
								String autowiredBeanName = autowiredBeanNames.iterator().next();
 | 
													String autowiredBeanName = autowiredBeanNames.iterator().next();
 | 
				
			||||||
								if (beanFactory.containsBean(autowiredBeanName)) {
 | 
													if (beanFactory.containsBean(autowiredBeanName)) {
 | 
				
			||||||
									if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
 | 
														if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
 | 
				
			||||||
										this.cachedFieldValue = new ShortcutDependencyDescriptor(desc, autowiredBeanName);
 | 
															this.cachedFieldValue = new ShortcutDependencyDescriptor(
 | 
				
			||||||
 | 
																	desc, autowiredBeanName, field.getType());
 | 
				
			||||||
									}
 | 
														}
 | 
				
			||||||
								}
 | 
													}
 | 
				
			||||||
							}
 | 
												}
 | 
				
			||||||
| 
						 | 
					@ -661,8 +662,8 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
 | 
				
			||||||
									String autowiredBeanName = it.next();
 | 
														String autowiredBeanName = it.next();
 | 
				
			||||||
									if (beanFactory.containsBean(autowiredBeanName)) {
 | 
														if (beanFactory.containsBean(autowiredBeanName)) {
 | 
				
			||||||
										if (beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
 | 
															if (beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
 | 
				
			||||||
											this.cachedMethodArguments[i] =
 | 
																this.cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
 | 
				
			||||||
													new ShortcutDependencyDescriptor(descriptors[i], autowiredBeanName);
 | 
																		descriptors[i], autowiredBeanName, paramTypes[i]);
 | 
				
			||||||
										}
 | 
															}
 | 
				
			||||||
									}
 | 
														}
 | 
				
			||||||
								}
 | 
													}
 | 
				
			||||||
| 
						 | 
					@ -705,16 +706,19 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
 | 
				
			||||||
	@SuppressWarnings("serial")
 | 
						@SuppressWarnings("serial")
 | 
				
			||||||
	private static class ShortcutDependencyDescriptor extends DependencyDescriptor {
 | 
						private static class ShortcutDependencyDescriptor extends DependencyDescriptor {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		private final String shortcutBeanName;
 | 
							private final String shortcutName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcutBeanName) {
 | 
							private final Class<?> requiredType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcutName, Class<?> requiredType) {
 | 
				
			||||||
			super(original);
 | 
								super(original);
 | 
				
			||||||
			this.shortcutBeanName = shortcutBeanName;
 | 
								this.shortcutName = shortcutName;
 | 
				
			||||||
 | 
								this.requiredType = requiredType;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		@Override
 | 
							@Override
 | 
				
			||||||
		public Object resolveShortcut(BeanFactory beanFactory) {
 | 
							public Object resolveShortcut(BeanFactory beanFactory) {
 | 
				
			||||||
			return resolveCandidate(this.shortcutBeanName, beanFactory);
 | 
								return resolveCandidate(this.shortcutName, this.requiredType, beanFactory);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -172,21 +172,6 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
 | 
				
			||||||
		throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
 | 
							throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Resolve the specified bean name, as a candidate result of the matching
 | 
					 | 
				
			||||||
	 * algorithm for this dependency, to a bean instance from the given factory.
 | 
					 | 
				
			||||||
	 * <p>The default implementation calls {@link BeanFactory#getBean(String)}.
 | 
					 | 
				
			||||||
	 * Subclasses may provide additional arguments or other customizations.
 | 
					 | 
				
			||||||
	 * @param beanName the bean name, as a candidate result for this dependency
 | 
					 | 
				
			||||||
	 * @param beanFactory the associated factory
 | 
					 | 
				
			||||||
	 * @return the bean instance (never {@code null})
 | 
					 | 
				
			||||||
	 * @since 4.3
 | 
					 | 
				
			||||||
	 * @see BeanFactory#getBean(String)
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public Object resolveCandidate(String beanName, BeanFactory beanFactory) {
 | 
					 | 
				
			||||||
		return beanFactory.getBean(beanName);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Resolve a shortcut for this dependency against the given factory, for example
 | 
						 * Resolve a shortcut for this dependency against the given factory, for example
 | 
				
			||||||
	 * taking some pre-resolved information into account.
 | 
						 * taking some pre-resolved information into account.
 | 
				
			||||||
| 
						 | 
					@ -196,12 +181,32 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
 | 
				
			||||||
	 * pre-cached information while still receiving {@link InjectionPoint} exposure etc.
 | 
						 * pre-cached information while still receiving {@link InjectionPoint} exposure etc.
 | 
				
			||||||
	 * @param beanFactory the associated factory
 | 
						 * @param beanFactory the associated factory
 | 
				
			||||||
	 * @return the shortcut result if any, or {@code null} if none
 | 
						 * @return the shortcut result if any, or {@code null} if none
 | 
				
			||||||
 | 
						 * @throws BeansException if the shortcut could not be obtained
 | 
				
			||||||
	 * @since 4.3.1
 | 
						 * @since 4.3.1
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public Object resolveShortcut(BeanFactory beanFactory) {
 | 
						public Object resolveShortcut(BeanFactory beanFactory) throws BeansException {
 | 
				
			||||||
		return null;
 | 
							return null;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Resolve the specified bean name, as a candidate result of the matching
 | 
				
			||||||
 | 
						 * algorithm for this dependency, to a bean instance from the given factory.
 | 
				
			||||||
 | 
						 * <p>The default implementation calls {@link BeanFactory#getBean(String)}.
 | 
				
			||||||
 | 
						 * Subclasses may provide additional arguments or other customizations.
 | 
				
			||||||
 | 
						 * @param beanName the bean name, as a candidate result for this dependency
 | 
				
			||||||
 | 
						 * @param requiredType the expected type of the bean (as an assertion)
 | 
				
			||||||
 | 
						 * @param beanFactory the associated factory
 | 
				
			||||||
 | 
						 * @return the bean instance (never {@code null})
 | 
				
			||||||
 | 
						 * @throws BeansException if the bean could not be obtained
 | 
				
			||||||
 | 
						 * @since 4.3.2
 | 
				
			||||||
 | 
						 * @see BeanFactory#getBean(String)
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
 | 
				
			||||||
 | 
								throws BeansException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return beanFactory.getBean(beanName, requiredType);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Increase this descriptor's nesting level.
 | 
						 * Increase this descriptor's nesting level.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1200,7 +1200,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		for (String candidateName : candidateNames) {
 | 
							for (String candidateName : candidateNames) {
 | 
				
			||||||
			if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) {
 | 
								if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) {
 | 
				
			||||||
				result.put(candidateName, descriptor.resolveCandidate(candidateName, this));
 | 
									result.put(candidateName, descriptor.resolveCandidate(candidateName, requiredType, this));
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) {
 | 
							if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) {
 | 
				
			||||||
| 
						 | 
					@ -1208,14 +1208,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
 | 
				
			||||||
			DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
 | 
								DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
 | 
				
			||||||
			for (String candidateName : candidateNames) {
 | 
								for (String candidateName : candidateNames) {
 | 
				
			||||||
				if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, fallbackDescriptor)) {
 | 
									if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, fallbackDescriptor)) {
 | 
				
			||||||
					result.put(candidateName, descriptor.resolveCandidate(candidateName, this));
 | 
										result.put(candidateName, descriptor.resolveCandidate(candidateName, requiredType, this));
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if (result.isEmpty()) {
 | 
								if (result.isEmpty()) {
 | 
				
			||||||
				// Consider self references before as a final pass
 | 
									// Consider self references before as a final pass
 | 
				
			||||||
				for (String candidateName : candidateNames) {
 | 
									for (String candidateName : candidateNames) {
 | 
				
			||||||
					if (isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, fallbackDescriptor)) {
 | 
										if (isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, fallbackDescriptor)) {
 | 
				
			||||||
						result.put(candidateName, descriptor.resolveCandidate(candidateName, this));
 | 
											result.put(candidateName, descriptor.resolveCandidate(candidateName, requiredType, this));
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -1399,11 +1399,10 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
 | 
				
			||||||
			public boolean isRequired() {
 | 
								public boolean isRequired() {
 | 
				
			||||||
				return false;
 | 
									return false;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					 | 
				
			||||||
			@Override
 | 
								@Override
 | 
				
			||||||
			public Object resolveCandidate(String beanName, BeanFactory beanFactory) {
 | 
								public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) {
 | 
				
			||||||
				return (!ObjectUtils.isEmpty(args) ? beanFactory.getBean(beanName, args) :
 | 
									return (!ObjectUtils.isEmpty(args) ? beanFactory.getBean(beanName, requiredType, args) :
 | 
				
			||||||
						super.resolveCandidate(beanName, beanFactory));
 | 
											super.resolveCandidate(beanName, requiredType, beanFactory));
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
		descriptorToUse.increaseNestingLevel();
 | 
							descriptorToUse.increaseNestingLevel();
 | 
				
			||||||
| 
						 | 
					@ -1509,8 +1508,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
 | 
				
			||||||
			else {
 | 
								else {
 | 
				
			||||||
				DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) {
 | 
									DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) {
 | 
				
			||||||
					@Override
 | 
										@Override
 | 
				
			||||||
					public Object resolveCandidate(String beanName, BeanFactory beanFactory) {
 | 
										public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) {
 | 
				
			||||||
						return beanFactory.getBean(beanName, args);
 | 
											return ((AbstractBeanFactory) beanFactory).getBean(beanName, requiredType, args);
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				};
 | 
									};
 | 
				
			||||||
				return doResolveDependency(descriptorToUse, this.beanName, null, null);
 | 
									return doResolveDependency(descriptorToUse, this.beanName, null, null);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,6 +26,7 @@ import java.util.concurrent.Executor;
 | 
				
			||||||
import java.util.concurrent.Future;
 | 
					import java.util.concurrent.Future;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.junit.Test;
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					import org.mockito.Mockito;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.springframework.aop.Advisor;
 | 
					import org.springframework.aop.Advisor;
 | 
				
			||||||
import org.springframework.aop.framework.Advised;
 | 
					import org.springframework.aop.framework.Advised;
 | 
				
			||||||
| 
						 | 
					@ -37,6 +38,7 @@ import org.springframework.context.annotation.AdviceMode;
 | 
				
			||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 | 
					import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 | 
				
			||||||
import org.springframework.context.annotation.Bean;
 | 
					import org.springframework.context.annotation.Bean;
 | 
				
			||||||
import org.springframework.context.annotation.Configuration;
 | 
					import org.springframework.context.annotation.Configuration;
 | 
				
			||||||
 | 
					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.util.ReflectionUtils;
 | 
					import org.springframework.util.ReflectionUtils;
 | 
				
			||||||
| 
						 | 
					@ -67,6 +69,18 @@ public class EnableAsyncTests {
 | 
				
			||||||
		asyncBean.work();
 | 
							asyncBean.work();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void proxyingOccursWithMockitoStub() {
 | 
				
			||||||
 | 
							AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
 | 
				
			||||||
 | 
							ctx.register(AsyncConfigWithMockito.class, AsyncBeanUser.class);
 | 
				
			||||||
 | 
							ctx.refresh();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							AsyncBeanUser asyncBeanUser = ctx.getBean(AsyncBeanUser.class);
 | 
				
			||||||
 | 
							AsyncBean asyncBean = asyncBeanUser.getAsyncBean();
 | 
				
			||||||
 | 
							assertThat(AopUtils.isAopProxy(asyncBean), is(true));
 | 
				
			||||||
 | 
							asyncBean.work();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void withAsyncBeanWithExecutorQualifiedByName() throws ExecutionException, InterruptedException {
 | 
						public void withAsyncBeanWithExecutorQualifiedByName() throws ExecutionException, InterruptedException {
 | 
				
			||||||
		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
 | 
							AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
 | 
				
			||||||
| 
						 | 
					@ -200,6 +214,20 @@ public class EnableAsyncTests {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static class AsyncBeanUser {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private final AsyncBean asyncBean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public AsyncBeanUser(AsyncBean asyncBean) {
 | 
				
			||||||
 | 
								this.asyncBean = asyncBean;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public AsyncBean getAsyncBean() {
 | 
				
			||||||
 | 
								return asyncBean;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@EnableAsync(annotation = CustomAsync.class)
 | 
						@EnableAsync(annotation = CustomAsync.class)
 | 
				
			||||||
	static class CustomAsyncAnnotationConfig {
 | 
						static class CustomAsyncAnnotationConfig {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -252,6 +280,17 @@ public class EnableAsyncTests {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Configuration
 | 
				
			||||||
 | 
						@EnableAsync
 | 
				
			||||||
 | 
						static class AsyncConfigWithMockito {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							@Bean @Lazy
 | 
				
			||||||
 | 
							public AsyncBean asyncBean() {
 | 
				
			||||||
 | 
								return Mockito.mock(AsyncBean.class);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Configuration
 | 
						@Configuration
 | 
				
			||||||
	@EnableAsync
 | 
						@EnableAsync
 | 
				
			||||||
	static class CustomExecutorAsyncConfig implements AsyncConfigurer {
 | 
						static class CustomExecutorAsyncConfig implements AsyncConfigurer {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue