我对Spring框架的理解

Spring框架,英文Spring Framework。何为框架?我们说Spring框架、Netty框架、Dubbo框架,其实框架就是一个半成品,它将相关领域的流程规范实现好了,但是只有框架是没有业务意义的,需要我们程序员往里面填充业务逻辑(业务逻辑体现在一个个Java的对象中),这样才能完整地实现一个业务系统。

Spring框架主要用于解决对象组织关系的,并可以根据需要将一些对象进行动态代理。Spring最核心的部分就是它的IoC容器,里面放着各种各样的bean(被Spring容器管理的java对象就叫Spring bean),我们的Java对象的生老病死都交给Spring IoC容器管理了,所以叫做IoC(Inversion of Control,控制反转了,即对象的创建权限被反转给Spring容器了,而不是我们自己)。在Spring IoC容器创建bean的时候,会根据容器中的信息来按需给bean创建动态代理,创建动态代理的目的是为了增强bean的功能,比如某些bean的方法需要具有事务的功能,那么事务相关的代码就会被放到切面里面,和业务代码不杂糅在一起,这样把非业务需求相关的系统需求织入到bean中的编程方式就是所谓的AOP(面向切面编程)。

其实,真正完成程序功能的是一个个java对象,你往Spring容器中放入不同的对象,这个容器就具备了不同的能力。比如你放入了KafkaTemplate bean,它就具备了可以发送kafka消息的能力。SpringMVC框架只不过在Spring容器中增加了一些能够处理web请求相关的bean,使得我们能够用SpringMVC来处理web请求。Spring容器只是提供了一个标准化的舞台,你的程序需要哪些功能,就把相应的bean交给Spring容器即可,它会按照你声明的@Autowired@PostConstruct@Transactional@EnableXX注解等形式将程序有机组合起来,完美交付给你使用。

IoC和AOP常常会被相提并论,其实我想说,它们的地位并不是相等的,AOP只是IoC在创建它的bean的时候用到的一个功能。之所以大家常常会将它们相提并论,是因为AOP确实在Spring框架中被玩出各种花样来,通过AOP,可以将访问数据库的Service方法加入事务的能力,通过AOP,可以给符合某一类条件的方法增加日志,通过AOP,可以统一将应用的RuntimeException进行处理,通过AOP,可以将所有流量入口的方法加上权限校验、入参检验等功能。而AOP就是利用了Spring的BeanPostProcessor这个扩展点。

好了,下面让我们来通过图片鸟瞰一下Spring框架的工作流程主线:

Spring Workflow好用不_java

Spring容器的工作流程:

  1. 创建Spring容器(低级容器)
    即创建DefaultListableBeanFactory对象,所有的XXApplicationContext内部都有它,不信你就看一下org.springframework.context.support.GenericApplicationContext,它的构造器内就创建了一个DefaultListableBeanFactory,GenericApplicationContext的所有子类的构造器都会调用到GenericApplicationContext的构造器,所以所有的XXApplicationContext内部都会有一个DefaultListableBeanFactory实例
  2. 做一些准备工作
    比如为了让容器具有一些额外特性,对容器进行的一些设置,可以参考org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory方法中做的事情。
  3. 装载BeanDefinitons
    正如Class是对java类的描述,描述了它有哪些字段,有哪些方法,方法上有哪些注解等等。BeanDefinition就是Spring对它容器中的bean的描述,描述了它的scope(singleton、prototype等),描述了它的beanClass,描述了它的初始化方法,描述了它的依赖的bean名字等等。有了BeanDefinition,Spring框架就可以用它来创建bean了。那么从哪里获取BeanDefinition呢?可以从xml中获取bean的配置,抽象成BeanDefinition,可以从包中扫描出有@Component、@Service等注解的类,抽象成BeanDefinition,可以从@Configuration类中的@Bean方法抽象成BeanDefinition,甚至你也可以自己创建一个BeanDefinition,将它塞到Spring容器中(通过BeanDefinitionRegistry接口中的方法)。可以看看Spring的org.springframework.context.annotation.AnnotationConfigApplicationContext#registerorg.springframework.context.annotation.AnnotationConfigApplicationContext#scanorg.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions方法是怎么装载BeanDefinition的。
  4. 后置处理一下Spring容器
    后置处理,即postProcess,可以给Spring容器增加更多的能力,比如给它添加一些能够注册更多BeanDefinition到Spring容器的BeanFactoryPostProcessor对象(比如ConfigurationClassPostProcessor,它的postProcessBeanDefinitionRegistry方法会从配置类上面解析出来的内容中获取更多的BeanDefinition,典型的注解莫过于MyBatis的@MapperScan注解了,它通常加在配置bean上)。
  5. 创建bean
    对于前面获得的所有的BeanDefinitions,挨个创建成bean,如果发现某个bean需要依赖另外的bean,就先去创建被依赖的bean,所以当你在IDEA中对org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean方法打断点调试的时候,往往会发现调用栈很深。
    创建完bean后还会后置处理它,在初始化方法前可以处理bean(通过各种BeanPostProcessor的postProcessBeforeInitialization方法)。
    然后初始化bean,就是调用它的init方法(每个bean创建的时候就会执行它的init方法,而不是等到容器启动完再逐一调用!)。
    执行完初始化方法后,还会后置处理bean(通过各种BeanPostProcessor的postProcessAfterInitialization方法)。比如找到所有的增强逻辑(增强逻辑在Advisor中,Aspect中的@Around方法和@Pointcut方法会被解析成Advisor),看看它们的pointcut是否match(匹配)当前bean,如果匹配,就为这个bean生成代理对象返回,这样放到容器中的其实是代理对象了(要想获取原来的被代理的对象,也叫目标对象,可以将代理bean安全地强制转换为Advised接口,然后调用getTargetSource().getTarget()方法获取到)。生成代理的方式有JDK动态代理,或者CGLIB的字节码增强。

Spring容器的分类

Spring容器,其实就是Java对象,有两类Spring容器,分别是BeanFactory接口的子类XXBeanFactory,和ApplicationContext接口的子类XXApplicationContext(其实ApplicationContext也继承了BeanFactory接口):

Spring Workflow好用不_Spring Workflow好用不_02


ApplicationContext自己也继承了BeanFactory接口,接口中的方法实现直接委托给XXApplicationContext内部包裹着的DefaultListableBeanFactory,这样ApplicationContext也可以降级成BeanFactory使用(迪米特法则的应用,也叫最小知道原则,即只暴露它作为BeanFactory的功能出去)。

BeanFactory结尾的容器是低级容器,如果想直接使用它,你需要像AbstractApplicationContext的refresh方法那样,对它做各种加工,才能让它智能起来。与其让每个人自己去搞这些模版式的代码,不如由Spring自己提供一些高级容器来让使用更方便,所以又有了ApplicationContext结尾的高级容器。高级容器也有好多个,不过不用担心,它们只是在某些地方不一样,从类名上就可以看出来,下面就随便列举几个:

ClassPathXmlApplicationContext:ClassPathXml前缀表明了它会从类路径的xml中寻找bean。

AnnotationConfigApplicationContext:AnnotationConfig前缀表明了它的bean都是通过注解定义的,比如@Component、@Bean等,不再从xml中寻找bean。

AnnotationConfigServletWebServerApplicationContext:ServletWebServer表示它是一个具有Servlet Web功能的容器。

AnnotationConfigReactiveWebServerApplicationContext:ReactiveWebServer表示它是一个响应式web服务容器(主要用于网关服务)。

Spring框架中很多类的名字很长,但是能让人见名知意,我感觉是一个值得学习的地方。