zoukankan      html  css  js  c++  java
  • 谈谈Spring的IoC之注解扫描

    问题

      IoC是Inversion of Control的缩写,翻译过来即“控制反转”。IoC可以说是Spring的灵魂,想要读懂Spring,必先读懂IoC。不过有时候硬着头皮直接看源码,就像雾里看花,并不能一窥真谛。想要理解Spring的IoC,不如反过来思考一下,如果我们自己去实现IoC,需要考虑什么?遵循依赖大于配置的原则,我们这里只讨论注解式的控制反转容器,XML配置的方式暂不考虑。由此我想到下面几个问题:

    1. 注解扫描
    2. 实例化
    3. 循环依赖
    4. 泛型注入

    本文的重点,就是围绕这几个问题,从spring的源码入手,解答心中的疑惑。

    注解扫描

      我们从一个ApplicationContext示例开始启动Spring:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26

    * 这是IoC的测试类
    */
    public class {
    public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(IoCTest.class);
    MyService service = context.getBean(MyService.class);
    System.out.println(service.getService());
    }
    }

    * 测试注入Bean的接口
    */
    public interface MyService {
    String getService();
    }


    * 测试的Bean
    */

    public class ServiceA implements MyService {
    public String getService() {
    return "serviceA";
    }
    }

    运行IoCTest的main,发现报错NoSuchBeanDefinitionException。因为IoCTest少了一个注解扫描的环节,我们加上一个@ComponentScan就可以运行并打印了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    * 这是IoC的测试类
    */
    @ComponentScan
    public class {
    public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(IoCTest.class);
    MyService service = context.getBean(MyService.class);
    System.out.println(service.getService());
    }
    }

    我们直接由AnnotationConfigApplicationContext入手:

    1
    2
    3
    4
    5
    public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
    this();
    register(annotatedClasses);
    refresh();
    }

    register()这里是注册IoCTest,我们主要看一下refresh(),这里refresh()方法由AbstractApplicationContext实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
    // Prepare this context for refreshing.
    prepareRefresh();

    // Tell the subclass to refresh the internal bean factory.
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    // Prepare the bean factory for use in this context.
    prepareBeanFactory(beanFactory);

    try {
    // Allows post-processing of the bean factory in context subclasses.
    postProcessBeanFactory(beanFactory);

    // Invoke factory processors registered as beans in the context.
    invokeBeanFactoryPostProcessors(beanFactory);

    // Register bean processors that intercept bean creation.
    registerBeanPostProcessors(beanFactory);

    // Initialize message source for this context.
    initMessageSource();

    // Initialize event multicaster for this context.
    initApplicationEventMulticaster();

    // Initialize other special beans in specific context subclasses.
    onRefresh();

    // Check for listener beans and register them.
    registerListeners();

    // Instantiate all remaining (non-lazy-init) singletons.
    finishBeanFactoryInitialization(beanFactory);

    // Last step: publish corresponding event.
    finishRefresh();
    }

    catch (BeansException ex) {
    if (logger.isWarnEnabled()) {
    logger.warn("Exception encountered during context initialization - " +
    "cancelling refresh attempt: " + ex);
    }

    // Destroy already created singletons to avoid dangling resources.
    destroyBeans();

    // Reset 'active' flag.
    cancelRefresh(ex);

    // Propagate exception to caller.
    throw ex;
    }

    finally {
    // Reset common introspection caches in Spring's core, since we
    // might not ever need metadata for singleton beans anymore...
    resetCommonCaches();
    }
    }
    }

    refresh()有一堆方法,不过还好Spring每一个方法的注释都很清晰。由于我们的重点在于分析注解扫描,所以我们主要看一下invokeBeanFactoryPostProcessors(beanFactory)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

    // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
    // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
    if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
    beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
    beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
    }

    调用PostProcessorRegistrationDelegate执行BeanFactory的PostProcessors:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public static void invokeBeanFactoryPostProcessors(
    ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    Set<String> processedBeans = new HashSet<>();
    if (beanFactory instanceof BeanDefinitionRegistry) {
    // ...上面省略了一部分代码...
    List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
    String[] postProcessorNames =
    beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    for (String ppName : postProcessorNames) {
    if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
    processedBeans.add(ppName);
    }
    }
    sortPostProcessors(currentRegistryProcessors, beanFactory);
    registryProcessors.addAll(currentRegistryProcessors);
    invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
    // ...省略下面一部分代码...
    }
    // ...限于篇幅省略下面的代码...
    }

    BeanFactory中获取BeanDefinitionRegistryPostProcessor的实例,而BeanDefinitionRegistryPostProcessor是一个接口,他只有一个实现ConfigurationClassPostProcessor,即这里currentRegistryProcessors中存放了一个ConfigurationClassPostProcessor的实例对象,看一下invokeBeanDefinitionRegistryPostProcessors方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9

    * Invoke the given BeanDefinitionRegistryPostProcessor beans.
    */
    private static void invokeBeanDefinitionRegistryPostProcessors(
    Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
    for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
    postProcessor.postProcessBeanDefinitionRegistry(registry);
    }
    }

    只做了一件事,调用BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法,现在我们来看一下ConfigurationClassPostProcessor的实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    * Derive further bean definitions from the configuration classes in the registry.
    */
    @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(BeanDefinitionRegistry registry)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    // ...省略上面一部分代码...
    // Parse each @Configuration class
    ConfigurationClassParser parser = new ConfigurationClassParser(
    this.metadataReaderFactory, this.problemReporter, this.environment,
    this.resourceLoader, this.componentScanBeanNameGenerator, registry);
    // 这里的configCandidates是BeanDefinitionHolder集合
    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
    do {
    parser.parse(candidates);
    parser.validate();

    Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
    configClasses.removeAll(alreadyParsed);

    // Read the model and create bean definitions based on its content
    if (this.reader == null) {
    this.reader = new ConfigurationClassBeanDefinitionReader(
    registry, this.sourceExtractor, this.resourceLoader, this.environment,
    this.importBeanNameGenerator, parser.getImportRegistry());
    }
    this.reader.loadBeanDefinitions(configClasses);
    alreadyParsed.addAll(configClasses);

    candidates.clear();
    if (registry.getBeanDefinitionCount() > candidateNames.length) {
    String[] newCandidateNames = registry.getBeanDefinitionNames();
    Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
    Set<String> alreadyParsedClasses = new HashSet<>();
    for (ConfigurationClass configurationClass : alreadyParsed) {
    alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
    }
    for (String candidateName : newCandidateNames) {
    if (!oldCandidateNames.contains(candidateName)) {
    BeanDefinition bd = registry.getBeanDefinition(candidateName);
    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) 大专栏  谈谈Spring的IoC之注解扫描 &&
    !alreadyParsedClasses.contains(bd.getBeanClassName())) {
    candidates.add(new BeanDefinitionHolder(bd, candidateName));
    }
    }
    }
    candidateNames = newCandidateNames;
    }
    }
    while (!candidates.isEmpty());
    // ...省略下面一部分代码...
    }

    candidates在这里其实是IoCTest的BeanDefinitionHolder集合。继续看ConfigurationClassParser的parse()方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public void parse(Set<BeanDefinitionHolder> configCandidates) {
    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);
    }
    }

    this.deferredImportSelectorHandler.process();
    }

    最终parse会调用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
    // ...省略上面一部分代码...
    // 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);
    }

    继续看doProcessConfigurationClass()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
    throws IOException {
    // ...省略上部分代码...
    // 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) {
    BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
    if (bdCand == null) {
    bdCand = holder.getBeanDefinition();
    }
    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
    parse(bdCand.getBeanClassName(), holder.getBeanName());
    }
    }
    }
    }
    // ...省略下部分代码...
    }

    至此终于进入我们的正题,注解扫描的主角this.componentScanParserComponentScanAnnotationParser登场。继续看ComponentScanAnnotationParserparse(AnnotationAttributes componentScan, final String declaringClass)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
    componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

    Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
    boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
    scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
    BeanUtils.instantiateClass(generatorClass));

    ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
    if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
    scanner.setScopedProxyMode(scopedProxyMode);
    }
    else {
    Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
    scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
    }

    scanner.setResourcePattern(componentScan.getString("resourcePattern"));

    for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
    for (TypeFilter typeFilter : typeFiltersFor(filter)) {
    scanner.addIncludeFilter(typeFilter);
    }
    }
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
    for (TypeFilter typeFilter : typeFiltersFor(filter)) {
    scanner.addExcludeFilter(typeFilter);
    }
    }

    boolean lazyInit = componentScan.getBoolean("lazyInit");
    if (lazyInit) {
    scanner.getBeanDefinitionDefaults().setLazyInit(true);
    }

    Set<String> basePackages = new LinkedHashSet<>();
    String[] basePackagesArray = componentScan.getStringArray("basePackages");
    for (String pkg : basePackagesArray) {
    String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
    ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
    Collections.addAll(basePackages, tokenized);
    }
    for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
    basePackages.add(ClassUtils.getPackageName(clazz));
    }
    if (basePackages.isEmpty()) {
    basePackages.add(ClassUtils.getPackageName(declaringClass));
    }

    scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
    @Override
    protected boolean matchClassName(String className) {
    return declaringClass.equals(className);
    }
    });
    return scanner.doScan(StringUtils.toStringArray(basePackages));
    }

    declaringClass在这个例子中的入参即spring.ioc.test.IoCTest,装配basePackages后使用ClassPathBeanDefinitionScannerdoScan(String… basePackages)进行扫描:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    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;
    }

    终于到了最后一步,findCandidateComponents(String basePackage),来看下Spring如何根据basePackages来扫描组装BeanDefinition

    1
    2
    3
    4
    5
    6
    7
    8
    public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
    return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
    }
    else {
    return scanCandidateComponents(basePackage);
    }
    }

    在这个示例中,this.componentsIndex为null,进入scanCandidateComponents(String basePackage)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
    String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
    resolveBasePackage(basePackage) + '/' + this.resourcePattern;
    Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
    boolean traceEnabled = logger.isTraceEnabled();
    boolean debugEnabled = logger.isDebugEnabled();
    for (Resource resource : resources) {
    if (traceEnabled) {
    logger.trace("Scanning " + resource);
    }
    if (resource.isReadable()) {
    try {
    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
    if (isCandidateComponent(metadataReader)) {
    ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
    sbd.setResource(resource);
    sbd.setSource(resource);
    if (isCandidateComponent(sbd)) {
    if (debugEnabled) {
    logger.debug("Identified candidate component class: " + resource);
    }
    candidates.add(sbd);
    }
    else {
    if (debugEnabled) {
    logger.debug("Ignored because not a concrete top-level class: " + resource);
    }
    }
    }
    else {
    if (traceEnabled) {
    logger.trace("Ignored because not matching any filter: " + resource);
    }
    }
    }
    catch (Throwable ex) {
    throw new BeanDefinitionStoreException(
    "Failed to read candidate component class: " + resource, ex);
    }
    }
    else {
    if (traceEnabled) {
    logger.trace("Ignored because not readable: " + resource);
    }
    }
    }
    }
    catch (IOException ex) {
    throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
    }
    return candidates;
    }

    加载packageSearchPath(**classpath:spring/ioc/test/**/.class**)下所有的class文件,并通过isCandidateComponent(MetadataReader metadataReader)进行过滤,上面的测试例子中只有MyService符合条件加入candidates集合。

  • 相关阅读:
    hdu 5101 Select
    hdu 5100 Chessboard
    cf B. I.O.U.
    cf C. Inna and Dima
    cf B. Inna and Nine
    cf C. Counting Kangaroos is Fun
    Radar Installation 贪心
    spfa模板
    Sequence
    棋盘问题
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12041167.html
Copyright © 2011-2022 走看看