---
# System prepended metadata

title: Spring

---

# Spring

## 什么是Spring

Spring是一款开源的轻量级Java开发框架，旨在提高开发人员的开发效率以及系统的可维护性

一般说的Spring框架指的是SpringFrameword，是很多模块的集合，使用这些模块可以很方便的协助我们进行开发，核心思想就是不重新造轮子，开箱即用

## 重要的Spring模块

![Spring主要模块](https://images.xiaozhuanlan.com/photo/2019/e0c60b4606711fc4a0b6faf03230247a.png)

上图对应4.x的版本，5.x中web模块Portlet已废弃，同步增加了用于异步响应式的WebFlux组件

- Spring Data Access/Integration：其中五个模块组成
  - spring-jdbc：提供了对数据库访问的抽象JDBC，不同的数据库都有字节独立的API用于操作本数据库，而Java程序只需要和JDBC API交互，就完成了对数据库的操作，屏蔽了不同数据库的影响
  - spring-transactions（又名spring-tx）：提供对事务的支持
  - spring-orm：提供对ORM框架的支持：在关系型数据库和对象之间 做一个映射
  - spring-oxm：提供对OXM框架的支持：将java对象和XML数据之间转换
  - spring-jms：Java消息服务
- Spring Web：由四模块组成
  - spring-web：对web功能的实现提供一些最基础的支持
  - spring-webmvc：提供对Spring MVC的实现
  - spring-websocket：提供了对webSocket的支持，可以让客户端与服务端双向通信
  - spring-webflux：提供对WebFlux的支持，与SpringMVC不同，它不需要Servlet API，是完全异步的
- Spring Core：核心模块，所有的功能基本都依赖于该类库，主要提供IoC依赖注入功能的支持
- Spring Aspects：为AspectJ的集成提供支持
- Spring AOP：提供了面向切面的编程实现
- Spring Test：对常用测试框架的支持

## Spring IOC & DI

### IOC

IoC，即 “控制反转”，是一种思想，是面向对象编程中的一种设计原则，可以用来减低计算机代码之间的耦合度。在开发中， IoC 意味着你设计好的对象交给容器控制，而不是使用传统的方式，在对象内部直接控制。

- 控制：指的是**对象创建（实例化、管理）的权利**，在传统设计中，通常直接在对象内部通过new的方式创建对象，是主动创建依赖对象；而io是指使用一个专门的容器来创建这些对象，即由IoC容器控制对象创建。即ioc容器控制了对象，控制了外部资源的获取

- 反转：指的是**控制权交给外部环境（Spring框架，Ioc容器）**，正转是由我们自己在程序中主动控制去获取依赖对象；反转是由容器来帮忙创建及注入依赖对象。因为由容器帮我们查找及注入依赖对象，对象只是被动的接受依赖，所以反转。依赖对象的获取被反转了。

传统应用程序都是由我们在类内部主动创建依赖对象，从而导致类与类之间高耦合，难于测试； **有了 IoC 容器后，把创建和查找依赖对象的控制权交给了容器，由容器注入组合对象，所以对象之间是松散耦合。** 这样也便于测试，利于功能复用，更重要的是使得程序的整个体系结构变得非常灵活。被动的等待 IoC 容器来创建并注入它所需的资源了。

在Spring中，IoC容器是Spring用来实现IoC的载体，实际上是个Map（key，value），Map中存放各种对象

### DI

即依赖注入，是组件之间依赖关系由容器在运行期决定，形象来说，即由容器动态的将某个依赖关系注入组件之中。依赖注入的目的并非为软件系统带来更多功能，而是为了提升组件重用的频率，并为系统搭建一个灵活，可扩展的平台。

DI的关键是："谁依赖谁，为什么需要依赖；谁注入谁，注入的什么"

- 谁依赖谁：应用程序依赖于IOC容器
- 为什么依赖：应用程序需要IOC容器来提供对象需要的外部资源；
- 谁注入谁：IOC容器注入应用程序某个对象，应用程序依赖的对象
- 注入了什么：就是注入某个对象所需要的外部资源（包括对象，资源，常量数据）

## Spring AOP

AOP（Aspect Oriented Programming），即面向切面编程，可以说是OOP（Object Oriented Programming，面向对象编程）的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构，用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系，但并不适合定义横向的关系，例如**日志功能**。日志代码往往横向地散布在所有对象层次中，而与它对应的对象的核心功能毫无关系对于其他类型的代码，如安全性、异常处理和透明的持续性也都是如此，这种散布在各处的无关的代码被称为**横切**（cross cutting），在OOP设计中，它导致了大量代码的重复，而不利于各个模块的重用。

- 切：横切逻辑，原有业务逻辑代码不动，只能操作横切逻辑的代码
- 面：横切逻辑往往需要影响多个方法，每个方法如果一个点，很多个方法点构成了面

使用"横切"技术，AOP把软件系统分为两个部分：**核心关注点**和**横切关注点**。业务处理的主要流程是核心关注点，与之关系不大的部分是横切关注点。横切关注点的一个特点是，他们经常发生在核心关注点的多处，而各处基本相似，比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点，将核心关注点和横切关注点分离开来。

实现 AOP 的技术的实现主要采用**动态代理技术**，利用截取消息的方式，在不改变原有业务逻辑的情况下，增强横切逻辑代码，根本上解耦合，避免横切逻辑代码重复。

## Spring Bean

### 什么是bean

bean代指**被IoC容器所管理的对象**，我们需要通过配置元数据来告诉容器帮助我们管理哪些对象，这个元数据可以是xml文件、注解或者java配置类

### bean的作用域有哪些

- singleton：唯一bean实例，Spring中的bean默认都是单例的，对单例设计模式的应用
- prototype：每次请求都会创建一个新的bean实例
- request：每一次http请求都会产生一个新的bean，该bean仅在当前http request内有效
- session：每一次来自新session的http请求都会产生一个新的bean，该bean仅在当前http session内有效
- global-session：全局session作用域，仅仅在基于portlet的web应用中才有意义，spring5取消。portlet是能够生成语义代码片段的小型web插件，基于portlet容器，可以像servlet一样处理http请求，但是每个portlet都有不同的会话

### 如果配置bean作用域

~~~java
//xml方式
<bean id="..." class="..." scope="singleton"></bean>
//注解方式
@Bean
@Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person personPrototype(){
    return new Person;
}
~~~

### 单例bean的线程安全问题

当多线程操作同一个对象的时候存在资源竞争，不过大部分bean实际都是无实例变量的如Dao，Service，常见解决办法有两种

1. 在bean中尽量避免定义可变的成员变量
2. 在类中定义一个ThreadLocal成员变量，将需要的可变遍历保存在ThreadLocal中

### 将一个类声明为bean的注解有哪些

我们一般使用`@Autowired`注解自动装配的bean，要想把类标识成可用于`@Autowired`注解自动装配的bean类，可以用以下注解实现

- `@component`：通用的注解，可标注任意类为Spring组件。
- `@Repository`：对应持久层即Dao层，主要用于数据库相关操作
- `@Service`：对应服务层，主要涉及一些复杂的逻辑，需要用到Dao层
- `@Controller`：对应SpringMvc控制层，接受用户请求并调用Service层返回数据给前端页面

### `@Component`和``@Bean`的区别是什么

- `@Component`注解作用于类，而`@bean`作用于方法
- `@Component`通常是通过类路径扫描来自动装配到容器中，`@Bean`通常是我们在标有该注解的方法中定义产生这个bean，`@bean`告诉Spring这是某个类的实例，当我需要的时候取拿取
- `@bean`的自定义性更强，很多地方只能通过`@bean`注解来注册bean，比如引用第三方库中的类需要装配时，只能通过`@bean`实现

### bean的生命周期

- Bean容器找到配置文件中的SpringBean的定义，利用JavaReflection Api 创建一个Bean的实例
- 如果有一些属性值的话，调用set（）方法设置一些属性值
- 如果实现了其他*.Aware接口，就调用对应的方法，（让bean获取自己在容器中的名字等）
- 如果实现了一些接口或加载了一些对象，执行相关的方法
- 当要销毁Bean时，如果实现了destroy方法，或配置文件中定义了destroy-method属性，执行指定的方法

![e8a473640bcd06e544c86fa729133ac3.png](https://img-blog.csdnimg.cn/img_convert/e8a473640bcd06e544c86fa729133ac3.png)

## Spring实例化对象的方式

- 无参构造方法实例化
  - 根据id获取配置文件中的bean对象，必须存在空的构造方法
  - 当各个bean的业务逻辑相互比较独立时，或者与外界关联较少时使用
- 工厂静态方法实例化
  - 通过反射调用工厂类的静态工厂方法，将其返回值作为Bean实例，在工厂中实例化其中bean对象
  - 可以统一管理各个bean的创建，各个bean在创建之前需要统一的初始化处理时使用
- 实例化工厂实现
  - 实例化工厂后实例化工厂中指定方法
  - 工厂方法也可以作为bean的控制，集成其他框架bean时的管理方法，能够使bean和工厂的角色互换

一般用第一种

## Spring MVC

### MVC是什么

mvc是模型（model），视图（view），控制器（controller）的简写，核心思想是通过将业务逻辑，数据，显示分离来组织代码![img](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/image-20210809181452421.png)

springMVC框架将后台分为Service层（处理业务）、Dao（数据库操作）、Entity（实体类）、Controller（控制层，返回数据给前台页面）

### SpringMVC工作原理

![img](https://img-blog.csdnimg.cn/img_convert/de6d2b213f112297298f3e223bf08f28.png)

流程说明：

1. 客户端发送请求，请求到DispatcherServlet（前端控制器）
2. 前端控制器根据请求信息调用HandLerMapping（映射处理器），解析请求对应的Handler（处理器）
3. 解析到对应的Handler后，开始由HandlerAdapter适配器处理请求
4. 这一步会根据第三步找到的Handler来调用真正的处理器开始处理请求，并处理相应的业务逻辑。这层是Controller控制器
5. 处理器处理完后，会返回一个ModelAndView对象，Model是返回的数据对象，view是逻辑上的view
6. ViewResolver会根据逻辑View找到实际的view
7. 前端控制器把返回的Model传给View（视图渲染）
8. 把View返回给请求者

## Spring框架中的设计模式

- 工厂模式：通过BeanFactory、ApplicationContext创建bean对象
- 代理模式：AOP功能的实现
- 单例模式：Bean默认是单例
- 模板方法模式：JdbcTemplate、hibernateTemplate等以Template结尾的数据库操作的类，就用到了模板模式
- 包装器设计模式：不同客户在访问中根据需要会去访问不同的数据库，可以根据需求动态切换不同的数据源
- 观察者模式：事件驱动模式就是经典的观察者模式的应用
- 适配器模式：AOP的增强或通知（advice）用到了适配器模式，SpringMVC也是用到了适配器模式适配Controller

## Spring常用注解

### 核心

- @SpringBootApplication：创建SpringBoot项目后会默认加在主类上，是项目的基石，可以看作下面三个注解的集合
- @EnableAutoConfiguration：启动SpringBoot的自动配置机制
- @ComponentScan：扫描被@Component（@Service，@Controller）注解的bean，会默认扫描该类所在包下所有类
- @Configuration：允许在Spring上下文中注册额外的bean或导入其他配置类

### Spring Bean相关

- @Autowired：自动导入对象到类中，被注入的类同样要被Spring容器管理，
- @Component：通用的注解，可标注任意类为Spring组件。
- @Repository：对应持久层即Dao层，主要用于数据库相关操作
- @Service：对应服务层，主要涉及一些复杂的逻辑，需要用到Dao层
- @Controller：对应SpringMVC控制层，主要用于接受用户请求并调用Service层返回数据给前端页面
- **@RestController**：将函数的返回值直接填入http响应体，返回Json或xml形式的数据，是Rest风格的控制器
- @Scope：声明SpringBoot作用域，参数可以有以下四种
  1. singleton：唯一bean实例，Spring中的bean默认是单例的
  2. prototype：每次请求都会创建一个新的bean实例
  3. request：每次http请求都会产生新的bean，只在当前http请求内有效
  4. session：每次http请求都会产生新的bean，只在当前http session内有效

### 处理HTTP相关



## Spring事务



## MyBatis 中使用 # {}和 $ {}书写占位符有什么区别 ？

①#将传入的数据都当成一个字符串，会对传入的数据自动加上引号
② $将传入的数据直接显示生成在 SQL 中。
注意：使用 $ 占位符可能会导致 SQL 注射攻击，能用#的地方就不要使用 $ ，写 order by 子句的时候应该用$而不是#。

## Mybatis的好处 ？

①把Sql语句从Java中独立出来。
②封装了底层的JDBC，API的调用，并且能够将结果集自动转换成JavaBean对象，简化了Java数据库编程的重复工作。
③自己编写Sql语句，更加的灵活。
④入参无需用对象封装（或者map封装）,使用@Param注解。

## SpringBoot

### 什么是 Spring Boot？

Spring Boot 是 Spring 开源组织下的子项目，是 Spring 组件一站式解决方案，主要是简化了使用 Spring 的难度，简省了繁重的配置，提供了各种启动器，开发者能快速上手。

### Spring Boot 有哪些优点？

Spring Boot 主要有如下优点：

容易上手，提升开发效率，为 Spring 开发提供一个更快、更广泛的入门体验。
开箱即用，远离繁琐的配置。
提供了一系列大型项目通用的非业务性功能，例如：内嵌服务器、安全管理、运行数据监控、运行状况检查和外部化配置等。
没有代码生成，也不需要XML配置。
避免大量的 Maven 导入和各种版本冲突。

### 自动装配是怎么实现的

通过注解来实现，配置了`@EnableAutoConfiguration`后，内部通过加载jar包下`META-INF/spring.factories`文件的信息，将其文件包装成Properties对象，筛选对象key值为EnableAutoConfiguration的数据，用improt将.class文件加载到ioc容器中，即实现自动装配