1. /*
  2. * Copyright 2002-2004 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.aop.framework;
  17. import java.util.ArrayList;
  18. import java.util.Collections;
  19. import java.util.HashMap;
  20. import java.util.Iterator;
  21. import java.util.List;
  22. import java.util.Map;
  23. import org.aopalliance.aop.AspectException;
  24. import org.aopalliance.intercept.Interceptor;
  25. import org.springframework.aop.Advisor;
  26. import org.springframework.aop.TargetSource;
  27. import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry;
  28. import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry;
  29. import org.springframework.aop.framework.adapter.UnknownAdviceTypeException;
  30. import org.springframework.aop.support.AopUtils;
  31. import org.springframework.aop.target.SingletonTargetSource;
  32. import org.springframework.beans.BeansException;
  33. import org.springframework.beans.factory.BeanFactory;
  34. import org.springframework.beans.factory.BeanFactoryAware;
  35. import org.springframework.beans.factory.BeanFactoryUtils;
  36. import org.springframework.beans.factory.FactoryBean;
  37. import org.springframework.beans.factory.ListableBeanFactory;
  38. import org.springframework.core.OrderComparator;
  39. /**
  40. * FactoryBean implementation for use to source AOP proxies from a Spring BeanFactory.
  41. *
  42. * <p>Interceptors and Advisors are identified by a list of bean names in the current
  43. * bean factory. These beans should be of type Interceptor or Advisor. The last entry
  44. * in the list can be the name of any bean in the factory. If it's neither an
  45. * Interceptor nor an Advisor, a new SingletonTargetSource is added to wrap it.
  46. *
  47. * <p>Global interceptors and advisors can be added at the factory level. The specified
  48. * ones are expanded in an interceptor list where an "xxx*" entry is included in the
  49. * list, matching the given prefix with the bean names (e.g. "global*" would match
  50. * both "globalBean1" and "globalBean2", "*" all defined interceptors). The matching
  51. * interceptors get applied according to their returned order value, if they
  52. * implement the Ordered interface. An interceptor name list may not conclude
  53. * with a global "xxx*" pattern, as global interceptors cannot invoke targets.
  54. *
  55. * <p>Creates a J2SE proxy when proxy interfaces are given, a CGLIB proxy for the
  56. * actual target class if not. Note that the latter will only work if the target class
  57. * does not have final methods, as a dynamic subclass will be created at runtime.
  58. *
  59. * <p>It's possible to cast a proxy obtained from this factory to Advisor, or to
  60. * obtain the ProxyFactoryBean reference and programmatically manipulate it.
  61. * This won't work for existing prototype references, which are independent. However,
  62. * it will work for prototypes subsequently obtained from the factory. Changes to
  63. * interception will work immediately on singletons (including existing references).
  64. * However, to change interfaces or target it's necessary to obtain a new instance
  65. * from the factory. This means that singleton instances obtained from the factory
  66. * do not have the same object identity. However, they do have the same interceptors
  67. * and target, and changing any reference will change all objects.
  68. *
  69. * @author Rod Johnson
  70. * @author Juergen Hoeller
  71. * @version $Id: ProxyFactoryBean.java,v 1.29 2004/05/23 20:13:06 jhoeller Exp $
  72. * @see #setInterceptorNames
  73. * @see #setProxyInterfaces
  74. * @see org.aopalliance.intercept.MethodInterceptor
  75. * @see org.springframework.aop.Advisor
  76. * @see org.springframework.aop.target.SingletonTargetSource
  77. */
  78. public class ProxyFactoryBean extends AdvisedSupport
  79. implements FactoryBean, BeanFactoryAware, AdvisedSupportListener {
  80. /**
  81. * This suffix in a value in an interceptor list indicates to expand globals.
  82. */
  83. public static final String GLOBAL_SUFFIX = "*";
  84. /**
  85. * Names of interceptor and pointcut beans in the factory.
  86. * Default is for globals expansion only.
  87. */
  88. private String[] interceptorNames;
  89. private boolean singleton = true;
  90. /** Default is global AdvisorAdapterRegistry */
  91. private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
  92. /**
  93. * Owning bean factory, which cannot be changed after this
  94. * object is initialized.
  95. */
  96. private BeanFactory beanFactory;
  97. private String targetName;
  98. /**
  99. * Map from PointCut or interceptor to bean name or null,
  100. * depending on where it was sourced from. If it's sourced
  101. * from a bean name, it will need to be refreshed each time a
  102. * new prototype instance is created.
  103. */
  104. private Map sourceMap = new HashMap();
  105. /** If this is a singleton, the cached instance */
  106. private Object singletonInstance;
  107. /**
  108. * Set the names of the interfaces we're proxying. If no interface
  109. * is given, a CGLIB for the actual class will be created.
  110. */
  111. public void setProxyInterfaces(String[] interfaceNames) throws AspectException, ClassNotFoundException {
  112. Class[] interfaces = AopUtils.toInterfaceArray(interfaceNames);
  113. setInterfaces(interfaces);
  114. }
  115. /**
  116. * Set the list of Interceptor/Advisor bean names. This must always be set
  117. * to use this factory bean in a bean factory.
  118. * <p>The referenced beans should be of type Interceptor, Advisor or Advice
  119. * The last entry in the list can be the name of any bean in the factory.
  120. * If it's neither an Interceptor nor an Advisor, a new SingletonTargetSource
  121. * is added to wrap it.
  122. * @see org.aopalliance.intercept.MethodInterceptor
  123. * @see org.springframework.aop.Advisor
  124. * @see org.aopalliance.aop.Advice
  125. * @see org.springframework.aop.target.SingletonTargetSource
  126. */
  127. public void setInterceptorNames(String[] interceptorNames) {
  128. this.interceptorNames = interceptorNames;
  129. }
  130. /**
  131. * Set the value of the singleton property. Governs whether this factory
  132. * should always return the same proxy instance (which implies the same target)
  133. * or whether it should return a new prototype instance, which implies that
  134. * the target and interceptors may be new instances also, if they are obtained
  135. * from prototype bean definitions.
  136. * This allows for fine control of independence/uniqueness in the object graph.
  137. * @param singleton
  138. */
  139. public void setSingleton(boolean singleton) {
  140. this.singleton = singleton;
  141. }
  142. /**
  143. * Specify the AdvisorAdapterRegistry to use.
  144. * Default is the global AdvisorAdapterRegistry.
  145. * @see org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry
  146. */
  147. public void setAdvisorAdapterRegistry(AdvisorAdapterRegistry advisorAdapterRegistry) {
  148. this.advisorAdapterRegistry = advisorAdapterRegistry;
  149. }
  150. public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
  151. this.beanFactory = beanFactory;
  152. logger.debug("Set BeanFactory. Will configure interceptor beans...");
  153. createAdvisorChain();
  154. logger.info("ProxyFactoryBean config: " + this);
  155. if (this.singleton) {
  156. // Eagerly initialize the shared singleton instance
  157. getSingletonInstance();
  158. // We must listen to superclass advice change events to recache singleton
  159. // instance if necessary
  160. addListener(this);
  161. }
  162. }
  163. /**
  164. * Return a proxy. Invoked when clients obtain beans
  165. * from this factory bean. Create an instance of the AOP proxy to be returned by this factory.
  166. * The instance will be cached for a singleton, and create on each call to
  167. * getObject() for a proxy.
  168. * @return Object a fresh AOP proxy reflecting the current
  169. * state of this factory
  170. */
  171. public Object getObject() throws BeansException {
  172. return (this.singleton) ? getSingletonInstance() : newPrototypeInstance();
  173. }
  174. public Class getObjectType() {
  175. return getTargetSource().getTargetClass();
  176. }
  177. public boolean isSingleton() {
  178. return this.singleton;
  179. }
  180. private Object getSingletonInstance() {
  181. if (this.singletonInstance == null) {
  182. // This object can configure the proxy directly if it's
  183. // being used as a singleton.
  184. this.singletonInstance = createAopProxy().getProxy();
  185. }
  186. return this.singletonInstance;
  187. }
  188. private synchronized Object newPrototypeInstance() {
  189. refreshAdvisorChain();
  190. refreshTarget();
  191. // In the case of a prototype, we need to give the proxy
  192. // an independent instance of the configuration.
  193. if (logger.isDebugEnabled()) {
  194. logger.debug("Creating copy of prototype ProxyFactoryBean config: " + this);
  195. }
  196. AdvisedSupport copy = new AdvisedSupport();
  197. copy.copyConfigurationFrom(this);
  198. if (logger.isDebugEnabled()) {
  199. logger.debug("Copy has config: " + copy);
  200. }
  201. return copy.createAopProxy().getProxy();
  202. }
  203. /**
  204. * Create the advisor (interceptor) chain. The advisors that are sourced
  205. * from a BeanFactory will be refreshed each time a new prototype instance
  206. * is added. Interceptors added programmatically through the factory API
  207. * are unaffected by such changes.
  208. */
  209. private void createAdvisorChain() throws AopConfigException, BeansException {
  210. if (this.interceptorNames == null || this.interceptorNames.length == 0) {
  211. return;
  212. }
  213. // Globals can't be last
  214. if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX)) {
  215. throw new AopConfigException("Target required after globals");
  216. }
  217. // Materialize interceptor chain from bean names
  218. for (int i = 0; i < this.interceptorNames.length; i++) {
  219. String name = this.interceptorNames[i];
  220. logger.debug("Configuring interceptor '" + name + "'");
  221. if (name.endsWith(GLOBAL_SUFFIX)) {
  222. if (!(this.beanFactory instanceof ListableBeanFactory)) {
  223. throw new AopConfigException("Can only use global advisors or interceptors with a ListableBeanFactory");
  224. }
  225. else {
  226. addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
  227. name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
  228. }
  229. }
  230. else {
  231. // add a named interceptor
  232. Object advice = this.beanFactory.getBean(this.interceptorNames[i]);
  233. addAdvisor(advice, this.interceptorNames[i]);
  234. }
  235. }
  236. }
  237. /**
  238. * Refresh named beans from the interceptor chain.
  239. * We need to do this every time a new prototype instance is returned,
  240. * to return distinct instances of prototype interfaces and pointcuts.
  241. */
  242. private void refreshAdvisorChain() {
  243. Advisor[] advisors = getAdvisors();
  244. for (int i = 0; i < advisors.length; i++) {
  245. String beanName = (String) this.sourceMap.get(advisors[i]);
  246. if (beanName != null) {
  247. logger.info("Refreshing bean named '" + beanName + "'");
  248. Object bean = this.beanFactory.getBean(beanName);
  249. Object refreshedAdvisor = namedBeanToAdvisorOrTargetSource(bean);
  250. // might have just refreshed target source
  251. if (refreshedAdvisor instanceof Advisor) {
  252. // What about aspect interfaces!? we're only updating
  253. replaceAdvisor(advisors[i], (Advisor) refreshedAdvisor);
  254. }
  255. else {
  256. setTargetSource((TargetSource) refreshedAdvisor);
  257. }
  258. // keep name mapping up to date
  259. this.sourceMap.put(refreshedAdvisor, beanName);
  260. }
  261. else {
  262. // We can't throw an exception here, as the user may have added additional
  263. // pointcuts programmatically we don't know about
  264. logger.info("Cannot find bean name for Advisor [" + advisors[i] +
  265. "] when refreshing advisor chain");
  266. }
  267. }
  268. }
  269. /**
  270. * Add all global interceptors and pointcuts.
  271. */
  272. private void addGlobalAdvisor(ListableBeanFactory beanFactory, String prefix) {
  273. String[] globalAdvisorNames = BeanFactoryUtils.beanNamesIncludingAncestors(beanFactory, Advisor.class);
  274. String[] globalInterceptorNames = BeanFactoryUtils.beanNamesIncludingAncestors(beanFactory, Interceptor.class);
  275. List beans = new ArrayList(globalAdvisorNames.length + globalInterceptorNames.length);
  276. Map names = new HashMap();
  277. for (int i = 0; i < globalAdvisorNames.length; i++) {
  278. String name = globalAdvisorNames[i];
  279. Object bean = beanFactory.getBean(name);
  280. beans.add(bean);
  281. names.put(bean, name);
  282. }
  283. for (int i = 0; i < globalInterceptorNames.length; i++) {
  284. String name = globalInterceptorNames[i];
  285. Object bean = beanFactory.getBean(name);
  286. beans.add(bean);
  287. names.put(bean, name);
  288. }
  289. Collections.sort(beans, new OrderComparator());
  290. for (Iterator it = beans.iterator(); it.hasNext();) {
  291. Object bean = it.next();
  292. String name = (String) names.get(bean);
  293. if (name.startsWith(prefix)) {
  294. addAdvisor(bean, name);
  295. }
  296. }
  297. }
  298. /**
  299. * Add the given interceptor, pointcut or object to the interceptor list.
  300. * Because of these three possibilities, we can't type the signature
  301. * more strongly.
  302. * @param next interceptor, pointcut or target object.
  303. * @param name bean name from which we obtained this object in our owning
  304. * bean factory
  305. */
  306. private void addAdvisor(Object next, String name) {
  307. logger.debug("Adding advisor or TargetSource [" + next + "] with name [" + name + "]");
  308. // We need to add a method pointcut so that our source reference matches
  309. // what we find from superclass interceptors.
  310. Object advisor = namedBeanToAdvisorOrTargetSource(next);
  311. if (advisor instanceof Advisor) {
  312. // if it wasn't just updating the TargetSource
  313. logger.debug("Adding advisor with name [" + name + "]");
  314. addAdvisor((Advisor) advisor);
  315. // Record the pointcut as descended from the given bean name.
  316. // This allows us to refresh the interceptor list, which we'll need to
  317. // do if we have to create a new prototype instance. Otherwise the new
  318. // prototype instance wouldn't be truly independent, because it might
  319. // reference the original instances of prototype interceptors.
  320. this.sourceMap.put(advisor, name);
  321. }
  322. else {
  323. logger.debug("Adding TargetSource [" + advisor + "] with name [" + name + "]");
  324. setTargetSource((TargetSource) advisor);
  325. // save target name
  326. this.targetName = name;
  327. }
  328. }
  329. private void refreshTarget() {
  330. logger.debug("Refreshing target with name '" + this.targetName + "'");
  331. if (this.targetName == null) {
  332. throw new AopConfigException("Target name cannot be null when refreshing!");
  333. }
  334. Object target = this.beanFactory.getBean(this.targetName);
  335. setTarget(target);
  336. }
  337. /**
  338. * Return Advisor or TargetSource.
  339. */
  340. private Object namedBeanToAdvisorOrTargetSource(Object next) {
  341. try {
  342. Advisor adv = this.advisorAdapterRegistry.wrap(next);
  343. return adv;
  344. }
  345. catch (UnknownAdviceTypeException ex) {
  346. // TODO consider checking that it's the last in the list?
  347. if (next instanceof TargetSource) {
  348. return (TargetSource) next;
  349. }
  350. else {
  351. // It's not a pointcut or interceptor.
  352. // It's a bean that needs an invoker around it.
  353. return new SingletonTargetSource(next);
  354. }
  355. }
  356. }
  357. /**
  358. * @see org.springframework.aop.framework.AdvisedSupportListener#activated(org.springframework.aop.framework.AdvisedSupport)
  359. */
  360. public void activated(AdvisedSupport advisedSupport) {
  361. }
  362. /**
  363. * Blow away and recache singleton to allow for advice changes.
  364. * @see org.springframework.aop.framework.AdvisedSupportListener#adviceChanged(org.springframework.aop.framework.AdvisedSupport)
  365. */
  366. public void adviceChanged(AdvisedSupport advisedSupport) {
  367. logger.info("Advice has changed; recaching singleton instance");
  368. this.singletonInstance = null;
  369. getSingletonInstance();
  370. }
  371. }