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