001/*
002 * Copyright 2002-2016 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.util.ReflectionUtils;
031import org.springframework.util.StringUtils;
032
033/**
034 * Simple object instantiation strategy for use in a BeanFactory.
035 *
036 * <p>Does not support Method Injection, although it provides hooks for subclasses
037 * to override to add Method Injection support, for example by overriding methods.
038 *
039 * @author Rod Johnson
040 * @author Juergen Hoeller
041 * @since 1.1
042 */
043public class SimpleInstantiationStrategy implements InstantiationStrategy {
044
045        private static final ThreadLocal<Method> currentlyInvokedFactoryMethod = new ThreadLocal<Method>();
046
047
048        /**
049         * Return the factory method currently being invoked or {@code null} if none.
050         * <p>Allows factory method implementations to determine whether the current
051         * caller is the container itself as opposed to user code.
052         */
053        public static Method getCurrentlyInvokedFactoryMethod() {
054                return currentlyInvokedFactoryMethod.get();
055        }
056
057
058        @Override
059        public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
060                // Don't override the class with CGLIB if no overrides.
061                if (bd.getMethodOverrides().isEmpty()) {
062                        Constructor<?> constructorToUse;
063                        synchronized (bd.constructorArgumentLock) {
064                                constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
065                                if (constructorToUse == null) {
066                                        final Class<?> clazz = bd.getBeanClass();
067                                        if (clazz.isInterface()) {
068                                                throw new BeanInstantiationException(clazz, "Specified class is an interface");
069                                        }
070                                        try {
071                                                if (System.getSecurityManager() != null) {
072                                                        constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {
073                                                                @Override
074                                                                public Constructor<?> run() throws Exception {
075                                                                        return clazz.getDeclaredConstructor((Class[]) null);
076                                                                }
077                                                        });
078                                                }
079                                                else {
080                                                        constructorToUse =      clazz.getDeclaredConstructor((Class[]) null);
081                                                }
082                                                bd.resolvedConstructorOrFactoryMethod = constructorToUse;
083                                        }
084                                        catch (Throwable ex) {
085                                                throw new BeanInstantiationException(clazz, "No default constructor found", ex);
086                                        }
087                                }
088                        }
089                        return BeanUtils.instantiateClass(constructorToUse);
090                }
091                else {
092                        // Must generate CGLIB subclass.
093                        return instantiateWithMethodInjection(bd, beanName, owner);
094                }
095        }
096
097        /**
098         * Subclasses can override this method, which is implemented to throw
099         * UnsupportedOperationException, if they can instantiate an object with
100         * the Method Injection specified in the given RootBeanDefinition.
101         * Instantiation should use a no-arg constructor.
102         */
103        protected Object instantiateWithMethodInjection(RootBeanDefinition bd, String beanName, BeanFactory owner) {
104                throw new UnsupportedOperationException("Method Injection not supported in SimpleInstantiationStrategy");
105        }
106
107        @Override
108        public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner,
109                        final Constructor<?> ctor, Object... args) {
110
111                if (bd.getMethodOverrides().isEmpty()) {
112                        if (System.getSecurityManager() != null) {
113                                // use own privileged to change accessibility (when security is on)
114                                AccessController.doPrivileged(new PrivilegedAction<Object>() {
115                                        @Override
116                                        public Object run() {
117                                                ReflectionUtils.makeAccessible(ctor);
118                                                return null;
119                                        }
120                                });
121                        }
122                        return BeanUtils.instantiateClass(ctor, args);
123                }
124                else {
125                        return instantiateWithMethodInjection(bd, beanName, owner, ctor, args);
126                }
127        }
128
129        /**
130         * Subclasses can override this method, which is implemented to throw
131         * UnsupportedOperationException, if they can instantiate an object with
132         * the Method Injection specified in the given RootBeanDefinition.
133         * Instantiation should use the given constructor and parameters.
134         */
135        protected Object instantiateWithMethodInjection(RootBeanDefinition bd, String beanName, BeanFactory owner,
136                        Constructor<?> ctor, Object... args) {
137
138                throw new UnsupportedOperationException("Method Injection not supported in SimpleInstantiationStrategy");
139        }
140
141        @Override
142        public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner,
143                        Object factoryBean, final Method factoryMethod, Object... args) {
144
145                try {
146                        if (System.getSecurityManager() != null) {
147                                AccessController.doPrivileged(new PrivilegedAction<Object>() {
148                                        @Override
149                                        public Object run() {
150                                                ReflectionUtils.makeAccessible(factoryMethod);
151                                                return null;
152                                        }
153                                });
154                        }
155                        else {
156                                ReflectionUtils.makeAccessible(factoryMethod);
157                        }
158
159                        Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
160                        try {
161                                currentlyInvokedFactoryMethod.set(factoryMethod);
162                                return factoryMethod.invoke(factoryBean, args);
163                        }
164                        finally {
165                                if (priorInvokedFactoryMethod != null) {
166                                        currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
167                                }
168                                else {
169                                        currentlyInvokedFactoryMethod.remove();
170                                }
171                        }
172                }
173                catch (IllegalArgumentException ex) {
174                        throw new BeanInstantiationException(factoryMethod,
175                                        "Illegal arguments to factory method '" + factoryMethod.getName() + "'; " +
176                                        "args: " + StringUtils.arrayToCommaDelimitedString(args), ex);
177                }
178                catch (IllegalAccessException ex) {
179                        throw new BeanInstantiationException(factoryMethod,
180                                        "Cannot access factory method '" + factoryMethod.getName() + "'; is it public?", ex);
181                }
182                catch (InvocationTargetException ex) {
183                        String msg = "Factory method '" + factoryMethod.getName() + "' threw exception";
184                        if (bd.getFactoryBeanName() != null && owner instanceof ConfigurableBeanFactory &&
185                                        ((ConfigurableBeanFactory) owner).isCurrentlyInCreation(bd.getFactoryBeanName())) {
186                                msg = "Circular reference involving containing bean '" + bd.getFactoryBeanName() + "' - consider " +
187                                                "declaring the factory method as static for independence from its containing instance. " + msg;
188                        }
189                        throw new BeanInstantiationException(factoryMethod, msg, ex.getTargetException());
190                }
191        }
192
193}