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}