Spring IoC之AnnotationConfigApplicationContext

546 阅读3分钟

AnnotationConfigApplicationContext

1.应用

1.1 使用方式

  使用方式一:这种方式也是现在项目中经常用到的方式,从这篇文章开始,就正式进入到 Spring IOC 源码解析。。。

public class MyTestStart {
 public static void main(String[] args) {
  ApplicationContext ann = new AnnotationConfigApplicationContext(MyConfig.class);
  MyService myService = ann.getBean(MyService.class);
  myService.test();
 }
}

  使用方式二:以编程的方式构建 AnnotationConfigApplicationContext,通过无参构造器初始化,然后通过 Register 方法 注册。

public class MyTestStart {
     public static void main(String[] args) {
         AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
         ctx.register(MyConfig.classs);
         ctx.refresh();
         MyService myService = ctx.getBean(MyService.class);
         myService.test();
     }
}

  当然也可以通过使用包扫描的方式,来使用 AnnotationConfigApplicationContext

public class MyTestStart {
     public static void main(String[] args) {
         AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
         ctx.scan("com.fchen");
         ctx.refresh();
         MyService myService = ctx.getBean(MyService.class);
         myService.test();
     }
}

1.2 配置类

@Configuration
@ComponentScan("com.fchen")
public class MyConfig {
 
}

1.3 Service类

@Service
public class MyService {

 private Logger log = LoggerFactory.getLogger(MyService.class);
 public void test(){
  System.out.println("Hello,MyService");
 }
}

2.源码解析

2.1 类关系图

  还是熟悉的套路,在看源码之前,先看类关系图,先整体后细节。类关系图如下: 类图

  • 1.从红色的线可以看出,这个 IOC 容器是属于 ApplicationContext 这一条设计路线,这就意味着他的功能更加的丰富。
  • 2.从蓝色的这条线可以看出,该类同样是 Resource 的子类实现。关于Spring 中资源与资源加载的文章,上一篇文章中有详细介绍。
  • 3.紫色这条线,还没有学习到,但是这一路劲也是极为重要,实现完成BeanDefinition向容器中注册。
  • 4.最后上述的 使用方式二 之所以可以这样使用是应为 实现了 AnnotationConfigRegistry 这个接口。通过 register () 完成编程式使用 AnnotationConfigApplicationContext

2.2 源码实现

  从容器的使用来看,都是通过 ApplicationContext ann = new AnnotationConfigApplicationContext("XXX"); 来完成容器的初始化。 就从这里入手,来一点点揭开 AnnotationConfigApplicationContext 的神秘面纱...

有参构造方法

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
    /**
     * 调用默认的构造方法,由于该类有父类,
     * 故而先调用父类的构造方法,在调用自己的构造方法
     * 在自己的构造方法中初始一个读取器和一个扫描器,
     * 第①部分!!!
     */
    this();
    /**
     * 向spring 的容器中注册bean
     * 第②部分!!!
     */
    register(componentClasses);
    /**
     * 初始化spring的环境
     * 第③部分!!!
     */
    refresh();
}

有参构造方法

public AnnotationConfigApplicationContext() {
    /**
     * 实例化 读取器
     * 将spring中加了注解的类转化为 一个 spring当中bean的描述文件
     */
    this.reader = new AnnotatedBeanDefinitionReader(this);
    /**
     * 实例化 扫描器
     * 能够扫描一个类,并转换为 spring当中bean的描述文件
     * 不通过显示的调用scanner的方法的情况下,spring中的包不是由该scanner扫描
     * 而是由Spring 在实例化 AnnotatedBeanDefinitionReader时
     *   自己new的一个 ClasspathBeanDefinitionScanner 对象扫描完成的
     * 即:这里初始化的扫描器通过程序员显示的调用才会生效。
     */
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

  在有惨的构造方法中,方法的调用逻辑分为三个部分,在后面的文章中都会有详细的介绍。在无参数的构造方法中主要是完成了 读取器扫描器 的初始化。他们在类中的定义如下:

 /**
 * 定义一个读取被加了注解的 bean的读取器
 */
private final AnnotatedBeanDefinitionReader reader;

/**
 * 定义一个包的扫描器
 */
private final ClassPathBeanDefinitionScanner scanner;

  AnnotatedBeanDefinitionReader 为通过编程的方式注册 Bean 提供适配,将spring中加了注解的类转化为一个 spring 当中 Bean 的描述文件。当然最终的注册都是通过 BeanDefinitionRegistry 来完成的。

   ClassPathBeanDefinitionScanner 将定义在类路径上的 Bean 转化为一个spring 当中 Bean 的描述文件,同样最终的注册都是 通过BeanDefinitionRegistry 来完成的。

  以AnnotationConfigApplicationContext 为IoC容器的实现,容器初始化图:

本文使用 mdnice 排版