前言
Spring是我们作为java开发最常用的框架了,但现如今,大部分项目实际使用的却是SpringBoot。由于它们存在一些关联,导致很多开发对其功能和目的有些混乱,例如说不清一些功能到底是Spring框架的,还是SpringBoot带的?又或者不能清晰的描述两个框架的各自功能和目的,今天,我们就来详细解释这些问题
一、从pom看关联
我们既然想知道两个框架的关联,不如直接开个工程,引入一个基础的Springboot启动器,然后通过依赖关系来分辨两者的联系
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.3.12.RELEASE</version> </dependency>
我们先看三个带标注的包,springBoot启动器是不含代码的,它的唯一作用就是引入各个指定了版本的包,其中最重要的就是spring-boot 和 spring-boot-autoconfigure 两个springboot核心包
而我们要看spring框架 和 springBoot 框架的关系,可以直接看两段红色的依赖性,图中,spring-boot-autoconfigure自动配置包不需要再引用任何东西,而作为核心支持的spring-boot包则需要引入spring框架核心包spring-context 和 spring-core
总结来看,如果我们只关注核心包,springboot框架毫无疑问是建立在spring框架之上的,在spring框架之上,加上一些类比如SpringApplication启动器等,并且配合上自动配置功能
二、spring的目的与不足
1. java EE
在Spring框架创建前,Java EE平台(即Java Enterprise Edition)是企业级应用程序开发的主要框架,该框架的提出主要是因为一个企业级应用的搭建涉及的内容太多,迫切需要一个规范来进行模块分层处理,于是sun公司(现在已经被Oracle收购)希望一些基础的共性的东西由容器去管理,而个性化的东西,如业务由组件去管理,由容器+组件的方式来提供服务。
所以这两方面:容器和组件。容器由各个开发商去根据规范开发,如WebSphere、WebLogic Server等。开发者则去开发组件,然后运行在容器中。由于容器满足了统一的规范,所以组件可以运行在不同的容器上
但 它太复杂了。在构建现代企业级应用程序时,开发人员往往需要大量的样板代码和配置,比如在web.xml文件中手动配置Servlet和JSP的映射关系,手动配置数据库连接池和JNDI数据源,手动配置EJB容器的属性以及部署EJB组件等,这些配置内容繁琐,容易出错,并且需要开发人员掌握很多细节,同时这些配置还需要在不同的JavaEE容器之间进行调整。
2. spring的目的
上面我们介绍了java EE的开发与配置过于繁杂,所以Rod Johnson于2002年创建了Spring框架,我们必须明白,spring并不是推翻了javaEE所规范的模式,Spring框架的主要目标是简化Java EE平台的开发,使开发人员可以更专注于业务逻辑,而非容器配置和其他样板代码。
比如上面的Servlet和JSP的映射关系,在spring里,可以由其MVC框架(也称为Spring Web MVC)将应用程序分解为模型、视图和控制器的模式,同时控制器可以通过注解来映射哪些请求应该由哪个方法处理,从而简化了Servlet和JSP映射配置,
除了springmvc以外,spring框架其实包含了多个包,其中 spring-core(包含spring-context、spring-beans、spring-aop)毫无疑问是重中之重。,这些核心包中包含了大量的底层设计,来帮助开发者减少代码负担
其中有两个功能和模式是最大的提升,能大量简化开发代码。这就是我们常说的Spring框架的两大核心 —— IOC 与 AOP
3. spring的特性
很多开发提及spring,都能知道Spring框架的两大核心 —— IOC 与 AOP,这也一直是面试高频题。但这两者对开发者有什么裨益却语焉不详了,我们就用最简单的话来解释一下
3.1 IOC(控制反转)
介绍:控制反转(Inversion of Control)的名字源于它对传统编程方法的反转。在传统编程方法中,程序员通过代码来控制各种对象之间的依赖关系,程序员需要显式地在代码中创建和管理各个对象并决定它们之间的依赖关系。而在IOC中,控制对象的创建和管理的不再是程序员自己,而是由容器来完成,即容器来“控制”对象的创建和管理,“反转”了对象的创建和管理的控制权
很明显能看出,IOC的引入,将各组件解耦了。按照传统编码方式,我们在A类中显式的new 出一个B类,如果我们要把B类替换成一个BB类,那么A类这个地方的引用也需要修改,引用B的地方如果很多呢?那岂不是要大面积改动?
你也许会自然而然的想到,难道不能把B类和BB类都实现同一个接口,或者BB类继承自B,然后通过向上转型解决这个问题吗? 很高兴你有这种想法,这种想法是可行的。但从工程角度而言,限制太大,很多类没有接口或无法继承,那么就没法用这个方法了。
那么通过容器解耦到底是怎么实现的呢?其实是通过DI(依赖注入) 解决的
可以看到传统模式,创建A对象,我就得新建个B和C,新建B又得新建C和D,如同套娃。而在IOC模式,创建A时,容器会主动为你寻找并装填B和C,不用你再新建(除非容器里也没有)。而提供B需要的C和D,同样不需要你管,容器会找到C和D,装填在B上,然后又把B为你装填在A上。
这种把A依赖的B和C主动装填上的模式,就叫依赖注入。依赖注入是IOC的一种具体实现,不仅让开发者不用频繁的new对象(尤其是对象还嵌套对象时),而且能减少重复对象,省事且省空间
3.2 AOP (面向切面编程)
AOP的基础是动态代理,而动态代理jdk和CGlib都可以实现,也就是说使用原始的java EE也是可以做出切面效果的,但是开发者得手动写大量的代码来进行切面的植入,而且往往还不具备通用性。
Spring则以框架的形式帮你完成了这部分代码,且实现了通用,你只需要按照要求,使用一些切面简单的配置就能完成大面积的切面增强
具体可以参考另一篇文章:Spring核心特性—— AOP(面向切面编程)
4. Spring的不足
我们说spring框架是为了减少传统java EE项目的配置强度和开发强度的,而且spring曾经的介绍是个
可以针对bean的生命周期进行管理的轻量级框架,然而,发展到现今的spring,功能越来越丰富,代码规模也越来越大,它“轻量级”的身份也不再那么贴合了。
此时的spring也逐渐露出诸多问题,由于日渐庞大的功能支持,导致使用者的学习成本大大增加且不说,就连其初衷也渐渐背离了,我们现在来说说spring的一些弊端:
- 配置繁琐,本身spring就是为了减少程序配置而创建的框架,然而发展至今,spring框架要做的功能和兼容也越来越繁杂,导致框架本身的xml配置也越来越多
- 复杂的体系,spring按照模块划分成了多个包,想用事务得引用spring-tx,测试得引用spring-test。对于初学者来说懵懂又麻烦
- 版本依赖,spring版本的升级很频繁,很容易出现spring组件间的版本冲突,需要手动管理各组件的版本依赖