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