Spring
# Spring IOC
# IoC(Inversion of Control:控制反转)
IoC是一种设计思想,而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。不过, IoC 并非 Spring 特有,在其他语言中也有应用。
在实际项目中,如果我们需要实例化一个Service,我们不可能每次都要去看构造函数,如果利用Ioc的话,在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。
在 Spring 中, IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个 Map(key,value),Map 中存放的是各种对象。
Spring 时代我们一般通过 XML 文件来配置 Bean,后来开发人员觉得 XML 文件来配置不太好,于是 SpringBoot 注解配置就慢慢开始流行起来。
# spring bean
Bean 代指的就是那些被 IoC 容器所管理的对象。
我们需要告诉 IoC 容器帮助我们管理哪些对象,这个是通过配置元数据来定义的。配置元数据可以是 XML 文件、注解或者 Java 配置类。
<!-- Constructor-arg with 'value' attribute -->
<bean id="..." class="...">
<constructor-arg value="..."/>
</bean>
# bean的注解
@Component
:通用的注解,可标注任意类为Spring
组件。如果一个 Bean 不知道属于哪个层,可以使用@Component
注解标注。@Repository
: 对应持久层即 Dao 层,主要用于数据库相关操作。@Service
: 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。@Controller
: 对应 Spring MVC 控制层,主要用户接受用户请求并调用 Service 层返回数据给前端页面。
# Spring AOP
# 定义
AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理,如下图所示:
# AspectJ 定义的通知类型有哪些?
Before(前置通知):目标对象的方法调用之前触发
After (后置通知):目标对象的方法调用之后触发
AfterReturning(返回通知):目标对象的方法调用完成,在返回结果值之后触发
AfterThrowing(异常通知) :目标对象的方法运行中抛出 / 触发异常后触发。AfterReturning 和 AfterThrowing 两者互斥。如果方法调用成功无异常,则会有返回值;如果方法抛出了异常,则不会有返回值。
Around (环绕通知):编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事,甚至不调用目标对象的方法。
# 多个切面的执行顺序如何控制?
1、通常使用@Order
注解直接定义切面顺序
// 值越小优先级越高
@Order(3)
@Component
@Aspect
public class LoggingAspect implements Ordered {
2、实现Ordered
接口重写 getOrder
方法。
@Component
@Aspect
public class LoggingAspect implements Ordered {
// ....
@Override
public int getOrder() {
// 返回值越小优先级越高
return 1;
}
}
# Spring MVC
# 定义
MVC 是模型(Model)、视图(View)、控制器(Controller)的简写,其核心思想是通过将业务逻辑、数据、显示分离来组织代码。
Spring MVC 下我们一般把后端项目分为 Service 层(处理业务)、Dao 层(数据库操作)、Entity 层(实体类)、Controller 层(控制层,返回数据给前台页面)。
# Spring MVC工作原理
1、客户端(浏览器)发送请求, DispatcherServlet
拦截请求。
2、DispatcherServlet
根据请求信息调用 HandlerMapping
。HandlerMapping
根据 uri 去匹配查找能处理的 Handler
(也就是我们平常说的 Controller
控制器) ,并会将请求涉及到的拦截器和 Handler
一起封装。
3、DispatcherServlet
调用 HandlerAdapter
适配执行 Handler
。
4、Handler
完成对用户请求的处理后,会返回一个 ModelAndView
对象给DispatcherServlet
,ModelAndView
顾名思义,包含了数据模型以及相应的视图的信息。Model
是返回的数据对象,View
是个逻辑上的 View
。
5、ViewResolver
会根据逻辑 View
查找实际的 View
。
6、DispaterServlet
把返回的 Model
传给 View
(视图渲染)。
7、把 View
返回给请求者(浏览器)
# 统一异常处理
推荐使用注解的方式统一异常处理,具体会使用到 @ControllerAdvice
+ @ExceptionHandler
这两个注解 。
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
@ExceptionHandler(BaseException.class)
public ResponseEntity<?> handleAppException(BaseException ex, HttpServletRequest request) {
//......
}
@ExceptionHandler(value = ResourceNotFoundException.class)
public ResponseEntity<ErrorReponse> handleResourceNotFoundException(ResourceNotFoundException ex, HttpServletRequest request) {
//......
}
}
这种异常处理方式下,会给所有或者指定的 Controller
织入异常处理的逻辑(AOP),当 Controller
中的方法抛出异常的时候,由被@ExceptionHandler
注解修饰的方法进行处理。
ExceptionHandlerMethodResolver
中 getMappedMethod
方法决定了异常具体被哪个被 @ExceptionHandler
注解修饰的方法处理异常。
@Nullable
private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
List<Class<? extends Throwable>> matches = new ArrayList<>();
//找到可以处理的所有异常信息。mappedMethods 中存放了异常和处理异常的方法的对应关系
for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
if (mappedException.isAssignableFrom(exceptionType)) {
matches.add(mappedException);
}
}
// 不为空说明有方法处理异常
if (!matches.isEmpty()) {
// 按照匹配程度从小到大排序
matches.sort(new ExceptionDepthComparator(exceptionType));
// 返回处理异常的方法
return this.mappedMethods.get(matches.get(0));
}
else {
return null;
}
}
# Spring框架中的设计模型
工厂设计模式 : Spring 使用工厂模式通过 BeanFactory
、ApplicationContext
创建 bean 对象。
代理设计模式 : Spring AOP 功能的实现。
单例设计模式 : Spring 中的 Bean 默认都是单例的。
模板方法模式 : Spring 中 jdbcTemplate
、hibernateTemplate
等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
适配器模式 : Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller
。
# SpringBoot常用的注解
1、SpringBootApplication:启动的时候
2、componentScan:扫描
3、RestController:复合注解:controller + requestbody
4、Transactional:事务注解
5、ControllerAdvice:统一处理异常
# Spring中事务失效的场景
1、非public权限修饰方法
2、内部捕获了异常
3、没有抛出异常
4、非RuntimeException抛出
# Spring是否能够继承
概念
- Java 是类层面的继承,子类可以继承父类的结构信息。
- Spring 是对象层面的继承,子类继承父类对象 的属性值。因此,Spring 中,不同类之间可以互相继承。
如何实现继承
直接在 bean 标签中添加 parent属性,属性值为 父类 bean 的 id 即可。例子如下:
<bean id="sonStudent" class="com.theSunAndSnow.entity.Student" parent="student1"></bean>
也可以覆盖父类的属性值,只需在 bean 标签中添加 property 标签即可,无特别语法
<bean id="sonStudent" class="com.theSunAndSnow.entity.Student" parent="student1">
<property name="name" value="Jay"></property>
</bean>
# Resultful
概述
符合特定的指南,是 Web API 实现的约束。鼓励客户端和服务器以无状态模式交换信息。 请记住,并非所有 API 都是 REST,但所有 RESTful 服务都是 API。
RESTful 就是用 URL 定位资源,用 HTTP 动词描述操作。
优点:
- 简洁
- 统一
- 开放
缺点:
1、多端 (多次数据交互)
2、过度获取/获取后的数据
3、API版本控制
4、版本
5、动词不好描述
# SpringBoot 的 Controller 层常用注解
1、@Controller
2、@ResponseBody
3、@RestController
4、@RequestMapping
5、@GetMapping和@PostMapping
6、@PathVariable(url占位符)
7、@RequestParam
8、@RequestBody
9、@ControllerAdvice(全局异常处理)
# Spring循环依赖问题
# 循环依赖的三种情况
# 三级缓存的作用
- 一级缓存:为“Spring 的单例属性”而生,就是个单例池,用来存放已经初始化完成的单例 Bean;
- 二级缓存:为“解决 AOP”而生,存放的是半成品的 AOP 的单例 Bean;
- 三级缓存:为“打破循环”而生,存放的是生成半成品单例 Bean 的工厂方法。
# SpringBean的生命周期
# Spring执行逻辑
- 先从“第一级缓存”找对象,有就返回,没有就找“二级缓存”;
- 找“二级缓存”,有就返回,没有就找“三级缓存”;
- 找“三级缓存”,找到了,就获取对象,放到“二级缓存”,从“三级缓存”移除。
# 能否干掉二级缓存
“二级缓存”的目的是为了避免因为 AOP 创建多个对象,其中存储的是半成品的 AOP 的单例 bean。
如果没有 AOP 的话,我们其实只要 1、3 级缓存,就可以满足要求。