Spring原理
(一)Spring家族
我们说的spring可能是spring家族也可能是springCore,我们这里就来简述下spring家族以及SpringCore的关系
1.Spring,Spring Boot和Spring MVC的关系以及区别
Spring(这里说的是SpringCore):简单来说,Spring是一个开发应用框架,具有轻量级、⼀ 站式、模块化的特点,其目的是用于简化企业级应用程序开发
SpringCore的主要功能就是管理对象以及他们的依赖关系,面向切面编程还有一些数据库的事务管理,web框架支持等(所以我们之前说Spring的主要核心思想是ioc和aop也是说的SpringCore)
同时SpringCore具备高度可开发性,并不强制依赖Spring框架,开发者可以自由选择Spring的部分或者全部,Spring可以继承其他第三方框架
Spring MVC(又叫Spring Web MVC):这是Spring的一个子框架,在SpringCore诞生后,我们用mvc的思想设计了这个MVC框架,主要用来开发web应用和网络接口,所以Spring Web MVC是一个Web框架
因为我们这个框架是SpringCore的子框架所以天生就与SpringCore这个框架集成,让我们更简单的支持Web开发
Spring Boot: Spring Boot就是对Spring的一个封装,为了简化Spring应用开发出现的,我们使用SpringBoot可以快速的搭建框架,降低开发成本,让开发人员减少精力在配置上和底层实现,版本管理上
我们很多人说Spring Boot是一个脚手架,其实是差不多的,如果更方便我们理解的话我们可以理解为大学生选课系统,你想上这节课就选择这节课,然后选课系统就帮你选择了这节课并给配教室给你以及一些其他处理,你只需要到时候去上课就行,同时上课还是老师给你上并不是选课系统上,她只是一个平台
⽐如想使⽤SpringBoot开发Web项⽬,只需要引⼊Spring MVC框架即可,Web开发的⼯作是 SpringMVC完成的,⽽不是SpringBoot,想完成数据访问,只需要引⼊Mybatis框架即可.】
最后一句话进行总结三者间关系:Spring MVC和Spring Boot都属于Spring,Spring MVC是基于SpringCore的一个MVC框架,而Spring Boot是基于Spring的一套快速开发整合包
我认为这里的难点就是因为很多人都习惯把名称省略,就比如把SpringCore和Spring家族都叫做Spring,然后让我们在不同情况下自行理解,这就会导致很多人懵了,你到底说的是那个Spring,还有Spring MVC全称叫Spring Web MVC如果我们省略为Spring MVC那么就可能会有些人忘记了他是个web框架,如果我们写全,那就很方便我们理解了,所以上面我所有的名称都没有进行省略,方便我们进行理解
(二)Bean的作用域
接下来我们来简单说一下Bean的作用域,之前我们在ioc&di中学习了Spring是如何帮我们管理对象的
我们通过五大注解和一个方法注解来声明对象,通过ApplicationContext或者BeanFactory来获取到对象,通过属性注入,构造方法或者Setter方法来注入Bean对象
这里又设计到几个面试题,我们之前讲过,所以这里就简单过一下
1.ApplicationContext与BeanFactory的关系和区别
2.三种注入方式的优缺点
3.@Autowired和@Resource的区别
4.ioc&di是什么
回到主题,我们之前通过ApplicationContext或者BeanFactory来获取到对象,我们发现我们Spring帮我们管理的对象是使用了单例模式,那这个单例我们就称为Bean的一个作用域
所以我们Bean的作用域就是指Bean在Spring框架中的某种行为模式
就比如我们的单例作用域:表示bean在这个Spring中只有一份,全局共享,无论你通过什么方式拿到这个Bean都是相同的对象
除了单例作用域,我们一共有6种作用域,后四种只有在Spring MVC环境才生效
1.singleton:单例作用域
2.prototype:原型作用域
3.request:请求作用域
4.session:会话作用域
5.Application:全局作用域
6.webSocket:HTTP WebSocket作⽤域
singleton:每个Spring IOC容器内同名称的bean就只能有这一个实例
prototype:每次使用该bean时会创建新的实例
request:每个http请求生命周期内,创建新的实例
session:每个http session生命周期内,创建新的实例
session生命周期的时长无法固定看我们自己的设计,只要是同一个客户端(例如同一台电脑同一台浏览器就是一个session)
application:每个ServletContext生命周期内,创建新的实例,我们可以把我们Spring项目理解为一个ServletContext生命周期,那这时有人问,那singleton呢?其实singleton是applicationcontext内只能有这一个实例。我们的ServletContext是可以有多个applicationcontext的,但是现在来看其实一般也只有一个
websocket:我们之前网络编程中也讲到过webSocket,所以这里不多赘述了,我们只需要知道,这个socket的生命周期是要看我们具体代码的
(三)Bean的生命周期
生命周期就是指从创建到销毁的整个生命过程
bean的生命周期分为以下五个部分
1.实例化(给Bean分配空间)
2.属性赋值(Bean的注入和装配,和一些变量的赋值这里会发生循环依赖,之前的博客有解决办法----通过三级缓存)
3.初始化
1)执行各种通知,我们可以实现以下接口BeanNameAware ,BeanFactoryAware, ApplicationContextAware并重写他们的对应方法就可以
2)执行初始化方法:比如xml定义的方法,或者使用注解@PostConstruct和执行后置方法
4.使用Bean
5.销毁Bean
销毁bean时我们也可以通过注解的方式来执行各种方法
(四)SpringBoot自动配置
SpringBoot自动配置就是Spring容器启动后,一些配置类,bean对象等自动存入到IOC容器中,不需要我们手动去声明,从而简化了开发,省去些繁琐配置操作
我们写代码的时候,发现jar包中的类并没有使用五大注解,那SpringBoot是如何将jar包中的配置类以及bean加载到Spring IOC中的
我们这里主要学习以下两个方面:
1.Spring是如何把对象加载到SpringIOC容器中的
2.SpringBoot是如何实现的
1.Spring加载Bean
我们要知道Spring是通过五大注解和@Bean这个方法注解来帮我们把Bean加载到SpringIOC这个容器中,以上前提就是我们需要这些被注解修饰的类与SpringBoor启动类在同一个目录下(SpringBootApplication标注的类就是SpringBoot项目的启动类)
而我们引入第三方Jar包时,第三方的jar代码是不在启动类目录下的,此时如何让Spring帮我们管理?
1)我们可以使用@ComponentScan组件扫描
通过这个注解我们会将指定路径的类添加到IOC中,这里是一个字符串数组,我们可以同时传多个路径
Spring没有使用这种方式,因为当我们引入大量第三方依赖时,就需要再启动类上配置不同依赖需要扫描的包,这种方式是十分繁琐的
2)使用@Import导入(使用@Import导入的类会被Spring加载到IOC容器中)
@Import导入主要有以下几种形式:
1.导入类
跟@ComponentScan一样,我们同时导入多个类
这种方式也很繁琐,所以SpringBoot没有采用
2.导入ImportSeletor接口实现类
ImportSeletor接口实现类
无非就是把导入的类放到一个统一的类中,然后再导入这个统一的类
但是上述方式有一个很明显的弊端,就是使用者需要知道第三方依赖中有哪些Bean对象或者配置类,如果落下其中的一些Bean就会导致我们项目中出现一些事故
所以我们希望第三方依赖把他需要注入的bean直接给我们提供一个注解,里面封装@Import注解,这样我们只需要导入第三方提供的注解,不需要关注里面需要注入的bean
我们SpringBoot就是采用的这种方式
(五)SpringBoot原理分析
源头就是SpringBoot的启动类
@SpringBootApplication标注的类就是SpringBoot项目的启动类
这个类同时也是SpringBoot实现自动配置的核心
我们来看一下他里面具体有什么
四大元注解
首先就是Target(描述注解的使用范围),Retention(描述注解保留的时间范围),Documented(描述在使用javadoc工具为类生成帮助文档时是否要保留其注解信息),Inherited(使被他修饰的注解具有继承性)
例如,如果我们定义了一个注解@HasInherited,并使用@Inherited对其进行修饰,然后将这个注解应用于一个父类上,那么继承这个父类的子类也会自动拥有@HasInherited注解。相反,如果我们定义了另一个注解@NoInherited而没有使用@Inherited修饰,那么即使我们将@NoInherited应用于父类,子类也不会继承这个注解。
@SpringBootConfiguration
@indexed注解就是加速应用启动的,所以本质上这个注解就是@Configuration这个注解
@ComponentScan(包扫描)
这个注解就是帮我们过滤了一些类和注解,这些类和注解我们是不需要进行扫描的,同时从声明该注解的类的包开始扫描,这也是为什么SpringBoot项目生命的注解类必须要在启动类的目录下
@EnableAutoConfiguration
我们来详细看一下这个注解,因为这个注解是主要实现自动配置的类
这个名字就很能说明问题,自动配置包同时导入了AutoConfigrationImportSelector这个类,所以们先点进这个类来看看
我们刚刚说可以通过实现ImportSelector并实现他的SelectImports方法,我在这个类中也看到了这个方法,我们来看一下
上面那个就直接返回空数组,所以我们直接往下看,我们把
autoConfigurationEntry转成一个字符数组返回,那就说明怎么得到的这个autoConfigurationEntry,就是我们真正的关键方法
我们把remove相关的都去掉,因为这些是删除的,再把filter删去我们不看因为是过滤器再把check删去,因为是进行验证的,同时我们看返回值,看哪一个方法得到的这个返回值,我们发现是
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
所以我们点进这两个方法去看一眼
但是刚刚我们落看了一个,exclusions也有删除的意思,所以这个方法也不看
我们就只剩下唯一一个方法
那这个方法就是获取所有基于 METAINF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ⽂件,META-INF/spring.factories ⽂件中配置类的集合.
在引入的依赖中,通常都包含以上两个文件,这里面包含了很多第三方依赖的配置文件
我们配置文件中使用
@Bean声明了一些对象,spring就会自动调用配置类中的@Bean标识的方法,把对象注册到IOC容器中
在加载⾃动配置类的时候,并不是将所有的配置全部加载进来,⽽是通过@Conditional等注解的判断 进⾏动态加载 @Conditional是spring底层注解,意思就是根据不同的条件,来进⾏⾃⼰不同的条件判断,如果满⾜指定的条件,那么配置类⾥边的配置才会⽣效
我们再来看这个注解,同样是点进这个类看看
总的来说就是获取到我们的包名并且变成一个数组,也就是说这个注解本质上就是把启动类所在的包下面所有组件都扫描到spring容器中
总结: