菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
84
0

框架源码系列七:Spring源码学习之BeanDefinition源码学习(BeanDefinition、Annotation 方式配置的BeanDefinition的解析)

原创
05/13 14:22
阅读数 41906

一、BeanDefinition

1. bean定义都定义了什么?

2、BeanDefinition的继承体系

 父类:

AttributeAccessor

可以在xmlbean定义里面加上DTD文件里面没有的属性,如

    <bean id="cbean" class="com.study.spring.samples.CBean" name="leeSamll" age="18">
        <constructor-arg type="String" value="cbean01"></constructor-arg>
    </bean>

BeanMetadataElement :

定义bean定义来源于哪里,在BeanDefinition 里面的getResourceDescrption里面获取

子类:

 

类图:

 

 

 

 请思考:为什么要增加AnnotatedBeanDefinition?用GenericBeanDefinition不可以吗?是不是注解方式的Bean定义信息的存放及使用方式与通用的Bean定义方式不一样了?

GenericBeanDefinition要定义的东西太多了,xml方式的处理和注解方式的处理可能不太一样了,所以做了扩展加入AnnotatedBeanDefinition,是为了只定义自己需要的东西,后面直接从AnnotatedBeanDefinition获取就行了

二、Annotation 方式配置的BeanDefinition的解析

1. 扫描的过程,如何扫描

 

    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方法说明:

组件索引方式获取bean定义:在pom.xml里面加入spring-context-indexer这个依赖,在编译的时候就会生成META-INF/spring.components文件(加了注解的类),然后bean定义就可以从spring.components里面获取,而不用在启动的时候去包下面扫描获取bean定义,变得很快

scanCandidateComponents方法说明:

 

 

 MetadataReader说明:

说明:

获取类的信息,不只只有反射,ASM也可以获取,ASM通过获取类的字节码,参观Class(ClassVisistor可以获取到类上的注解和类对应的信息(类的属性、类的方法等),用到的ASM组件有ClassReaderClassVisistor

2. 注解的解析

2.1 如何从扫到的 .class 文件中获得注解信息?

我们自己实现是如何做的:

1)Class.forname("className")加载类获得Class对象

2)反射获取注解

3)判断是否存在组件,存在为其创建BeanDefinition

4)看指定了名字没,如果没有,应用名字生成策略生成一个名字

5)注册BeanDefinition

2.2 Spring的解析过程

spring解析注解利用的是ASM,ASM是一个低层次的字节码操作库

2.3 提问:spring 中通过 ASM 字节码操作库来读取的类信息、注解信息。它没有加载类,为什么不用加载类的方式?

   因为加载类都要放到内存里面,用不到的话内存就会浪费了,用ASM加载类不会进内存里面。在spring的org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.scanCandidateComponents(String)的方法里面的这段代码读取类的信息、注解信息的

MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);

 

 * Copyright 2002-2009 the original author or authors.

package org.springframework.core.type.classreading;

import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;

/**
 * Simple facade for accessing class metadata,
 * as read by an ASM {@link org.springframework.asm.ClassReader}.
 *
 * @author Juergen Hoeller
 * @since 2.5
 */
public interface MetadataReader {

    /**
     * Return the resource reference for the class file.
     */
    Resource getResource();

    /**
     * Read basic class metadata for the underlying class.
     */
    ClassMetadata getClassMetadata();

    /**
     * Read full annotation metadata for the underlying class,
     * including metadata for annotated methods.
     */
    AnnotationMetadata getAnnotationMetadata();

}

2.4 MetadataReader读取到类信息、注解信息后,如何进行判断及创建BeanDefinition的,往BeanDefintion中给入了哪些信息。

 在spring的org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.scanCandidateComponents(String)的方法里面的这段代码判断和创建BeanDefinition的

                        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);
                                }
                            }
                        }

判断是否是候选组件的方法:

    /**
     * Determine whether the given class does not match any exclude filter
     * and does match at least one include filter.
     * @param metadataReader the ASM ClassReader for the class
     * @return whether the class qualifies as a candidate component
     */
    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
        for (TypeFilter tf : this.excludeFilters) {
            if (tf.match(metadataReader, getMetadataReaderFactory())) {
                return false;
            }
        }
        for (TypeFilter tf : this.includeFilters) {
            if (tf.match(metadataReader, getMetadataReaderFactory())) {
                return isConditionMatch(metadataReader);
            }
        }
        return false;
    }

2.5 请思考,我们可以自己定义标注组件的注解吗?【扩展点】

可以,示例如下:

package com.study.leesamll.spring.ext;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.stereotype.Component;

@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface MyComponetAnno {

    String value() default "";
}

使用:

package com.study.leesamll.spring.service;

import java.util.Locale;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;

import com.study.leesmall.spring.ext.MyComponetAnno;

//@Componet
//@Service
@MyComponetAnno
public class Abean {

    @Autowired
    private ApplicationContext applicationContext;

    public Abean() {
        System.out.println("-----------------Abean 被实例化了。。。。。。。。。");
    }

    public void doSomething() {
        System.out.println(this + " do something .....mike.love="
                + this.applicationContext.getEnvironment().getProperty("mike.love"));
        System.out
                .println("-----------mike.name=" + this.applicationContext.getMessage("mike.name", null, Locale.CHINA));
    }
}

2.6 扫描的过滤这块你在实际项目中是否用过?如何使用的?【扩展点】

示例:

package com.study.leesmall.spring.ext;

import java.io.IOException;

import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

import com.study.leesmall.spring.service.Abean;

public class MyTypeFilter implements TypeFilter {

    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
            throws IOException {
        // 使用metadataReader中的类信息、注解信息来进行你的过滤判断逻辑
        return metadataReader.getClassMetadata().getClassName().equals(Abean.class.getName());
    }

}

TypeFilter的子类:

 

请思考:像@Controller 注解,它和@Service、@Component 注解有不同的意图,这种不同的意图将会在哪里实现?如果我们自己也有类似的需要自定义组件注解,是不就可以模仿@Controller。猜想 spring是如何做到灵活扩展这个的?

3、BeanDefinition注册 

在前面我们已经拿到BeanDefinition了,下面就是注册BeanDefinition了

1.目标

1.1 搞清楚BeanFactory中BeanDefinition是如何存储的?
1.2 搞清楚注册的过程是怎样的。

2. 思路

2.1 思考:我们原来是如何来存储beanName,BeanDefinition的?
  用并发的Map来存,beanName作为键,BeanDefinition作为值
2.2 思考:我们注册BeanDefinition的处理逻辑是怎样的?
  首先判断beanName是不是合法的,如果是合法的再放到Map里面

3. 看spring的注册过程

入口:DefaultListableBeanFactory.registerBeanDefinition(String beanName,BeanDefinitionbeanDefinition)

还是先拿到调用栈来分析:

处理BeanDefinition的过程:

String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);

说明:Spring里面默认的bean的名字的生成策略是拿类的名称来当bean的名称

看一下AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);方法里面是怎么处理注解BeanDefinition的:

下面就来看具体的注册bean定义逻辑:

 

PS:

1.Maven怎么加依赖时怎么同时下载源码?
Eclipse-window-preference-maven-勾选download artifact sources
下载慢的话把maven的setting.xml配置文件的镜像地址换成阿里的更快

完整代码获取地址:https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/spring-source-study

 

发表评论

0/200
84 点赞
0 评论
收藏