欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring多种加载Bean方式解析

程序员文章站 2024-02-28 22:40:52
1 定义bean的方式 常见的定义bean的方式有: 通过xml的方式,例如:

1 定义bean的方式

常见的定义bean的方式有:

通过xml的方式,例如:

<bean id="dictionaryrelmap" class="java.util.hashmap"/>

通过注解的方式,在class上使用@component等注解,例如

@component
public class xxxservicer{
 ....
}

通过在@configuration类下的@bean的方式,例如

@configuration
public class xxxconfiguration{
 @bean
 public mybean mybean(){
   return new mybean();
 }
}

虽然这三种定义bean的方式不一样,对应的处理细节也不一样,但是从大的逻辑上来看,都是一样。主要的流程如下图: 最关键的就是问题就是这么去找到定义bean的方式,然后生成beandefinition后注册到spring上下文中,由spring自动创建bean的实例。

Spring多种加载Bean方式解析

2 beandefinition

beandefinition是一个接口,用来描述一个bean实例,例如是singleton还是prototype,属性的值是什么,构造函数的参数是什么等。简单来说,通过一个beandefinition我们就可以完成一个bean实例化。 beandefinition及其主要的子类:

Spring多种加载Bean方式解析

下面简单说一下各个子类:

  1. rootbeandefinition和childbeandefinition: 这2个beandefinition是相对的关系,自spring 2.5 出来以后,已经被genericbeandefinition代替。因为这样强迫我们在编写代码的时候就必须知道他们之间的关系。
  2. genericbeandefinition: 相比于rootbeandefinition和childbeandefinition在定义的时候就必须硬编码,genericbeandefinition的优点可以动态的为genericbeandefinition设置parent。
  3. annotatedbeandefinition:看名字就是知道是用来读取通过注解定义bean。

3 通过xml文件定义bean

通过xml定义bean是最早的spring定义bean的方式。因此,怎么把xml标签解析为beandefinition(), 入口是在org.springframework.beans.factory.xml.xmlbeandefinitionreader这个类,但是实际干活的是在org.springframework.beans.factory.xml.beandefinitionparserdelegate。代码很多,但实际逻辑很简单,就是解析spring定义的<bean> <property> 等标签 。

4 通过@component等spring支持的注解加载bean

如果要使用@component等注解定义bean,一个前提条件是:有<context:component-scan/>或者@componentscan注解。但这2个方式还是有一点点区别:

4.1 <context:component-scan/>

由于<context:component-scan/>是一个xml标签,因此是在解析xml,生成的类org.springframework.context.annotation.componentscanbeandefinitionparser,关键代码:

@override
public beandefinition parse(element element, parsercontext parsercontext) {
    //获取base-package标签
  string basepackage = element.getattribute(base_package_attribute);
  basepackage = parsercontext.getreadercontext().getenvironment().resolveplaceholders(basepackage);
  string[] basepackages = stringutils.tokenizetostringarray(basepackage,
      configurableapplicationcontext.config_location_delimiters);

  // 实际处理类是classpathbeandefinitionscanner 
  classpathbeandefinitionscanner scanner = configurescanner(parsercontext, element);
  //扫描basepackage下所有的类,如果有@component等标签就是注册到spring中
  set<beandefinitionholder> beandefinitions = scanner.doscan(basepackages);
  registercomponents(parsercontext.getreadercontext(), beandefinitions, element);
  return null;
}

4.2 @componentscan

注解对应生成的类是org.springframework.context.annotation.componentscanannotationparser 其实最后实际干活的还是classpathbeandefinitionscanner这个。componentscanannotationparser类的生成是伴随着@configuration这个注解处理过程中(意思说@componentscan必须和@configuration一起使用)。而处理@configuration其实是org.springframework.context.annotation.configurationclasspostprocessor。是不是感觉有点绕。

其实简单来说,在处理@configuration的时候发现有@componentscan注解,就会生成componentscanannotationparser去扫描@component注解

4.3 classpathbeandefinitionscanner

上面说到了,无论注解还是标签的方式,最后都会交给classpathbeandefinitionscanner这个类来处理,这个类做的就是1.扫描basepackage下所有class,如果有@component等注解,读取@component相关属性,生成scannedgenericbeandefinition,注册到spring中。

5 通过@bean方式

前面说了@componentscan是在@configuration处理过程中的一环,既然@bean注解也是必须和@configuration一起使用,那么说明@bean的处理也是在@configuration中,其实最后是交给configurationclassbeandefinitionreader这个类来处理的,关键代码:

private void loadbeandefinitionsforconfigurationclass(configurationclass configclass,
    trackedconditionevaluator trackedconditionevaluator) {

    //如果自己是通过@import注解定义的,那么需要把自己注册到spring中
  if (configclass.isimported()) {
    registerbeandefinitionforimportedconfigurationclass(configclass);
  }
  //这里就是处理方法上的@bean
  for (beanmethod beanmethod : configclass.getbeanmethods()) {
    loadbeandefinitionsforbeanmethod(beanmethod);
  }
  //处理@importresource,里面解析xml就是上面说到的解析xml的xmlbeandefinitionreader
  loadbeandefinitionsfromimportedresources(configclass.getimportedresources());
  loadbeandefinitionsfromregistrars(configclass.getimportbeandefinitionregistrars());
}

6 把beandefinition实例化

前面分别说了怎么把不同定义bean的方式转换为beandefinition加入到spring中去(确切来说是保持在beanfactory的beandefinitionmap中),实例是在applicationcontext最后阶段,关键代码在defaultlistablebeanfactory中

 @override
 public void preinstantiatesingletons() throws beansexception {
   for (string beanname : beannames) {
    rootbeandefinition bd = getmergedlocalbeandefinition(beanname);
    if (!bd.isabstract() && bd.issingleton() && !bd.islazyinit()) {
      if (isfactorybean(beanname)) {
        final factorybean<?> factory = (factorybean<?>) getbean(factory_bean_prefix + beanname);
        boolean iseagerinit;
        if (system.getsecuritymanager() != null && factory instanceof smartfactorybean) {
          iseagerinit = accesscontroller.doprivileged(new privilegedaction<boolean>() {
            @override
            public boolean run() {
              return ((smartfactorybean<?>) factory).iseagerinit();
            }
          }, getaccesscontrolcontext());
        }
        else {
          iseagerinit = (factory instanceof smartfactorybean &&
                ((smartfactorybean<?>) factory).iseagerinit());
        }
        if (iseagerinit) {
          getbean(beanname);
        }
      }
      else {
        getbean(beanname);
      }
    }
  }
}

通过getbean最后最后实例的代码,在abstractautowirecapablebeanfactory中

protected object initializebean(final string beanname, final object bean, rootbeandefinition mbd) {
  //处理xxaware接口
  if (system.getsecuritymanager() != null) {
    accesscontroller.doprivileged(new privilegedaction<object>() {
      @override
      public object run() {
        invokeawaremethods(beanname, bean);
        return null;
      }
    }, getaccesscontrolcontext());
  }
  else {
    invokeawaremethods(beanname, bean);
  }
  object wrappedbean = bean;
  if (mbd == null || !mbd.issynthetic()) {
    // 调用beanpostprocessors#postprocessbeforeinitialization
    wrappedbean = applybeanpostprocessorsbeforeinitialization(wrappedbean, beanname);
  }
  try {
    //初始化,先判断是否是initializingbean,
    invokeinitmethods(beanname, wrappedbean, mbd);
  }
  catch (throwable ex) {
    throw new beancreationexception(
        (mbd != null ? mbd.getresourcedescription() : null),
        beanname, "invocation of init method failed", ex);
  }
  if (mbd == null || !mbd.issynthetic()) {
    // 调用beanpostprocessors#postprocessafterinitialization
    wrappedbean = applybeanpostprocessorsafterinitialization(wrappedbean, beanname);
  }
  return wrappedbean;
}

从上面初始化可以看出,initializebean和beanpostprocessors的调用顺序

7 总结

综上分析,spring加载bean其实大的思想都是一样的,先读取相关信息生成beandefinition,然后通过beandefinition初始化bean。如果知道了上面了套路以后,就可以清楚怎么自定义xml标签或者自定义注解向spring中注入bean。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。