深入浅出Spring架构设计
      发布时间:2022-01-13 13:46      作者:admin      点击:

媒介

为什么必要Spring? 什么是Spring?

对于如许的题目,大片面人都是处于一栽朦显着胧的状态,说的出来,但又不是十足说的出来,今天吾们就以架构设计的角度尝试解开Spring的奥秘面纱。

本篇文章以由浅入深的手段进走介绍,行家不消惊慌,吾能够保证,只要你会编程就能望懂。

本篇文章基于Spring 5.2.8,浏览时长也许必要20分钟

案例

吾们先来望一个案例:有一个幼伙,有一辆吉利车, 平时就开吉利车上班

代码实现:

public class GeelyCar {      public void run(){         System.out.println("geely running");     } } 
public class Boy {   // 倚赖GeelyCar     private final GeelyCar geelyCar = new GeelyCar();      public void drive(){         geelyCar.run();     } } 

有镇日,幼伙赢利了,又买了辆红旗,想开新车。

浅易,把倚赖换成HongQiCar

代码实现:

public class HongQiCar {      public void run(){         System.out.println("hongqi running");     } } 
public class Boy {     // 修改倚赖为HongQiCar     private final HongQiCar hongQiCar = new HongQiCar();      public void drive(){         hongQiCar.run();     } } 

新车开腻了,又想换回老车,这时候,就会展现一个题目:这个代码不息在改来改去

很鲜明,这个案例违背了吾们的倚赖倒置原则(DIP):程序不该倚赖于实现,而答倚赖于抽象

优化后

现在吾们对代码进走如下优化:

Boy倚赖于Car接口,而之前的GeelyCar与HongQiCar为Car接口实现

代码实现:

定义出Car接口

public interface Car {      void run(); } 

将之前的GeelyCar与HongQiCar改为Car的实现类

public class GeelyCar implements Car {      @Override     public void run(){         System.out.println("geely running");     } } 

HongQiCar相通

Person此时倚赖的为Car接口

public class Boy {   // 倚赖于接口     private final Car car;        public Person(Car car){         this.car = car;     }      public void drive(){         car.run();     } } 

此时幼伙想换什么车开,就传入什么参数即可,代码不再发生转折。

限制性

以上案例改造后望首来实在异国什么毛病了,但照样存在肯定的限制性,倘若此时添加新的场景:

有镇日幼伙喝酒了没法开车,必要找个代驾。代驾并不关心他给哪个幼伙开车,也不关心开的是什么车,幼伙就骤然成了个抽象,这时代码又要进走改动了,代驾倚赖幼伙的代码能够会长这个样子:

private final Boy boy = new YoungBoy(new HongQiCar()); 

随着体系的复杂度添加,如许的题目就会越来越多,越来越难以维护,那么吾们答当如何解决这个题目呢?

思考

最先,吾们能够肯定:行使倚赖倒置原则是异国题目的,它在肯定水平上解决了吾们的题目。

吾们觉得出题目的地方是在传入参数的过程:程序必要什么吾们就传入什么,一但体系中展现多重倚赖的类相关,这个传入的参数就会变得极其复杂。

也许吾们能够把思路反转一下:吾们有什么,程序就用什么!

当吾们只实现HongQiCar和YoungBoy时,代驾就行使的是开着HongQiCar的YoungBoy!

当吾们只实现GeelyCar和OldBoy时,代驾自然而然就转折成了开着GeelyCar的OldBoy!

而如何反转,就是Spring所解决的一大难题。

Spring介绍

Spring是一个一站式轻量级重量级的开发框架,主意是为晓畅决企业级行使开发的复杂性,它为开发Java行使程序挑供周详的基础架构声援,让Java开发者不再必要关心类与类之间的倚赖相关,能够凝神的开发行使程序(crud)。

Spring为企业级开发挑供给了雄厚的功能,而这些功能的底层都倚赖于它的两个中央特性:倚赖注入(DI)和面向切面编程(AOP)。

Spring的中央概念 IoC容器

IoC的全称为Inversion of Control,意为限制反转,IoC也被称为倚赖性注入(DI),这是一个经历倚赖注入对象的过程:对象仅经历组织函数、工厂手段,或者在对象实例化在其上竖立的属性来定义其倚赖相关(即与它们组相符的其他对象),然后容器在创建bean时注入这些必要的倚赖。这个过程从根本上说是Bean本身经历行使直接构建类或诸如服务定位模式的机制,来限制其倚赖相关的实例化或位置的反过程(因此被称为限制反转)。

倚赖倒置原则是IoC的设计原理,倚赖注入是IoC的实现手段。

容器

在Spring中,吾们能够行使XML、Java注脚或Java代码的手段来编写配置新闻,而经历配置新闻,获取相关实例化、配置和拼装对象的表明,进走实例化、配置和拼装行使对象的称为容器。

通俗情况下,吾们只必要添加几个注脚,如许容器进走创建和初首化后,吾们就能够得到一个可配置的,可实走的体系或行使程序。

Bean

在Spring中,由Spring IOC容器进走实例化—>拼装管理—>组成程序骨架的对象称为Bean。Bean就是行使程序中多多对象之一。

以上三点串首来就是:Spring内部是一个安放Bean的IoC容器,经历倚赖注入的手段处理Bean之间的倚赖相关。

AOP

面向切面编程(Aspect-oriented Programming),是相迎面向对象编程(OOP)的一栽功能补充,OOP面向的主要对象是类,而AOP则是切面。在处理日志、坦然管理、事务管理等方面有特意主要的作用。AOP是Spring框架主要的组件,固然IOC容器异国倚赖AOP,但是AOP挑供了特意富强的功能,用来对IOC做补充。

AOP能够让吾们在不修改原有代码的情况下,对吾们的营业功能进走加强:将一段功能切入到吾们指定的位置,如在手段的调用链之间打印日志。

Spring的益处

1、Spring经历DI、AOP来简化企业级Java开发

2、Spring的矮侵占式设计,让代码的污浊极矮

3、Spring的IoC容器降矮了营业对象之间的复杂性,让组件之间互相解耦

4、Spring的AOP声援批准将一些通用义务如坦然、事务、日志等进走荟萃式处理,从而挑高了更益的复用性

5、Spring的高度盛开性,并不强制行使十足倚赖于Spring,开发者可解放选用Spring框架的片面或通盘

6、Spring的高度扩展性,让开发者能够容易的让本身的框架在Spring上进走集成

7、Spring的生态极其完善,集成了各栽特出的框架,让开发者能够容易的行使它们

吾们能够异国Java,但是不及异国Spring~

用Spring改造案例

吾们现在已经意识了什么是Spring,现在就尝试行使Spring对案例进走改造一下

正本的组织异国转折,只需在GeelyCar或HongQiCar上添加@Component注脚,Boy在行使时加上@Autowired注脚

代码形式:

@Componentpublic class GeelyCar implements Car { @Override public void run() {  System.out.println("geely car running"); }} 

HongQiCar相通

在Spring中,当类标识了@Component注脚后就外示这是一个Bean,能够被IoC容器所管理

@Componentpublic class Boy {  // 行使Autowired注脚外示car必要进走倚赖注入 @Autowired private Car car; public void driver(){  car.run(); }} 

吾们之前所说的:吾们实现什么,程序就行使什么,在这边就等同于吾们在哪个类上标识了Component注脚,哪个类就会是一个Bean,Spring就会行使它注入Boy的属性Car中

因而当吾们给GeelyCar标识Component注脚时,Boy开的车就是GeelyCar,当吾们给HongQiCar标识Component注脚时,Boy开的车就是HongQiCar

自然,吾们不能够在GeelyCar和HongQiCar上同时标识Component注脚,由于如许Spring就不晓畅用哪个Car进走注入了——Spring也有选择难得症(or 一boy不及开俩车?)

行使Spring启动程序

// 通知Spring从哪个包下扫描Bean,不写就是现在包路径@ComponentScan(basePackages = "com.my.spring.test.demo")public class Main { public static void main(String[] args) {  // 将Main(配置新闻)传入到ApplicationContext(IoC容器)中  ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);  // 从(IoC容器)中获取到吾们的boy  Boy boy = (Boy) context.getBean("boy");  // 开车  boy.driver(); }} 

这边就能够把吾们刚刚介绍Spring的知识进走解读了

把具有ComponentScan注脚(配置新闻)的Main类,传给AnnotationConfigApplicationContext(IoC容器)进走初首化,就等于:IoC容器经历获取配置新闻进走实例化、管理和拼装Bean。

而如何进走倚赖注入则是在IoC容器内部完善的,这也是本文要商议的重点

思考

吾们经历一个改造案例完善的意识了Spring的基本功能,也对之前的概念有了一个具象化的体验,而吾们还并不晓畅Spring的倚赖注入这一内部行为是如何完善的,所谓知其然更要知其因而然,结相符吾们的现有知识,以及对Spring的理解,大胆推想推想一下吧(这是很主要的能力哦)

其实推想就是指:倘若让吾们本身实现,吾们会如何实现这个过程?

最先,吾们要晓畅吾们必要做的事情是什么:扫描指定包下面的类,进走实例化,并按照倚赖相关组相符益。

步骤分解:

扫描指定包下面的类 -> 倘若这个类标识了Component注脚(是个Bean) -> 把这个类的新闻存首来

进走实例化 -> 遍历存益的类新闻 -> 经历反射把这些类进走实例化

按照倚赖相关组相符 -> 解析类新闻 -> 判定类中是否有必要进走倚赖注入的字段 -> 对字段进走注入

方案实现

吾们现在已经有了一个望首来像是那么一回事的解决方案,现在就尝试把这个方案实现出来

定义注脚

最先吾们必要定义出必要用到的注脚:ComponentScan,Component,Autowired

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface ComponentScan {      String basePackages() default ""; } 
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Component {      String value() default ""; } 
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Autowired { } 
扫描指定包下面的类

扫描指定包下的一切类,听首来雷联相符时摸不着头脑,其实它等同于另一个题目:如何遍历文件现在录?

那么存放类新闻答该用什么呢?吾们望望上面例子中getBean的手段,是不是像Map中的经历key获取value?而Map中还有很多实现,但线程坦然的却只有一个,那就是ConcurrentHashMap(别跟吾说HashTable)

定义存放类新闻的map

private final Map<String, Class<?>> classMap = new ConcurrentHashMap<>(16); 

详细流程,下面同样附上代码实现:

代码实现,能够与流程图结相符不雅旁观:

扫描类新闻

private void scan(Class<?> configClass) {   // 解析配置类,获取到扫描包路径   String basePackages = this.getBasePackages(configClass);   // 行使扫描包路径进走文件遍历操作   this.doScan(basePackages); } 
private String getBasePackages(Class<?> configClass) {   // 从ComponentScan注脚中获取扫描包路径   ComponentScan componentScan = configClass.getAnnotation(ComponentScan.class);   return componentScan.basePackages(); } 
private void doScan(String basePackages) {   // 获取资源新闻   URI resource = this.getResource(basePackages);    File dir = new File(resource.getPath());   for (File file : dir.listFiles()) {     if (file.isDirectory()) {       // 递归扫描       doScan(basePackages + "." + file.getName());     }     else {       // com.my.spring.example + . + Boy.class -> com.my.spring.example.Boy       String className = basePackages + "." + file.getName().replace(".class", "");       // 将class存放到classMap中       this.registerClass(className);     }   } } 
private void registerClass(String className){   try {     // 加载类新闻     Class<?> clazz = classLoader.loadClass(className);     // 判定是否标识Component注脚     if(clazz.isAnnotationPresent(Component.class)){       // 生成beanName com.my.spring.example.Boy -> boy       String beanName = this.generateBeanName(clazz);       // car: com.my.spring.example.Car       classMap.put(beanName, clazz);     }   } catch (ClassNotFoundException ignore) {} } 
实例化

现在已经把一切正当的类都解析益了,接下来就是实例化的过程了

定义存放Bean的Map

private final Map<String, Object> beanMap = new ConcurrentHashMap<>(16); 

详细流程,下面同样给出代码实现:

代码实现,能够与流程图结相符不雅旁观:

遍历classMap进走实例化Bean

public void instantiateBean() {   for (String beanName : classMap.keySet()) {     getBean(beanName);   } } 
public Object getBean(String beanName){   // 先从缓存中获取   Object bean = beanMap.get(beanName);   if(bean != null){     return bean;   }   return this.createBean(beanName); } 
private Object createBean(String beanName){   Class<?> clazz = classMap.get(beanName);   try {     // 创建bean     Object bean = this.doCreateBean(clazz);     // 将bean存到容器中     beanMap.put(beanName, bean);     return bean;   } catch (IllegalAccessException e) {     throw new RuntimeException(e);   } } 
private Object doCreateBean(Class<?> clazz) throws IllegalAccessException {  // 实例化bean  Object bean = this.newInstance(clazz);  // 填充字段,将字段设值  this.populateBean(bean, clazz);  return bean;} 
private Object newInstance(Class<?> clazz){  try {    // 这边只声援默认组织器    return clazz.getDeclaredConstructor().newInstance();  } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {    throw new RuntimeException(e);  }} 
private void populateBean(Object bean, Class<?> clazz) throws IllegalAccessException {  // 解析class新闻,判定类中是否有必要进走倚赖注入的字段  final Field[] fields = clazz.getDeclaredFields();  for (Field field : fields) {    Autowired autowired = field.getAnnotation(Autowired.class);    if(autowired != null){      // 获取bean      Object value = this.resolveBean(field.getType());      field.setAccessible(true);      field.set(bean, value);    }  }} 
private Object resolveBean(Class<?> clazz){  // 先判定clazz是否为一个接口,是则判定classMap中是否存在子类  if(clazz.isInterface()){    // 一时只声援classMap只有一个子类的情况    for (Map.Entry<String, Class<?>> entry : classMap.entrySet()) {      if (clazz.isAssignableFrom(entry.getValue())) {        return getBean(entry.getValue());      }    }    throw new RuntimeException("找不到能够进走倚赖注入的bean");  }else {    return getBean(clazz);  }} 
public Object getBean(Class<?> clazz){   // 生成bean的名称   String beanName = this.generateBeanName(clazz);   // 此处对答最最先的getBean手段   return this.getBean(beanName); } 
组相符

两个中央手段已经写益了,接下把它们组相符首来,吾把它们实现在自定义的ApplicationContext类中,组织手段如下:

public ApplicationContext(Class<?> configClass) {   // 1.扫描配置新闻中指定包下的类   this.scan(configClass);   // 2.实例化扫描到的类   this.instantiateBean(); } 

UML类图:

测试

代码组织与案例相通,这边展现一下吾们本身的Spring是否能够平常运走

运走平常,中国人不骗中国人

源码会在文末给出

回顾

现在,吾们已经按照设想的方案进走了实现,运走的情况也达到了预期的成果。但倘若仔细钻研一下,再结相符吾们平时行使Spring的场景,就会发现这一份代码的不少题目:

1、无法声援组织器注入,自然也异国声援手段注入,这是属于功能上的缺失。

2、加载类新闻的题目,加载类时吾们行使的是classLoader.loadClass的手段,固然这避免了类的初首化(可千万别用Class.forName的手段),但照样不走避免的把类元新闻加载到了元空间中,当吾们扫描包下有不必要的类时,这就铺张了吾们的内存。

3、无法解决bean之间的循环倚赖,比如有一个A对象倚赖了B对象, B对象又倚赖了A对象,这个时候吾们再来望望代码逻辑,就会发现此时会陷入物化循环。

4、扩展性很差,吾们把一切的功能都写在一个类里,当想要完善功能(比如以上3个题目)时,就必要反复修改这个类,这个类也会变得越来越肥胖,别说迭代新功能,维护都会令人头疼。

优化方案

对于前三个题目都相通于功能上的题目,功能嘛,改一改就益了。

吾们必要偏重关注的是第四个题目,一款框架想要变得特出,那么它的迭代能力肯定要益,如许功能才能变得雄厚,而迭代能力的影响因素有很多,其中之一就是它的扩展性。

那么答该如何挑高吾们的方案的扩展性呢,六大设计原则给了吾们很益的请示作用。

在方案中,ApplicationContext做了很多事情, 主要能够分为两大块

1、扫描指定包下的类

2、实例化Bean

借助单一职责原则的思维:一个类只做一栽事,一个手段只做一件事。

吾们把扫描指定包下的类这件事单独行使一个处理器进走处理,由于扫描配置是从配置类而来,那吾们就叫他配置类处理器:ConfigurationCalssProcessor

实例化Bean这件事情也同样如此,实例化Bean又分为了两件事:实例化和倚赖注入

实例化Bean就是相等于一个生产Bean的过程,吾们就把这件事行使一个工厂类进走处理,它就叫做:BeanFactory,既然是在生产Bean,那就必要质料(Class),因而吾们把classMap和beanMap都定义到这边

而倚赖注入的过程,其实就是在处理Autowired注脚,那它就叫做: AutowiredAnnotationBeanProcessor

吾们还在晓畅,在Spring中,不光仅只有这栽行使手段,还有xml,mvc,SpringBoot的手段,因而吾们将ApplicationContext进走抽象,只实现骨干流程,正本的注脚手段交由AnnotationApplicationContext实现。

借助倚赖倒置原则:程序答当倚赖于抽象

在异日,类新闻不光仅能够从类新闻来,也能够从配置文件而来,因而吾们将ConfigurationCalssProcessor抽象

而倚赖注入的手段纷歧定非得是用Autowried注脚标识,也能够是别的注脚标识,比如Resource,因而吾们将AutowiredAnnotationBeanProcessor抽象

Bean的类型也能够有很多,能够是单例的,能够使多例的,也能够是个工厂Bean,因而吾们将BeanFactory抽象

现在,吾们借助两大设计原则对吾们的方案进走了优化,相比于之前可谓是”洗手不干“。

Spring的设计

在上一步,吾们实现了本身的方案,并基于一些设想进走了扩展性优化,现在,吾们就来意识一下实际上Spring的设计

那么,在Spring中又是由哪些"角色"组成的呢?

1、Bean: Spring行为一个IoC容器,最主要的自然是Bean咯

2、BeanFactory: 生产与管理Bean的工厂

3、BeanDefinition: Bean的定义,也就是吾们方案中的Class,Spring对它进走了封装

4、BeanDefinitionRegistry: 相通于Bean与BeanFactory的相关,BeanDefinitionRegistry用于管理BeanDefinition

5、BeanDefinitionRegistryPostProcessor: 用于在解析配置类时的处理器,相通于吾们方案中的ClassProcessor

6、BeanFactoryPostProcessor: BeanDefinitionRegistryPostProcessor父类,让吾们能够再解析配置类之后进走后置处理

7、BeanPostProcessor: Bean的后置处理器,用于在生产Bean的过程中进走一些处理,比如倚赖注入,相通吾们的AutowiredAnnotationBeanProcessor

8、ApplicationContext: 倘若说以上的角色都是在工厂中生产Bean的工人,那么ApplicationContext就是吾们Spring的门面,ApplicationContext与BeanFactory是一栽组相符的相关,因而它十足扩展了BeanFactory的功能,并在其基础上添加了更多特定于企业的功能,比如吾们熟知的ApplicationListener(事件监听器)

以上说的相通其实有一些本末倒置了,由于实际上答该是吾们方案中的实现相通于Spring中的实现,如许说只是为了让行家更益的理解

吾们在经历了本身方案的设计与优化后,对这些角色其实是特意容易理解的

接下来,吾们就一个一个的详细晓畅一下

BeanFactory

BeanFactory是Spring中的一个顶级接口,它定义了获取Bean的手段,Spring中还有另一个接口叫SingletonBeanRegistry,它定义的是操作单例Bean的手段,这边吾将这两个放在一首进走介绍,由于它们大体相通,SingletonBeanRegistry的解释上也写了能够与BeanFactory接口一首实现,方便联相符管理。

BeanFactory

1、ListableBeanFactory:接口,定义了获取Bean/BeanDefinition列外相关的手段,如getBeansOfType(Class type)

2、AutowireCapableBeanFactory:接口,定义了Bean生命周期相关的手段,如创建bean, 倚赖注入,初首化

3、AbstractBeanFactory:抽象类,基本上实现了一切相关Bean操作的手段,定义了Bean生命周期相关的抽象手段

4、AbstractAutowireCapableBeanFactory:抽象类,继承了AbstractBeanFactory,实现了Bean生命周期相关的内容,固然是个抽象类,但它异国抽象手段

5、DefaultListableBeanFactory:继承与实现以上一切类和接口,是为Spring中最底层的BeanFactory, 自身实现了ListableBeanFactory接口

6、ApplicationContext:也是一个接口,吾们会在下面有特意对它的介绍

SingletonBeanRegistry

1、DefaultSingletonBeanRegistry: 定义了Bean的缓存池,相通于吾们的BeanMap,实现了相关单例的操作,比如getSingleton(面试常问的三级缓存就在这边)

2、FactoryBeanRegistrySupport:挑供了对FactoryBean的声援,比如从FactoryBean中获取Bean

BeanDefinition

BeanDefinition其实也是个接口(想不到吧),这边定义了很多和类新闻相关的操作手段,方便在生产Bean的时候直接行使,比如getBeanClassName

它的也许组织如下(这边举例RootBeanDefinition子类):

内里的各栽属性想必行家也绝不生硬

同样的,它也有很多实现类:

1、AnnotatedGenericBeanDefinition:解析配置类与解析Import注脚带入的类时,就会行使它进走封装

2、ScannedGenericBeanDefinition:封装经历@ComponentScan扫描包所得到的类新闻

3、ConfigurationClassBeanDefinition:封装经历@Bean注脚所得到的类新闻

4、RootBeanDefinition:ConfigurationClassBeanDefinition父类,通俗在Spring内部行使,将其他的BeanDefition转化成该类

BeanDefinitionRegistry

定义了与BeanDefiniton相关的操作,如registerBeanDefinition,getBeanDefinition,在BeanFactory中,实现类就是DefaultListableBeanFactory

BeanDefinitionRegistryPostProcessor

插话:讲到这边,有异国发现Spring的命名极其规范,Spring团队曾言Spring中的类名都是频频推敲才确认的,真是名副其实呀,因而望Spring源码真的是一件很安详的事情,望望类名手段名就能猜出它们的功能了。

该接口只定义了一个功能:处理BeanDefinitonRegistry,也就是解析配置类中的Import、Component、ComponentScan等注脚进走响答的处理,处理完毕后将这些类注册成对答的BeanDefinition

在Spring内部中,只有一个实现:ConfigurationClassPostProcessor

BeanFactoryPostProcessor

所谓BeanFactory的后置处理器,它定义了在解析完配置类后能够调用的处理逻辑,相通于一个插槽,倘若吾们想在配置类解析完后做点什么,就能够实现该接口。

在Spring内部中,同样只有ConfigurationClassPostProcessor实现了它:用于特意处理加了Configuration注脚的类

这边串场一个幼题目,如知以下代码:

@Configuraiton public class MyConfiguration{   @Bean   public Car car(){       return new Car(wheel());   }   @Bean   public Wheel wheel(){       return new Wheel();   } } 

问:Wheel对象在Spring启动时,被new了几次?为什么?

BeanPostProcessor

江湖翻译:Bean的后置处理器

该后置处理器贯穿了Bean的生命周期整个过程,在Bean的创建过程中,统统被调用了9次,至于哪9次吾们下次再来探究,以下介绍它的实现类以及作用

1、AutowiredAnnotationBeanPostProcessor:用于揣度组织器进走实例化,以及处理Autowired和Value注脚

2、CommonAnnotationBeanPostProcessor:处理Java规范中的注脚,如Resource、PostConstruct

3、ApplicationListenerDetector: 在Bean的初首化后行使,将实现了ApplicationListener接口的bean添加到事件监听器列外中

4、ApplicationContextAwareProcessor:用于回调实现了Aware接口的Bean

5、ImportAwareBeanPostProcessor: 用于回调实现了ImportAware接口的Bean

ApplicationContext

ApplicationContext行为Spring的中央,以门面模式阻隔了BeanFactory,以模板手段模式定义了Spring启动流程的骨架,又以策略模式调用了各式各样的Processor......实在是错综复杂又精妙绝伦!

它的实现类如下:

1、ConfigurableApplicationContext:接口,定义了配置与生命周期相关操作,如refresh

2、AbstractApplicationContext: 抽象类,实现了refresh手段,refresh手段行为Spring中央中的中央,能够说整个Spring皆在refresh之中,一切子类都经历refresh手段启动,在调用该手段之后,将实例化一切单例

3、AnnotationConfigApplicationContext: 在启动时行使相关的注脚读取器与扫描器,去Spring容器中注册必要用的处理器,而后在refresh手段在被主流程调用即可

4、AnnotationConfigWebApplicationContext:实现loadBeanDefinitions手段,以期在refresh流程中被调用,从而加载BeanDefintion

5、ClassPathXmlApplicationContext: 同上

从子类的情况能够望出,子类的分别之处在于如何加载BeanDefiniton, AnnotationConfigApplicationContext是经历配置类处理器(ConfigurationClassPostProcessor)加载的,而AnnotationConfigWebApplicationContext与ClassPathXmlApplicationContext则是经历本身实现loadBeanDefinitions手段,其他流程则十足相反

Spring的流程

以上,吾们已经晓畅了Spring中的主要角色以及作用,现在吾们尝试把它们组相符首来,构建一个Spring的启动流程

同样以吾们常用的AnnotationConfigApplicationContext为例

图中只画出了Spring中的片面也许流程,详细内容吾们会在后面的章节睁开

幼结

所谓万事起头难,本文初衷就是能让行家以由浅入深的手段意识Spring,初步竖立Spring的认知体系,晓畅Spring的内部架构,对Spring的认知不再浮于外观。

现在头已经开了,信任后面内容的学习也将水到渠来。

本篇文章既讲是Spring的架构设计,也期待能成为吾们以后复习Spring集体内容时行使的手册。

末了,望完文章之后,信任对以下面试常问的题目回答首来也是万无一失

1、什么是BeanDefinition?

2、BeanFactory与ApplicationContext的相关?

3、后置处理器的分类与作用?

4、Spring的主要流程是怎么样的?

倘若幼友人觉得没手段很益回答上来的话就再望望文章,或者在评论区留下本身的见解吧

益啦,吾是敖丙,你晓畅的越多,你不晓畅的越多,吾们下期见。

【编辑选举】

鸿蒙官方战略配相符共建——HarmonyOS技术社区 HarmonyOS 自定义组件之可拖拽圆形进度条 HarmonyOS从Text的宝藏属性想到的TabList新玩法 鸿蒙设备开发Hi3861-IoT落地-自动门锁 HarmonyOS 项现在实战之通讯录(Java) Kotlin在鸿蒙开发中的实践
 
 

Powered by 轮理片在人线2021-老扒让儿媳欲仙欲死手机官网-四个男人搞 @2018 RSS地图 HTML地图

Copyright 站群系统 © 2013-2021 365建站器 版权所有