View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.beans.factory.support;
18  
19  import java.lang.reflect.Constructor;
20  import java.lang.reflect.InvocationTargetException;
21  import java.lang.reflect.Method;
22  import java.security.AccessController;
23  import java.security.PrivilegedAction;
24  import java.security.PrivilegedExceptionAction;
25  
26  import org.springframework.beans.BeanInstantiationException;
27  import org.springframework.beans.BeanUtils;
28  import org.springframework.beans.factory.BeanFactory;
29  import org.springframework.beans.factory.config.ConfigurableBeanFactory;
30  import org.springframework.util.ReflectionUtils;
31  import org.springframework.util.StringUtils;
32  
33  /**
34   * Simple object instantiation strategy for use in a BeanFactory.
35   *
36   * <p>Does not support Method Injection, although it provides hooks for subclasses
37   * to override to add Method Injection support, for example by overriding methods.
38   *
39   * @author Rod Johnson
40   * @author Juergen Hoeller
41   * @since 1.1
42   */
43  public class SimpleInstantiationStrategy implements InstantiationStrategy {
44  
45  	private static final ThreadLocal<Method> currentlyInvokedFactoryMethod = new ThreadLocal<Method>();
46  
47  
48  	/**
49  	 * Return the factory method currently being invoked or {@code null} if none.
50  	 * <p>Allows factory method implementations to determine whether the current
51  	 * caller is the container itself as opposed to user code.
52  	 */
53  	public static Method getCurrentlyInvokedFactoryMethod() {
54  		return currentlyInvokedFactoryMethod.get();
55  	}
56  
57  
58  	@Override
59  	public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
60  		// Don't override the class with CGLIB if no overrides.
61  		if (bd.getMethodOverrides().isEmpty()) {
62  			Constructor<?> constructorToUse;
63  			synchronized (bd.constructorArgumentLock) {
64  				constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
65  				if (constructorToUse == null) {
66  					final Class<?> clazz = bd.getBeanClass();
67  					if (clazz.isInterface()) {
68  						throw new BeanInstantiationException(clazz, "Specified class is an interface");
69  					}
70  					try {
71  						if (System.getSecurityManager() != null) {
72  							constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {
73  								@Override
74  								public Constructor<?> run() throws Exception {
75  									return clazz.getDeclaredConstructor((Class[]) null);
76  								}
77  							});
78  						}
79  						else {
80  							constructorToUse =	clazz.getDeclaredConstructor((Class[]) null);
81  						}
82  						bd.resolvedConstructorOrFactoryMethod = constructorToUse;
83  					}
84  					catch (Exception ex) {
85  						throw new BeanInstantiationException(clazz, "No default constructor found", ex);
86  					}
87  				}
88  			}
89  			return BeanUtils.instantiateClass(constructorToUse);
90  		}
91  		else {
92  			// Must generate CGLIB subclass.
93  			return instantiateWithMethodInjection(bd, beanName, owner);
94  		}
95  	}
96  
97  	/**
98  	 * Subclasses can override this method, which is implemented to throw
99  	 * 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.getReturnType(),
175 					"Illegal arguments to factory method '" + factoryMethod.getName() + "'; " +
176 					"args: " + StringUtils.arrayToCommaDelimitedString(args), ex);
177 		}
178 		catch (IllegalAccessException ex) {
179 			throw new BeanInstantiationException(factoryMethod.getReturnType(),
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.getReturnType(), msg, ex.getTargetException());
190 		}
191 	}
192 
193 }