Package org.springframework.context.annotation

Source Code of org.springframework.context.annotation.ClassPathBeanDefinitionScanner

/*
* Copyright 2002-2011 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.context.annotation;

import java.util.LinkedHashSet;
import java.util.Set;

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.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionDefaults;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;
import org.springframework.util.PatternMatchUtils;

/**
* A bean definition scanner that detects bean candidates on the classpath,
* registering corresponding bean definitions with a given registry ({@code BeanFactory}
* or {@code ApplicationContext}).
*
* <p>Candidate classes are detected through configurable type filters. The
* default filters include classes that are annotated with Spring's
* {@link org.springframework.stereotype.Component @Component},
* {@link org.springframework.stereotype.Repository @Repository},
* {@link org.springframework.stereotype.Service @Service}, or
* {@link org.springframework.stereotype.Controller @Controller} stereotype.
*
* <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and
* JSR-330's {@link javax.inject.Named} annotations, if available.
*
* @author Mark Fisher
* @author Juergen Hoeller
* @author Chris Beams
* @since 2.5
* @see AnnotationConfigApplicationContext#scan
* @see org.springframework.stereotype.Component
* @see org.springframework.stereotype.Repository
* @see org.springframework.stereotype.Service
* @see org.springframework.stereotype.Controller
*/
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {

  private final BeanDefinitionRegistry registry;

  private BeanDefinitionDefaults beanDefinitionDefaults = new BeanDefinitionDefaults();

  private String[] autowireCandidatePatterns;

  private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();

  private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();

  private boolean includeAnnotationConfig = true;


  /**
   * Create a new {@code ClassPathBeanDefinitionScanner} for the given bean factory.
   * @param registry the {@code BeanFactory} to load bean definitions into, in the form
   * of a {@code BeanDefinitionRegistry}
   */
  public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
    this(registry, true);
  }

  /**
   * Create a new {@code ClassPathBeanDefinitionScanner} for the given bean factory.
   * <p>If the passed-in bean factory does not only implement the
   * {@code BeanDefinitionRegistry} interface but also the {@code ResourceLoader}
   * interface, it will be used as default {@code ResourceLoader} as well. This will
   * usually be the case for {@link org.springframework.context.ApplicationContext}
   * implementations.
   * <p>If given a plain {@code BeanDefinitionRegistry}, the default {@code ResourceLoader}
   * will be a {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
   * <p>If the the passed-in bean factory also implements {@link EnvironmentCapable} its
   * environment will be used by this reader.  Otherwise, the reader will initialize and
   * use a {@link org.springframework.core.env.StandardEnvironment}. All
   * {@code ApplicationContext} implementations are {@code EnvironmentCapable}, while
   * normal {@code BeanFactory} implementations are not.
   * @param registry the {@code BeanFactory} to load bean definitions into, in the form
   * of a {@code BeanDefinitionRegistry}
   * @param useDefaultFilters whether to include the default filters for the
   * {@link org.springframework.stereotype.Component @Component},
   * {@link org.springframework.stereotype.Repository @Repository},
   * {@link org.springframework.stereotype.Service @Service}, and
   * {@link org.springframework.stereotype.Controller @Controller} stereotype
   * annotations.
   * @see #setResourceLoader
   * @see #setEnvironment
   */
  public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
    this(registry, useDefaultFilters, getOrCreateEnvironment(registry));
  }

  /**
   * Create a new {@code ClassPathBeanDefinitionScanner} for the given bean factory and
   * using the given {@link Environment} when evaluating bean definition profile metadata.
   * <p>If the passed-in bean factory does not only implement the {@code
   * BeanDefinitionRegistry} interface but also the {@link ResourceLoader} interface, it
   * will be used as default {@code ResourceLoader} as well. This will usually be the
   * case for {@link org.springframework.context.ApplicationContext} implementations.
   * <p>If given a plain {@code BeanDefinitionRegistry}, the default {@code ResourceLoader}
   * will be a {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
   * @param registry the {@code BeanFactory} to load bean definitions into, in the form
   * of a {@code BeanDefinitionRegistry}
   * @param useDefaultFilters whether to include the default filters for the
   * @param environment the Spring {@link Environment} to use when evaluating bean
   * definition profile metadata.
   * {@link org.springframework.stereotype.Component @Component},
   * {@link org.springframework.stereotype.Repository @Repository},
   * {@link org.springframework.stereotype.Service @Service}, and
   * {@link org.springframework.stereotype.Controller @Controller} stereotype
   * annotations.
   * @since 3.1
   * @see #setResourceLoader
   */
  public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment) {
    super(useDefaultFilters, environment);

    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    this.registry = registry;

    // Determine ResourceLoader to use.
    if (this.registry instanceof ResourceLoader) {
      setResourceLoader((ResourceLoader) this.registry);
    }
  }


  /**
   * Return the BeanDefinitionRegistry that this scanner operates on.
   */
  public final BeanDefinitionRegistry getRegistry() {
    return this.registry;
  }

  /**
   * Set the defaults to use for detected beans.
   * @see BeanDefinitionDefaults
   */
  public void setBeanDefinitionDefaults(BeanDefinitionDefaults beanDefinitionDefaults) {
    this.beanDefinitionDefaults =
        (beanDefinitionDefaults != null ? beanDefinitionDefaults : new BeanDefinitionDefaults());
  }

  /**
   * Set the name-matching patterns for determining autowire candidates.
   * @param autowireCandidatePatterns the patterns to match against
   */
  public void setAutowireCandidatePatterns(String[] autowireCandidatePatterns) {
    this.autowireCandidatePatterns = autowireCandidatePatterns;
  }

  /**
   * Set the BeanNameGenerator to use for detected bean classes.
   * <p>Default is a {@link AnnotationBeanNameGenerator}.
   */
  public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
    this.beanNameGenerator = (beanNameGenerator != null ? beanNameGenerator : new AnnotationBeanNameGenerator());
  }

  /**
   * Set the ScopeMetadataResolver to use for detected bean classes.
   * Note that this will override any custom "scopedProxyMode" setting.
   * <p>The default is an {@link AnnotationScopeMetadataResolver}.
   * @see #setScopedProxyMode
   */
  public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
    this.scopeMetadataResolver = (scopeMetadataResolver != null ? scopeMetadataResolver : new AnnotationScopeMetadataResolver());
  }

  /**
   * Specify the proxy behavior for non-singleton scoped beans.
   * Note that this will override any custom "scopeMetadataResolver" setting.
   * <p>The default is {@link ScopedProxyMode#NO}.
   * @see #setScopeMetadataResolver
   */
  public void setScopedProxyMode(ScopedProxyMode scopedProxyMode) {
    this.scopeMetadataResolver = new AnnotationScopeMetadataResolver(scopedProxyMode);
  }

  /**
   * Specify whether to register annotation config post-processors.
   * <p>The default is to register the post-processors. Turn this off
   * to be able to ignore the annotations or to process them differently.
   */
  public void setIncludeAnnotationConfig(boolean includeAnnotationConfig) {
    this.includeAnnotationConfig = includeAnnotationConfig;
  }


  /**
   * Perform a scan within the specified base packages.
   * @param basePackages the packages to check for annotated classes
   * @return number of beans registered
   */
  public int scan(String... basePackages) {
    int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

    doScan(basePackages);

    // Register annotation config processors, if necessary.
    if (this.includeAnnotationConfig) {
      AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }

    return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
  }

  /**
   * Perform a scan within the specified base packages,
   * returning the registered bean definitions.
   * <p>This method does <i>not</i> register an annotation config processor
   * but rather leaves this up to the caller.
   * @param basePackages the packages to check for annotated classes
   * @return set of beans registered if any for tooling registration purposes (never {@code null})
   */
  protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
    for (String basePackage : basePackages) {
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
      for (BeanDefinition candidate : candidates) {
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
        candidate.setScope(scopeMetadata.getScopeName());
        String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
        if (candidate instanceof AbstractBeanDefinition) {
          postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
        }
        if (candidate instanceof AnnotatedBeanDefinition) {
          AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
        }
        if (checkCandidate(beanName, candidate)) {
          BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
          definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
          beanDefinitions.add(definitionHolder);
          registerBeanDefinition(definitionHolder, this.registry);
        }
      }           
    }
    return beanDefinitions;
  }

  /**
   * Apply further settings to the given bean definition,
   * beyond the contents retrieved from scanning the component class.
   * @param beanDefinition the scanned bean definition
   * @param beanName the generated bean name for the given bean
   */
  protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
    beanDefinition.applyDefaults(this.beanDefinitionDefaults);
    if (this.autowireCandidatePatterns != null) {
      beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
    }
  }

  /**
   * Register the specified bean with the given registry.
   * <p>Can be overridden in subclasses, e.g. to adapt the registration
   * process or to register further bean definitions for each scanned bean.
   * @param definitionHolder the bean definition plus bean name for the bean
   * @param registry the BeanDefinitionRegistry to register the bean with
   */
  protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
  }


  /**
   * Check the given candidate's bean name, determining whether the corresponding
   * bean definition needs to be registered or conflicts with an existing definition.
   * @param beanName the suggested name for the bean
   * @param beanDefinition the corresponding bean definition
   * @return <code>true</code> if the bean can be registered as-is;
   * <code>false</code> if it should be skipped because there is an
   * existing, compatible bean definition for the specified name
   * @throws ConflictingBeanDefinitionException if an existing, incompatible
   * bean definition has been found for the specified name
   */
  protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
    if (!this.registry.containsBeanDefinition(beanName)) {
      return true;
    }
    BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
    BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
    if (originatingDef != null) {
      existingDef = originatingDef;
    }
    if (isCompatible(beanDefinition, existingDef)) {
      return false;
    }
    throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +
        "' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
        "non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
  }

  /**
   * Determine whether the given new bean definition is compatible with
   * the given existing bean definition.
   * <p>The default implementation simply considers them as compatible
   * when the bean class name matches.
   * @param newDefinition the new bean definition, originated from scanning
   * @param existingDefinition the existing bean definition, potentially an
   * explicitly defined one or a previously generated one from scanning
   * @return whether the definitions are considered as compatible, with the
   * new definition to be skipped in favor of the existing definition
   */
  protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) {
    return (!(existingDefinition instanceof ScannedGenericBeanDefinition) ||  // explicitly registered overriding bean
        newDefinition.getSource().equals(existingDefinition.getSource()) ||  // scanned same file twice
        newDefinition.equals(existingDefinition))// scanned equivalent class twice
  }


  /**
   * Get the Environment from the given registry if possible, otherwise return a new
   * StandardEnvironment.
   */
  private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    if (registry instanceof EnvironmentCapable) {
      return ((EnvironmentCapable) registry).getEnvironment();
    }
    return new StandardEnvironment();
  }

}
TOP

Related Classes of org.springframework.context.annotation.ClassPathBeanDefinitionScanner

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.