diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java index 53f00e5e47..a4e5c326e4 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 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. @@ -79,7 +79,7 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt @Override protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner, - @Nullable Constructor ctor, @Nullable Object... args) { + @Nullable Constructor ctor, Object... args) { // Must generate CGLIB subclass... return new CglibSubclassCreator(bd, owner).instantiate(ctor, args); @@ -113,7 +113,7 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt * Ignored if the {@code ctor} parameter is {@code null}. * @return new instance of the dynamically generated subclass */ - public Object instantiate(@Nullable Constructor ctor, @Nullable Object... args) { + public Object instantiate(@Nullable Constructor ctor, Object... args) { Class subclass = createEnhancedSubclass(this.beanDefinition); Object instance; if (ctor == null) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java index 5edd74648e..f44b2a2f40 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java @@ -79,6 +79,8 @@ import org.springframework.util.StringUtils; */ class ConstructorResolver { + private static final Object[] EMPTY_ARGS = new Object[0]; + private static final NamedThreadLocal currentInjectionPoint = new NamedThreadLocal<>("Current injection point"); @@ -111,8 +113,8 @@ class ConstructorResolver { * or {@code null} if none (-> use constructor argument values from bean definition) * @return a BeanWrapper for the new instance */ - public BeanWrapper autowireConstructor(final String beanName, final RootBeanDefinition mbd, - @Nullable Constructor[] chosenCtors, @Nullable final Object[] explicitArgs) { + public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, + @Nullable Constructor[] chosenCtors, @Nullable Object[] explicitArgs) { BeanWrapperImpl bw = new BeanWrapperImpl(); this.beanFactory.initBeanWrapper(bw); @@ -141,7 +143,35 @@ class ConstructorResolver { } } - if (constructorToUse == null) { + if (constructorToUse == null || argsToUse == null) { + // Take specified constructors, if any. + Constructor[] candidates = chosenCtors; + if (candidates == null) { + Class beanClass = mbd.getBeanClass(); + try { + candidates = (mbd.isNonPublicAccessAllowed() ? + beanClass.getDeclaredConstructors() : beanClass.getConstructors()); + } + catch (Throwable ex) { + throw new BeanCreationException(mbd.getResourceDescription(), beanName, + "Resolution of declared constructors on bean Class [" + beanClass.getName() + + "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex); + } + } + + if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) { + Constructor uniqueCandidate = candidates[0]; + if (uniqueCandidate.getParameterCount() == 0) { + synchronized (mbd.constructorArgumentLock) { + mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate; + mbd.constructorArgumentsResolved = true; + mbd.resolvedConstructorArguments = EMPTY_ARGS; + } + bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS)); + return bw; + } + } + // Need to resolve the constructor. boolean autowiring = (chosenCtors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); @@ -157,20 +187,6 @@ class ConstructorResolver { minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); } - // Take specified constructors, if any. - Constructor[] candidates = chosenCtors; - if (candidates == null) { - Class beanClass = mbd.getBeanClass(); - try { - candidates = (mbd.isNonPublicAccessAllowed() ? - beanClass.getDeclaredConstructors() : beanClass.getConstructors()); - } - catch (Throwable ex) { - throw new BeanCreationException(mbd.getResourceDescription(), beanName, - "Resolution of declared constructors on bean Class [" + beanClass.getName() + - "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex); - } - } AutowireUtils.sortConstructors(candidates); int minTypeDiffWeight = Integer.MAX_VALUE; Set> ambiguousConstructors = null; @@ -264,23 +280,23 @@ class ConstructorResolver { } } - try { - final InstantiationStrategy strategy = this.beanFactory.getInstantiationStrategy(); - Object beanInstance; + bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse)); + return bw; + } + + private Object instantiate( + String beanName, RootBeanDefinition mbd, Constructor constructorToUse, Object[] argsToUse) { + try { + InstantiationStrategy strategy = this.beanFactory.getInstantiationStrategy(); if (System.getSecurityManager() != null) { - final Constructor ctorToUse = constructorToUse; - final Object[] argumentsToUse = argsToUse; - beanInstance = AccessController.doPrivileged((PrivilegedAction) () -> - strategy.instantiate(mbd, beanName, this.beanFactory, ctorToUse, argumentsToUse), + return AccessController.doPrivileged((PrivilegedAction) () -> + strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse), this.beanFactory.getAccessControlContext()); } else { - beanInstance = strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse); + return strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse); } - - bw.setBeanInstance(beanInstance); - return bw; } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, @@ -330,7 +346,7 @@ class ConstructorResolver { * the {@link RootBeanDefinition#isNonPublicAccessAllowed()} flag. * Called as the starting point for factory method determination. */ - private Method[] getCandidateMethods(final Class factoryClass, final RootBeanDefinition mbd) { + private Method[] getCandidateMethods(Class factoryClass, RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { return AccessController.doPrivileged((PrivilegedAction) () -> (mbd.isNonPublicAccessAllowed() ? @@ -358,7 +374,7 @@ class ConstructorResolver { * @return a BeanWrapper for the new instance */ public BeanWrapper instantiateUsingFactoryMethod( - final String beanName, final RootBeanDefinition mbd, @Nullable final Object[] explicitArgs) { + String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) { BeanWrapperImpl bw = new BeanWrapperImpl(); this.beanFactory.initBeanWrapper(bw); @@ -421,13 +437,27 @@ class ConstructorResolver { factoryClass = ClassUtils.getUserClass(factoryClass); Method[] rawCandidates = getCandidateMethods(factoryClass, mbd); - List candidateSet = new ArrayList<>(); + List candidateList = new ArrayList<>(); for (Method candidate : rawCandidates) { if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) { - candidateSet.add(candidate); + candidateList.add(candidate); + } + } + + if (candidateList.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) { + Method uniqueCandidate = candidateList.get(0); + if (uniqueCandidate.getParameterCount() == 0) { + synchronized (mbd.constructorArgumentLock) { + mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate; + mbd.constructorArgumentsResolved = true; + mbd.resolvedConstructorArguments = EMPTY_ARGS; + } + bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS)); + return bw; } } - Method[] candidates = candidateSet.toArray(new Method[0]); + + Method[] candidates = candidateList.toArray(new Method[0]); AutowireUtils.sortFactoryMethods(candidates); ConstructorArgumentValues resolvedValues = null; @@ -460,7 +490,7 @@ class ConstructorResolver { if (paramTypes.length >= minNrOfArgs) { ArgumentsHolder argsHolder; - if (explicitArgs != null){ + if (explicitArgs != null) { // Explicit arguments given -> arguments length must match exactly. if (paramTypes.length != explicitArgs.length) { continue; @@ -533,7 +563,7 @@ class ConstructorResolver { argTypes.add(arg != null ? arg.getClass().getSimpleName() : "null"); } } - else if (resolvedValues != null){ + else if (resolvedValues != null) { Set valueHolders = new LinkedHashSet<>(resolvedValues.getArgumentCount()); valueHolders.addAll(resolvedValues.getIndexedArgumentValues().values()); valueHolders.addAll(resolvedValues.getGenericArgumentValues()); @@ -571,24 +601,24 @@ class ConstructorResolver { } } - try { - Object beanInstance; + bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse)); + return bw; + } + private Object instantiate( + String beanName, RootBeanDefinition mbd, Object factoryBean, Method factoryMethod, Object[] args) { + + try { if (System.getSecurityManager() != null) { - final Object fb = factoryBean; - final Method factoryMethod = factoryMethodToUse; - final Object[] args = argsToUse; - beanInstance = AccessController.doPrivileged((PrivilegedAction) () -> - this.beanFactory.getInstantiationStrategy().instantiate(mbd, beanName, this.beanFactory, fb, factoryMethod, args), + return AccessController.doPrivileged((PrivilegedAction) () -> + this.beanFactory.getInstantiationStrategy().instantiate( + mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args), this.beanFactory.getAccessControlContext()); } else { - beanInstance = this.beanFactory.getInstantiationStrategy().instantiate( - mbd, beanName, this.beanFactory, factoryBean, factoryMethodToUse, argsToUse); + return this.beanFactory.getInstantiationStrategy().instantiate( + mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args); } - - bw.setBeanInstance(beanInstance); - return bw; } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/InstantiationStrategy.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/InstantiationStrategy.java index e2f38783ff..029ab0e98c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/InstantiationStrategy.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/InstantiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 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. @@ -38,8 +38,8 @@ public interface InstantiationStrategy { /** * Return an instance of the bean with the given name in this factory. * @param bd the bean definition - * @param beanName the name of the bean when it's created in this context. - * The name can be {@code null} if we're autowiring a bean which doesn't + * @param beanName the name of the bean when it is created in this context. + * The name can be {@code null} if we are autowiring a bean which doesn't * belong to the factory. * @param owner the owning BeanFactory * @return a bean instance for this bean definition @@ -52,8 +52,8 @@ public interface InstantiationStrategy { * Return an instance of the bean with the given name in this factory, * creating it via the given constructor. * @param bd the bean definition - * @param beanName the name of the bean when it's created in this context. - * The name can be {@code null} if we're autowiring a bean which doesn't + * @param beanName the name of the bean when it is created in this context. + * The name can be {@code null} if we are autowiring a bean which doesn't * belong to the factory. * @param owner the owning BeanFactory * @param ctor the constructor to use @@ -62,14 +62,14 @@ public interface InstantiationStrategy { * @throws BeansException if the instantiation attempt failed */ Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner, - Constructor ctor, @Nullable Object... args) throws BeansException; + Constructor ctor, Object... args) throws BeansException; /** * Return an instance of the bean with the given name in this factory, * creating it via the given factory method. * @param bd the bean definition - * @param beanName the name of the bean when it's created in this context. - * The name can be {@code null} if we're autowiring a bean which doesn't + * @param beanName the name of the bean when it is created in this context. + * The name can be {@code null} if we are autowiring a bean which doesn't * belong to the factory. * @param owner the owning BeanFactory * @param factoryBean the factory bean instance to call the factory method on, @@ -80,7 +80,7 @@ public interface InstantiationStrategy { * @throws BeansException if the instantiation attempt failed */ Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner, - @Nullable Object factoryBean, Method factoryMethod, @Nullable Object... args) + @Nullable Object factoryBean, Method factoryMethod, Object... args) throws BeansException; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleInstantiationStrategy.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleInstantiationStrategy.java index 42449980d3..35b91cc4de 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleInstantiationStrategy.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleInstantiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 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. @@ -104,7 +104,7 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy { @Override public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner, - final Constructor ctor, @Nullable Object... args) { + final Constructor ctor, Object... args) { if (!bd.hasMethodOverrides()) { if (System.getSecurityManager() != null) { @@ -114,7 +114,7 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy { return null; }); } - return (args != null ? BeanUtils.instantiateClass(ctor, args) : BeanUtils.instantiateClass(ctor)); + return BeanUtils.instantiateClass(ctor, args); } else { return instantiateWithMethodInjection(bd, beanName, owner, ctor, args); @@ -128,14 +128,14 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy { * Instantiation should use the given constructor and parameters. */ protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, - BeanFactory owner, @Nullable Constructor ctor, @Nullable Object... args) { + BeanFactory owner, @Nullable Constructor ctor, Object... args) { throw new UnsupportedOperationException("Method Injection not supported in SimpleInstantiationStrategy"); } @Override public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner, - @Nullable Object factoryBean, final Method factoryMethod, @Nullable Object... args) { + @Nullable Object factoryBean, final Method factoryMethod, Object... args) { try { if (System.getSecurityManager() != null) {