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:
Juergen Hoeller 2012-10-12 16:23:31 +02:00 committed by unknown
parent cfdcb54711
commit 3c59725b94
3 changed files with 77 additions and 15 deletions

View File

@ -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() {

View File

@ -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);

View File

@ -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 {