LoadTimeWeaverAware beans are consistently being created early for JPA weaving to work reliably
Includes a change for factory methods that declare their return type as FactoryBean: When asked for a specific type match (e.g. LoadTimeWeaverAware), we do check early singleton instances as well (reusing the instances that we create for getObjectType checks). This is necessary in order to make @Bean method introspection as capable as XML bean definition introspection, even in case of the @Bean method using a generic FactoryBean declaration for its return type (instead of the FactoryBean impl class). Issue: SPR-9857
This commit is contained in:
parent
cfdcb54711
commit
3c59725b94
|
|
@ -590,6 +590,10 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
}
|
||||
}
|
||||
}
|
||||
if (FactoryBean.class.equals(beanClass) && mbd.isSingleton() &&
|
||||
(typesToMatch.length > 1 || (typesToMatch.length == 1 && !typesToMatch[0].equals(FactoryBean.class)))) {
|
||||
return getSingletonFactoryBeanForTypeCheck(beanName, mbd).getClass();
|
||||
}
|
||||
return beanClass;
|
||||
}
|
||||
|
||||
|
|
@ -642,10 +646,9 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
Method[] candidates = ReflectionUtils.getUniqueDeclaredMethods(factoryClass);
|
||||
Set<Class<?>> returnTypes = new HashSet<Class<?>>(1);
|
||||
for (Method factoryMethod : candidates) {
|
||||
if (Modifier.isStatic(factoryMethod.getModifiers()) == isStatic
|
||||
&& factoryMethod.getName().equals(mbd.getFactoryMethodName())
|
||||
&& factoryMethod.getParameterTypes().length >= minNrOfArgs) {
|
||||
|
||||
if (Modifier.isStatic(factoryMethod.getModifiers()) == isStatic &&
|
||||
factoryMethod.getName().equals(mbd.getFactoryMethodName()) &&
|
||||
factoryMethod.getParameterTypes().length >= minNrOfArgs) {
|
||||
Class<?> returnType = GenericTypeResolver.resolveReturnTypeForGenericMethod(factoryMethod, args);
|
||||
if (returnType != null) {
|
||||
returnTypes.add(returnType);
|
||||
|
|
@ -684,12 +687,12 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
// Try to obtain the FactoryBean's object type without instantiating it at all.
|
||||
BeanDefinition fbDef = getBeanDefinition(factoryBeanName);
|
||||
if (fbDef instanceof AbstractBeanDefinition) {
|
||||
Class<?> fbClass = ((AbstractBeanDefinition)fbDef).getBeanClass();
|
||||
Class<?> fbClass = ((AbstractBeanDefinition) fbDef).getBeanClass();
|
||||
if (ClassUtils.isCglibProxyClass(fbClass)) {
|
||||
// CGLIB subclass methods hide generic parameters. look at the superclass.
|
||||
fbClass = fbClass.getSuperclass();
|
||||
}
|
||||
// find the given factory method, taking into account that in the case of
|
||||
// Find the given factory method, taking into account that in the case of
|
||||
// @Bean methods, there may be parameters present.
|
||||
ReflectionUtils.doWithMethods(fbClass,
|
||||
new ReflectionUtils.MethodCallback() {
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ import org.springframework.context.event.ContextStartedEvent;
|
|||
import org.springframework.context.event.ContextStoppedEvent;
|
||||
import org.springframework.context.event.SimpleApplicationEventMulticaster;
|
||||
import org.springframework.context.expression.StandardBeanExpressionResolver;
|
||||
import org.springframework.context.weaving.LoadTimeWeaverAware;
|
||||
import org.springframework.context.weaving.LoadTimeWeaverAwareProcessor;
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.core.Ordered;
|
||||
|
|
@ -915,6 +916,12 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
|||
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
|
||||
}
|
||||
|
||||
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
|
||||
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, true);
|
||||
for (String weaverAwareName : weaverAwareNames) {
|
||||
getBean(weaverAwareName);
|
||||
}
|
||||
|
||||
// Stop using the temporary ClassLoader for type matching.
|
||||
beanFactory.setTempClassLoader(null);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
|
|
@ -16,14 +16,17 @@
|
|||
|
||||
package org.springframework.context.annotation.configuration;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Required;
|
||||
import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor;
|
||||
|
|
@ -32,6 +35,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
|||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ListFactoryBean;
|
||||
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
|
|
@ -46,8 +50,7 @@ import org.springframework.context.annotation.Scope;
|
|||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Miscellaneous system tests covering {@link Bean} naming, aliases, scoping and error
|
||||
|
|
@ -65,7 +68,7 @@ public class ConfigurationClassProcessingTests {
|
|||
* When complete, the factory is ready to service requests for any {@link Bean} methods
|
||||
* declared by <var>configClasses</var>.
|
||||
*/
|
||||
private BeanFactory initBeanFactory(Class<?>... configClasses) {
|
||||
private ListableBeanFactory initBeanFactory(Class<?>... configClasses) {
|
||||
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
|
||||
for (Class<?> configClass : configClasses) {
|
||||
String configBeanName = configClass.getName();
|
||||
|
|
@ -126,6 +129,42 @@ public class ConfigurationClassProcessingTests {
|
|||
assertEquals(stringBean, "foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configWithObjectReturnType() {
|
||||
BeanFactory factory = initBeanFactory(ConfigWithNonSpecificReturnTypes.class);
|
||||
assertEquals(Object.class, factory.getType("stringBean"));
|
||||
assertFalse(factory.isTypeMatch("stringBean", String.class));
|
||||
String stringBean = factory.getBean("stringBean", String.class);
|
||||
assertEquals(stringBean, "foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configWithFactoryBeanReturnType() {
|
||||
ListableBeanFactory factory = initBeanFactory(ConfigWithNonSpecificReturnTypes.class);
|
||||
assertEquals(List.class, factory.getType("factoryBean"));
|
||||
assertTrue(factory.isTypeMatch("factoryBean", List.class));
|
||||
assertEquals(FactoryBean.class, factory.getType("&factoryBean"));
|
||||
assertTrue(factory.isTypeMatch("&factoryBean", FactoryBean.class));
|
||||
assertTrue(factory.isTypeMatch("&factoryBean", BeanClassLoaderAware.class));
|
||||
assertTrue(factory.isTypeMatch("&factoryBean", ListFactoryBean.class));
|
||||
assertTrue(factory.getBean("factoryBean") instanceof List);
|
||||
|
||||
String[] beanNames = factory.getBeanNamesForType(FactoryBean.class);
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("&factoryBean", beanNames[0]);
|
||||
|
||||
beanNames = factory.getBeanNamesForType(BeanClassLoaderAware.class);
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("&factoryBean", beanNames[0]);
|
||||
|
||||
beanNames = factory.getBeanNamesForType(ListFactoryBean.class);
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("&factoryBean", beanNames[0]);
|
||||
|
||||
beanNames = factory.getBeanNamesForType(List.class);
|
||||
assertEquals("factoryBean", beanNames[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configurationWithPrototypeScopedBeans() {
|
||||
BeanFactory factory = initBeanFactory(ConfigWithPrototypeBean.class);
|
||||
|
|
@ -186,6 +225,19 @@ public class ConfigurationClassProcessingTests {
|
|||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class ConfigWithNonSpecificReturnTypes {
|
||||
public @Bean Object stringBean() {
|
||||
return "foo";
|
||||
}
|
||||
public @Bean FactoryBean factoryBean() {
|
||||
ListFactoryBean fb = new ListFactoryBean();
|
||||
fb.setSourceList(Arrays.asList("element1", "element2"));
|
||||
return fb;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class ConfigWithBeanWithAliases {
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue