001/* 002 * Copyright 2002-2018 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.framework.autoproxy; 018 019import java.util.ArrayList; 020import java.util.List; 021 022import org.apache.commons.logging.Log; 023import org.apache.commons.logging.LogFactory; 024 025import org.springframework.aop.Advisor; 026import org.springframework.beans.factory.BeanCreationException; 027import org.springframework.beans.factory.BeanCurrentlyInCreationException; 028import org.springframework.beans.factory.BeanFactoryUtils; 029import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 030import org.springframework.lang.Nullable; 031import org.springframework.util.Assert; 032 033/** 034 * Helper for retrieving standard Spring Advisors from a BeanFactory, 035 * for use with auto-proxying. 036 * 037 * @author Juergen Hoeller 038 * @since 2.0.2 039 * @see AbstractAdvisorAutoProxyCreator 040 */ 041public class BeanFactoryAdvisorRetrievalHelper { 042 043 private static final Log logger = LogFactory.getLog(BeanFactoryAdvisorRetrievalHelper.class); 044 045 private final ConfigurableListableBeanFactory beanFactory; 046 047 @Nullable 048 private volatile String[] cachedAdvisorBeanNames; 049 050 051 /** 052 * Create a new BeanFactoryAdvisorRetrievalHelper for the given BeanFactory. 053 * @param beanFactory the ListableBeanFactory to scan 054 */ 055 public BeanFactoryAdvisorRetrievalHelper(ConfigurableListableBeanFactory beanFactory) { 056 Assert.notNull(beanFactory, "ListableBeanFactory must not be null"); 057 this.beanFactory = beanFactory; 058 } 059 060 061 /** 062 * Find all eligible Advisor beans in the current bean factory, 063 * ignoring FactoryBeans and excluding beans that are currently in creation. 064 * @return the list of {@link org.springframework.aop.Advisor} beans 065 * @see #isEligibleBean 066 */ 067 public List<Advisor> findAdvisorBeans() { 068 // Determine list of advisor bean names, if not cached already. 069 String[] advisorNames = this.cachedAdvisorBeanNames; 070 if (advisorNames == null) { 071 // Do not initialize FactoryBeans here: We need to leave all regular beans 072 // uninitialized to let the auto-proxy creator apply to them! 073 advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( 074 this.beanFactory, Advisor.class, true, false); 075 this.cachedAdvisorBeanNames = advisorNames; 076 } 077 if (advisorNames.length == 0) { 078 return new ArrayList<>(); 079 } 080 081 List<Advisor> advisors = new ArrayList<>(); 082 for (String name : advisorNames) { 083 if (isEligibleBean(name)) { 084 if (this.beanFactory.isCurrentlyInCreation(name)) { 085 if (logger.isTraceEnabled()) { 086 logger.trace("Skipping currently created advisor '" + name + "'"); 087 } 088 } 089 else { 090 try { 091 advisors.add(this.beanFactory.getBean(name, Advisor.class)); 092 } 093 catch (BeanCreationException ex) { 094 Throwable rootCause = ex.getMostSpecificCause(); 095 if (rootCause instanceof BeanCurrentlyInCreationException) { 096 BeanCreationException bce = (BeanCreationException) rootCause; 097 String bceBeanName = bce.getBeanName(); 098 if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) { 099 if (logger.isTraceEnabled()) { 100 logger.trace("Skipping advisor '" + name + 101 "' with dependency on currently created bean: " + ex.getMessage()); 102 } 103 // Ignore: indicates a reference back to the bean we're trying to advise. 104 // We want to find advisors other than the currently created bean itself. 105 continue; 106 } 107 } 108 throw ex; 109 } 110 } 111 } 112 } 113 return advisors; 114 } 115 116 /** 117 * Determine whether the aspect bean with the given name is eligible. 118 * <p>The default implementation always returns {@code true}. 119 * @param beanName the name of the aspect bean 120 * @return whether the bean is eligible 121 */ 122 protected boolean isEligibleBean(String beanName) { 123 return true; 124 } 125 126}