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集合。

  • 相关阅读:
    java_000.JAVA语言课堂测试试卷01
    2018暑假第八周总结&暑假代码成果总结(8.27-9.2)
    C#_最基础的计算器
    Java_学生信息管理系统——数组版——初次编写
    Java_计算器001,支持加减乘除,括号,小数点,√,^ 运算
    Java_计算器001,支持加减乘除,括号运算
    2018暑假第七周总结(8.20-8.26)
    2018暑假第六周总结(8.13-8.19)
    2018暑假第五周总结(8.6-8.12)
    poj 1984(带权并查集)
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12041167.html
Copyright © 2011-2022 走看看