Browse Source

Avoid JDK proxy against CGLIB Factory interface and assert required type when resolving dependency

Issue: SPR-14478
pull/1119/head
Juergen Hoeller 9 years ago
parent
commit
0e3f0bd9d0
  1. 5
      spring-aop/src/main/java/org/springframework/aop/framework/ProxyProcessorSupport.java
  2. 18
      spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java
  3. 37
      spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java
  4. 17
      spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
  5. 39
      spring-context/src/test/java/org/springframework/scheduling/annotation/EnableAsyncTests.java

5
spring-aop/src/main/java/org/springframework/aop/framework/ProxyProcessorSupport.java

@ -1,5 +1,5 @@ @@ -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");
* you may not use this file except in compliance with the License.
@ -139,7 +139,8 @@ public class ProxyProcessorSupport extends ProxyConfig implements Ordered, BeanC @@ -139,7 +139,8 @@ public class ProxyProcessorSupport extends ProxyConfig implements Ordered, BeanC
* @return whether the given interface is an internal language interface
*/
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"));
}
}

18
spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java

@ -577,7 +577,8 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean @@ -577,7 +577,8 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName)) {
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 @@ -661,8 +662,8 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
String autowiredBeanName = it.next();
if (beanFactory.containsBean(autowiredBeanName)) {
if (beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
this.cachedMethodArguments[i] =
new ShortcutDependencyDescriptor(descriptors[i], autowiredBeanName);
this.cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
descriptors[i], autowiredBeanName, paramTypes[i]);
}
}
}
@ -705,16 +706,19 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean @@ -705,16 +706,19 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
@SuppressWarnings("serial")
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);
this.shortcutBeanName = shortcutBeanName;
this.shortcutName = shortcutName;
this.requiredType = requiredType;
}
@Override
public Object resolveShortcut(BeanFactory beanFactory) {
return resolveCandidate(this.shortcutBeanName, beanFactory);
return resolveCandidate(this.shortcutName, this.requiredType, beanFactory);
}
}

37
spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java

@ -172,21 +172,6 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable @@ -172,21 +172,6 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
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
* taking some pre-resolved information into account.
@ -196,12 +181,32 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable @@ -196,12 +181,32 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
* pre-cached information while still receiving {@link InjectionPoint} exposure etc.
* @param beanFactory the associated factory
* @return the shortcut result if any, or {@code null} if none
* @throws BeansException if the shortcut could not be obtained
* @since 4.3.1
*/
public Object resolveShortcut(BeanFactory beanFactory) {
public Object resolveShortcut(BeanFactory beanFactory) throws BeansException {
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.

17
spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java

@ -1200,7 +1200,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @@ -1200,7 +1200,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
for (String candidateName : candidateNames) {
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)) {
@ -1208,14 +1208,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @@ -1208,14 +1208,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
for (String candidateName : candidateNames) {
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()) {
// Consider self references before as a final pass
for (String candidateName : candidateNames) {
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 @@ -1399,11 +1399,10 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
public boolean isRequired() {
return false;
}
@Override
public Object resolveCandidate(String beanName, BeanFactory beanFactory) {
return (!ObjectUtils.isEmpty(args) ? beanFactory.getBean(beanName, args) :
super.resolveCandidate(beanName, beanFactory));
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) {
return (!ObjectUtils.isEmpty(args) ? beanFactory.getBean(beanName, requiredType, args) :
super.resolveCandidate(beanName, requiredType, beanFactory));
}
};
descriptorToUse.increaseNestingLevel();
@ -1509,8 +1508,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @@ -1509,8 +1508,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
else {
DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) {
@Override
public Object resolveCandidate(String beanName, BeanFactory beanFactory) {
return beanFactory.getBean(beanName, args);
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) {
return ((AbstractBeanFactory) beanFactory).getBean(beanName, requiredType, args);
}
};
return doResolveDependency(descriptorToUse, this.beanName, null, null);

39
spring-context/src/test/java/org/springframework/scheduling/annotation/EnableAsyncTests.java

@ -26,6 +26,7 @@ import java.util.concurrent.Executor; @@ -26,6 +26,7 @@ import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.Advised;
@ -37,6 +38,7 @@ import org.springframework.context.annotation.AdviceMode; @@ -37,6 +38,7 @@ import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
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.util.ReflectionUtils;
@ -67,6 +69,18 @@ public class EnableAsyncTests { @@ -67,6 +69,18 @@ public class EnableAsyncTests {
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
public void withAsyncBeanWithExecutorQualifiedByName() throws ExecutionException, InterruptedException {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
@ -200,6 +214,20 @@ public class EnableAsyncTests { @@ -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)
static class CustomAsyncAnnotationConfig {
}
@ -252,6 +280,17 @@ public class EnableAsyncTests { @@ -252,6 +280,17 @@ public class EnableAsyncTests {
}
@Configuration
@EnableAsync
static class AsyncConfigWithMockito {
@Bean @Lazy
public AsyncBean asyncBean() {
return Mockito.mock(AsyncBean.class);
}
}
@Configuration
@EnableAsync
static class CustomExecutorAsyncConfig implements AsyncConfigurer {

Loading…
Cancel
Save