深入浅出Spring架构策画

发布日期:2022-08-07 17:39    点击次数:199
前言

为何需求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 Progra妹妹ing),是相当面向工具编程(OOP)的一种功用增补,OOP面向的首要工具是类,而AOP则是切面。在处理惩罚日志、安好打点、事件打点等方面有极度首要的浸染。AOP是Spring框架首要的组件,诚然IOC容器没有寄托AOP,然则AOP供应了极度强盛的功用,用来对IOC做增补。

AOP可以或许让我们在不编削原有代码的环境下,对我们的业务功用举行增强:将一段功用切入到我们指定的职位地方,如在编制的调用链之间打印日志。

Spring的所长

一、Spring经由过程DI、AOP来简化企业级Java开发

二、Spring的低侵入式策画,让代码的传染极低

三、Spring的IoC容器升高了业务工具之间的宏壮性,让组件之间彼此解耦

四、Spring的AOP支持准许将一些通用使命如安好、事件、日志等举行会合式处理惩罚,从而行进了更好的复用性

五、Spring的高度开放性,实在不强逼应用齐全寄托于Spring,开发者可自由选用Spring框架的部份或全副

六、Spring的高度扩张性,让开发者可以或许等闲的让本身的框架在Spring长举行集成

七、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的场景,就会缔造这一份代码的良多成就:

一、无法支持布局器注入,固然也没有支持编制注入,这是属于功用上的缺失。

二、加载类信息的成就,加载类时我们应用的是classLoader.loadClass的编制,诚然这防止了类的初始化(可万万别用Class.forName的编制),但照旧不成防止的把类元信息加载到了元空间中,当我们扫描包下有不需求的类时,这就糟践了我们的内存。

三、无法经管bean之间的循环寄托,好比有一个A工具寄托了B工具, B工具又寄托了A工具,这个岁月我们再来看看代码逻辑,就会缔造此时会陷入死循环。

四、扩张性很差,我们把全体的功用都写在一个类里,当想要完善功用(比喻以上3个成就)时,就需求频繁编削这个类,这个类也会变得越来越臃肿,别说迭代新功用,回护都市使人头疼。

优化规划

关于前三个成就都近似于功用上的成就,科技动态功用嘛,改一改就行了。

我们需求侧重关注的是第四个成就,一款框架想要变得优异,那末它的迭代才能必定要好,这样功用材干变得雄厚,而迭代才能的影响要素有良多,个中之一就是它的扩张性。

那末该当怎么行进我们的规划的扩张性呢,六大策画原则给了我们很好的引导浸染。

在规划中,ApplicationContext做了良多工作, 首要可以或许分为两大块

一、扫描指定包下的类

二、实例化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中又是由哪些"角色"变成的呢?

一、Bean: Spring作为一个IoC容器,最首要的固然是Bean咯

二、BeanFactory: 临蓐与打点Bean的工厂

三、BeanDefinition: Bean的定义,也就是我们规划中的Class,Spring对它举行了封装

四、BeanDefinitionRegistry: 近似于Bean与BeanFactory的纠葛,BeanDefinitionRegistry用于打点BeanDefinition

五、BeanDefinitionRegistryPostProcessor: 用于在剖析设置类时的处理惩罚器,近似于我们规划中的ClassProcessor

六、BeanFactoryPostProcessor: BeanDefinitionRegistryPostProcessor父类,让我们可以或许再剖析设置类今后举行后置处理惩罚

七、BeanPostProcessor: Bean的后置处理惩罚器,用于在临蓐Bean的进程及第行一些处理惩罚,比喻寄托注入,近似我们的AutowiredAnnotationBeanProcessor

八、ApplicationContext: 假定说以上的角色都是在工厂中临蓐Bean的工人,那末ApplicationContext就是我们Spring的门面,ApplicationContext与BeanFactory是一种组合的纠葛,所以它齐全扩张了BeanFactory的功用,并在其根抵上增加了更多特定于企业的功用,比喻我们熟知的ApplicationListener(事宜监听器)

以上说的近似实在有一些轻重倒置了,因为理论上该当是我们规划中的实现近似于Spring中的实现,这样说只是为了让巨匠更好的理解

我们在阅历了本身规划的策画与优化后,对这些角色理论上是极度苟且理解的

接上去,我们就一个一个的详细相识一下

BeanFactory

BeanFactory是Spring中的一个顶级接口,它定义了取得Bean的编制,Spring中另有另外一个接口叫SingletonBeanRegistry,它定义的是操作单例Bean的编制,这里我将这两个放在一起举行介绍,因为它们概略沟通,SingletonBeanRegistry的注释上也写了可以或许与BeanFactory接口一起实现,方便统一打点。

BeanFactory

一、ListableBeanFactory:接口,定义了取得Bean/BeanDefinition列表相干的编制,如getBeansOfType(Class type)

二、AutowireCapableBeanFactory:接口,定义了Bean生命周期相干的编制,如创立bean, 寄托注入,初始化

三、AbstractBeanFactory:笼统类,根抵上实现了全体无关Bean操作的编制,定义了Bean生命周期相干的笼统编制

四、AbstractAutowireCapableBeanFactory:笼统类,继承了AbstractBeanFactory,实现了Bean生命周期相干的内容,诚然是个笼统类,但它没有笼统编制

五、DefaultListableBeanFactory:继承与实现以上全体类和接口,是为Spring中最底层的BeanFactory, 本身实现了ListableBeanFactory接口

六、ApplicationContext:也是一个接口,我们会在上面有专门对它的介绍

SingletonBeanRegistry

一、DefaultSingletonBeanRegistry: 定义了Bean的缓存池,近似于我们的BeanMap,实现了无关单例的操作,比喻getSingleton(笔试常问的三级缓存就在这里)

二、FactoryBeanRegistrySupport:供应了对FactoryBean的支持,比喻从FactoryBean中取得Bean

BeanDefinition

BeanDefinition实在也是个接口(想不到吧),这里定义了良多和类信息相干的操作编制,方便在临蓐Bean的光阳间策应用,比喻getBeanClassName

它的可能布局以下(这里举例RootBeanDefinition子类):

内里的种种属性想必巨匠也绝不目生

一样的,它也有良多实现类:

一、AnnotatedGenericBeanDefinition:剖析设置类与剖析Import表明带入的类时,就会应用它举行封装

二、ScannedGenericBeanDefinition:封装经由过程@ComponentScan扫描包所失去的类信息

三、ConfigurationClassBeanDefinition:封装经由过程@Bean表明所失去的类信息

四、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次我们下次再来探讨,下列介绍它的实现类以及浸染

一、AutowiredAnnotationBeanPostProcessor:用于推测布局器举行实例化,以及处理惩罚Autowired和Value表明

二、Co妹妹onAnnotationBeanPostProcessor:处理惩罚Java尺度中的表明,如Resource、PostConstruct

三、ApplicationListenerDetector: 在Bean的初始化后应用,将实现了ApplicationListener接口的bean增加到事宜监听器列表中

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

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

ApplicationContext

ApplicationContext作为Spring的焦点,以门面情势断绝了BeanFactory,以模板编制情势定义了Spring启动流程的骨架,又以计策情势调用了种种各样的Processor......实在是地面楼阁又精妙绝伦!

它的实现类以下:

一、ConfigurableApplicationContext:接口,定义了设置与生命周期相干操作,如refresh

二、AbstractApplicationContext: 笼统类,实现了refresh编制,refresh编制作为Spring焦点中的焦点,可以或许说全副Spring皆在refresh当中,全体子类都经由过程refresh编制启动,在调用该编制今后,将实例化全体单例

三、AnnotationConfigApplicationContext: 在启动时应用相干的表明读取器与扫描器,往Spring容器中注册需求用的处理惩罚器,尔后在refresh编制在被主流程调用即可

四、AnnotationConfigWebApplicationContext:实现loadBeanDefinitions编制,以期在refresh流程中被调用,从而加载BeanDefintion

五、ClassPathXmlApplicationContext: 同上

从子类的环境可以或许看出,子类的差别之处在于怎么加载BeanDefiniton, AnnotationConfigApplicationContext是经由过程设置类处理惩罚器(ConfigurationClassPostProcessor)加载的,而AnnotationConfigWebApplicationContext与ClassPathXmlApplicationContext则是经由过程本身实现loadBeanDefinitions编制,别的流程则齐全分歧

Spring的流程

以上,我们已经清楚了Spring中的首要角色以及浸染,今朝我们查验测验把它们组合起来,构建一个Spring的启动流程

一样以我们经常使用的AnnotationConfigApplicationContext为例

图中只画出了Spring中的部份可能流程,详细内容我们会在后面的章节开展

小结

所谓万事结尾难,本文初衷就是能让巨匠以由浅入深的编制熟习Spring,开端直立Spring的认知体系,显明Spring的外部架构,对Spring的认知再也不浮于详情。

今朝头已经开了,信赖后面内容的深造也将水到渠来。

本篇文章既讲是Spring的架构策画,也停留能成为我们今后复习Spring总体内容时应用的手册。

最后,看完文章今后,信赖对以上笔试常问的成就回覆起来也是轻而易举

一、什么是BeanDefinition?

二、BeanFactory与ApplicationContext的纠葛?

三、后置处理惩罚器的分类与浸染?

四、Spring的首要流程是怎样的?

假定小搭档感应没措施很好回覆下去的话就再看看文章,或许在驳斥区留下本身的见识吧

好啦,我是敖丙,你晓得的越多,你不晓得的越多,我们下期见。