001/*
002 * Copyright 2002-2017 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.support;
018
019import java.io.IOException;
020import java.io.ObjectInputStream;
021
022import org.aopalliance.aop.Advice;
023
024import org.springframework.beans.factory.BeanFactory;
025import org.springframework.beans.factory.BeanFactoryAware;
026import org.springframework.beans.factory.config.ConfigurableBeanFactory;
027import org.springframework.lang.Nullable;
028import org.springframework.util.Assert;
029
030/**
031 * Abstract BeanFactory-based PointcutAdvisor that allows for any Advice
032 * to be configured as reference to an Advice bean in a BeanFactory.
033 *
034 * <p>Specifying the name of an advice bean instead of the advice object itself
035 * (if running within a BeanFactory) increases loose coupling at initialization time,
036 * in order to not initialize the advice object until the pointcut actually matches.
037 *
038 * @author Juergen Hoeller
039 * @since 2.0.2
040 * @see #setAdviceBeanName
041 * @see DefaultBeanFactoryPointcutAdvisor
042 */
043@SuppressWarnings("serial")
044public abstract class AbstractBeanFactoryPointcutAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
045
046        @Nullable
047        private String adviceBeanName;
048
049        @Nullable
050        private BeanFactory beanFactory;
051
052        @Nullable
053        private transient volatile Advice advice;
054
055        private transient volatile Object adviceMonitor = new Object();
056
057
058        /**
059         * Specify the name of the advice bean that this advisor should refer to.
060         * <p>An instance of the specified bean will be obtained on first access
061         * of this advisor's advice. This advisor will only ever obtain at most one
062         * single instance of the advice bean, caching the instance for the lifetime
063         * of the advisor.
064         * @see #getAdvice()
065         */
066        public void setAdviceBeanName(@Nullable String adviceBeanName) {
067                this.adviceBeanName = adviceBeanName;
068        }
069
070        /**
071         * Return the name of the advice bean that this advisor refers to, if any.
072         */
073        @Nullable
074        public String getAdviceBeanName() {
075                return this.adviceBeanName;
076        }
077
078        @Override
079        public void setBeanFactory(BeanFactory beanFactory) {
080                this.beanFactory = beanFactory;
081                resetAdviceMonitor();
082        }
083
084        private void resetAdviceMonitor() {
085                if (this.beanFactory instanceof ConfigurableBeanFactory) {
086                        this.adviceMonitor = ((ConfigurableBeanFactory) this.beanFactory).getSingletonMutex();
087                }
088                else {
089                        this.adviceMonitor = new Object();
090                }
091        }
092
093        /**
094         * Specify a particular instance of the target advice directly,
095         * avoiding lazy resolution in {@link #getAdvice()}.
096         * @since 3.1
097         */
098        public void setAdvice(Advice advice) {
099                synchronized (this.adviceMonitor) {
100                        this.advice = advice;
101                }
102        }
103
104        @Override
105        public Advice getAdvice() {
106                Advice advice = this.advice;
107                if (advice != null) {
108                        return advice;
109                }
110
111                Assert.state(this.adviceBeanName != null, "'adviceBeanName' must be specified");
112                Assert.state(this.beanFactory != null, "BeanFactory must be set to resolve 'adviceBeanName'");
113
114                if (this.beanFactory.isSingleton(this.adviceBeanName)) {
115                        // Rely on singleton semantics provided by the factory.
116                        advice = this.beanFactory.getBean(this.adviceBeanName, Advice.class);
117                        this.advice = advice;
118                        return advice;
119                }
120                else {
121                        // No singleton guarantees from the factory -> let's lock locally but
122                        // reuse the factory's singleton lock, just in case a lazy dependency
123                        // of our advice bean happens to trigger the singleton lock implicitly...
124                        synchronized (this.adviceMonitor) {
125                                advice = this.advice;
126                                if (advice == null) {
127                                        advice = this.beanFactory.getBean(this.adviceBeanName, Advice.class);
128                                        this.advice = advice;
129                                }
130                                return advice;
131                        }
132                }
133        }
134
135        @Override
136        public String toString() {
137                StringBuilder sb = new StringBuilder(getClass().getName());
138                sb.append(": advice ");
139                if (this.adviceBeanName != null) {
140                        sb.append("bean '").append(this.adviceBeanName).append("'");
141                }
142                else {
143                        sb.append(this.advice);
144                }
145                return sb.toString();
146        }
147
148
149        //---------------------------------------------------------------------
150        // Serialization support
151        //---------------------------------------------------------------------
152
153        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
154                // Rely on default serialization, just initialize state after deserialization.
155                ois.defaultReadObject();
156
157                // Initialize transient fields.
158                resetAdviceMonitor();
159        }
160
161}