Spring源码分析(三)Bean生命周期源码解析1:扫描生成BeanDefinition

Spring最重要的功能就是帮助程序员创建对象(也就是IOC),而启动Spring就是为创建Bean对象做准备,如果先分析Spring启动过程的源码,会比较难理解,因为你可能不知道为什么要做这些准备动作,所以我们先明白Spring到底是怎么去创建Bean的,也就是先弄明白Bean的生命周期,之后再分析Spring启动做了什么会更容易理解。

Bean的生命周期就是指:在Spring中,一个Bean是如何生成的,如何销毁的

Bean生命周期流程图:
在这里插入图片描述

前言

之前说过,AnnotationConfigApplicationContext的构造函数里会初始化扫描器:
在这里插入图片描述
其中第一行StartupStep,是Spring 5.3之后加的新特性,现在有三个实现
在这里插入图片描述
DefaultStartupStep是一个空实现,而FlightRecorderStartupStep使用了 jdk 的JFR 事件机制(jdk9之后新特性)实现了。
在这里插入图片描述
JFR:简单来说就是在jdk层面,提供了一种系统监控机制
感兴趣的可以看下这篇博客:https://zhuanlan.zhihu.com/p/122247741

回过头来看一下这段代码:
在这里插入图片描述
其实就是Spring利用jdk新特性,记录某些步骤的执行时间、等其他统计信息。
后续源码中看到StartupStep,可以直接忽视

言归正传,这个扫描器是在哪里触发扫描的呢?
其实是在Spring启动、刷新容器的时候,在invokeBeanFactoryPostProcessors方法里会去调scanner的scan方法完成扫描,但是本节先不讲,后续章节会单独讲。
在这里插入图片描述

源码分析

入口

直接从扫描器的扫描入口方法看:
org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan

public int scan(String... basePackages) {
    //查看当前容器里存在多少个Bean
    int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

    //扫描,核心看这里
    doScan(basePackages);

    // Register annotation config processors, if necessary.
    if (this.includeAnnotationConfig) {
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }
    
	// 返回本次扫描出多少个BeanDefinition
    return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}

其中这个registry,在运行的时候其实就是DefaultListableBeanFactory,DefaultListableBeanFactory实现了这个接口(这里利用了接口隔离原则)。
在这里插入图片描述
继续看核心doScan方法

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) {
        //1. 核心扫描逻辑在这个方法,会走到父类去
        //注意返回结果BeanDefinition里,只设置了部分属性:
        //	只在Object beanClass属性上设置了String className
        //	以及class对应的Resource资源文件、元数据注解信息
        //  其他信息都还没处理
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        for (BeanDefinition candidate : candidates) {
            //解析Scope注解
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());

            //2. 生成Bean的名字
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            
            if (candidate instanceof AbstractBeanDefinition) {
                //3. 给BeanDefinition设置一些默认的值
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            
            if (candidate instanceof AnnotatedBeanDefinition) {
                //4. 解析@Lazy、@Primary、@DependsOn、@Role、@Description,并给BeanDefinition赋值
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            
            //5. 检查Spring容器中是否已经存在该beanName
            if (checkCandidate(beanName, candidate)) {
                //将BeanDefinition和beanName封装成一个Holder对象
                //所以其实BeanDefinition里是没有beanName属性的,另外应该也是考虑到实现别名功能吧
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                definitionHolder =
                        AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                
                beanDefinitions.add(definitionHolder);
                //6. 注册
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

1. 核心扫描逻辑

org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
	//这个componentsIndex见1.4
    if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
        return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
    }
    else {
        //大部分走这里
        //注意此方法返回的BeanDefinition
        //	只在Object beanClass属性上设置了String className
        //  以及class对应的Resource资源文件、元数据注解信息
        //	其他属性都还没有处理
        return scanCandidateComponents(basePackage);
    }
}
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    //basePackage包路径,格式比如:com.yth.service
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
        //packageSearchPath = classpath*:com/yth/service/**/*.class
        //因此只会找当前包路径下的class文件
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
        boolean traceEnabled = logger.isTraceEnabled();
        boolean debugEnabled = logger.isDebugEnabled();
        //遍历每一个class文件
        for (Resource resource : resources) {
            if (traceEnabled) {
                logger.trace("Scanning " + resource);
            }
            try {
                //元数据读取器,利用asm技术读取类的信息(上一章提过)
                MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                //1.1 通过元数据读取器,判断是否是候选Bean
                //这里判断主要包含:excluteFilters、includeFilters、@Conditional
                if (isCandidateComponent(metadataReader)) {
                    //1.2 构造ScannedGenericBeanDefinition
                    ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                    sbd.setSource(resource);
                    //1.3 判断当前BeanDefinition是否是候选Bean
                    //主要判断类是否是独立的、是否是具体类..
                    if (isCandidateComponent(sbd)) {
                        if (debugEnabled) {...}
                        candidates.add(sbd);
                    }
                    else {...}
                }
                else {...}
            }
            catch (FileNotFoundException ex) {...}
            catch (Throwable ex) {...}
        }
    }
    catch (IOException ex) {...}
    return candidates;
}

1.1 判断是否是候选Bean(excluteFilters、includeFilters、@Conditional)

org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent(org.springframework.core.type.classreading.MetadataReader)

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    //排除过滤器,判断
    for (TypeFilter tf : this.excludeFilters) {
        if (tf.match(metadataReader, getMetadataReaderFactory())) {
            return false;
        }
    }
    //包含过滤器判断
    //符合includeFilters的会进行条件匹配,通过了才是Bean
    for (TypeFilter tf : this.includeFilters) {
        //默认的includeFilter会看有没有@Component注解
        if (tf.match(metadataReader, getMetadataReaderFactory())) {
            //另外会看是否符合@Conditional的条件
            return isConditionMatch(metadataReader);
        }
    }
    return false;
}

由此可见,排除的优先级更高!
另外在ClassPathBeanDefinitionScanner的构造函数里会初始化一个默认的includeFilter:
在这里插入图片描述

关于条件注解的使用(源码就不看了):
比如自定义一个条件,实现Condition注解:

public class MyConditional implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        try {
            //存在com.yth.UserService这个类,则条件成立
            context.getClassLoader().loadClass("com.yth.UserService");
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}

使用:

@Component
@Conditional(MyConditional.class)
public class OrderService {
    public void test() {
        System.out.println("orderService test");
    }
}

这样Spring扫描的时候发现class上有@Conditional注解,就会进行条件判断,最终调用Condition的matches方法,true则认为是匹配

源码中没有@Conditional注解则不进行条件判断:
在这里插入图片描述

另外像SpringBoot中的@ConditionalOnClass、@ConditionalOnBean…底层都是@Conditional实现的…
在这里插入图片描述

1.2 构造ScannedGenericBeanDefinition

这里只需要注意:
目前只是把当前类的名字设置到了BeanDefinition的beanClass属性!BeanDefinition的其他属性还都没有处理。
在这里插入图片描述
注意beanClass是Object类型:
在这里插入图片描述
此时赋值的类型是String,不是Class对象
当真正需要创建这个类实例的时候,才会去加载类的Class对象

1.3 判断是否是候选Bean(类是否独立、具体)

org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent(org.springframework.beans.factory.annotation.AnnotatedBeanDefinition)

protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    AnnotationMetadata metadata = beanDefinition.getMetadata();
	//要求类是独立的,且
    //	是具体类
	//	或抽象类,但是有@Lookup注解标记的方法
    return (metadata.isIndependent() && (metadata.isConcrete() ||
            (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}
  • isIndependent:是否独立
    像下面这个内部类Member,就不属于独立的,Member2是静态内部类,是独立的
    在这里插入图片描述

  • isConcrete:是否是具体的,即不是接口、也不是抽象类

  • isAbstract:是否是抽象类

  • hasAnnotatedMethods(Lookup.class.getName()):判断是否有@LookUp注解

这里提一下LookUp注解有什么用:

@Component
@Scope("prototype")//多例
public class User {
    
}

@Component
public class OrderService {
    @Autowired
    private User user;

    public void test() {
        System.out.println(user);
    }

}

public static void main(String[] args) throws IOException {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    ((OrderService)context.getBean("orderService")).test();
    ((OrderService)context.getBean("orderService")).test();

}

这个Demo,User虽然是多例的,但是OrderService是单例,只会创建一次,所以只会注入一次,user对象是同一个:
在这里插入图片描述
但是如果我希望每调一次test,user拿到的都是新new的,就要用到@LookUp注解
在这里插入图片描述

1.4 componentsIndex是干啥的?

index,索引,加速用的?确实。
在这里插入图片描述

背景:项目里有很多类需要被Spring扫描,按照正常逻辑Spring可能需要扫描很久。
如果想要提高扫描性能,Spring提供了一个解决方案:

  • 直接在一个文件里面配置,告诉Spring当前项目里我们有哪些Component,Spring就不用自己扫描了,直接读文件。

操作:
在resources -> META-INF 下创建spring.components文件
直接定义当前项目里哪些类是Component
在这里插入图片描述
当你配置了这个文件以后,这个配置文件你就可以理解为就是这个componentsIndex
在这里插入图片描述

private Set<BeanDefinition> addCandidateComponentsFromIndex(CandidateComponentsIndex index, String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
        Set<String> types = new HashSet<>();
        //遍历过滤器
        for (TypeFilter filter : this.includeFilters) {
            //过滤器对应的"索引类型"
            String stereotype = extractStereotype(filter);
            if (stereotype == null) {
                throw new IllegalArgumentException("Failed to extract stereotype from " + filter);
            }
            //主要看这
            types.addAll(index.getCandidateTypes(basePackage, stereotype));
        }
        boolean traceEnabled = logger.isTraceEnabled();
        boolean debugEnabled = logger.isDebugEnabled();
        //这里后面的逻辑和正常扫描是一样的
        for (String type : types) {...}
    }
    catch (IOException ex) {...}
    return candidates;
}

org.springframework.context.index.CandidateComponentsIndex#getCandidateTypes

public Set<String> getCandidateTypes(String basePackage, String stereotype) {
    //这里index其实就是spring.components文件的内容
    //  文件里左边是类型,右边是stereotype,可以理解为“索引类型”
    List<Entry> candidates = this.index.get(stereotype);
    if (candidates != null) {
        return candidates.parallelStream()
                //匹配包路径
                .filter(t -> t.match(basePackage))
                //返回type,就是类名
                .map(t -> t.type)
                .collect(Collectors.toSet());
    }
    return Collections.emptySet();
}

配置了spring.components文件,Spring就不会真的扫描了,直接从spring.components文件里找当前所能匹配的Bean。
在这里插入图片描述

2. 生成Bean的名字

org.springframework.beans.factory.support.BeanNameGenerator#generateBeanName
默认实现:
在这里插入图片描述
org.springframework.context.annotation.AnnotationBeanNameGenerator#generateBeanName

public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
    if (definition instanceof AnnotatedBeanDefinition) {
        //获取注解@Component value属性所指定的BeanName
        String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
        //有可能没配,返回空
        if (StringUtils.hasText(beanName)) {
            // Explicit bean name found.
            return beanName;
        }
    }
    // Fallback: generate a unique default bean name.
    return buildDefaultBeanName(definition, registry);
}

//默认的BeanName,核心方法就是Introspector.decapitalize(JDK提供的)
protected String buildDefaultBeanName(BeanDefinition definition) {
    String beanClassName = definition.getBeanClassName();
    Assert.state(beanClassName != null, "No bean class name set");
    String shortClassName = ClassUtils.getShortName(beanClassName);
	//前两个字母都是大写直接返回了,否则首字母小写
    return Introspector.decapitalize(shortClassName);
}

默认beanName:前两个字母都是大写直接返回,否则首字母小写

3. 给BeanDefinition设置默认值

org.springframework.context.annotation.ClassPathBeanDefinitionScanner#postProcessBeanDefinition

protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
    beanDefinition.applyDefaults(this.beanDefinitionDefaults);
    if (this.autowireCandidatePatterns != null) {
        beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
    }
}

org.springframework.beans.factory.support.AbstractBeanDefinition#applyDefaults

public void applyDefaults(BeanDefinitionDefaults defaults) {
    Boolean lazyInit = defaults.getLazyInit();
    if (lazyInit != null) {
        setLazyInit(lazyInit);
    }
    setAutowireMode(defaults.getAutowireMode());
    setDependencyCheck(defaults.getDependencyCheck());
    setInitMethodName(defaults.getInitMethodName());
    setEnforceInitMethod(false);
    setDestroyMethodName(defaults.getDestroyMethodName());
    setEnforceDestroyMethod(false);
}

在这里插入图片描述

4. 解析注解给BeanDefinition赋值

org.springframework.context.annotation.AnnotationConfigUtils#processCommonDefinitionAnnotations(org.springframework.beans.factory.annotation.AnnotatedBeanDefinition)

public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
    processCommonDefinitionAnnotations(abd, abd.getMetadata());
}

static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
    AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
    if (lazy != null) {
        abd.setLazyInit(lazy.getBoolean("value"));
    }
    else if (abd.getMetadata() != metadata) {
        lazy = attributesFor(abd.getMetadata(), Lazy.class);
        if (lazy != null) {
            abd.setLazyInit(lazy.getBoolean("value"));
        }
    }

    if (metadata.isAnnotated(Primary.class.getName())) {
        abd.setPrimary(true);
    }
    AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
    if (dependsOn != null) {
        abd.setDependsOn(dependsOn.getStringArray("value"));
    }

    AnnotationAttributes role = attributesFor(metadata, Role.class);
    if (role != null) {
        abd.setRole(role.getNumber("value").intValue());
    }
    AnnotationAttributes description = attributesFor(metadata, Description.class);
    if (description != null) {
        abd.setDescription(description.getString("value"));
    }
}

5. 检查Spring容器中是否已经存在该beanName

org.springframework.context.annotation.ClassPathBeanDefinitionScanner#checkCandidate

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;
    }
    //是否可兼容,如果兼容返回false表示不会重新注册到Spring容器中
    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() + "]");
}

大部分情况如果容器里已经有,则直接抛异常,除非“兼容”的情况,但也不会再注册

protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) {
    //主要针对扫描的情况!!!
    //	判断BeanDefinition的source是否相同、或者BeanDefinition本身是否相同
    return (!(existingDefinition instanceof ScannedGenericBeanDefinition) ||  // explicitly registered overriding bean
            (newDefinition.getSource() != null && newDefinition.getSource().equals(existingDefinition.getSource())) ||  // scanned same file twice
            newDefinition.equals(existingDefinition));  // scanned equivalent class twice
}

主要针对扫描的情况!!!(ScannedGenericBeanDefinition是通过扫描生成的BeanDefinition)

那么什么情况下会出现这种情况呢?举个例子:

public static void main(String[] args) throws IOException {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(AppConfig.class);
    context.register(AppConfig1.class);
    context.refresh();
    ((OrderService)context.getBean("orderService")).test();
}

@ComponentScan("com.yth")
public class AppConfig {}

@ComponentScan("com.yth")
public class AppConfig1 {}

这种情况下,Spring会去扫描两次,所以肯定会扫描到重复的class,于是就存在可兼容的BeanDefinition

6. 注册

org.springframework.context.annotation.ClassPathBeanDefinitionScanner#registerBeanDefinition

protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}

org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition

public static void registerBeanDefinition(
        BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
        throws BeanDefinitionStoreException {

    // Register bean definition under primary name.
    String beanName = definitionHolder.getBeanName();
    // 我们知道registry具体实现类就是DefaultListableBeanFactory
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

    // Register aliases for bean name, if any.
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        for (String alias : aliases) {
            registry.registerAlias(beanName, alias);
        }
    }
}

我们知道registry具体实现类就是DefaultListableBeanFactory,注册到beanDefinitionMap:
org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
        throws BeanDefinitionStoreException {

    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");

    if (beanDefinition instanceof AbstractBeanDefinition) {...}

    BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    if (existingDefinition != null) {...}
    else {
        if (hasBeanCreationStarted()) {...}
        else {
            // Still in startup registration phase
            // 注册到beanDefinitionMap
            this.beanDefinitionMap.put(beanName, beanDefinition);
            // 单独注册了一下Bean的名字
            this.beanDefinitionNames.add(beanName);
            removeManualSingletonName(beanName);
        }
        this.frozenBeanDefinitionNames = null;
    }

    if (existingDefinition != null || containsSingleton(beanName)) {...}
    else if (isConfigurationFrozen()) {...}
}

总结

Spring扫描底层流程:
在这里插入图片描述

  1. 首先,通过ResourcePatternResolver获得指定包路径下的所有.class文件(Spring源码中将此文件包装成了Resource对象)
  2. 遍历每个Resource对象
  3. 利用MetadataReaderFactory解析Resource对象得到MetadataReader(在Spring源码中MetadataReaderFactory具体的实现类为CachingMetadataReaderFactory,MetadataReader的具体实现类为SimpleMetadataReader)
  4. 利用MetadataReader进行excludeFilters和includeFilters,以及条件注解@Conditional的筛选(条件注解并不难理解:某个类上是否存在@Conditional注解,如果存在则调用注解中所指定的类的match方法进行匹配,匹配成功则通过筛选,匹配失败则pass掉。)
  5. 筛选通过后,基于metadataReader生成ScannedGenericBeanDefinition
  6. 再基于metadataReader判断是不是对应的类是不是接口或抽象类
  7. 如果筛选通过,那么就表示扫描到了一个Bean,将ScannedGenericBeanDefinition加入结果集

MetadataReader表示类的元数据读取器,主要包含了一个AnnotationMetadata,功能有

  1. 获取类的名字、
  2. 获取父类的名字
  3. 获取所实现的所有接口名
  4. 获取所有内部类的名字
  5. 判断是不是抽象类
  6. 判断是不是接口
  7. 判断是不是一个注解
  8. 获取拥有某个注解的方法集合
  9. 获取类上添加的所有注解信息
  10. 获取类上添加的所有注解类型集合

值得注意的是,CachingMetadataReaderFactory解析某个.class文件得到MetadataReader对象是利用的ASM技术,并没有加载这个类到JVM。并且,最终得到的ScannedGenericBeanDefinition对象,beanClass属性存储的是当前类的名字,而不是class对象。(beanClass属性的类型是Object,它即可以存储类的名字,也可以存储class对象)

最后,上面是说的通过扫描得到BeanDefinition对象,我们还可以通过直接定义BeanDefinition,或解析spring.xml文件的,或者@Bean注解得到BeanDefinition对象。(后续章节会分析@Bean注解是怎么生成BeanDefinition的)。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

犬豪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值