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

spring5 源码深度解析----- 被面试官给虐懵了,竟然是因为我不懂@Configuration配置类及@Bean的原理

程序员文章站 2022-05-22 20:10:27
@Configuration注解提供了全新的bean创建方式。最初spring通过xml配置文件初始化bean并完成依赖注入工作。从spring3.0开始,在spring framework模块中提供了这个注解,搭配@Bean等注解,可以完全不依赖xml配置,在运行时完成bean的创建和初始化工作。 ......

@configuration注解提供了全新的bean创建方式。最初spring通过xml配置文件初始化bean并完成依赖注入工作。从spring3.0开始,在spring framework模块中提供了这个注解,搭配@bean等注解,可以完全不依赖xml配置,在运行时完成bean的创建和初始化工作。例如:

public interface ibean {

}

public class appbean implements ibean{

}

//@configuration申明了appconfig是一个配置类
@configuration 
public class appconfig {
    //@bean注解申明了一个bean,bean名称默认为方法名appbean
    @bean 
    ibean appbean(){
        return new appbean();
    }
}

默认情况下bean的名称和方法名称相同,你也可以使用name属性来指定,如@bean(name = "mybean")

@configuration注解使用

我们先来看看@configuration 这个注解的定义

@target(elementtype.type)
@retention(retentionpolicy.runtime)
@documented
@component //@component元注解
public @interface configuration {
    string value() default "";
}

我们看到源码里面,@configuration 标记了@component元注解,因此可以被@componentscan扫描并处理,在spring容器初始化时configuration类 会被注册到bean容器中,最后还会实例化。

使用@autowired/@inject

因为@configuration本身也是一个@component,因此配置类本身也会被注册到应用上下文,并且也可以使用ioc的@autowired/@inject等注解来注入所需bean。我们来修改配置类如下:

@configuration
public class appconfig {
    @autowired
    public environment env;
    @bean
    ibean appbean(){
        return new appbean();
    }
}

使用@compomentscan

配置类也可以自己添加注解@compomentscan,来显式扫描需使用组件。

@configuration 使用@component 进行原注解,因此@configuration 类也可以被组件扫描到(特别是使用xml元素)。

@configuration
@componentscan("abc.xxx")
public class appconfig {
    @bean
    ibean appbean(){
        return new appbean();
    }
}

在这里认识几个注解: @controller, @service, @repository, @component

  • @controller: 表明一个注解的类是一个"controller",也就是控制器,可以把它理解为mvc 模式的controller 这个角色。这个注解是一个特殊的@component,允许实现类通过类路径的扫描扫描到。它通常与@requestmapping 注解一起使用。

  • @service: 表明这个带注解的类是一个"service",也就是服务层,可以把它理解为mvc 模式中的service层这个角色,这个注解也是一个特殊的@component,允许实现类通过类路径的扫描扫描到

  • @repository: 表明这个注解的类是一个"repository",团队实现了javaee 模式中像是作为"data access object" 可能作为dao来使用,当与 persistenceexceptiontranslationpostprocessor 结合使用时,这样注释的类有资格获得spring转换的目的。这个注解也是@component 的一个特殊实现,允许实现类能够被自动扫描到

  • @component: 表明这个注释的类是一个组件,当使用基于注释的配置和类路径扫描时,这些类被视为自动检测的候选者。

@target({elementtype.type})
@retention(retentionpolicy.runtime)
@documented
@component
public @interface controller {

    @aliasfor(annotation = component.class)
    string value() default "";

}

@target({elementtype.type})
@retention(retentionpolicy.runtime)
@documented
@component
public @interface service {

    @aliasfor(annotation = component.class)
    string value() default "";

}

@target({elementtype.type})
@retention(retentionpolicy.runtime)
@documented
@component
public @interface repository {

    @aliasfor(annotation = component.class)
    string value() default "";

}

我们可以看到@controller, @service, @repository这三个注解上都有@component这个注解

也就是说,上面四个注解标记的类都能够通过@componentscan 扫描到,上面四个注解最大的区别就是使用的场景和语义不一样,比如你定义一个service类想要被spring进行管理,你应该把它定义为@service 而不是@controller因为我们从语义上讲,@service更像是一个服务的类,而不是一个控制器的类,@component通常被称作组件,它可以标注任何你没有严格予以说明的类,比如说是一个配置类,它不属于mvc模式的任何一层,这个时候你更习惯于把它定义为 @component。@controller,@service,@repository 的注解上都有@component,所以这三个注解都可以用@component进行替换。

同@import注解组合使用

新建一个配置类,例如数据库配置类:

@configuration
public class databaseconfig {
    @bean
    public datasource datasource(){
        return new datasource(){
            ...
        };
    }
}

然后在appconfig中用@import来导入配置类

@configuration
@import(databaseconfig.class)
public class appconfig {
    @autowired
    public datasource datasource; //注入的bean在databaseconfig.class中定义
    @bean
    ibean appbean(){
        return new appbean();
    }
}

最后执行:

applicationcontext context = new annotationconfigapplicationcontext(appconfig.class);
databaseconfig datasourceconfig = context.getbean(databaseconfig.class);

可以看到只注册了appconfig.class,容器自动会把@import指向的配置类初始化。

同@profile注解组合使用

在配置类中可以申明@profile注解,仅当满足profile条件时,才会处理配置类,也可以将@profile注解加载配置类中的每一个@bean来实现更细粒度的条件控制。

@configuration
@profile("develop")
public class databaseconfig {
    @bean
    public datasource datasource(){
        return new datasource(){...};
    }
}

嵌套使用@configuration

在配置类中可以创建静态内部类,并添加@configuration注解,这样上下文只需要注册最外面的配置类,内部的配置类会自动被加载。这样做省略了@import,因为本身就在配置类内部,无需再特别指定了。

@configuration
public class appconfig {
    @autowired
    public datasource datasource; //注入的bean在内部定义

    @configuration
    public static class databaseconfig{
        @bean
        datasource datasource(){
            return new datasource() {...};
        }
    }
    
    @bean
    ibean appbean(){
        return new appbean();
    }
}

注意:任何嵌套的@configuration 都必须是static 的。

@lazy初始化

默认情况下,配置类中的bean都随着应用上下文被初始化,可以在配置类中添加@lazy注解来延迟初始化,当然也可以在每个@bean注解上添加,来实现更细粒度的控制。

@configuration
@lazy//延时加载
public class appconfig {
    @bean
    ibean appbean(){
        return new appbean();
    }
}

配置类约束

  • 配置类必须为显式申明的类,而不能通过工厂类方法返回实例。允许运行时类增强。
  • 配置类不允许标记final。
  • 配置类必须全局可见(不允许定义在方法本地内部类中)
  • 嵌套配置类必须申明为static 内部类
  • @bean方法不可以再创建新的配置类(所有实例都当做bean处理,不解析相关配置注解)

@configuration源码

applicationcontext的refresh方法

在我之前的一篇文章spring5 源码深度解析-----applicationcontext容器refresh过程中写过,spring容器启动时,即applicationcontext接口实现类的对象实例执行refresh方法时,在bean初始化完成之前,有一个扩展点,用来操作beanfactory,来扩展对应的功能,比喻往beanfactory中注册beandefintion,我们回顾一下applicationcontext的refresh函数:

 1 public void refresh() throws beansexception, illegalstateexception {
 2     synchronized (this.startupshutdownmonitor) {
 3         //准备刷新的上下文 环境  
 4         preparerefresh();
 5         //初始化beanfactory,并进行xml文件读取  
 6         configurablelistablebeanfactory beanfactory = obtainfreshbeanfactory();
 7         //对beanfactory进行各种功能填充  
 8         preparebeanfactory(beanfactory);
 9         try {
10             postprocessbeanfactory(beanfactory);
11             //激活各种beanfactory处理器  
12             invokebeanfactorypostprocessors(beanfactory);
13             //注册拦截bean创建的bean处理器,这里只是注册,真正的调用实在getbean时候 
14             registerbeanpostprocessors(beanfactory);
15             //为上下文初始化message源,即不同语言的消息体,国际化处理  
16             initmessagesource();
17             //初始化应用消息广播器,并放入“applicationeventmulticaster”bean中  
18             initapplicationeventmulticaster();
19             //留给子类来初始化其它的bean  
20             onrefresh();
21             //在所有注册的bean中查找listener bean,注册到消息广播器中  
22             registerlisteners();
23             //初始化剩下的单实例(非惰性的)  
24             finishbeanfactoryinitialization(beanfactory);
25             //完成刷新过程,通知生命周期处理器lifecycleprocessor刷新过程,同时发出contextrefreshevent通知别人  
26             finishrefresh();
27         }
28         catch (beansexception ex) {
29             if (logger.iswarnenabled()) {
30                 logger.warn("exception encountered during context initialization - " +
31                         "cancelling refresh attempt: " + ex);
32             }
33             destroybeans();
34             cancelrefresh(ex);
35             throw ex;
36         }
37         finally {
38             resetcommoncaches();
39         }
40     }
41 }

看到第12行,在初始化beanfactory后,会激活各种beanfactory处理器,我们来看看invokebeanfactorypostprocessors方法

 1 public static void invokebeanfactorypostprocessors(configurablelistablebeanfactory beanfactory, list<beanfactorypostprocessor> beanfactorypostprocessors) {
 2 
 3     // invoke beandefinitionregistrypostprocessors first, if any.
 4     // 1、首先调用beandefinitionregistrypostprocessors
 5     set<string> processedbeans = new hashset<>();
 6 
 7     // beanfactory是beandefinitionregistry类型
 8     if (beanfactory instanceof beandefinitionregistry) {
 9         beandefinitionregistry registry = (beandefinitionregistry) beanfactory;
10         // 定义beanfactorypostprocessor
11         list<beanfactorypostprocessor> regularpostprocessors = new arraylist<>();
12         // 定义beandefinitionregistrypostprocessor集合
13         list<beandefinitionregistrypostprocessor> registryprocessors = new arraylist<>();
14 
15         // 循环手动注册的beanfactorypostprocessors
16         for (beanfactorypostprocessor postprocessor : beanfactorypostprocessors) {
17             // 如果是beandefinitionregistrypostprocessor的实例话,则调用其postprocessbeandefinitionregistry方法,对bean进行注册操作
18             if (postprocessor instanceof beandefinitionregistrypostprocessor) {
19                 // 如果是beandefinitionregistrypostprocessor类型,则直接调用其postprocessbeandefinitionregistry
20                 beandefinitionregistrypostprocessor registryprocessor = (beandefinitionregistrypostprocessor) postprocessor;
21                 registryprocessor.postprocessbeandefinitionregistry(registry);
22                 registryprocessors.add(registryprocessor);
23             }
24             // 否则则将其当做普通的beanfactorypostprocessor处理,直接加入regularpostprocessors集合,以备后续处理
25             else {
26                 regularpostprocessors.add(postprocessor);
27             }
28         }
29         //略....
30     }
31 
32     // 2、如果不是beandefinitionregistry的实例,那么直接调用其回调函数即可-->postprocessbeanfactory
33     else {
34         // invoke factory processors registered with the context instance.
35         invokebeanfactorypostprocessors(beanfactorypostprocessors, beanfactory);
36     }
37     //略....
38 }

我们看看第21行,看看其实现类,如下截图,发现其中有一个configurationclasspostprocessor,这个类就是本章的重点

configurationclasspostprocessor这个beanfactorypostprocessor,来开启整个@configuration注解的系列类的加载的,即开启基于@configuration的类配置代替beans标签的容器配置的相关bean的加载。

spring5 源码深度解析----- 被面试官给虐懵了,竟然是因为我不懂@Configuration配置类及@Bean的原理

configurationclasspostprocessor

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);
   //解析java类配置bean
   processconfigbeandefinitions(registry);
}

processconfigbeandefinitions(registry)处理逻辑:

public void processconfigbeandefinitions(beandefinitionregistry registry) {
   list<beandefinitionholder> configcandidates = new arraylist<>();
  //所有已经注册的bean 
   string[] candidatenames = registry.getbeandefinitionnames();
   //遍历bean定义信息
   for (string beanname : candidatenames) {
      beandefinition beandef = registry.getbeandefinition(beanname);
      if (configurationclassutils.isfullconfigurationclass(beandef) ||
            configurationclassutils.isliteconfigurationclass(beandef)) {
         if (logger.isdebugenabled()) {
            logger.debug("bean definition has already been processed as a configuration class: " + beandef);
         }
      }
    //1.如果当前的bean是javabean配置类(含有@configuration注解的类),则加入到集合configcandidates中,
      else if (configurationclassutils.checkconfigurationclasscandidate(beandef, this.metadatareaderfactory)) {
         configcandidates.add(new beandefinitionholder(beandef, beanname));
      }
   }

   // return immediately if no @configuration classes were found
  // 没有@configuration注解的类,直接退出
   if (configcandidates.isempty()) {
      return;
   }

   // 多个java配置类,按@ordered注解排序
   configcandidates.sort((bd1, bd2) -> {
      int i1 = configurationclassutils.getorder(bd1.getbeandefinition());
      int i2 = configurationclassutils.getorder(bd2.getbeandefinition());
      return integer.compare(i1, i2);
   });

   // detect any custom bean name generation strategy supplied through the enclosing application context
   singletonbeanregistry sbr = null;
   if (registry instanceof singletonbeanregistry) {
      sbr = (singletonbeanregistry) registry;
      if (!this.localbeannamegeneratorset) {
         beannamegenerator generator = (beannamegenerator) sbr.getsingleton(configuration_bean_name_generator);
         if (generator != null) {
            this.componentscanbeannamegenerator = generator;
            this.importbeannamegenerator = generator;
         }
      }
   }

   if (this.environment == null) {
      this.environment = new standardenvironment();
   }

   // parse each @configuration class
  //初始化一个configurationclassparser解析器,可以解析@congiguration配置类
   configurationclassparser parser = new configurationclassparser(
         this.metadatareaderfactory, this.problemreporter, this.environment,
         this.resourceloader, this.componentscanbeannamegenerator, registry);

   set<beandefinitionholder> candidates = new linkedhashset<>(configcandidates);
   set<configurationclass> alreadyparsed = new hashset<>(configcandidates.size());
   do {
   //2.解析java配置类
      parser.parse(candidates);
   //主要校验配置类不能使用final修饰符(cglib代理是生成一个子类,因此原先的类不能使用final修饰)
      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());
      }
    //3.加载bean定义信息,主要实现将@bean @configuration @import @importresource @importregistrar注册为bean
      this.reader.loadbeandefinitions(configclasses);
      alreadyparsed.addall(configclasses);
      //清空已处理的配置类
      candidates.clear();
   //再次获取容器中bean定义数量  如果大于 之前获取的bean定义数量,则说明有新的bean注册到容器中,需要再次解析
      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);
        //新注册的bean如果也是@configuration配置类,则添加到数据,等待解析
               if (configurationclassutils.checkconfigurationclasscandidate(bd, this.metadatareaderfactory) &&
                     !alreadyparsedclasses.contains(bd.getbeanclassname())) {
                  candidates.add(new beandefinitionholder(bd, candidatename));
               }
            }
         }
         candidatenames = newcandidatenames;
      }
   }
   while (!candidates.isempty());

   // register the importregistry as a bean in order to support importaware @configuration classes
   if (sbr != null && !sbr.containssingleton(import_registry_bean_name)) {
      sbr.registersingleton(import_registry_bean_name, parser.getimportregistry());
   }

   if (this.metadatareaderfactory instanceof cachingmetadatareaderfactory) {
      // clear cache in externally provided metadatareaderfactory; this is a no-op
      // for a shared cache since it'll be cleared by the applicationcontext.
      ((cachingmetadatareaderfactory) this.metadatareaderfactory).clearcache();
   }
}

processconfigbeandefinitions整个方法可以大体划分为三个阶段:

  1. 从容器中获取和configuration有关系的beandefinition
  2. 以该beandefinition为起点,进行解析操作,得到解析结果集
  3. 将解析到的结果集加载到容器中,即构造成一个beandefinition放到容器中待初始化

1、判断类是否与@configuration有关

在上面第1步中,有@configuration注解的会加入到集合当中,这个判断是在configurationclassutils#checkconfigurationclasscandidate当中实现

public static boolean checkconfigurationclasscandidate(beandefinition beandef, metadatareaderfactory metadatareaderfactory) {
    string classname = beandef.getbeanclassname();
    if (classname == null || beandef.getfactorymethodname() != null) {
        return false;
    }
    //获取注解元数据信息
    annotationmetadata metadata;
    if (beandef instanceof annotatedbeandefinition &&
            classname.equals(((annotatedbeandefinition) beandef).getmetadata().getclassname())) {
        metadata = ((annotatedbeandefinition) beandef).getmetadata();
    }
    else if (beandef instanceof abstractbeandefinition && ((abstractbeandefinition) beandef).hasbeanclass()) {
        class<?> beanclass = ((abstractbeandefinition) beandef).getbeanclass();
        metadata = new standardannotationmetadata(beanclass, true);
    }
    else {
        try {
            metadatareader metadatareader = metadatareaderfactory.getmetadatareader(classname);
            metadata = metadatareader.getannotationmetadata();
        }
        catch (ioexception ex) {
            return false;
        }
    }
    // 查找当前注解是否是与@configuration相关
    // 该方法还会判断该注解上的注解是否有@configuration,一直往上寻找
    // 因为有的注解为复合注解
    if (isfullconfigurationcandidate(metadata)) {
        beandef.setattribute(configuration_class_attribute, configuration_class_full);
    }
    // 查找当前注解上是否有componentscan、component、import、importresource注解
    //如果没有则查找bean注解,同上,一直往上查找
    else if (isliteconfigurationcandidate(metadata)) {
        beandef.setattribute(configuration_class_attribute, configuration_class_lite);
    }
    else {
        return false;
    }
    return true;
}

我们看看isfullconfigurationcandidate和isliteconfigurationcandidate

public static boolean isfullconfigurationcandidate(annotationmetadata metadata) {
    return metadata.isannotated(configuration.class.getname());
}
public static boolean isliteconfigurationcandidate(annotationmetadata metadata) {
    // do not consider an interface or an annotation...
    if (metadata.isinterface()) {
        return false;
    }

    // any of the typical annotations found?
    for (string indicator : candidateindicators) {
        if (metadata.isannotated(indicator)) {
            return true;
        }
    }

    // finally, let's look for @bean methods...
    try {
        return metadata.hasannotatedmethods(bean.class.getname());
    }
    catch (throwable ex) {
        if (logger.isdebugenabled()) {
            logger.debug("failed to introspect @bean methods on class [" + metadata.getclassname() + "]: " + ex);
        }
        return false;
    }
}

private static final set<string> candidateindicators = new hashset<>(8);

static {
    candidateindicators.add(component.class.getname());
    candidateindicators.add(componentscan.class.getname());
    candidateindicators.add(import.class.getname());
    candidateindicators.add(importresource.class.getname());
}

2、解析java配置类parser.parse(candidates)

parser.parse(candidates)方法最终调用processconfigurationclass方法来处理@configuration配置类,configurationclassparser. processconfigurationclass()方法实现代码如下:

protected void processconfigurationclass(configurationclass configclass) throws ioexception {
  //判断是否需要解析
   if (this.conditionevaluator.shouldskip(configclass.getmetadata(), configurationphase.parse_configuration)) {
      return;
   }
   //判断同一个配置类是否重复加载过,如果重复加载过,则合并,否则从集合中移除旧的配置类,后续逻辑将处理新的配置类
   configurationclass existingclass = this.configurationclasses.get(configclass);
   if (existingclass != null) {
      if (configclass.isimported()) {
         if (existingclass.isimported()) {
            existingclass.mergeimportedby(configclass);
         }
         // otherwise ignore new imported config class; existing non-imported class overrides it.
         return;
      }
      else {
         // explicit bean definition found, probably replacing an import.
         // let's remove the old one and go with the new one.
         this.configurationclasses.remove(configclass);
         this.knownsuperclasses.values().removeif(configclass::equals);
      }
   }

   // 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方法主要实现从配置类中解析所有bean,包括处理内部类,父类以及各种注解

configurationclassparser. doprocessconfigurationclass()解析配置类逻辑如下:

protected final sourceclass doprocessconfigurationclass(configurationclass configclass, sourceclass sourceclass)
      throws ioexception {

   //递归处理任何成员(嵌套)类
   processmemberclasses(configclass, sourceclass);

   // 处理@propertysource注解
   for (annotationattributes propertysource : annotationconfigutils.attributesforrepeatable(
         sourceclass.getmetadata(), propertysources.class,
         org.springframework.context.annotation.propertysource.class)) {
      if (this.environment instanceof configurableenvironment) {
         processpropertysource(propertysource);
      }
      else {
         logger.warn("ignoring @propertysource annotation on [" + sourceclass.getmetadata().getclassname() +
               "]. reason: environment must implement configurableenvironment");
      }
   }

   // 处理@componentscan 
   //获取@componentscan注解信息
   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) {

         // 按@cmponentscan注解扫描bean
         set<beandefinitionholder> scannedbeandefinitions =
               this.componentscanparser.parse(componentscan, sourceclass.getmetadata().getclassname());
         // 遍历扫描出的bean定义是否是配置类bean
         for (beandefinitionholder holder : scannedbeandefinitions) {
            beandefinition bdcand = holder.getbeandefinition().getoriginatingbeandefinition();
            if (bdcand == null) {
               bdcand = holder.getbeandefinition();
            }
            //若果扫描出的bean定义是配置类(含有@configuration),则继续调用parse方法,内部再次调用doprocessconfigurationclas(),递归解析
            if (configurationclassutils.checkconfigurationclasscandidate(bdcand, this.metadatareaderfactory)) {
               parse(bdcand.getbeanclassname(), holder.getbeanname());
            }
         }
      }
   }

   //处理@import注解
   processimports(configclass, sourceclass, getimports(sourceclass), true);

   //处理@importresource注解
   annotationattributes importresource = annotationconfigutils.attributesfor(sourceclass.getmetadata(), importresource.class);
   if (importresource != null) {
      string[] resources = importresource.getstringarray("locations");
      class<? extends beandefinitionreader> readerclass = importresource.getclass("reader");
      for (string resource : resources) {
         string resolvedresource = this.environment.resolverequiredplaceholders(resource);
         configclass.addimportedresource(resolvedresource, readerclass);
      }
   }

   //处理@bean注解 
   set<methodmetadata> beanmethods = retrievebeanmethodmetadata(sourceclass);
   for (methodmetadata methodmetadata : beanmethods) {
      //将解析出的所有@bean注解方法添加到configclass配置类信息中
      configclass.addbeanmethod(new beanmethod(methodmetadata, configclass));
   }

   //处理接口中所有添加@bean注解的方法,内部通过遍历所有接口,解析得到@bean注解方法,并添加到configclass配置类信息中
   processinterfaces(configclass, sourceclass);

   // 如果有父类,则返回父类,递归执行doprocessconfigurationclass()解析父类
   if (sourceclass.getmetadata().hassuperclass()) {
      string superclass = sourceclass.getmetadata().getsuperclassname();
      if (superclass != null && !superclass.startswith("java") &&
            !this.knownsuperclasses.containskey(superclass)) {
         this.knownsuperclasses.put(superclass, configclass);
         // superclass found, return its annotation metadata and recurse
         return sourceclass.getsuperclass();
      }
   }

   // no superclass -> processing is complete
   return null;
}

下面看两个很重要的注解@bean和@componentscan的实现过程

  • @componentscan注解解析过程

set<beandefinitionholder> scannedbeandefinitions =  this.componentscanparser.parse(componentscan, sourceclass.getmetadata().getclassname());

@componentscan注解解析,从上面的代码可以看出@componentscan注解解析通过调用componentscanannotationparser的parse方法完成,而parse()方法内部处理了一些scanner属性(过滤器设置)和basepackages包名处理,最终通过调用classpathbeandefinitionscanner.doscan方法实现扫面工作

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

doscan扫描basepackages下所有bean

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) {
    //根据basepackage加载包下所有java文件,并扫描出所有bean组件    
      set<beandefinition> candidates = findcandidatecomponents(basepackage);
    //遍历beandefition
      for (beandefinition candidate : candidates) {
      //解析作用域scope
         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);
         }
      //通用注解解析到candidate结构中,主要是处理lazy, primary dependson, role ,description这五个注解
         if (candidate instanceof annotatedbeandefinition) {
            annotationconfigutils.processcommondefinitionannotations((annotatedbeandefinition) candidate);
         }
      //检查当前bean是否已经注册,不存在则注册
         if (checkcandidate(beanname, candidate)) {
            beandefinitionholder definitionholder = new beandefinitionholder(candidate, beanname);
            definitionholder = annotationconfigutils.applyscopedproxymode(scopemetadata, definitionholder, this.registry);
            beandefinitions.add(definitionholder);
        // 注册到ioc容器中,主要是一些@component组件,@bean注解方法并没有在此处注册,beanname和beandefinition 键值对
            registerbeandefinition(definitionholder, this.registry);
         }
      }
   }
   return beandefinitions;
}

classpathbeandefinitionscanner.scancandidatecomponents实现bean定义信息扫描

private set<beandefinition> scancandidatecomponents(string basepackage) {
   set<beandefinition> candidates = new linkedhashset<>();
   try {
   // @componentscan("com.sl.springlearning.extension")包路径处理:packagesearchpath = classpath*:com/sl/springlearning/extension/**/*.class
      string packagesearchpath = resourcepatternresolver.classpath_all_url_prefix +
            resolvebasepackage(basepackage) + '/' + this.resourcepattern;
   //获取当前包下所有的class文件
      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);
          //按照scanner过滤器过滤,比如配置类本身将被过滤掉,没有@component等组件注解的类将过滤掉
               //包含@component注解的组件将创建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);
                     }
                  }
               } 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;
}
  • @bean注解解析过程

retrievebeanmethodmetadata方法实现了@bean方法的解析,但并未将实现bean实例的创建。

private set<methodmetadata> retrievebeanmethodmetadata(sourceclass sourceclass) {
   annotationmetadata original = sourceclass.getmetadata();
    //获取所有@bean注解的方法
   set<methodmetadata> beanmethods = original.getannotatedmethods(bean.class.getname());
    // 如果配置类中有多个@bean注解的方法,则排序
   if (beanmethods.size() > 1 && original instanceof standardannotationmetadata) {
      // try reading the class file via asm for deterministic declaration order...
      // unfortunately, the jvm's standard reflection returns methods in arbitrary
      // order, even between different runs of the same application on the same jvm.
      try {
         annotationmetadata asm =
               this.metadatareaderfactory.getmetadatareader(original.getclassname()).getannotationmetadata();
         set<methodmetadata> asmmethods = asm.getannotatedmethods(bean.class.getname());
         if (asmmethods.size() >= beanmethods.size()) {
            set<methodmetadata> selectedmethods = new linkedhashset<>(asmmethods.size());
            for (methodmetadata asmmethod : asmmethods) {
               for (methodmetadata beanmethod : beanmethods) {
                  if (beanmethod.getmethodname().equals(asmmethod.getmethodname())) {
                     selectedmethods.add(beanmethod);
                     break;
                  }
               }
            }
            if (selectedmethods.size() == beanmethods.size()) {
               // all reflection-detected methods found in asm method set -> proceed
               beanmethods = selectedmethods;
            }
         }
      }
      catch (ioexception ex) {
         logger.debug("failed to read class file via asm for determining @bean method order", ex);
         // no worries, let's continue with the reflection metadata we started with...
      }
   }
   return beanmethods;
}

3.加载bean定义信息  this.reader.loadbeandefinitions(configclasses)

  回到configurationclasspostprocessor#processconfigbeandefinitions方法,当调用完parse方法之后,能得到一批configurationclass集合,但是这时候只是获取到,而容器中还没有对应的注册信息,那么接下来就是对这批集合进行注册处理

  configurationclassbeandefinitionreader.loadbeandefinitions()方法的功能就是将之前解析出的configclasses配置类信息中所有配置相关的信息添加到spring的bean定义,主要是配置类中的@bean注解方法,配置类@importresource和@import(实现importbeandefinitionregistrar接口方式)的bean注册

configurationclassbeandefinitionreader.loadbeandefinitions()方法 实现逻辑如下:

public void loadbeandefinitions(set<configurationclass> configurationmodel) {
   trackedconditionevaluator trackedconditionevaluator = new trackedconditionevaluator();
   for (configurationclass configclass : configurationmodel) {
      loadbeandefinitionsforconfigurationclass(configclass, trackedconditionevaluator);
   }
}
private void loadbeandefinitionsforconfigurationclass(
      configurationclass configclass, trackedconditionevaluator trackedconditionevaluator) {
     if (trackedconditionevaluator.shouldskip(configclass)) {
      string beanname = configclass.getbeanname();
      if (stringutils.haslength(beanname) && this.registry.containsbeandefinition(beanname)) {
         this.registry.removebeandefinition(beanname);
      }
      this.importregistry.removeimportingclass(configclass.getmetadata().getclassname());
      return;
   }
    //与@import注解相关
   if (configclass.isimported()) {
      registerbeandefinitionforimportedconfigurationclass(configclass);
   }
  //将@bean方法注册为bean
   for (beanmethod beanmethod : configclass.getbeanmethods()) {
      loadbeandefinitionsforbeanmethod(beanmethod);
   }
   //将configclass中中importresource指定的资源注册为bean
   loadbeandefinitionsfromimportedresources(configclass.getimportedresources());
    //将configclass中importedregistrar注册为bean
   loadbeandefinitionsfromregistrars(configclass.getimportbeandefinitionregistrars());
}

主要看下loadbeandefinitionsforbeanmethod方法

private void loadbeandefinitionsforbeanmethod(beanmethod beanmethod) {
    configurationclass configclass = beanmethod.getconfigurationclass();
    methodmetadata metadata = beanmethod.getmetadata();
    //获取方法名
    string methodname = metadata.getmethodname();

    // do we need to mark the bean as skipped by its condition?
    if (this.conditionevaluator.shouldskip(metadata, configurationphase.register_bean)) {
        configclass.skippedbeanmethods.add(methodname);
        return;
    }
    if (configclass.skippedbeanmethods.contains(methodname)) {
        return;
    }

    //获取@bean注解的元数据信息
    annotationattributes bean = annotationconfigutils.attributesfor(metadata, bean.class);
    assert.state(bean != null, "no @bean annotation attributes");

    // consider name and any aliases
    //获取@bean注解是否有name属性,如@bean(name = "mybean")
    list<string> names = new arraylist<>(arrays.aslist(bean.getstringarray("name")));
    //默认bean的名称和方法名称相同,但是如果设置了name,就取name作为beanname
    string beanname = (!names.isempty() ? names.remove(0) : methodname);

    //创建一个beanmethod的beandefinition
    configurationclassbeandefinition beandef = new configurationclassbeandefinition(configclass, metadata);
    beandef.setresource(configclass.getresource());
    beandef.setsource(this.sourceextractor.extractsource(metadata, configclass.getresource()));

    //设置工厂方法
    //后期bean的实例化,getbean的时候,会判断beanmethod是否存在factorymethod,如果存在,就使用反射调用工厂方法,返回工厂方法中的对象
    if (metadata.isstatic()) {
        // static @bean method
        beandef.setbeanclassname(configclass.getmetadata().getclassname());
        beandef.setfactorymethodname(methodname);
    }
    else {
        // instance @bean method
        beandef.setfactorybeanname(configclass.getbeanname());
        beandef.setuniquefactorymethodname(methodname);
    }
    //....
    this.registry.registerbeandefinition(beanname, beandeftoregister);
}

上面只列出了核心代码,主要是构造了beandefinition,然后注册进容器,而beandefinition的一些属性则是由注解中获取,这部分代码省略。

另外,可以看到@bean的方式构造的beandefinition的时候,与普通的不同,这种方式是会设置工厂方法去初始化,也就是说,appconfig 类下的appbean方法被spring当成一个工厂方法,也就是说这种方式与下列的初始化方式原理类似:

<bean id="appconfig" class="com.example.springboot.springbootdemo.bean.appconfig"/>

<bean id="appbean" factory-bean="appconfig" factory-method="appbean"></bean>

总结

处理逻辑理了一遍后,看一下configurationclasspostprocessor处理器解析@configuration配置类主要过程:

  1. spring容器初始化时注册默认后置处理器configurationclasspostprocessor

  2. spring容器初始化执行refresh()方法中调用configurationclasspostprocessor

  3. configurationclasspostprocessor处理器借助configurationclassparser完成配置类解析

  4. configurationclassparser配置内解析过程中完成嵌套的memberclass、@propertysource注解、@componentscan注解(扫描package下的所有class并进行迭代解析,主要是@component组件解析及注册)、@importresource、@bean等处理

  5. 完成@bean注册, @importresource指定bean的注册以及@import(实现importbeandefinitionregistrar接口方式)的bean注册

  6.有@bean注解的方法在解析的时候作为configurationclass的一个属性,最后还是会转换成beandefinition进行处理, 而实例化的时候会作为一个工厂方法进行bean的创建

 

上一篇: 挣钱

下一篇: 母鸡的腿