spring注解:@ComponentScan,@Bean,@Import,@Component
Posted Small leaf
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring注解:@ComponentScan,@Bean,@Import,@Component相关的知识,希望对你有一定的参考价值。
我们知道我们要扫描某些包下的注解类,或者某些注解类,可以用@ComponentScan去扫描。我们需要类能被扫描到,可能会用到@Configuration,@Service,@Repository,这三个都使用了@Component注解。实际上扫描的是Component注解。我们也可以用@Bean来定义一个bean,也可以用Import来导入一个bean或者一个资源文件。
那么这些注解spring是如何扫描并且注册到spring容器的呢?
@Configuration
@Import(ImportConfig.class)
public class JavaConfig
@Test
public void test()
ApplicationContext app = new AnnotationConfigApplicationContext(JavaConfig.class);
Student student = (Student) app.getBean("student");
student.say();
我们从AnnotationConfigApplicationContext出发
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry
来看AnnotationConfigApplicationContext的构造函数
public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory)
super(beanFactory);
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses)
this();
register(annotatedClasses);
refresh();
public AnnotationConfigApplicationContext(String... basePackages)
this();
scan(basePackages);
refresh();
进入this.reader = new AnnotatedBeanDefinitionReader(this);
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment)
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source)
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
if (beanFactory != null)
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator))
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver))
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(4);
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME))
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME))
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME))
RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME))
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME))
RootBeanDefinition def = new RootBeanDefinition();
try
def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
AnnotationConfigUtils.class.getClassLoader()));
catch (ClassNotFoundException ex)
throw new IllegalStateException(
"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME))
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME))
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
return beanDefs;
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME))
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
以上面的代码查看,ConfigurationClassPostProcessor被加入到了spring容器当中了。
同理AutowiredAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor等都被加入了spring容器当中了。
而他们都实现了BeanPostProcessor,从前面两篇spring生命周期来看,在spring生命周期中会执行相应的方法。
CommonAnnotationBeanPostProcessor讲过@PostConstruct会使用这个类来进行初始化方法。
重点讲解ConfigurationClassPostProcessor类,它会去读取@Component,@Bean,@Import等等。
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor
ConfigurationClassPostProcessor他继承了BeanDefinitionRegistryPostProcessor
/**
* Extension to the standard @link BeanFactoryPostProcessor SPI, allowing for
* the registration of further bean definitions <i>before</i> regular
* BeanFactoryPostProcessor detection kicks in. In particular,
* BeanDefinitionRegistryPostProcessor may register further bean definitions
* which in turn define BeanFactoryPostProcessor instances.
*
* @author Juergen Hoeller
* @since 3.0.1
* @see org.springframework.context.annotation.ConfigurationClassPostProcessor
*/
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor
我们前面讲过实现BeanDefinitionRegistryPostProcessor,在spring生命周期中执行顺序为
- 构造函数
- BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry
- BeanFactoryPostProcessor.postProcessBeanFactory
ConfigurationClassPostProcessor的构造函数是默认的。
ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId))
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
if (this.factoriesPostProcessed.contains(registryId))
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
this.registriesPostProcessed.add(registryId);
processConfigBeanDefinitions(registry);
processConfigBeanDefinitions主要看
//处理相关注册的类
parser.parse(candidates);
//注册这些类到spring容器当中
this.reader.loadBeanDefinitions(configClasses);
public void parse(Set<BeanDefinitionHolder> configCandidates)
this.deferredImportSelectors = new LinkedList<>();
for (BeanDefinitionHolder holder : configCandidates)
BeanDefinition bd = holder.getBeanDefinition();
try
if (bd instanceof AnnotatedBeanDefinition)
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass())
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
else
parse(bd.getBeanClassName(), holder.getBeanName());
catch (BeanDefinitionStoreException ex)
throw ex;
catch (Throwable ex)
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
processDeferredImportSelectors();
进入parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION))
return;
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null)
if (configClass.isImported())
if (existingClass.isImported())
existingClass.mergeImportedBy(configClass);
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
else
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
for (Iterator<ConfigurationClass> it = this.knownSuperclasses.values().iterator(); it.hasNext();)
if (configClass.equals(it.next()))
it.remove();
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass);
do
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
先来看
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION))
return;
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase)
if (metadata == null || !metadata.isAnnotated(Conditional.class.getName()))
return false;
if (phase == null)
if (metadata instanceof AnnotationMetadata &&
ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata))
return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
List<Condition> conditions = new ArrayList<>();
for (String[] conditionClasses : getConditionClasses(metadata))
for (String conditionClass : conditionClasses)
Condition condition = getCondition(conditionClass, this.context.getClassLoader());
conditions.add(condition);
AnnotationAwareOrderComparator.sort(conditions);
for (Condition condition : conditions)
ConfigurationPhase requiredPhase = null;
if (condition instanceof ConfigurationCondition)
requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
if (requiredPhase == null || requiredPhase == phase)
if (!condition.matches(this.context, metadata))
return true;
return false;
看我们的类有没有使用@Conditional,有使用就进行判断,如果相关条件不通过则丢弃该类,也就是该类不会被注入到spring容器当中。这里就涉及到springboot中很多的条件注解。
关于这个注解以及相关的的条件注解如何实现放在下次讲。
进入sourceClass = doProcessConfigurationClass(configClass, sourceClass);
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class))
if (this.environment instanceof ConfigurableEnvironment)
processPropertySource(propertySource);
else
logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN))
for (AnnotationAttributes componentScan : componentScans)
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions)
if (ConfigurationClassUtils.checkConfigurationClassCandidate(
holder.getBeanDefinition(), this.metadataReaderFactory))
parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
// Process any @ImportResource annotations
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null)
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources)
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods)
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass())
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass))
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
// No superclass -> processing is complete
return null;
我们从注释就可以看出,这里处理了本类所相关的注解,@PropertySource,@ComponentScan,@Import,@ImportResource,@Bean。
这里我着重讲解@Import,其他的看代码就很容器理解,也可以按照我这样一步步分析。
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException
if (importCandidates.isEmpty())
return;
if (checkForCircularImports && isChainedImportOnStack(configClass))
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
else
this.importStack.push(configClass);
try
for (SourceClass candidate : importCandidates)
if (candidate.isAssignable(ImportSelector.class))
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector)
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
else
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class))
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
else
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
catch (BeanDefinitionStoreException ex)
throw ex;
catch (Throwable ex)
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
finally
this.importStack.pop();
我们使用的@Import(ImportConfig.class)
public class ImportConfig
@Bean
public Student student()
return new Student();
ImportConfig只是一个普通的类。
processImports会先判断import中的是ImportSelector还是ImportBeanDefinitionRegistrar,做相应的处理。
关于这两个接口,我想单独拿出来讲解,不放在这里,以免篇幅过长。
读者也可以自己思考。
我们看
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
看注释该类不是ImportSelector或者ImportBeanDefinitionRegistrar我们把它当做@Configuration类来处理。
再执行
processConfigurationClass(candidate.asConfigClass(configClass));
又回到了我们最开始。
我们解析JavaConfig,解析到@Import中的ImportConfig,发现ImportConfig不是ImportSelector和ImportBeanDefinitionRegistrar,我们把它当做Configuration做同样的处理,如此递归下去,直接解析完所有的类。
到这里我们有所的Configuration,Import配置类等等都解析出来了。
来看
this.reader.loadBeanDefinitions(configClasses);
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel)
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel)
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
TrackedConditionEvaluator trackedConditionEvaluator)
if (trackedConditionEvaluator.shouldSkip(configClass))
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName))
this.registry.removeBeanDefinition(beanName);
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
if (configClass.isImported())
registerBeanDefinitionForImportedConfigurationClass(configClass);
for (BeanMethod beanMethod : configClass.getBeanMethods())
loadBeanDefinitionsForBeanMethod(beanMethod);
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
这里就把我们的配置类注册到spring容器当中了。
回顾一下:
ConfigurationClassPostProcessor使我们实现注册的核心类,在我们项目启动的时候,该类就被加载进来,在spring生命周期中,进行我们配置类的检测,将相关的类注册到spring容器当中。
ConfigurationClassPostProcessor先去扫描@Configuration,然后处理它@PropertySource,@ComponentScan,@Import,@ImportResource,@Bean。递归下去直到扫描完毕。这些类被处理后都放入spring容器当中。
@PropertySource:读取配置文件
@ComponentScan:扫描相应包下的注解类,或者扫描指定的类
@ImportResource:导入资源文件
@Bean:实例化一个bean放入spring容器当中。
@Import:实例化一个指定的类,当这个类没有实现ImportSelector或者ImportBeanDefinitionRegistrar,会把它当中一个普通的@Configuration来处理。
讲到这里相信大家对spring如何加载java注解配置文件已经基本了解。那么关于@Import中的ImportSelector和ImportBeanDefinitionRegistrar他们是用来干嘛的呢?
@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default ;
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default ;
springboot中EnableAutoConfiguration自动配置,使用了EnableAutoConfigurationImportSelector
public class EnableAutoConfigurationImportSelector
extends AutoConfigurationImportSelector
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered
public interface DeferredImportSelector extends ImportSelector
究竟是怎样实现自动配置。
下回分解吧。。。
以上是关于spring注解:@ComponentScan,@Bean,@Import,@Component的主要内容,如果未能解决你的问题,请参考以下文章
Spring注解驱动开发第4讲——自定义TypeFilter指定@ComponentScan注解的过滤规则
spring注解:@ComponentScan,@Bean,@Import,@Component