001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.beans.factory.support;
018:
019: import java.lang.reflect.Constructor;
020: import java.lang.reflect.Method;
021:
022: import net.sf.cglib.proxy.Callback;
023: import net.sf.cglib.proxy.CallbackFilter;
024: import net.sf.cglib.proxy.Enhancer;
025: import net.sf.cglib.proxy.MethodInterceptor;
026: import net.sf.cglib.proxy.MethodProxy;
027: import net.sf.cglib.proxy.NoOp;
028: import org.apache.commons.logging.Log;
029: import org.apache.commons.logging.LogFactory;
030:
031: import org.springframework.beans.factory.BeanFactory;
032:
033: /**
034: * Default object instantiation strategy for use in BeanFactories.
035: * Uses CGLIB to generate subclasses dynamically if methods need to be
036: * overridden by the container, to implement Method Injection.
037: *
038: * <p>Using Method Injection features requires CGLIB on the classpath.
039: * However, the core IoC container will still run without CGLIB being available.
040: *
041: * @author Rod Johnson
042: * @author Juergen Hoeller
043: * @since 1.1
044: */
045: public class CglibSubclassingInstantiationStrategy extends
046: SimpleInstantiationStrategy {
047:
048: /**
049: * Index in the CGLIB callback array for passthrough behavior,
050: * in which case the subclass won't override the original class.
051: */
052: private static final int PASSTHROUGH = 0;
053:
054: /**
055: * Index in the CGLIB callback array for a method that should
056: * be overridden to provide method lookup.
057: */
058: private static final int LOOKUP_OVERRIDE = 1;
059:
060: /**
061: * Index in the CGLIB callback array for a method that should
062: * be overridden using generic Methodreplacer functionality.
063: */
064: private static final int METHOD_REPLACER = 2;
065:
066: protected Object instantiateWithMethodInjection(
067: RootBeanDefinition beanDefinition, String beanName,
068: BeanFactory owner) {
069:
070: // Must generate CGLIB subclass.
071: return new CglibSubclassCreator(beanDefinition, owner)
072: .instantiate(null, null);
073: }
074:
075: protected Object instantiateWithMethodInjection(
076: RootBeanDefinition beanDefinition, String beanName,
077: BeanFactory owner, Constructor ctor, Object[] args) {
078:
079: return new CglibSubclassCreator(beanDefinition, owner)
080: .instantiate(ctor, args);
081: }
082:
083: /**
084: * An inner class so we don't have a CGLIB dependency in core.
085: */
086: private static class CglibSubclassCreator {
087:
088: private static final Log logger = LogFactory
089: .getLog(CglibSubclassCreator.class);
090:
091: private final RootBeanDefinition beanDefinition;
092:
093: private final BeanFactory owner;
094:
095: public CglibSubclassCreator(RootBeanDefinition beanDefinition,
096: BeanFactory owner) {
097: this .beanDefinition = beanDefinition;
098: this .owner = owner;
099: }
100:
101: /**
102: * Create a new instance of a dynamically generated subclasses implementing the
103: * required lookups.
104: * @param ctor constructor to use. If this is <code>null</code>, use the
105: * no-arg constructor (no parameterization, or Setter Injection)
106: * @param args arguments to use for the constructor.
107: * Ignored if the ctor parameter is <code>null</code>.
108: * @return new instance of the dynamically generated class
109: */
110: public Object instantiate(Constructor ctor, Object[] args) {
111: Enhancer enhancer = new Enhancer();
112: enhancer.setSuperclass(this .beanDefinition.getBeanClass());
113: enhancer.setCallbackFilter(new CallbackFilterImpl());
114: enhancer.setCallbacks(new Callback[] { NoOp.INSTANCE,
115: new LookupOverrideMethodInterceptor(),
116: new ReplaceOverrideMethodInterceptor() });
117:
118: return (ctor == null) ? enhancer.create() : enhancer
119: .create(ctor.getParameterTypes(), args);
120: }
121:
122: /**
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.
126: */
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: }
135:
136: public boolean equals(Object other) {
137: return (other.getClass().equals(getClass()) && ((CglibIdentitySupport) other)
138: .getBeanDefinition().equals(beanDefinition));
139: }
140:
141: public int hashCode() {
142: return beanDefinition.hashCode();
143: }
144: }
145:
146: /**
147: * CGLIB MethodInterceptor to override methods, replacing them with an
148: * implementation that returns a bean looked up in the container.
149: */
150: private class LookupOverrideMethodInterceptor extends
151: CglibIdentitySupport implements MethodInterceptor {
152:
153: public Object intercept(Object obj, Method method,
154: Object[] args, MethodProxy mp) throws Throwable {
155: // Cast is safe, as CallbackFilter filters are used selectively.
156: LookupOverride lo = (LookupOverride) beanDefinition
157: .getMethodOverrides().getOverride(method);
158: return owner.getBean(lo.getBeanName());
159: }
160: }
161:
162: /**
163: * CGLIB MethodInterceptor to override methods, replacing them with a call
164: * to a generic MethodReplacer.
165: */
166: private class ReplaceOverrideMethodInterceptor extends
167: CglibIdentitySupport implements MethodInterceptor {
168:
169: public Object intercept(Object obj, Method method,
170: Object[] args, MethodProxy mp) throws Throwable {
171: ReplaceOverride ro = (ReplaceOverride) beanDefinition
172: .getMethodOverrides().getOverride(method);
173: // TODO could cache if a singleton for minor performance optimization
174: MethodReplacer mr = (MethodReplacer) owner.getBean(ro
175: .getMethodReplacerBeanName());
176: return mr.reimplement(obj, method, args);
177: }
178: }
179:
180: /**
181: * CGLIB object to filter method interception behavior.
182: */
183: private class CallbackFilterImpl extends CglibIdentitySupport
184: implements CallbackFilter {
185:
186: public int accept(Method method) {
187: MethodOverride methodOverride = beanDefinition
188: .getMethodOverrides().getOverride(method);
189: if (logger.isTraceEnabled()) {
190: logger.trace("Override for '" + method.getName()
191: + "' is [" + methodOverride + "]");
192: }
193: if (methodOverride == null) {
194: return PASSTHROUGH;
195: } else if (methodOverride instanceof LookupOverride) {
196: return LOOKUP_OVERRIDE;
197: } else if (methodOverride instanceof ReplaceOverride) {
198: return METHOD_REPLACER;
199: }
200: throw new UnsupportedOperationException(
201: "Unexpected MethodOverride subclass: "
202: + methodOverride.getClass().getName());
203: }
204: }
205: }
206:
207: }
|