001/*
002 * Copyright 2012-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 *      http://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.boot.context.properties;
018
019import java.lang.annotation.Annotation;
020import java.lang.reflect.Method;
021import java.util.HashMap;
022import java.util.Map;
023import java.util.concurrent.atomic.AtomicReference;
024
025import org.springframework.beans.BeansException;
026import org.springframework.beans.factory.config.BeanDefinition;
027import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
028import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
029import org.springframework.core.annotation.AnnotationUtils;
030import org.springframework.util.ClassUtils;
031import org.springframework.util.ReflectionUtils;
032
033/**
034 * Utility class to memorize {@code @Bean} definition meta data during initialization of
035 * the bean factory.
036 *
037 * @author Dave Syer
038 * @since 1.1.0
039 */
040public class ConfigurationBeanFactoryMetadata implements BeanFactoryPostProcessor {
041
042        /**
043         * The bean name that this class is registered with.
044         */
045        public static final String BEAN_NAME = ConfigurationBeanFactoryMetadata.class
046                        .getName();
047
048        private ConfigurableListableBeanFactory beanFactory;
049
050        private final Map<String, FactoryMetadata> beansFactoryMetadata = new HashMap<>();
051
052        @Override
053        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
054                        throws BeansException {
055                this.beanFactory = beanFactory;
056                for (String name : beanFactory.getBeanDefinitionNames()) {
057                        BeanDefinition definition = beanFactory.getBeanDefinition(name);
058                        String method = definition.getFactoryMethodName();
059                        String bean = definition.getFactoryBeanName();
060                        if (method != null && bean != null) {
061                                this.beansFactoryMetadata.put(name, new FactoryMetadata(bean, method));
062                        }
063                }
064        }
065
066        public <A extends Annotation> Map<String, Object> getBeansWithFactoryAnnotation(
067                        Class<A> type) {
068                Map<String, Object> result = new HashMap<>();
069                for (String name : this.beansFactoryMetadata.keySet()) {
070                        if (findFactoryAnnotation(name, type) != null) {
071                                result.put(name, this.beanFactory.getBean(name));
072                        }
073                }
074                return result;
075        }
076
077        public <A extends Annotation> A findFactoryAnnotation(String beanName,
078                        Class<A> type) {
079                Method method = findFactoryMethod(beanName);
080                return (method != null) ? AnnotationUtils.findAnnotation(method, type) : null;
081        }
082
083        public Method findFactoryMethod(String beanName) {
084                if (!this.beansFactoryMetadata.containsKey(beanName)) {
085                        return null;
086                }
087                AtomicReference<Method> found = new AtomicReference<>(null);
088                FactoryMetadata metadata = this.beansFactoryMetadata.get(beanName);
089                Class<?> factoryType = this.beanFactory.getType(metadata.getBean());
090                String factoryMethod = metadata.getMethod();
091                if (ClassUtils.isCglibProxyClass(factoryType)) {
092                        factoryType = factoryType.getSuperclass();
093                }
094                ReflectionUtils.doWithMethods(factoryType, (method) -> {
095                        if (method.getName().equals(factoryMethod)) {
096                                found.compareAndSet(null, method);
097                        }
098                });
099                return found.get();
100        }
101
102        private static class FactoryMetadata {
103
104                private final String bean;
105
106                private final String method;
107
108                FactoryMetadata(String bean, String method) {
109                        this.bean = bean;
110                        this.method = method;
111                }
112
113                public String getBean() {
114                        return this.bean;
115                }
116
117                public String getMethod() {
118                        return this.method;
119                }
120
121        }
122
123}