diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index ff8eac44036..bbc3e573379 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -443,37 +443,43 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac * @see #doCreateBean */ @Override - protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) - throws BeanCreationException { - + protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException { if (logger.isDebugEnabled()) { logger.debug("Creating instance of bean '" + beanName + "'"); } - // Make sure bean class is actually resolved at this point. - resolveBeanClass(mbd, beanName); + RootBeanDefinition mbdToUse = mbd; + + // Make sure bean class is actually resolved at this point, and + // clone the bean definition in case of a dynamically resolved Class + // which cannot be stored in the shared merged bean definition. + Class resolvedClass = resolveBeanClass(mbd, beanName); + if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) { + mbdToUse = new RootBeanDefinition(mbd); + mbdToUse.setBeanClass(resolvedClass); + } // Prepare method overrides. try { - mbd.prepareMethodOverrides(); + mbdToUse.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { - throw new BeanDefinitionStoreException(mbd.getResourceDescription(), + throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed", ex); } try { // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. - Object bean = resolveBeforeInstantiation(beanName, mbd); + Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } catch (Throwable ex) { - throw new BeanCreationException(mbd.getResourceDescription(), beanName, + throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", ex); } - Object beanInstance = doCreateBean(beanName, mbd, args); + Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isDebugEnabled()) { logger.debug("Finished creating instance of bean '" + beanName + "'"); } @@ -833,8 +839,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac /** * Obtain a "shortcut" singleton FactoryBean instance to use for a - * {@code getObjectType()} call, without full initialization - * of the FactoryBean. + * {@code getObjectType()} call, without full initialization of the FactoryBean. * @param beanName the name of the bean * @param mbd the bean definition for the bean * @return the FactoryBean instance, or {@code null} to indicate @@ -875,8 +880,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac /** * Obtain a "shortcut" non-singleton FactoryBean instance to use for a - * {@code getObjectType()} call, without full initialization - * of the FactoryBean. + * {@code getObjectType()} call, without full initialization of the FactoryBean. * @param beanName the name of the bean * @param mbd the bean definition for the bean * @return the FactoryBean instance, or {@code null} to indicate diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index cb53fcbc5d8..160631d16c0 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -1333,20 +1333,44 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp } private Class doResolveBeanClass(RootBeanDefinition mbd, Class... typesToMatch) throws ClassNotFoundException { + ClassLoader beanClassLoader = getBeanClassLoader(); + ClassLoader classLoaderToUse = beanClassLoader; if (!ObjectUtils.isEmpty(typesToMatch)) { + // When just doing type checks (i.e. not creating an actual instance yet), + // use the specified temporary class loader (e.g. in a weaving scenario). ClassLoader tempClassLoader = getTempClassLoader(); if (tempClassLoader != null) { + classLoaderToUse = tempClassLoader; if (tempClassLoader instanceof DecoratingClassLoader) { DecoratingClassLoader dcl = (DecoratingClassLoader) tempClassLoader; for (Class typeToMatch : typesToMatch) { dcl.excludeClass(typeToMatch.getName()); } } - String className = mbd.getBeanClassName(); - return (className != null ? ClassUtils.forName(className, tempClassLoader) : null); } } - return mbd.resolveBeanClass(getBeanClassLoader()); + String className = mbd.getBeanClassName(); + if (className != null) { + Object evaluated = evaluateBeanDefinitionString(className, mbd); + if (!className.equals(evaluated)) { + // A dynamically resolved expression, supported as of 4.2... + if (evaluated instanceof Class) { + return (Class) evaluated; + } + else if (evaluated instanceof String) { + return ClassUtils.forName((String) evaluated, classLoaderToUse); + } + else { + throw new IllegalStateException("Invalid class name expression result: " + evaluated); + } + } + // When resolving against a temporary class loader, exit early in order + // to avoid storing the resolved Class in the bean definition. + if (classLoaderToUse != beanClassLoader) { + return ClassUtils.forName(className, classLoaderToUse); + } + } + return mbd.resolveBeanClass(beanClassLoader); } /** diff --git a/spring-context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java b/spring-context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java index e940b51c9e7..7fb41c022e9 100644 --- a/spring-context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java +++ b/spring-context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -101,14 +101,14 @@ public class ApplicationContextExpressionTests { ac.registerBeanDefinition("tb0", bd0); GenericBeanDefinition bd1 = new GenericBeanDefinition(); - bd1.setBeanClass(TestBean.class); + bd1.setBeanClassName("#{tb0.class}"); bd1.setScope("myScope"); bd1.getConstructorArgumentValues().addGenericArgumentValue("XXX#{tb0.name}YYY#{mySpecialAttr}ZZZ"); bd1.getConstructorArgumentValues().addGenericArgumentValue("#{mySpecialAttr}"); ac.registerBeanDefinition("tb1", bd1); GenericBeanDefinition bd2 = new GenericBeanDefinition(); - bd2.setBeanClass(TestBean.class); + bd2.setBeanClassName("#{tb1.class.name}"); bd2.setScope("myScope"); bd2.getPropertyValues().add("name", "{ XXX#{tb0.name}YYY#{mySpecialAttr}ZZZ }"); bd2.getPropertyValues().add("age", "#{mySpecialAttr}");