Avoid JDK proxy against CGLIB Factory interface and assert required type when resolving dependency
Issue: SPR-14478
(cherry picked from commit 0e3f0bd)
This commit is contained in:
parent
fe17f8da41
commit
503d65d570
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -1210,7 +1210,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)) {
|
||||||
|
|
@ -1218,14 +1218,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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1479,9 +1479,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||||
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();
|
||||||
|
|
@ -1526,8 +1526,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);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 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.
|
||||||
|
|
@ -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