001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.beans.factory.support;
018
019import java.lang.reflect.Constructor;
020import java.lang.reflect.InvocationTargetException;
021import java.lang.reflect.Method;
022import java.security.AccessController;
023import java.security.PrivilegedAction;
024import java.security.PrivilegedExceptionAction;
025
026import org.springframework.beans.BeanInstantiationException;
027import org.springframework.beans.BeanUtils;
028import org.springframework.beans.factory.BeanFactory;
029import org.springframework.beans.factory.config.ConfigurableBeanFactory;
030import org.springframework.lang.Nullable;
031import org.springframework.util.ReflectionUtils;
032import org.springframework.util.StringUtils;
033
034/**
035 * Simple object instantiation strategy for use in a BeanFactory.
036 *
037 * <p>Does not support Method Injection, although it provides hooks for subclasses
038 * to override to add Method Injection support, for example by overriding methods.
039 *
040 * @author Rod Johnson
041 * @author Juergen Hoeller
042 * @since 1.1
043 */
044public class SimpleInstantiationStrategy implements InstantiationStrategy {
045
046        private static final ThreadLocal<Method> currentlyInvokedFactoryMethod = new ThreadLocal<>();
047
048
049        /**
050         * Return the factory method currently being invoked or {@code null} if none.
051         * <p>Allows factory method implementations to determine whether the current
052         * caller is the container itself as opposed to user code.
053         */
054        @Nullable
055        public static Method getCurrentlyInvokedFactoryMethod() {
056                return currentlyInvokedFactoryMethod.get();
057        }
058
059
060        @Override
061        public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
062                // Don't override the class with CGLIB if no overrides.
063                if (!bd.hasMethodOverrides()) {
064                        Constructor<?> constructorToUse;
065                        synchronized (bd.constructorArgumentLock) {
066                                constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
067                                if (constructorToUse == null) {
068                                        final Class<?> clazz = bd.getBeanClass();
069                                        if (clazz.isInterface()) {
070                                                throw new BeanInstantiationException(clazz, "Specified class is an interface");
071                                        }
072                                        try {
073                                                if (System.getSecurityManager() != null) {
074                                                        constructorToUse = AccessController.doPrivileged(
075                                                                        (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
076                                                }
077                                                else {
078                                                        constructorToUse = clazz.getDeclaredConstructor();
079                                                }
080                                                bd.resolvedConstructorOrFactoryMethod = constructorToUse;
081                                        }
082                                        catch (Throwable ex) {
083                                                throw new BeanInstantiationException(clazz, "No default constructor found", ex);
084                                        }
085                                }
086                        }
087                        return BeanUtils.instantiateClass(constructorToUse);
088                }
089                else {
090                        // Must generate CGLIB subclass.
091                        return instantiateWithMethodInjection(bd, beanName, owner);
092                }
093        }
094
095        /**
096         * Subclasses can override this method, which is implemented to throw
097         * UnsupportedOperationException, if they can instantiate an object with
098         * the Method Injection specified in the given RootBeanDefinition.
099         * Instantiation should use a no-arg constructor.
100         */
101        protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
102                throw new UnsupportedOperationException("Method Injection not supported in SimpleInstantiationStrategy");
103        }
104
105        @Override
106        public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
107                        final Constructor<?> ctor, Object... args) {
108
109                if (!bd.hasMethodOverrides()) {
110                        if (System.getSecurityManager() != null) {
111                                // use own privileged to change accessibility (when security is on)
112                                AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
113                                        ReflectionUtils.makeAccessible(ctor);
114                                        return null;
115                                });
116                        }
117                        return BeanUtils.instantiateClass(ctor, args);
118                }
119                else {
120                        return instantiateWithMethodInjection(bd, beanName, owner, ctor, args);
121                }
122        }
123
124        /**
125         * Subclasses can override this method, which is implemented to throw
126         * UnsupportedOperationException, if they can instantiate an object with
127         * the Method Injection specified in the given RootBeanDefinition.
128         * Instantiation should use the given constructor and parameters.
129         */
130        protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName,
131                        BeanFactory owner, @Nullable Constructor<?> ctor, Object... args) {
132
133                throw new UnsupportedOperationException("Method Injection not supported in SimpleInstantiationStrategy");
134        }
135
136        @Override
137        public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
138                        @Nullable Object factoryBean, final Method factoryMethod, Object... args) {
139
140                try {
141                        if (System.getSecurityManager() != null) {
142                                AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
143                                        ReflectionUtils.makeAccessible(factoryMethod);
144                                        return null;
145                                });
146                        }
147                        else {
148                                ReflectionUtils.makeAccessible(factoryMethod);
149                        }
150
151                        Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
152                        try {
153                                currentlyInvokedFactoryMethod.set(factoryMethod);
154                                Object result = factoryMethod.invoke(factoryBean, args);
155                                if (result == null) {
156                                        result = new NullBean();
157                                }
158                                return result;
159                        }
160                        finally {
161                                if (priorInvokedFactoryMethod != null) {
162                                        currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
163                                }
164                                else {
165                                        currentlyInvokedFactoryMethod.remove();
166                                }
167                        }
168                }
169                catch (IllegalArgumentException ex) {
170                        throw new BeanInstantiationException(factoryMethod,
171                                        "Illegal arguments to factory method '" + factoryMethod.getName() + "'; " +
172                                        "args: " + StringUtils.arrayToCommaDelimitedString(args), ex);
173                }
174                catch (IllegalAccessException ex) {
175                        throw new BeanInstantiationException(factoryMethod,
176                                        "Cannot access factory method '" + factoryMethod.getName() + "'; is it public?", ex);
177                }
178                catch (InvocationTargetException ex) {
179                        String msg = "Factory method '" + factoryMethod.getName() + "' threw exception";
180                        if (bd.getFactoryBeanName() != null && owner instanceof ConfigurableBeanFactory &&
181                                        ((ConfigurableBeanFactory) owner).isCurrentlyInCreation(bd.getFactoryBeanName())) {
182                                msg = "Circular reference involving containing bean '" + bd.getFactoryBeanName() + "' - consider " +
183                                                "declaring the factory method as static for independence from its containing instance. " + msg;
184                        }
185                        throw new BeanInstantiationException(factoryMethod, msg, ex.getTargetException());
186                }
187        }
188
189}