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