001/*
002 * Copyright 2002-2019 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.Method;
021
022import org.apache.commons.logging.Log;
023import org.apache.commons.logging.LogFactory;
024
025import org.springframework.beans.BeanInstantiationException;
026import org.springframework.beans.BeanUtils;
027import org.springframework.beans.factory.BeanFactory;
028import org.springframework.beans.factory.config.ConfigurableBeanFactory;
029import org.springframework.cglib.core.ClassGenerator;
030import org.springframework.cglib.core.DefaultGeneratorStrategy;
031import org.springframework.cglib.core.SpringNamingPolicy;
032import org.springframework.cglib.proxy.Callback;
033import org.springframework.cglib.proxy.CallbackFilter;
034import org.springframework.cglib.proxy.Enhancer;
035import org.springframework.cglib.proxy.Factory;
036import org.springframework.cglib.proxy.MethodInterceptor;
037import org.springframework.cglib.proxy.MethodProxy;
038import org.springframework.cglib.proxy.NoOp;
039import org.springframework.util.StringUtils;
040
041/**
042 * Default object instantiation strategy for use in BeanFactories.
043 *
044 * <p>Uses CGLIB to generate subclasses dynamically if methods need to be
045 * overridden by the container to implement <em>Method Injection</em>.
046 *
047 * @author Rod Johnson
048 * @author Juergen Hoeller
049 * @author Sam Brannen
050 * @since 1.1
051 */
052public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationStrategy {
053
054        /**
055         * Index in the CGLIB callback array for passthrough behavior,
056         * in which case the subclass won't override the original class.
057         */
058        private static final int PASSTHROUGH = 0;
059
060        /**
061         * Index in the CGLIB callback array for a method that should
062         * be overridden to provide <em>method lookup</em>.
063         */
064        private static final int LOOKUP_OVERRIDE = 1;
065
066        /**
067         * Index in the CGLIB callback array for a method that should
068         * be overridden using generic <em>method replacer</em> functionality.
069         */
070        private static final int METHOD_REPLACER = 2;
071
072
073        @Override
074        protected Object instantiateWithMethodInjection(RootBeanDefinition bd, String beanName, BeanFactory owner) {
075                return instantiateWithMethodInjection(bd, beanName, owner, null);
076        }
077
078        @Override
079        protected Object instantiateWithMethodInjection(RootBeanDefinition bd, String beanName, BeanFactory owner,
080                        Constructor<?> ctor, Object... args) {
081
082                // Must generate CGLIB subclass...
083                return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);
084        }
085
086
087        /**
088         * An inner class created for historical reasons to avoid external CGLIB dependency
089         * in Spring versions earlier than 3.2.
090         */
091        private static class CglibSubclassCreator {
092
093                private static final Class<?>[] CALLBACK_TYPES = new Class<?>[]
094                                {NoOp.class, LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class};
095
096                private final RootBeanDefinition beanDefinition;
097
098                private final BeanFactory owner;
099
100                CglibSubclassCreator(RootBeanDefinition beanDefinition, BeanFactory owner) {
101                        this.beanDefinition = beanDefinition;
102                        this.owner = owner;
103                }
104
105                /**
106                 * Create a new instance of a dynamically generated subclass implementing the
107                 * required lookups.
108                 * @param ctor constructor to use. If this is {@code null}, use the
109                 * no-arg constructor (no parameterization, or Setter Injection)
110                 * @param args arguments to use for the constructor.
111                 * Ignored if the {@code ctor} parameter is {@code null}.
112                 * @return new instance of the dynamically generated subclass
113                 */
114                public Object instantiate(Constructor<?> ctor, Object... args) {
115                        Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
116                        Object instance;
117                        if (ctor == null) {
118                                instance = BeanUtils.instantiateClass(subclass);
119                        }
120                        else {
121                                try {
122                                        Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
123                                        instance = enhancedSubclassConstructor.newInstance(args);
124                                }
125                                catch (Exception ex) {
126                                        throw new BeanInstantiationException(this.beanDefinition.getBeanClass(),
127                                                        "Failed to invoke constructor for CGLIB enhanced subclass [" + subclass.getName() + "]", ex);
128                                }
129                        }
130                        // SPR-10785: set callbacks directly on the instance instead of in the
131                        // enhanced class (via the Enhancer) in order to avoid memory leaks.
132                        Factory factory = (Factory) instance;
133                        factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
134                                        new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
135                                        new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
136                        return instance;
137                }
138
139                /**
140                 * Create an enhanced subclass of the bean class for the provided bean
141                 * definition, using CGLIB.
142                 */
143                private Class<?> createEnhancedSubclass(RootBeanDefinition beanDefinition) {
144                        Enhancer enhancer = new Enhancer();
145                        enhancer.setSuperclass(beanDefinition.getBeanClass());
146                        enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
147                        if (this.owner instanceof ConfigurableBeanFactory) {
148                                ClassLoader cl = ((ConfigurableBeanFactory) this.owner).getBeanClassLoader();
149                                enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(cl));
150                        }
151                        enhancer.setCallbackFilter(new MethodOverrideCallbackFilter(beanDefinition));
152                        enhancer.setCallbackTypes(CALLBACK_TYPES);
153                        return enhancer.createClass();
154                }
155        }
156
157
158        /**
159         * Class providing hashCode and equals methods required by CGLIB to
160         * ensure that CGLIB doesn't generate a distinct class per bean.
161         * Identity is based on class and bean definition.
162         */
163        private static class CglibIdentitySupport {
164
165                private final RootBeanDefinition beanDefinition;
166
167                public CglibIdentitySupport(RootBeanDefinition beanDefinition) {
168                        this.beanDefinition = beanDefinition;
169                }
170
171                public RootBeanDefinition getBeanDefinition() {
172                        return this.beanDefinition;
173                }
174
175                @Override
176                public boolean equals(Object other) {
177                        return (getClass() == other.getClass() &&
178                                        this.beanDefinition.equals(((CglibIdentitySupport) other).beanDefinition));
179                }
180
181                @Override
182                public int hashCode() {
183                        return this.beanDefinition.hashCode();
184                }
185        }
186
187
188        /**
189         * CGLIB GeneratorStrategy variant which exposes the application ClassLoader
190         * as thread context ClassLoader for the time of class generation
191         * (in order for ASM to pick it up when doing common superclass resolution).
192         */
193        private static class ClassLoaderAwareGeneratorStrategy extends DefaultGeneratorStrategy {
194
195                private final ClassLoader classLoader;
196
197                public ClassLoaderAwareGeneratorStrategy(ClassLoader classLoader) {
198                        this.classLoader = classLoader;
199                }
200
201                @Override
202                public byte[] generate(ClassGenerator cg) throws Exception {
203                        if (this.classLoader == null) {
204                                return super.generate(cg);
205                        }
206
207                        Thread currentThread = Thread.currentThread();
208                        ClassLoader threadContextClassLoader;
209                        try {
210                                threadContextClassLoader = currentThread.getContextClassLoader();
211                        }
212                        catch (Throwable ex) {
213                                // Cannot access thread context ClassLoader - falling back...
214                                return super.generate(cg);
215                        }
216
217                        boolean overrideClassLoader = !this.classLoader.equals(threadContextClassLoader);
218                        if (overrideClassLoader) {
219                                currentThread.setContextClassLoader(this.classLoader);
220                        }
221                        try {
222                                return super.generate(cg);
223                        }
224                        finally {
225                                if (overrideClassLoader) {
226                                        // Reset original thread context ClassLoader.
227                                        currentThread.setContextClassLoader(threadContextClassLoader);
228                                }
229                        }
230                }
231        }
232
233
234        /**
235         * CGLIB callback for filtering method interception behavior.
236         */
237        private static class MethodOverrideCallbackFilter extends CglibIdentitySupport implements CallbackFilter {
238
239                private static final Log logger = LogFactory.getLog(MethodOverrideCallbackFilter.class);
240
241                public MethodOverrideCallbackFilter(RootBeanDefinition beanDefinition) {
242                        super(beanDefinition);
243                }
244
245                @Override
246                public int accept(Method method) {
247                        MethodOverride methodOverride = getBeanDefinition().getMethodOverrides().getOverride(method);
248                        if (logger.isTraceEnabled()) {
249                                logger.trace("MethodOverride for " + method + ": " + methodOverride);
250                        }
251                        if (methodOverride == null) {
252                                return PASSTHROUGH;
253                        }
254                        else if (methodOverride instanceof LookupOverride) {
255                                return LOOKUP_OVERRIDE;
256                        }
257                        else if (methodOverride instanceof ReplaceOverride) {
258                                return METHOD_REPLACER;
259                        }
260                        throw new UnsupportedOperationException("Unexpected MethodOverride subclass: " +
261                                        methodOverride.getClass().getName());
262                }
263        }
264
265
266        /**
267         * CGLIB MethodInterceptor to override methods, replacing them with an
268         * implementation that returns a bean looked up in the container.
269         */
270        private static class LookupOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor {
271
272                private final BeanFactory owner;
273
274                public LookupOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) {
275                        super(beanDefinition);
276                        this.owner = owner;
277                }
278
279                @Override
280                public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
281                        // Cast is safe, as CallbackFilter filters are used selectively.
282                        LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
283                        Object[] argsToUse = (args.length > 0 ? args : null);  // if no-arg, don't insist on args at all
284                        if (StringUtils.hasText(lo.getBeanName())) {
285                                return this.owner.getBean(lo.getBeanName(), argsToUse);
286                        }
287                        else {
288                                return this.owner.getBean(method.getReturnType(), argsToUse);
289                        }
290                }
291        }
292
293
294        /**
295         * CGLIB MethodInterceptor to override methods, replacing them with a call
296         * to a generic MethodReplacer.
297         */
298        private static class ReplaceOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor {
299
300                private final BeanFactory owner;
301
302                public ReplaceOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) {
303                        super(beanDefinition);
304                        this.owner = owner;
305                }
306
307                @Override
308                public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
309                        ReplaceOverride ro = (ReplaceOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
310                        // TODO could cache if a singleton for minor performance optimization
311                        MethodReplacer mr = this.owner.getBean(ro.getMethodReplacerBeanName(), MethodReplacer.class);
312                        return mr.reimplement(obj, method, args);
313                }
314        }
315
316}