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}