Package org.springframework.boot

Source Code of org.springframework.boot.BeanDefinitionLoader$ClassExcludeFilter

/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed 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 org.springframework.boot;

import groovy.lang.Closure;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.filter.AbstractTypeHierarchyTraversingFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

/**
* Loads bean definitions from underlying sources, including XML and JavaConfig. Acts as a
* simple facade over {@link AnnotatedBeanDefinitionReader},
* {@link XmlBeanDefinitionReader} and {@link ClassPathBeanDefinitionScanner}. See
* {@link SpringApplication} for the types of sources that are supported.
*
* @author Phillip Webb
* @see #setBeanNameGenerator(BeanNameGenerator)
*/
class BeanDefinitionLoader {

  private static final ResourceLoader DEFAULT_RESOURCE_LOADER = new PathMatchingResourcePatternResolver();

  private final Object[] sources;

  private final AnnotatedBeanDefinitionReader annotatedReader;

  private final XmlBeanDefinitionReader xmlReader;

  private GroovyBeanDefinitionReader groovyReader;

  private final ClassPathBeanDefinitionScanner scanner;

  private ResourceLoader resourceLoader;

  /**
   * Create a new {@link BeanDefinitionLoader} that will load beans into the specified
   * {@link BeanDefinitionRegistry}.
   * @param registry the bean definition registry that will contain the loaded beans
   * @param sources the bean sources
   */
  public BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
    Assert.notNull(registry, "Registry must not be null");
    Assert.notEmpty(sources, "Sources must not be empty");
    this.sources = sources;
    this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
    this.xmlReader = new XmlBeanDefinitionReader(registry);
    if (isGroovyPresent()) {
      this.groovyReader = new GroovyBeanDefinitionReader(registry);
    }
    this.scanner = new ClassPathBeanDefinitionScanner(registry);
    this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
  }

  /**
   * Set the bean name generator to be used by the underlying readers and scanner.
   * @param beanNameGenerator the bean name generator
   */
  public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
    this.annotatedReader.setBeanNameGenerator(beanNameGenerator);
    this.xmlReader.setBeanNameGenerator(beanNameGenerator);
    this.scanner.setBeanNameGenerator(beanNameGenerator);
  }

  /**
   * Set the resource loader to be used by the underlying readers and scanner.
   * @param resourceLoader the resource loader
   */
  public void setResourceLoader(ResourceLoader resourceLoader) {
    this.resourceLoader = resourceLoader;
    this.xmlReader.setResourceLoader(resourceLoader);
    this.scanner.setResourceLoader(resourceLoader);
  }

  /**
   * Set the environment to be used by the underlying readers and scanner.
   * @param environment
   */
  public void setEnvironment(ConfigurableEnvironment environment) {
    this.annotatedReader.setEnvironment(environment);
    this.xmlReader.setEnvironment(environment);
    this.scanner.setEnvironment(environment);
  }

  /**
   * Load the sources into the reader.
   * @return the number of loaded beans
   */
  public int load() {
    int count = 0;
    for (Object source : this.sources) {
      count += load(source);
    }
    return count;
  }

  private int load(Object source) {
    Assert.notNull(source, "Source must not be null");
    if (source instanceof Class<?>) {
      return load((Class<?>) source);
    }
    if (source instanceof Resource) {
      return load((Resource) source);
    }
    if (source instanceof Package) {
      return load((Package) source);
    }
    if (source instanceof CharSequence) {
      return load((CharSequence) source);
    }
    throw new IllegalArgumentException("Invalid source type " + source.getClass());
  }

  private int load(Class<?> source) {
    if (isGroovyPresent()) {
      // Any GroovyLoaders added in beans{} DSL can contribute beans here
      if (GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
        GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
            GroovyBeanDefinitionSource.class);
        load(loader);
      }
    }
    if (isComponent(source)) {
      this.annotatedReader.register(source);
      return 1;
    }
    return 0;
  }

  private int load(GroovyBeanDefinitionSource source) {
    int before = this.xmlReader.getRegistry().getBeanDefinitionCount();
    this.groovyReader.beans(source.getBeans());
    int after = this.xmlReader.getRegistry().getBeanDefinitionCount();
    return after - before;
  }

  private int load(Resource source) {
    if (source.getFilename().endsWith(".groovy")) {
      if (this.groovyReader == null) {
        throw new BeanDefinitionStoreException(
            "Cannot load Groovy beans without Groovy on classpath");
      }
      return this.groovyReader.loadBeanDefinitions(source);
    }
    return this.xmlReader.loadBeanDefinitions(source);
  }

  private int load(Package source) {
    return this.scanner.scan(source.getName());
  }

  private int load(CharSequence source) {

    String resolvedSource = this.xmlReader.getEnvironment().resolvePlaceholders(
        source.toString());

    // Attempt as a Class
    try {
      return load(ClassUtils.forName(resolvedSource, null));
    }
    catch (IllegalArgumentException ex) {
      // swallow exception and continue
    }
    catch (ClassNotFoundException ex) {
      // swallow exception and continue
    }

    // Attempt as resources
    Resource[] resources = findResources(resolvedSource);
    int loadCount = 0;
    boolean atLeastOneResourceExists = false;
    for (Resource resource : resources) {
      if (resource != null && resource.exists()) {
        atLeastOneResourceExists = true;
        loadCount += load(resource);
      }
    }
    if (atLeastOneResourceExists) {
      return loadCount;
    }

    // Attempt as package
    Package packageResource = findPackage(resolvedSource);
    if (packageResource != null) {
      return load(packageResource);
    }

    throw new IllegalArgumentException("Invalid source '" + resolvedSource + "'");
  }

  private boolean isGroovyPresent() {
    return ClassUtils.isPresent("groovy.lang.MetaClass", null);
  }

  private Resource[] findResources(String source) {
    ResourceLoader loader = this.resourceLoader != null ? this.resourceLoader
        : DEFAULT_RESOURCE_LOADER;
    try {
      if (loader instanceof ResourcePatternResolver) {
        return ((ResourcePatternResolver) loader).getResources(source);
      }
      return new Resource[] { loader.getResource(source) };
    }
    catch (IOException ex) {
      throw new IllegalStateException("Error reading source '" + source + "'");
    }
  }

  private Package findPackage(CharSequence source) {
    Package pkg = Package.getPackage(source.toString());
    if (pkg != null) {
      return pkg;
    }
    try {
      // Attempt to find a class in this package
      ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(
          getClass().getClassLoader());
      Resource[] resources = resolver.getResources(ClassUtils
          .convertClassNameToResourcePath(source.toString()) + "/*.class");
      for (Resource resource : resources) {
        String className = StringUtils.stripFilenameExtension(resource
            .getFilename());
        load(Class.forName(source.toString() + "." + className));
        break;
      }
    }
    catch (Exception ex) {
      // swallow exception and continue
    }
    return Package.getPackage(source.toString());
  }

  private boolean isComponent(Class<?> type) {
    // This has to be a bit of a guess. The only way to be sure that this type is
    // eligible is to make a bean definition out of it and try to instantiate it.
    if (AnnotationUtils.findAnnotation(type, Component.class) != null) {
      return true;
    }
    // Nested anonymous classes are not eligible for registration, nor are groovy
    // closures
    if (type.getName().matches(".*\\$_.*closure.*") || type.isAnonymousClass()
        || type.getConstructors() == null || type.getConstructors().length == 0) {
      return false;
    }
    return true;
  }

  /**
   * Simple {@link TypeFilter} used to ensure that specified {@link Class} sources are
   * not accidentally re-added during scanning.
   */
  private static class ClassExcludeFilter extends AbstractTypeHierarchyTraversingFilter {

    private final Set<String> classNames = new HashSet<String>();

    public ClassExcludeFilter(Object... sources) {
      super(false, false);
      for (Object source : sources) {
        if (source instanceof Class<?>) {
          this.classNames.add(((Class<?>) source).getName());
        }
      }
    }

    @Override
    protected boolean matchClassName(String className) {
      return this.classNames.contains(className);
    }

  }

  protected interface GroovyBeanDefinitionSource {

    Closure<?> getBeans();

  }

}
TOP

Related Classes of org.springframework.boot.BeanDefinitionLoader$ClassExcludeFilter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.