什么是Spring IoC

IoC翻译过来是控制反转。控制指的是对象创建「实例化、管理」的权利;反转指的是控制权交给外部环境「Spring框架、IoC容器」。IoC的思想就是将原本在程序中手动创建对象的控制权交由Spring框架处理。

在实际项目中一个 Service 类可能依赖了很多其他的类,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。

在Spring中,IoC容器是Spring用来实现IoC的载体,IoC容器实际上就是个Map,Map中存放的是各种对象。

什么是Spring Bean

简单来说,Bean代指的就是那些被 IoC 容器所管理的对象。我们需要告诉IoC容器帮助我们管理哪些对象,这个是通过配置元数据来定义的。配置元数据可以是XML文件、注解或者Java配置类。

将一个类声明为Bean的注解

  • @Component :通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用 @Component 注解标注。
  • @Repository : 对应持久层即 Dao 层,主要用于数据库相关操作。
  • @Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。
  • @Controller : 对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面

@Autowired注入Bean

Autowired 属于 Spring 内置的注解,默认的注入方式为 byType(根据类型进行匹配),也就是说会优先根据接口类型去匹配并注入 Bean (接口的实现类)。

这会有什么问题呢? 当一个接口存在多个实现类的话,byType 这种方式就无法正确注入对象了,因为这个时候 Spring 会同时找到多个满足条件的选择,默认情况下它自己不知道选择哪一个。这种情况下,注入方式会变为 byName(根据名称进行匹配),这个名称通常就是类名(首字母小写)。

举个例子,SmsService接口有两个实现类。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 报错,byName 和 byType 都无法匹配到 bean
@Autowired
private SmsService smsService;

// 正确注入 SmsServiceImpl1 对象对应的 bean
@Autowired
private SmsService smsServiceImpl1;

// 正确注入 SmsServiceImpl1 对象对应的 bean
// smsServiceImpl1 就是我们上面所说的名称
@Autowired
@Qualifier(value = "smsServiceImpl1")
private SmsService smsService;

建议通过@Qualifier注解来显式执行名称。

@Resource注入Bean

Resource是JDK提供的注解,默认注入方式为byName

1
2
@Resource(name = "smsServiceImpl1")
private SmsService smsService;

Bean的作用域有哪些

Spring 中 Bean 的作用域通常有下面几种:

  • singleton : IoC 容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。
  • prototype : 每次获取都会创建一个新的 bean 实例。也就是说,连续 getBean() 两次,得到的是不同的 Bean 实例。
  • request (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。
  • session (仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。
  • application/global-session (仅 Web 应用可用): 每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。
  • websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。

什么是AOP

AOP (Aspect-Oriented Programming: 面向切面编程) 能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。

什么是MVC

MVC 是模型 (Model)、视图 (View)、控制器 (Controller) 的简写,其核心思想是通过将业务逻辑、数据、显示分离来组织代码。

Spring MVC 下我们一般把后端项目分为 Service 层(处理业务)、Dao 层(数据库操作)、Pojo 层(实体类)、Controller 层 (控制层,返回数据给前台页面)

Spring MVC的核心组件

  • DispatcherServlet核心的中央处理器,负责接收请求、分发,并给予客户端响应。
  • HandlerMapping处理器映射器,根据 uri 去匹配查找能处理的 Handler ,并会将请求涉及到的拦截器和 Handler 一起封装。
  • HandlerAdapter处理器适配器,根据 HandlerMapping 找到的 Handler ,适配执行对应的 Handler
  • Handler请求处理器,处理实际请求的处理器。
  • ViewResolver视图解析器,根据 Handler 返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给 DispatcherServlet 响应客户端

流程处理

de6d2b213f112297298f3e223bf08f28

  1. 客户端(浏览器)发送请求, DispatcherServlet 拦截请求。
  2. DispatcherServlet 根据请求信息调用 HandlerMappingHandlerMapping 根据 uri 去匹配查找能处理的 Handler(也就是我们平常说的 Controller 控制器) ,并会将请求涉及到的拦截器和 Handler 一起封装。
  3. DispatcherServlet 调用 HandlerAdapter 适配执行 Handler
  4. Handler 完成对用户请求的处理后,会返回一个 ModelAndView 对象给 DispatcherServletModelAndView 顾名思义,包含了数据模型以及相应的视图的信息。Model 是返回的数据对象,View 是个逻辑上的 View
  5. ViewResolver 会根据逻辑 View 查找实际的 View
  6. DispaterServlet 把返回的 Model 传给 View(视图渲染)。
  7. View 返回给请求者(浏览器)

Spring中用到了哪些设计模式

  • 工厂设计模式 : Spring 使用工厂模式通过 BeanFactoryApplicationContext 创建 bean 对象。
  • 代理设计模式 : Spring AOP 功能的实现。
  • 单例设计模式 : Spring 中的 Bean 默认都是单例的。
  • 模板方法模式 : Spring 中 jdbcTemplatehibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
  • 包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
  • 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
  • 适配器模式 : Spring AOP 的增强或通知 (Advice) 使用到了适配器模式、spring MVC 中也是用到了适配器模式适配 Controller

Spring事务中的事务传播行为

事务传播行为是为了解决业务层方法之间互相调用的事务问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Service
Class A {
@Autowired
B b;
@Transactional(propagation = Propagation.REQUIRED)
public void aMethod {
//do something
b.bMethod();
}
}
@Service
Class B {
@Transactional(propagation = Propagation.REQUIRED)
public void bMethod {
//do something
}
}

当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。

  • TransactionDefinition.PROPAGATION_REQUIRED:使用的最多的一个事务传播行为,我们平时经常使用的 @Transactional 注解默认使用就是这个事务传播行为。如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  • TransactionDefinitioin.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • _NESTED:如果当前没有事务,则创建一个新的事务;否则创建一个事务作为当前事务的嵌套事务来运行
  • _MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常

Spring事务的隔离级别

一共有五种隔离级别

  1. ISOLATION_DEFAULT:使用后端数据库默认的隔离级别。MySQL是可重复读,Oracle是读取已提交
  2. _READ_UNCOMMITED:读取未提交,可能导致脏读、幻读、不可重复读
  3. _READ_COMMITED:读取已提交,可能导致幻读和不可重复读
  4. _REPEATABLE_READ:可能导致幻读
  5. _SERIALIZABLE:完全服从ACID的隔离级别

Transactional注解的rollbackFor

@Transactional 注解中如果不配置 rollbackFor 属性,那么事务只会在遇到 RuntimeException 的时候才会回滚,加上 rollbackFor=Exception.class, 可以让事务在遇到非运行时异常时也回滚。

Spring Security有哪些控制请求访问权限的方法

  • permitAll() :无条件允许任何形式访问,不管你登录还是没有登录。
  • anonymous() :允许匿名访问,也就是没有登录才可以访问。
  • denyAll() :无条件决绝任何形式的访问。
  • authenticated():只允许已认证的用户访问。
  • fullyAuthenticated() :只允许已经登录或者通过 remember-me 登录的用户访问。
  • hasRole(String) : 只允许指定的角色访问。
  • hasAnyRole(String) : 指定一个或者多个角色,满足其一的用户即可访问。
  • hasAuthority(String) :只允许具有指定权限的用户访问
  • hasAnyAuthority(String) :指定一个或者多个权限,满足其一的用户即可访问。
  • hasIpAddress(String) : 只允许指定 ip 的用户访问

如何对密码加密

这些加密算法实现类的父类是PasswordEncoder,如果想要自己实现一个加密算法的话,也需要继承PasswordEncoder。这个接口一共有3个必须实现的方法。

1
2
3
4
5
6
7
8
9
10
public interface PasswordEncoder {
// 加密也就是对原始密码进行编码
String encode(CharSequence var1);
// 比对原始密码和数据库中保存的密码
boolean matches(CharSequence var1, String var2);
// 判断加密密码是否需要再次进行加密,默认返回 false
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}

官方推荐使用基于 bcrypt 强哈希函数的加密算法实现类。

介绍一下SpringBootApplication注解/SpringBoot的自动配置是如何实现的

SpringBootApplication可以看作是@Configuration@EnableAutoConfiguration@ComponentScan注解的集合。

  • 第一个是允许在上下文中注册额外的bean或导入其他配置类
  • 第二个是启动SpringBoot的自动配置机制
  • 第三个是扫描被@Component(@Service,@Controller)注解的bean,注解默认会扫描该类所在的包下所有的类

第二个注解是启动自动配置的关键。

  1. 引入Starter组件
  2. SpringBoot基于约定去Starter组件的路径下去找配置类
  3. SpringBoot使用ImportSelector去导入这些配置类,并根据@Conditional动态加载配置类里面的Bean到容器

常用注解

Spring Bean相关:

  • @Autowired:自动导入对象到类中,被注入进的类同样要被Spring容器管理
  • @RestController:该注解是@Controller和@ResponseBody的合集,表示这是个控制器bean。并且是将函数的返回值直接填入HTTP响应体中,是REST风格的控制器
  • @Component:通用的注解。如果一个Bean不知道属于哪个层,可以使用@Component注解
  • @Repository:对应Dao层,主要用于数据库相关操作
  • @Service:对应服务层,涉及业务逻辑
  • @Controller:接收用户请求并调用Service层返回数据给前段页面

HTTP请求相关:

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping

前后端传值:

  • @RequestParam:获取查询参数
  • @Pathvariable:获取路径参数
  • @RequestBody:用于读取Request的body 部分,并且Content-Type为applicatioin/json格式的数据,接收到数据之后会自动将数据绑定到Java对象上去。

参考文章:JavaGuide