001/* 002 * Copyright 2002-2020 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.aspectj.annotation; 018 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.List; 022import java.util.Map; 023import java.util.concurrent.ConcurrentHashMap; 024 025import org.aspectj.lang.reflect.PerClauseKind; 026 027import org.springframework.aop.Advisor; 028import org.springframework.beans.factory.BeanFactoryUtils; 029import org.springframework.beans.factory.ListableBeanFactory; 030import org.springframework.lang.Nullable; 031import org.springframework.util.Assert; 032 033/** 034 * Helper for retrieving @AspectJ beans from a BeanFactory and building 035 * Spring Advisors based on them, for use with auto-proxying. 036 * 037 * @author Juergen Hoeller 038 * @since 2.0.2 039 * @see AnnotationAwareAspectJAutoProxyCreator 040 */ 041public class BeanFactoryAspectJAdvisorsBuilder { 042 043 private final ListableBeanFactory beanFactory; 044 045 private final AspectJAdvisorFactory advisorFactory; 046 047 @Nullable 048 private volatile List<String> aspectBeanNames; 049 050 private final Map<String, List<Advisor>> advisorsCache = new ConcurrentHashMap<>(); 051 052 private final Map<String, MetadataAwareAspectInstanceFactory> aspectFactoryCache = new ConcurrentHashMap<>(); 053 054 055 /** 056 * Create a new BeanFactoryAspectJAdvisorsBuilder for the given BeanFactory. 057 * @param beanFactory the ListableBeanFactory to scan 058 */ 059 public BeanFactoryAspectJAdvisorsBuilder(ListableBeanFactory beanFactory) { 060 this(beanFactory, new ReflectiveAspectJAdvisorFactory(beanFactory)); 061 } 062 063 /** 064 * Create a new BeanFactoryAspectJAdvisorsBuilder for the given BeanFactory. 065 * @param beanFactory the ListableBeanFactory to scan 066 * @param advisorFactory the AspectJAdvisorFactory to build each Advisor with 067 */ 068 public BeanFactoryAspectJAdvisorsBuilder(ListableBeanFactory beanFactory, AspectJAdvisorFactory advisorFactory) { 069 Assert.notNull(beanFactory, "ListableBeanFactory must not be null"); 070 Assert.notNull(advisorFactory, "AspectJAdvisorFactory must not be null"); 071 this.beanFactory = beanFactory; 072 this.advisorFactory = advisorFactory; 073 } 074 075 076 /** 077 * Look for AspectJ-annotated aspect beans in the current bean factory, 078 * and return to a list of Spring AOP Advisors representing them. 079 * <p>Creates a Spring Advisor for each AspectJ advice method. 080 * @return the list of {@link org.springframework.aop.Advisor} beans 081 * @see #isEligibleBean 082 */ 083 public List<Advisor> buildAspectJAdvisors() { 084 List<String> aspectNames = this.aspectBeanNames; 085 086 if (aspectNames == null) { 087 synchronized (this) { 088 aspectNames = this.aspectBeanNames; 089 if (aspectNames == null) { 090 List<Advisor> advisors = new ArrayList<>(); 091 aspectNames = new ArrayList<>(); 092 String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( 093 this.beanFactory, Object.class, true, false); 094 for (String beanName : beanNames) { 095 if (!isEligibleBean(beanName)) { 096 continue; 097 } 098 // We must be careful not to instantiate beans eagerly as in this case they 099 // would be cached by the Spring container but would not have been weaved. 100 Class<?> beanType = this.beanFactory.getType(beanName, false); 101 if (beanType == null) { 102 continue; 103 } 104 if (this.advisorFactory.isAspect(beanType)) { 105 aspectNames.add(beanName); 106 AspectMetadata amd = new AspectMetadata(beanType, beanName); 107 if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { 108 MetadataAwareAspectInstanceFactory factory = 109 new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); 110 List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory); 111 if (this.beanFactory.isSingleton(beanName)) { 112 this.advisorsCache.put(beanName, classAdvisors); 113 } 114 else { 115 this.aspectFactoryCache.put(beanName, factory); 116 } 117 advisors.addAll(classAdvisors); 118 } 119 else { 120 // Per target or per this. 121 if (this.beanFactory.isSingleton(beanName)) { 122 throw new IllegalArgumentException("Bean with name '" + beanName + 123 "' is a singleton, but aspect instantiation model is not singleton"); 124 } 125 MetadataAwareAspectInstanceFactory factory = 126 new PrototypeAspectInstanceFactory(this.beanFactory, beanName); 127 this.aspectFactoryCache.put(beanName, factory); 128 advisors.addAll(this.advisorFactory.getAdvisors(factory)); 129 } 130 } 131 } 132 this.aspectBeanNames = aspectNames; 133 return advisors; 134 } 135 } 136 } 137 138 if (aspectNames.isEmpty()) { 139 return Collections.emptyList(); 140 } 141 List<Advisor> advisors = new ArrayList<>(); 142 for (String aspectName : aspectNames) { 143 List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName); 144 if (cachedAdvisors != null) { 145 advisors.addAll(cachedAdvisors); 146 } 147 else { 148 MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName); 149 advisors.addAll(this.advisorFactory.getAdvisors(factory)); 150 } 151 } 152 return advisors; 153 } 154 155 /** 156 * Return whether the aspect bean with the given name is eligible. 157 * @param beanName the name of the aspect bean 158 * @return whether the bean is eligible 159 */ 160 protected boolean isEligibleBean(String beanName) { 161 return true; 162 } 163 164}