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.aop.framework;
018:
019: import java.io.IOException;
020: import java.io.ObjectInputStream;
021: import java.io.Serializable;
022: import java.util.ArrayList;
023: import java.util.Arrays;
024: import java.util.Collections;
025: import java.util.HashMap;
026: import java.util.Iterator;
027: import java.util.List;
028: import java.util.Map;
029:
030: import org.aopalliance.aop.Advice;
031: import org.aopalliance.intercept.Interceptor;
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034:
035: import org.springframework.aop.Advisor;
036: import org.springframework.aop.TargetSource;
037: import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry;
038: import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry;
039: import org.springframework.aop.framework.adapter.UnknownAdviceTypeException;
040: import org.springframework.aop.target.SingletonTargetSource;
041: import org.springframework.beans.BeansException;
042: import org.springframework.beans.factory.BeanClassLoaderAware;
043: import org.springframework.beans.factory.BeanFactory;
044: import org.springframework.beans.factory.BeanFactoryAware;
045: import org.springframework.beans.factory.BeanFactoryUtils;
046: import org.springframework.beans.factory.FactoryBean;
047: import org.springframework.beans.factory.FactoryBeanNotInitializedException;
048: import org.springframework.beans.factory.ListableBeanFactory;
049: import org.springframework.core.OrderComparator;
050: import org.springframework.util.ClassUtils;
051: import org.springframework.util.ObjectUtils;
052:
053: /**
054: * {@link org.springframework.beans.factory.FactoryBean} implementation that builds an
055: * AOP proxy based on beans in Spring {@link org.springframework.beans.factory.BeanFactory}.
056: *
057: * <p>{@link org.aopalliance.intercept.MethodInterceptor MethodInterceptors} and
058: * {@link org.springframework.aop.Advisor Advisors} are identified by a list of bean
059: * names in the current bean factory, specified through the "interceptorNames" property.
060: * The last entry in the list can be the name of a target bean or a
061: * {@link org.springframework.aop.TargetSource}; however, it is normally preferable
062: * to use the "targetName"/"target"/"targetSource" properties instead.
063: *
064: * <p>Global interceptors and advisors can be added at the factory level. The specified
065: * ones are expanded in an interceptor list where an "xxx*" entry is included in the
066: * list, matching the given prefix with the bean names (e.g. "global*" would match
067: * both "globalBean1" and "globalBean2", "*" all defined interceptors). The matching
068: * interceptors get applied according to their returned order value, if they implement
069: * the {@link org.springframework.core.Ordered} interface.
070: *
071: * <p>Creates a JDK proxy when proxy interfaces are given, and a CGLIB proxy for the
072: * actual target class if not. Note that the latter will only work if the target class
073: * does not have final methods, as a dynamic subclass will be created at runtime.
074: *
075: * <p>It's possible to cast a proxy obtained from this factory to {@link Advised},
076: * or to obtain the ProxyFactoryBean reference and programmatically manipulate it.
077: * This won't work for existing prototype references, which are independent. However,
078: * it will work for prototypes subsequently obtained from the factory. Changes to
079: * interception will work immediately on singletons (including existing references).
080: * However, to change interfaces or target it's necessary to obtain a new instance
081: * from the factory. This means that singleton instances obtained from the factory
082: * do not have the same object identity. However, they do have the same interceptors
083: * and target, and changing any reference will change all objects.
084: *
085: * @author Rod Johnson
086: * @author Juergen Hoeller
087: * @see #setInterceptorNames
088: * @see #setProxyInterfaces
089: * @see org.aopalliance.intercept.MethodInterceptor
090: * @see org.springframework.aop.Advisor
091: * @see Advised
092: */
093: public class ProxyFactoryBean extends ProxyCreatorSupport implements
094: FactoryBean, BeanClassLoaderAware, BeanFactoryAware {
095:
096: /**
097: * This suffix in a value in an interceptor list indicates to expand globals.
098: */
099: public static final String GLOBAL_SUFFIX = "*";
100:
101: protected final Log logger = LogFactory.getLog(getClass());
102:
103: private String[] interceptorNames;
104:
105: private String targetName;
106:
107: private boolean autodetectInterfaces = true;
108:
109: private boolean singleton = true;
110:
111: private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry
112: .getInstance();
113:
114: /**
115: * Indicates whether the proxy should be frozen before creation.
116: */
117: private boolean freezeProxy = false;
118:
119: private transient ClassLoader beanClassLoader = ClassUtils
120: .getDefaultClassLoader();
121:
122: /**
123: * Owning bean factory, which cannot be changed after this
124: * object is initialized.
125: */
126: private transient BeanFactory beanFactory;
127:
128: /** Whether the advisor chain has already been initialized */
129: private boolean advisorChainInitialized = false;
130:
131: /** If this is a singleton, the cached singleton proxy instance */
132: private Object singletonInstance;
133:
134: /**
135: * Set the names of the interfaces we're proxying. If no interface
136: * is given, a CGLIB for the actual class will be created.
137: * <p>This is essentially equivalent to the "setInterfaces" method,
138: * but mirrors TransactionProxyFactoryBean's "setProxyInterfaces".
139: * @see #setInterfaces
140: * @see AbstractSingletonProxyFactoryBean#setProxyInterfaces
141: */
142: public void setProxyInterfaces(Class[] proxyInterfaces)
143: throws ClassNotFoundException {
144: setInterfaces(proxyInterfaces);
145: }
146:
147: /**
148: * Set the list of Advice/Advisor bean names. This must always be set
149: * to use this factory bean in a bean factory.
150: * <p>The referenced beans should be of type Interceptor, Advisor or Advice
151: * The last entry in the list can be the name of any bean in the factory.
152: * If it's neither an Advice nor an Advisor, a new SingletonTargetSource
153: * is added to wrap it. Such a target bean cannot be used if the "target"
154: * or "targetSource" or "targetName" property is set, in which case the
155: * "interceptorNames" array must contain only Advice/Advisor bean names.
156: * @see org.aopalliance.intercept.MethodInterceptor
157: * @see org.springframework.aop.Advisor
158: * @see org.aopalliance.aop.Advice
159: * @see org.springframework.aop.target.SingletonTargetSource
160: */
161: public void setInterceptorNames(String[] interceptorNames) {
162: this .interceptorNames = interceptorNames;
163: }
164:
165: /**
166: * Set the name of the target bean. This is an alternative to specifying
167: * the target name at the end of the "interceptorNames" array.
168: * <p>You can also specify a target object or a TargetSource object
169: * directly, via the "target"/"targetSource" property, respectively.
170: * @see #setInterceptorNames(String[])
171: * @see #setTarget(Object)
172: * @see #setTargetSource(org.springframework.aop.TargetSource)
173: */
174: public void setTargetName(String targetName) {
175: this .targetName = targetName;
176: }
177:
178: /**
179: * Set whether to autodetect proxy interfaces if none specified.
180: * <p>Default is "true". Turn this flag off to create a CGLIB
181: * proxy for the full target class if no interfaces specified.
182: * @see #setProxyTargetClass
183: */
184: public void setAutodetectInterfaces(boolean autodetectInterfaces) {
185: this .autodetectInterfaces = autodetectInterfaces;
186: }
187:
188: /**
189: * Set the value of the singleton property. Governs whether this factory
190: * should always return the same proxy instance (which implies the same target)
191: * or whether it should return a new prototype instance, which implies that
192: * the target and interceptors may be new instances also, if they are obtained
193: * from prototype bean definitions. This allows for fine control of
194: * independence/uniqueness in the object graph.
195: */
196: public void setSingleton(boolean singleton) {
197: this .singleton = singleton;
198: }
199:
200: /**
201: * Specify the AdvisorAdapterRegistry to use.
202: * Default is the global AdvisorAdapterRegistry.
203: * @see org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry
204: */
205: public void setAdvisorAdapterRegistry(
206: AdvisorAdapterRegistry advisorAdapterRegistry) {
207: this .advisorAdapterRegistry = advisorAdapterRegistry;
208: }
209:
210: public void setFrozen(boolean frozen) {
211: this .freezeProxy = frozen;
212: }
213:
214: public void setBeanClassLoader(ClassLoader classLoader) {
215: this .beanClassLoader = classLoader;
216: }
217:
218: public void setBeanFactory(BeanFactory beanFactory) {
219: this .beanFactory = beanFactory;
220: checkInterceptorNames();
221: }
222:
223: /**
224: * Return a proxy. Invoked when clients obtain beans from this factory bean.
225: * Create an instance of the AOP proxy to be returned by this factory.
226: * The instance will be cached for a singleton, and create on each call to
227: * <code>getObject()</code> for a proxy.
228: * @return a fresh AOP proxy reflecting the current state of this factory
229: */
230: public Object getObject() throws BeansException {
231: initializeAdvisorChain();
232: if (isSingleton()) {
233: return getSingletonInstance();
234: } else {
235: if (this .targetName == null) {
236: logger
237: .warn("Using non-singleton proxies with singleton targets is often undesirable."
238: + "Enable prototype proxies by setting the 'targetName' property.");
239: }
240: return newPrototypeInstance();
241: }
242: }
243:
244: /**
245: * Return the type of the proxy. Will check the singleton instance if
246: * already created, else fall back to the proxy interface (in case of just
247: * a single one), the target bean type, or the TargetSource's target class.
248: * @see org.springframework.aop.TargetSource#getTargetClass
249: */
250: public Class getObjectType() {
251: synchronized (this ) {
252: if (this .singletonInstance != null) {
253: return this .singletonInstance.getClass();
254: }
255: }
256: Class[] ifcs = getProxiedInterfaces();
257: if (ifcs.length == 1) {
258: return ifcs[0];
259: } else if (ifcs.length > 1) {
260: return createCompositeInterface(ifcs);
261: } else if (this .targetName != null && this .beanFactory != null) {
262: return this .beanFactory.getType(this .targetName);
263: } else {
264: return getTargetClass();
265: }
266: }
267:
268: public boolean isSingleton() {
269: return this .singleton;
270: }
271:
272: /**
273: * Create a composite interface Class for the given interfaces,
274: * implementing the given interfaces in one single Class.
275: * <p>The default implementation builds a JDK proxy class for the
276: * given interfaces.
277: * @param interfaces the interfaces to merge
278: * @return the merged interface as Class
279: * @see java.lang.reflect.Proxy#getProxyClass
280: */
281: protected Class createCompositeInterface(Class[] interfaces) {
282: return ClassUtils.createCompositeInterface(interfaces,
283: this .beanClassLoader);
284: }
285:
286: /**
287: * Return the singleton instance of this class's proxy object,
288: * lazily creating it if it hasn't been created already.
289: * @return the shared singleton proxy
290: */
291: private synchronized Object getSingletonInstance() {
292: if (this .singletonInstance == null) {
293: this .targetSource = freshTargetSource();
294: if (this .autodetectInterfaces
295: && getProxiedInterfaces().length == 0
296: && !isProxyTargetClass()) {
297: // Rely on AOP infrastructure to tell us what interfaces to proxy.
298: Class targetClass = getTargetClass();
299: if (targetClass == null) {
300: throw new FactoryBeanNotInitializedException(
301: "Cannot determine target class for proxy");
302: }
303: setInterfaces(ClassUtils
304: .getAllInterfacesForClass(targetClass));
305: }
306: // Initialize the shared singleton instance.
307: super .setFrozen(this .freezeProxy);
308: this .singletonInstance = getProxy(createAopProxy());
309: }
310: return this .singletonInstance;
311: }
312:
313: /**
314: * Create a new prototype instance of this class's created proxy object,
315: * backed by an independent AdvisedSupport configuration.
316: * @return a totally independent proxy, whose advice we may manipulate in isolation
317: */
318: private synchronized Object newPrototypeInstance() {
319: // In the case of a prototype, we need to give the proxy
320: // an independent instance of the configuration.
321: // In this case, no proxy will have an instance of this object's configuration,
322: // but will have an independent copy.
323: if (logger.isTraceEnabled()) {
324: logger
325: .trace("Creating copy of prototype ProxyFactoryBean config: "
326: + this );
327: }
328:
329: ProxyCreatorSupport copy = new ProxyCreatorSupport(
330: getAopProxyFactory());
331: // The copy needs a fresh advisor chain, and a fresh TargetSource.
332: TargetSource targetSource = freshTargetSource();
333: copy.copyConfigurationFrom(this , targetSource,
334: freshAdvisorChain());
335: if (this .autodetectInterfaces
336: && getProxiedInterfaces().length == 0
337: && !isProxyTargetClass()) {
338: // Rely on AOP infrastructure to tell us what interfaces to proxy.
339: copy.setInterfaces(ClassUtils
340: .getAllInterfacesForClass(targetSource
341: .getTargetClass()));
342: }
343: copy.setFrozen(this .freezeProxy);
344:
345: if (logger.isTraceEnabled()) {
346: logger.trace("Using ProxyCreatorSupport copy: " + copy);
347: }
348: return getProxy(copy.createAopProxy());
349: }
350:
351: /**
352: * Return the proxy object to expose.
353: * <p>The default implementation uses a <code>getProxy</code> call with
354: * the factory's bean class loader. Can be overridden to specify a
355: * custom class loader.
356: * @param aopProxy the prepared AopProxy instance to get the proxy from
357: * @return the proxy object to expose
358: * @see AopProxy#getProxy(ClassLoader)
359: */
360: protected Object getProxy(AopProxy aopProxy) {
361: return aopProxy.getProxy(this .beanClassLoader);
362: }
363:
364: /**
365: * Check the interceptorNames list whether it contains a target name as final element.
366: * If found, remove the final name from the list and set it as targetName.
367: */
368: private void checkInterceptorNames() {
369: if (!ObjectUtils.isEmpty(this .interceptorNames)) {
370: String finalName = this .interceptorNames[this .interceptorNames.length - 1];
371: if (this .targetName == null
372: && this .targetSource == EMPTY_TARGET_SOURCE) {
373: // The last name in the chain may be an Advisor/Advice or a target/TargetSource.
374: // Unfortunately we don't know; we must look at type of the bean.
375: if (!finalName.endsWith(GLOBAL_SUFFIX)
376: && !isNamedBeanAnAdvisorOrAdvice(finalName)) {
377: // Must be an interceptor.
378: this .targetName = finalName;
379: if (logger.isDebugEnabled()) {
380: logger
381: .debug("Bean with name '"
382: + finalName
383: + "' concluding interceptor chain "
384: + "is not an advisor class: treating it as a target or TargetSource");
385: }
386: String[] newNames = new String[this .interceptorNames.length - 1];
387: System.arraycopy(this .interceptorNames, 0,
388: newNames, 0, newNames.length);
389: this .interceptorNames = newNames;
390: }
391: }
392: }
393: }
394:
395: /**
396: * Look at bean factory metadata to work out whether this bean name,
397: * which concludes the interceptorNames list, is an Advisor or Advice,
398: * or may be a target.
399: * @param beanName bean name to check
400: * @return true if it's an Advisor or Advice
401: */
402: private boolean isNamedBeanAnAdvisorOrAdvice(String beanName) {
403: Class namedBeanClass = this .beanFactory.getType(beanName);
404: if (namedBeanClass != null) {
405: return Advisor.class.isAssignableFrom(namedBeanClass)
406: || Advice.class.isAssignableFrom(namedBeanClass);
407: }
408: // Treat it as an Advisor if we can't tell.
409: return true;
410: }
411:
412: /**
413: * Create the advisor (interceptor) chain. Aadvisors that are sourced
414: * from a BeanFactory will be refreshed each time a new prototype instance
415: * is added. Interceptors added programmatically through the factory API
416: * are unaffected by such changes.
417: */
418: private synchronized void initializeAdvisorChain()
419: throws AopConfigException, BeansException {
420: if (this .advisorChainInitialized) {
421: return;
422: }
423:
424: if (!ObjectUtils.isEmpty(this .interceptorNames)) {
425:
426: if (this .beanFactory == null) {
427: throw new IllegalStateException(
428: "No BeanFactory available anymore (probably due to serialization) "
429: + "- cannot resolve interceptor names "
430: + Arrays.asList(this .interceptorNames));
431: }
432:
433: // Globals can't be last unless we specified a targetSource using the property...
434: if (this .interceptorNames[this .interceptorNames.length - 1]
435: .endsWith(GLOBAL_SUFFIX)
436: && this .targetName == null
437: && this .targetSource == EMPTY_TARGET_SOURCE) {
438: throw new AopConfigException(
439: "Target required after globals");
440: }
441:
442: // Materialize interceptor chain from bean names.
443: for (int i = 0; i < this .interceptorNames.length; i++) {
444: String name = this .interceptorNames[i];
445: if (logger.isTraceEnabled()) {
446: logger.trace("Configuring advisor or advice '"
447: + name + "'");
448: }
449:
450: if (name.endsWith(GLOBAL_SUFFIX)) {
451: if (!(this .beanFactory instanceof ListableBeanFactory)) {
452: throw new AopConfigException(
453: "Can only use global advisors or interceptors with a ListableBeanFactory");
454: }
455: addGlobalAdvisor(
456: (ListableBeanFactory) this .beanFactory,
457: name.substring(0, name.length()
458: - GLOBAL_SUFFIX.length()));
459: }
460:
461: else {
462: // If we get here, we need to add a named interceptor.
463: // We must check if it's a singleton or prototype.
464: Object advice = null;
465: if (this .singleton
466: || this .beanFactory
467: .isSingleton(this .interceptorNames[i])) {
468: // Add the real Advisor/Advice to the chain.
469: advice = this .beanFactory
470: .getBean(this .interceptorNames[i]);
471: } else {
472: // It's a prototype Advice or Advisor: replace with a prototype.
473: // Avoid unnecessary creation of prototype bean just for advisor chain initialization.
474: advice = new PrototypePlaceholderAdvisor(
475: this .interceptorNames[i]);
476: }
477: addAdvisorOnChainCreation(advice,
478: this .interceptorNames[i]);
479: }
480: }
481: }
482:
483: this .advisorChainInitialized = true;
484: }
485:
486: /**
487: * Return an independent advisor chain.
488: * We need to do this every time a new prototype instance is returned,
489: * to return distinct instances of prototype Advisors and Advices.
490: */
491: private List freshAdvisorChain() {
492: Advisor[] advisors = getAdvisors();
493: List freshAdvisors = new ArrayList(advisors.length);
494:
495: for (int i = 0; i < advisors.length; i++) {
496: if (advisors[i] instanceof PrototypePlaceholderAdvisor) {
497: PrototypePlaceholderAdvisor pa = (PrototypePlaceholderAdvisor) advisors[i];
498: if (logger.isDebugEnabled()) {
499: logger.debug("Refreshing bean named '"
500: + pa.getBeanName() + "'");
501: }
502: // Replace the placeholder with a fresh prototype instance resulting
503: // from a getBean() lookup
504: if (this .beanFactory == null) {
505: throw new IllegalStateException(
506: "No BeanFactory available anymore (probably due to serialization) "
507: + "- cannot resolve prototype advisor '"
508: + pa.getBeanName() + "'");
509: }
510: Object bean = this .beanFactory
511: .getBean(pa.getBeanName());
512: Advisor refreshedAdvisor = namedBeanToAdvisor(bean);
513: freshAdvisors.add(refreshedAdvisor);
514: } else {
515: // Add the shared instance.
516: freshAdvisors.add(advisors[i]);
517: }
518: }
519: return freshAdvisors;
520: }
521:
522: /**
523: * Add all global interceptors and pointcuts.
524: */
525: private void addGlobalAdvisor(ListableBeanFactory beanFactory,
526: String prefix) {
527: String[] globalAdvisorNames = BeanFactoryUtils
528: .beanNamesForTypeIncludingAncestors(beanFactory,
529: Advisor.class);
530: String[] globalInterceptorNames = BeanFactoryUtils
531: .beanNamesForTypeIncludingAncestors(beanFactory,
532: Interceptor.class);
533: List beans = new ArrayList(globalAdvisorNames.length
534: + globalInterceptorNames.length);
535: Map names = new HashMap();
536: for (int i = 0; i < globalAdvisorNames.length; i++) {
537: String name = globalAdvisorNames[i];
538: Object bean = beanFactory.getBean(name);
539: beans.add(bean);
540: names.put(bean, name);
541: }
542: for (int i = 0; i < globalInterceptorNames.length; i++) {
543: String name = globalInterceptorNames[i];
544: Object bean = beanFactory.getBean(name);
545: beans.add(bean);
546: names.put(bean, name);
547: }
548: Collections.sort(beans, new OrderComparator());
549: for (Iterator it = beans.iterator(); it.hasNext();) {
550: Object bean = it.next();
551: String name = (String) names.get(bean);
552: if (name.startsWith(prefix)) {
553: addAdvisorOnChainCreation(bean, name);
554: }
555: }
556: }
557:
558: /**
559: * Invoked when advice chain is created.
560: * <p>Add the given advice, advisor or object to the interceptor list.
561: * Because of these three possibilities, we can't type the signature
562: * more strongly.
563: * @param next advice, advisor or target object
564: * @param name bean name from which we obtained this object in our owning
565: * bean factory
566: */
567: private void addAdvisorOnChainCreation(Object next, String name) {
568: // We need to convert to an Advisor if necessary so that our source reference
569: // matches what we find from superclass interceptors.
570: Advisor advisor = namedBeanToAdvisor(next);
571: if (logger.isTraceEnabled()) {
572: logger.trace("Adding advisor with name '" + name + "'");
573: }
574: addAdvisor((Advisor) advisor);
575: }
576:
577: /**
578: * Return a TargetSource to use when creating a proxy. If the target was not
579: * specified at the end of the interceptorNames list, the TargetSource will be
580: * this class's TargetSource member. Otherwise, we get the target bean and wrap
581: * it in a TargetSource if necessary.
582: */
583: private TargetSource freshTargetSource() {
584: if (this .targetName == null) {
585: if (logger.isTraceEnabled()) {
586: logger
587: .trace("Not refreshing target: Bean name not specified in 'interceptorNames'.");
588: }
589: return this .targetSource;
590: } else {
591: if (this .beanFactory == null) {
592: throw new IllegalStateException(
593: "No BeanFactory available anymore (probably due to serialization) "
594: + "- cannot resolve target with name '"
595: + this .targetName + "'");
596: }
597: if (logger.isDebugEnabled()) {
598: logger.debug("Refreshing target with name '"
599: + this .targetName + "'");
600: }
601: Object target = this .beanFactory.getBean(this .targetName);
602: return (target instanceof TargetSource ? (TargetSource) target
603: : new SingletonTargetSource(target));
604: }
605: }
606:
607: /**
608: * Convert the following object sourced from calling getBean() on a name in the
609: * interceptorNames array to an Advisor or TargetSource.
610: */
611: private Advisor namedBeanToAdvisor(Object next) {
612: try {
613: return this .advisorAdapterRegistry.wrap(next);
614: } catch (UnknownAdviceTypeException ex) {
615: // We expected this to be an Advisor or Advice,
616: // but it wasn't. This is a configuration error.
617: throw new AopConfigException(
618: "Unknown advisor type "
619: + next.getClass()
620: + "; Can only include Advisor or Advice type beans in interceptorNames chain except for last entry,"
621: + "which may also be target or TargetSource",
622: ex);
623: }
624: }
625:
626: /**
627: * Blow away and recache singleton on an advice change.
628: */
629: protected void adviceChanged() {
630: super .adviceChanged();
631: if (this .singleton) {
632: logger
633: .debug("Advice has changed; recaching singleton instance");
634: synchronized (this ) {
635: this .singletonInstance = null;
636: }
637: }
638: }
639:
640: //---------------------------------------------------------------------
641: // Serialization support
642: //---------------------------------------------------------------------
643:
644: private void readObject(ObjectInputStream ois) throws IOException,
645: ClassNotFoundException {
646: // Rely on default serialization; just initialize state after deserialization.
647: ois.defaultReadObject();
648:
649: // Initialize transient fields.
650: this .beanClassLoader = ClassUtils.getDefaultClassLoader();
651: }
652:
653: /**
654: * Used in the interceptor chain where we need to replace a bean with a prototype
655: * on creating a proxy.
656: */
657: private static class PrototypePlaceholderAdvisor implements
658: Advisor, Serializable {
659:
660: private final String beanName;
661:
662: private final String message;
663:
664: public PrototypePlaceholderAdvisor(String beanName) {
665: this .beanName = beanName;
666: this .message = "Placeholder for prototype Advisor/Advice with bean name '"
667: + beanName + "'";
668: }
669:
670: public String getBeanName() {
671: return beanName;
672: }
673:
674: public Advice getAdvice() {
675: throw new UnsupportedOperationException(
676: "Cannot invoke methods: " + this .message);
677: }
678:
679: public boolean isPerInstance() {
680: throw new UnsupportedOperationException(
681: "Cannot invoke methods: " + this .message);
682: }
683:
684: public String toString() {
685: return this.message;
686: }
687: }
688:
689: }
|