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}