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