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: * @since 1.1
043: */
044: public class CglibSubclassingInstantiationStrategy extends
045: SimpleInstantiationStrategy {
046:
047: /**
048: * Index in the CGLIB callback array for passthrough behavior,
049: * in which case the subclass won't override the original class.
050: */
051: private static final int PASSTHROUGH = 0;
052:
053: /**
054: * Index in the CGLIB callback array for a method that should
055: * be overridden to provide method lookup.
056: */
057: private static final int LOOKUP_OVERRIDE = 1;
058:
059: /**
060: * Index in the CGLIB callback array for a method that should
061: * be overridden using generic Methodreplacer functionality.
062: */
063: private static final int METHOD_REPLACER = 2;
064:
065: protected Object instantiateWithMethodInjection(
066: RootBeanDefinition beanDefinition, String beanName,
067: BeanFactory owner) {
068:
069: // Must generate CGLIB subclass.
070: return new CglibSubclassCreator(beanDefinition, owner)
071: .instantiate(null, null);
072: }
073:
074: protected Object instantiateWithMethodInjection(
075: RootBeanDefinition beanDefinition, String beanName,
076: BeanFactory owner, Constructor ctor, Object[] args) {
077:
078: return new CglibSubclassCreator(beanDefinition, owner)
079: .instantiate(ctor, args);
080: }
081:
082: /**
083: * An inner class so we don't have a CGLIB dependency in core.
084: */
085: private static class CglibSubclassCreator {
086:
087: private static final Log logger = LogFactory
088: .getLog(CglibSubclassCreator.class);
089:
090: private final RootBeanDefinition beanDefinition;
091:
092: private final BeanFactory owner;
093:
094: public CglibSubclassCreator(RootBeanDefinition beanDefinition,
095: BeanFactory owner) {
096: this .beanDefinition = beanDefinition;
097: this .owner = owner;
098: }
099:
100: /**
101: * Create a new instance of a dynamically generated subclasses implementing the
102: * required lookups.
103: * @param ctor constructor to use. If this is <code>null</code>, use the
104: * no-arg constructor (no parameterization, or Setter Injection)
105: * @param args arguments to use for the constructor.
106: * Ignored if the ctor parameter is <code>null</code>.
107: * @return new instance of the dynamically generated class
108: */
109: public Object instantiate(Constructor ctor, Object[] args) {
110: Enhancer enhancer = new Enhancer();
111: enhancer.setSuperclass(this .beanDefinition.getBeanClass());
112: enhancer.setCallbackFilter(new CallbackFilterImpl());
113: enhancer.setCallbacks(new Callback[] { NoOp.INSTANCE,
114: new LookupOverrideMethodInterceptor(),
115: new ReplaceOverrideMethodInterceptor() });
116:
117: return (ctor == null) ? enhancer.create() : enhancer
118: .create(ctor.getParameterTypes(), args);
119: }
120:
121: /**
122: * Class providing hashCode and equals methods required by CGLIB to
123: * ensure that CGLIB doesn't generate a distinct class per bean.
124: * Identity is based on class and bean definition.
125: */
126: private class CglibIdentitySupport {
127: /**
128: * Exposed for equals method to allow access to enclosing class field
129: */
130: protected RootBeanDefinition getBeanDefinition() {
131: return beanDefinition;
132: }
133:
134: public int hashCode() {
135: return beanDefinition.hashCode();
136: }
137:
138: public boolean equals(Object other) {
139: return (other.getClass() == getClass())
140: && ((CglibIdentitySupport) other)
141: .getBeanDefinition() == beanDefinition;
142: }
143: }
144:
145: /**
146: * CGLIB MethodInterceptor to override methods, replacing them with an
147: * implementation that returns a bean looked up in the container.
148: */
149: private class LookupOverrideMethodInterceptor extends
150: CglibIdentitySupport implements MethodInterceptor {
151:
152: public Object intercept(Object obj, Method method,
153: Object[] args, MethodProxy mp) throws Throwable {
154: // Cast is safe, as CallbackFilter filters are used selectively.
155: LookupOverride lo = (LookupOverride) beanDefinition
156: .getMethodOverrides().getOverride(method);
157: return owner.getBean(lo.getBeanName());
158: }
159: }
160:
161: /**
162: * CGLIB MethodInterceptor to override methods, replacing them with a call
163: * to a generic MethodReplacer.
164: */
165: private class ReplaceOverrideMethodInterceptor extends
166: CglibIdentitySupport implements MethodInterceptor {
167:
168: public Object intercept(Object obj, Method method,
169: Object[] args, MethodProxy mp) throws Throwable {
170: ReplaceOverride ro = (ReplaceOverride) beanDefinition
171: .getMethodOverrides().getOverride(method);
172: // TODO could cache if a singleton for minor performance optimization
173: MethodReplacer mr = (MethodReplacer) owner.getBean(ro
174: .getMethodReplacerBeanName());
175: return mr.reimplement(obj, method, args);
176: }
177: }
178:
179: /**
180: * CGLIB object to filter method interception behavior.
181: */
182: private class CallbackFilterImpl extends CglibIdentitySupport
183: implements CallbackFilter {
184:
185: public int accept(Method method) {
186: MethodOverride methodOverride = beanDefinition
187: .getMethodOverrides().getOverride(method);
188: if (logger.isTraceEnabled()) {
189: logger.trace("Override for '" + method.getName()
190: + "' is [" + methodOverride + "]");
191: }
192: if (methodOverride == null) {
193: return PASSTHROUGH;
194: } else if (methodOverride instanceof LookupOverride) {
195: return LOOKUP_OVERRIDE;
196: } else if (methodOverride instanceof ReplaceOverride) {
197: return METHOD_REPLACER;
198: }
199: throw new UnsupportedOperationException(
200: "Unexpected MethodOverride subclass: "
201: + methodOverride.getClass().getName());
202: }
203: }
204: }
205:
206: }
|