/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.spring.beans.factory.annotation; import com.alibaba.spring.context.annotation.ExposingClassPathBeanDefinitionScanner; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.context.EnvironmentAware; import org.springframework.context.ResourceLoaderAware; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import static com.alibaba.spring.util.AnnotatedBeanDefinitionRegistryUtils.resolveAnnotatedBeanNameGenerator; import static com.alibaba.spring.util.AnnotationUtils.tryGetMergedAnnotation; import static com.alibaba.spring.util.WrapperUtils.unwrap; import static java.util.Arrays.asList; import static java.util.Collections.unmodifiableSet; import static org.springframework.util.ClassUtils.resolveClassName; import static org.springframework.util.CollectionUtils.isEmpty; /** * An abstract class for the extension to {@link BeanDefinitionRegistryPostProcessor}, which will execute two main registration * methods orderly: * <ol> * <li>{@link #registerPrimaryBeanDefinitions(ExposingClassPathBeanDefinitionScanner, String[])} : Scan and register * the primary {@link BeanDefinition BeanDefinitions} that were annotated by * {@link #getSupportedAnnotationTypes() the supported annotation types}, and then return the {@link Map} with bean name plus * aliases if present and primary {@link AnnotatedBeanDefinition AnnotatedBeanDefinitions}, * it's allowed to be override * </li> * <li>{@link #registerSecondaryBeanDefinitions(ExposingClassPathBeanDefinitionScanner, Map, String[])} : * it's mandatory to be override by the sub-class to register secondary {@link BeanDefinition BeanDefinitions} * if required * </li> * </ol> * * @author <a href="mailto:[email protected]">Mercy</a> * @since 1.0.6 */ @SuppressWarnings("unchecked") public abstract class AnnotationBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor, BeanFactoryAware, EnvironmentAware, ResourceLoaderAware, BeanClassLoaderAware { protected final Log logger = LogFactory.getLog(getClass()); private final Set<Class<? extends Annotation>> supportedAnnotationTypes; private final Set<String> packagesToScan; private ConfigurableListableBeanFactory beanFactory; private ConfigurableEnvironment environment; private ResourceLoader resourceLoader; private ClassLoader classLoader; public AnnotationBeanDefinitionRegistryPostProcessor(Class<? extends Annotation> primaryAnnotationType, Class<?>... basePackageClasses) { this(primaryAnnotationType, resolveBasePackages(basePackageClasses)); } public AnnotationBeanDefinitionRegistryPostProcessor(Class<? extends Annotation> primaryAnnotationType, String... packagesToScan) { this(primaryAnnotationType, asList(packagesToScan)); } public AnnotationBeanDefinitionRegistryPostProcessor(Class<? extends Annotation> primaryAnnotationType, Iterable<String> packagesToScan) { this.supportedAnnotationTypes = new LinkedHashSet<Class<? extends Annotation>>(); addSupportedAnnotationType(primaryAnnotationType); this.packagesToScan = new LinkedHashSet<String>(); Iterator<String> iterator = packagesToScan.iterator(); while (iterator.hasNext()) { this.packagesToScan.add(iterator.next()); } } public void addSupportedAnnotationType(Class<? extends Annotation>... annotationTypes) { Assert.notEmpty(annotationTypes, "The argument of annotation types can't be empty"); Assert.noNullElements(annotationTypes, "Any element of annotation types can't be null"); this.supportedAnnotationTypes.addAll(asList(annotationTypes)); } private static String[] resolveBasePackages(Class<?>... basePackageClasses) { int size = basePackageClasses.length; String[] basePackages = new String[size]; for (int i = 0; i < size; i++) { basePackages[i] = basePackageClasses[i].getPackage().getName(); } return basePackages; } protected static Annotation getAnnotation(AnnotatedElement annotatedElement, Class<? extends Annotation> annotationType) { Annotation annotation = tryGetMergedAnnotation(annotatedElement, annotationType); if (annotation == null) { annotation = annotatedElement.getAnnotation(annotationType); } return annotation; } @Override public final void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { String[] basePackages = resolveBasePackages(getPackagesToScan()); if (!ObjectUtils.isEmpty(basePackages)) { registerBeanDefinitions(registry, basePackages); } else { if (logger.isWarnEnabled()) { logger.warn("packagesToScan is empty , The BeanDefinition's registry will be ignored!"); } } } private void registerBeanDefinitions(BeanDefinitionRegistry registry, String[] basePackages) { ExposingClassPathBeanDefinitionScanner scanner = new ExposingClassPathBeanDefinitionScanner(registry, false, getEnvironment(), getResourceLoader()); BeanNameGenerator beanNameGenerator = resolveAnnotatedBeanNameGenerator(registry); // Set the BeanNameGenerator scanner.setBeanNameGenerator(beanNameGenerator); // Add the AnnotationTypeFilter for annotationTypes for (Class<? extends Annotation> supportedAnnotationType : getSupportedAnnotationTypes()) { scanner.addIncludeFilter(new AnnotationTypeFilter(supportedAnnotationType)); } // Register the primary BeanDefinitions Map<String, AnnotatedBeanDefinition> primaryBeanDefinitions = registerPrimaryBeanDefinitions(scanner, basePackages); // Register the secondary BeanDefinitions registerSecondaryBeanDefinitions(scanner, primaryBeanDefinitions, basePackages); } /** * Scan and register the primary {@link BeanDefinition BeanDefinitions} that were annotated by * {@link #getSupportedAnnotationTypes() the supported annotation types}, and then return the {@link Map} with bean name plus * aliases if present and primary {@link AnnotatedBeanDefinition AnnotatedBeanDefinitions}. * <p> * Current method is allowed to be override by the sub-class to change the registration logic * * @param scanner {@link ExposingClassPathBeanDefinitionScanner} * @param basePackages the base packages to scan * @return the {@link Map} with bean name plus aliases if present and primary * {@link AnnotatedBeanDefinition AnnotatedBeanDefinitions} */ protected Map<String, AnnotatedBeanDefinition> registerPrimaryBeanDefinitions(ExposingClassPathBeanDefinitionScanner scanner, String[] basePackages) { // Scan and register Set<BeanDefinitionHolder> primaryBeanDefinitionHolders = scanner.doScan(basePackages); // Log the primary BeanDefinitions logPrimaryBeanDefinitions(primaryBeanDefinitionHolders, basePackages); Map<String, AnnotatedBeanDefinition> primaryBeanDefinitions = new LinkedHashMap<String, AnnotatedBeanDefinition>(); for (BeanDefinitionHolder beanDefinitionHolder : primaryBeanDefinitionHolders) { putPrimaryBeanDefinitions(primaryBeanDefinitions, beanDefinitionHolder); } // return return primaryBeanDefinitions; } private void putPrimaryBeanDefinitions(Map<String, AnnotatedBeanDefinition> primaryBeanDefinitions, BeanDefinitionHolder beanDefinitionHolder) { BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition(); if (beanDefinition instanceof AnnotatedBeanDefinition) { AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition; putPrimaryBeanDefinition(primaryBeanDefinitions, annotatedBeanDefinition, beanDefinitionHolder.getBeanName()); putPrimaryBeanDefinition(primaryBeanDefinitions, annotatedBeanDefinition, beanDefinitionHolder.getAliases()); } else { if (logger.isErrorEnabled()) { logger.error("What's the problem? Please investigate " + beanDefinitionHolder); } } } private void putPrimaryBeanDefinition(Map<String, AnnotatedBeanDefinition> primaryBeanDefinitions, AnnotatedBeanDefinition annotatedBeanDefinition, String... keys) { if (!ObjectUtils.isEmpty(keys)) { for (String key : keys) { primaryBeanDefinitions.put(key, annotatedBeanDefinition); } } } /** * Register the secondary {@link BeanDefinition BeanDefinitions} * <p> * Current method is allowed to be override by the sub-class to change the registration logic * * @param scanner the {@link ExposingClassPathBeanDefinitionScanner} instance * @param primaryBeanDefinitions the {@link Map} with bean name plus aliases if present and primary * {@link AnnotatedBeanDefinition AnnotatedBeanDefinitions}, which may be empty * @param basePackages the base packages to scan */ protected abstract void registerSecondaryBeanDefinitions(ExposingClassPathBeanDefinitionScanner scanner, Map<String, AnnotatedBeanDefinition> primaryBeanDefinitions, String[] basePackages); @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // DO NOTHING } private void logPrimaryBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitionHolders, String[] basePackages) { if (isEmpty(beanDefinitionHolders)) { if (logger.isWarnEnabled()) { logger.warn("No Spring Bean annotation @" + getSupportedAnnotationTypeNames() + " was found under base packages" + asList(basePackages)); } } else { if (logger.isInfoEnabled()) { logger.info(beanDefinitionHolders.size() + " annotations " + getSupportedAnnotationTypeNames() + " components { " + beanDefinitionHolders + " } were scanned under packages" + asList(basePackages)); } } } /** * Resolve the placeholders for the raw scanned packages to scan * * @param packagesToScan the raw scanned packages to scan * @return non-null */ protected String[] resolveBasePackages(Set<String> packagesToScan) { Set<String> resolvedPackagesToScan = new LinkedHashSet<String>(packagesToScan.size()); for (String packageToScan : packagesToScan) { if (StringUtils.hasText(packageToScan)) { String resolvedPackageToScan = getEnvironment().resolvePlaceholders(packageToScan.trim()); resolvedPackagesToScan.add(resolvedPackageToScan); } } // Set to Array return packagesToScan.toArray(new String[packagesToScan.size()]); } protected final Class<?> resolveBeanClass(BeanDefinitionHolder beanDefinitionHolder) { BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition(); return resolveBeanClass(beanDefinition); } protected final Class<?> resolveBeanClass(BeanDefinition beanDefinition) { String beanClassName = beanDefinition.getBeanClassName(); return resolveClassName(beanClassName, getClassLoader()); } @Override public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } public Set<Class<? extends Annotation>> getSupportedAnnotationTypes() { return unmodifiableSet(supportedAnnotationTypes); } protected Set<String> getSupportedAnnotationTypeNames() { Set<String> supportedAnnotationTypeNames = new LinkedHashSet<String>(); for (Class<? extends Annotation> supportedAnnotationType : supportedAnnotationTypes) { supportedAnnotationTypeNames.add(supportedAnnotationType.getName()); } return unmodifiableSet(supportedAnnotationTypeNames); } public Set<String> getPackagesToScan() { return packagesToScan; } public ConfigurableListableBeanFactory getBeanFactory() { return beanFactory; } public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = unwrap(beanFactory); } public ConfigurableEnvironment getEnvironment() { return environment; } @Override public void setEnvironment(Environment environment) { this.environment = unwrap(environment); } public ResourceLoader getResourceLoader() { return resourceLoader; } @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } public ClassLoader getClassLoader() { return classLoader; } public void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } }