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