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