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.Method;
21  
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  
25  import org.springframework.beans.BeanInstantiationException;
26  import org.springframework.beans.BeanUtils;
27  import org.springframework.beans.factory.BeanFactory;
28  import org.springframework.cglib.core.SpringNamingPolicy;
29  import org.springframework.cglib.proxy.Callback;
30  import org.springframework.cglib.proxy.CallbackFilter;
31  import org.springframework.cglib.proxy.Enhancer;
32  import org.springframework.cglib.proxy.Factory;
33  import org.springframework.cglib.proxy.MethodInterceptor;
34  import org.springframework.cglib.proxy.MethodProxy;
35  import org.springframework.cglib.proxy.NoOp;
36  import org.springframework.util.StringUtils;
37  
38  /**
39   * Default object instantiation strategy for use in BeanFactories.
40   *
41   * <p>Uses CGLIB to generate subclasses dynamically if methods need to be
42   * overridden by the container to implement <em>Method Injection</em>.
43   *
44   * @author Rod Johnson
45   * @author Juergen Hoeller
46   * @author Sam Brannen
47   * @since 1.1
48   */
49  public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationStrategy {
50  
51  	/**
52  	 * Index in the CGLIB callback array for passthrough behavior,
53  	 * in which case the subclass won't override the original class.
54  	 */
55  	private static final int PASSTHROUGH = 0;
56  
57  	/**
58  	 * Index in the CGLIB callback array for a method that should
59  	 * be overridden to provide <em>method lookup</em>.
60  	 */
61  	private static final int LOOKUP_OVERRIDE = 1;
62  
63  	/**
64  	 * Index in the CGLIB callback array for a method that should
65  	 * be overridden using generic <em>method replacer</em> functionality.
66  	 */
67  	private static final int METHOD_REPLACER = 2;
68  
69  
70  	@Override
71  	protected Object instantiateWithMethodInjection(RootBeanDefinition bd, String beanName, BeanFactory owner) {
72  		return instantiateWithMethodInjection(bd, beanName, owner, null);
73  	}
74  
75  	@Override
76  	protected Object instantiateWithMethodInjection(RootBeanDefinition bd, String beanName, BeanFactory owner,
77  			Constructor<?> ctor, Object... args) {
78  
79  		// Must generate CGLIB subclass...
80  		return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);
81  	}
82  
83  
84  	/**
85  	 * An inner class created for historical reasons to avoid external CGLIB dependency
86  	 * in Spring versions earlier than 3.2.
87  	 */
88  	private static class CglibSubclassCreator {
89  
90  		private static final Class<?>[] CALLBACK_TYPES = new Class<?>[]
91  				{NoOp.class, LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class};
92  
93  		private final RootBeanDefinition beanDefinition;
94  
95  		private final BeanFactory owner;
96  
97  		CglibSubclassCreator(RootBeanDefinition beanDefinition, BeanFactory owner) {
98  			this.beanDefinition = beanDefinition;
99  			this.owner = owner;
100 		}
101 
102 		/**
103 		 * Create a new instance of a dynamically generated subclass implementing the
104 		 * required lookups.
105 		 * @param ctor constructor to use. If this is {@code null}, use the
106 		 * no-arg constructor (no parameterization, or Setter Injection)
107 		 * @param args arguments to use for the constructor.
108 		 * Ignored if the {@code ctor} parameter is {@code null}.
109 		 * @return new instance of the dynamically generated subclass
110 		 */
111 		public Object instantiate(Constructor<?> ctor, Object... args) {
112 			Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
113 			Object instance;
114 			if (ctor == null) {
115 				instance = BeanUtils.instantiate(subclass);
116 			}
117 			else {
118 				try {
119 					Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
120 					instance = enhancedSubclassConstructor.newInstance(args);
121 				}
122 				catch (Exception ex) {
123 					throw new BeanInstantiationException(this.beanDefinition.getBeanClass(),
124 							"Failed to invoke constructor for CGLIB enhanced subclass [" + subclass.getName() + "]", ex);
125 				}
126 			}
127 			// SPR-10785: set callbacks directly on the instance instead of in the
128 			// enhanced class (via the Enhancer) in order to avoid memory leaks.
129 			Factory factory = (Factory) instance;
130 			factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
131 					new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
132 					new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
133 			return instance;
134 		}
135 
136 		/**
137 		 * Create an enhanced subclass of the bean class for the provided bean
138 		 * definition, using CGLIB.
139 		 */
140 		private Class<?> createEnhancedSubclass(RootBeanDefinition beanDefinition) {
141 			Enhancer enhancer = new Enhancer();
142 			enhancer.setSuperclass(beanDefinition.getBeanClass());
143 			enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
144 			enhancer.setCallbackFilter(new MethodOverrideCallbackFilter(beanDefinition));
145 			enhancer.setCallbackTypes(CALLBACK_TYPES);
146 			return enhancer.createClass();
147 		}
148 	}
149 
150 
151 	/**
152 	 * Class providing hashCode and equals methods required by CGLIB to
153 	 * ensure that CGLIB doesn't generate a distinct class per bean.
154 	 * Identity is based on class and bean definition.
155 	 */
156 	private static class CglibIdentitySupport {
157 
158 		private final RootBeanDefinition beanDefinition;
159 
160 		public CglibIdentitySupport(RootBeanDefinition beanDefinition) {
161 			this.beanDefinition = beanDefinition;
162 		}
163 
164 		public RootBeanDefinition getBeanDefinition() {
165 			return this.beanDefinition;
166 		}
167 
168 		@Override
169 		public boolean equals(Object other) {
170 			return (getClass().equals(other.getClass()) &&
171 					this.beanDefinition.equals(((CglibIdentitySupport) other).beanDefinition));
172 		}
173 
174 		@Override
175 		public int hashCode() {
176 			return this.beanDefinition.hashCode();
177 		}
178 	}
179 
180 
181 	/**
182 	 * CGLIB callback for filtering method interception behavior.
183 	 */
184 	private static class MethodOverrideCallbackFilter extends CglibIdentitySupport implements CallbackFilter {
185 
186 		private static final Log logger = LogFactory.getLog(MethodOverrideCallbackFilter.class);
187 
188 		public MethodOverrideCallbackFilter(RootBeanDefinition beanDefinition) {
189 			super(beanDefinition);
190 		}
191 
192 		@Override
193 		public int accept(Method method) {
194 			MethodOverride methodOverride = getBeanDefinition().getMethodOverrides().getOverride(method);
195 			if (logger.isTraceEnabled()) {
196 				logger.trace("Override for '" + method.getName() + "' is [" + methodOverride + "]");
197 			}
198 			if (methodOverride == null) {
199 				return PASSTHROUGH;
200 			}
201 			else if (methodOverride instanceof LookupOverride) {
202 				return LOOKUP_OVERRIDE;
203 			}
204 			else if (methodOverride instanceof ReplaceOverride) {
205 				return METHOD_REPLACER;
206 			}
207 			throw new UnsupportedOperationException("Unexpected MethodOverride subclass: " +
208 					methodOverride.getClass().getName());
209 		}
210 	}
211 
212 
213 	/**
214 	 * CGLIB MethodInterceptor to override methods, replacing them with an
215 	 * implementation that returns a bean looked up in the container.
216 	 */
217 	private static class LookupOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor {
218 
219 		private final BeanFactory owner;
220 
221 		public LookupOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) {
222 			super(beanDefinition);
223 			this.owner = owner;
224 		}
225 
226 		@Override
227 		public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
228 			// Cast is safe, as CallbackFilter filters are used selectively.
229 			LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
230 			Object[] argsToUse = (args.length > 0 ? args : null);  // if no-arg, don't insist on args at all
231 			if (StringUtils.hasText(lo.getBeanName())) {
232 				return this.owner.getBean(lo.getBeanName(), argsToUse);
233 			}
234 			else {
235 				return this.owner.getBean(method.getReturnType(), argsToUse);
236 			}
237 		}
238 	}
239 
240 
241 	/**
242 	 * CGLIB MethodInterceptor to override methods, replacing them with a call
243 	 * to a generic MethodReplacer.
244 	 */
245 	private static class ReplaceOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor {
246 
247 		private final BeanFactory owner;
248 
249 		public ReplaceOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) {
250 			super(beanDefinition);
251 			this.owner = owner;
252 		}
253 
254 		@Override
255 		public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
256 			ReplaceOverride ro = (ReplaceOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
257 			// TODO could cache if a singleton for minor performance optimization
258 			MethodReplacer mr = this.owner.getBean(ro.getMethodReplacerBeanName(), MethodReplacer.class);
259 			return mr.reimplement(obj, method, args);
260 		}
261 	}
262 
263 }