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}