
springboot复习
spring复习
@AutoConfiguration
扫描SPI,读取配置文件,扫描@Conditional来按需加载所有的Bean↓
解析并创建对应的BeanDefinintion,用来记录Bean的元数据(有没有代理,是否单例等等,类似于Class类对象记录类元数据)
依次进行doCreateBean(递归执行,并非异步)
实例化(也就是根据BeanDefinintion创建对象,但是还没有填充属性【空壳】)
初始化
- (填充属性(@Autowired等,依赖注入)
- 依次走三级缓存,有依赖直接从缓存填充,没有依赖则创建对应依赖)
- 执行Aware接口【让 Bean 能拿到 Spring 容器内部的对象】
- BeanPostProcessor前置【暴露给外部的hooks】
- 执行@PostConstruct的方法【每个bean独立执行一次】
- BeanPostProcessor后置【暴露给外部的hooks】
- 创建AOP代理对象
三级缓存
- 一级存完整实例(最终的getBean)【singletonObjects】
- 二级存不完全代理对象【为了解决aop情况下的循环依赖,因为aop需要延迟在初始化后做增强,目的是避免生命周期混乱】(若不存在aop,1级和3级完全能够解决循环依赖问题)【earlySingletonObjects】
- 三级存BeanFactory,lambda生成bean实例(调用一次就会生成一次实例)【singletonFactories】
AOP【基于原始对象做增强】:
- 实现了接口:JDK动态代理
- 没实现接口:CGLIB子类代理
-
扫描@Aspect切面
-
创建Advisor
-
在postProcessAfterInitialization判断bean是否需要增强,增强则返回代理对象【代理对象持有原始对象】,不增强则返回原始对象
-
相关注解:
@Before:前置通知@AfterReturning:正常返回后置@AfterThrowing:异常后置@After:最终后置(finally)@Around:环绕通知(最强,能控制目标方法执不执行)
-
动态代理
在运行时,生成全新的类,替原始对象干活,前后加逻辑
- jdk代理,必须要有接口,实现类来实现,代理类和原对象平级
- cglib代理,不需要接口,代理类是原类的子类,通过继承重写方法实现增强
代理对象中持有原始对象,还有拦截器链,在拦截器链中按顺序执行增强。
this导致代理失效,this指向是原始对象,故失效
事务相关
@Transactional本质就是代理包裹Connection(数据库连接)
本质上在你原对象的基础上写一个try catch,设置autocommit=false,手动处理提交/回滚
默认捕获RuntimeException
因为它也是代理,所以想它能够正常使用:
- 方法必须是public
- 必须走代理(不能this.)
- 类必须被Spring管理
传播机制
-
required(默认),有事务加入,无则创建,同一个事务,同生共死
-
required_news,每次创建新事务,内层事务不影响外层(无论内层提交或回滚都不影响外层)各玩各的
-
nested,嵌套事务,内层可以回滚到checkpoint,外层会连带整个内层回滚,(子流程失败不影响主,但是主失败了子也得回退,例如记录日志,日志失败不影响业务),父死子葬
-
supports,有就用,没有就不用
-
not_supports,强制不用事务
-
never,禁止事务,有事务则抛出异常
-
mandatory,必须有事务,没有则抛异常
失效场景
- 使用this,没走代理
- 非public
- 异常被catch
- 类没被spring管理
- 多例/手动new对象
- 错误的传播机制
- 数据库不支持事务
其他使用
@Transactional(readOnly=true),只读事务,出现insert/update等回滚
@Transactional(timeout=x),超时x秒后回滚
IOC:控制反转,统一bean管理,声明自动注入依赖,不用手动new
注入方式:
- 构造器注入
- set注入
- @Autowired,@Resource,@Inject
MVC
servlet是一套基于java web开发的规范(接口)
servlet出现之前,java做web开发只能手写socket,出现之后,tomcat,jetty都是对servlet的实现
mvc是对sevlet规范化的上层封装,框架化封装,(中央分发+路由框架)
大体流程
启动时
-
在xml时代,mvc是需要在tomcat配置DispatcherServlet的(本质就是个servlet)
-
在tomcat启动时,加载DispatcherServlet
-
spring容器初始化,加载controller,service等bean
-
扫描所有@RequestMapping路径
-
路径 -> 对应方法建立到映射表HandlerMapping中
-
初始化视图解析器(用于解析视图,不过目前大部分都是restful,估计也很少用模板了)
请求进入
- 发起请求,请求到达tomcat,tomcat接收请求并封装成HttpServletRequest,HttpServletResponse,并交给匹配的sevlet->DispatcherServlet
- DispatcherServlet拿到请求,根据handlerMapping找对应的映射,包含Controller方法,和一堆拦截器
- 执行(请求拦截器),若false了直接中断请求
- 拦截器执行结束,找HandlerAdapter调用对应的Controller方法
- HandlerAdapter解析参数,自动转换参数类型,封装成java对象(Bo)
- 执行Controller方法,处理返回值(返回值一般是模板路径)
- ViewResolver 视图解析器 拼接前后缀,找到对应的模板,并渲染
- 执行postHandle拦截器(响应拦截器)
- DispatcherServlet 返回响应
如果是RestController(也就是目前主流的方式,目前都是前后端分离,后端返回json数据)
在执行完controller方法后,会直接把返回值通过HttpMessageConverter序列化成json,直接写到响应流(HttpServletResponese)
补充:如果在方法中直接用响应流返回了,(例如导出)走响应拦截器后直接把流返回给前端了
MVC补充
filter和拦截器的区别
-
过滤器是servlet规范,在DispatchServlet之前执行
- 能拿到req,res,拿不到controller
- 适合编码解码,跨域限流,通用请求包裹
-
拦截器是MVC组件,在DispatchServlet后,Controller前后
- 能拿到handler(controller方法)
- 适合登录校验,权限,日志,参数记录
请求参数如何绑定
HandlerMethodArgumentResolver参数解析器
Spring 内置一堆解析器:
- RequestParamMethodArgumentResolver
- PathVariableMethodArgumentResolver
- RequestResponseBodyMethodProcessor(@RequestBody)
- ServletRequestMethodArgumentResolver(HttpServletRequest)
返回值怎么绑定
HandlerMethodReturnValueHandler
- 返回 String → 视图解析器
- 返回 Object + @ResponseBody → MessageConverter → JSON
- 返回 ModelAndView → 数据 + 视图
- 返回 void → 认为你自己处理了 response
全局异常
@RestControllerAdvice
- 抛出异常被ExceptionHandler捕获,直接返回json
- 跳过postHandle,会走afterCompletion
静态资源为什么不进controller
ResourceHttpRequestHandler处理静态资源,可在mvcconfig配置放行
MultipartFile 文件上传原理
- 前端form表单指定enctype="multipart/form-data"
- spring用StandardServletMultipartResolver 解析
- 把文件解析成MultipartFile,接收
mvc核心组件
- DispatcherServlet 前端控制器
- HandlerMapping 路径映射
- HandlerAdapter 执行方法
- HandlerExceptionResolver 异常解析
- ViewResolver 视图解析
- View 视图渲染
- MessageConverter 消息转换(JSON)
- ArgumentResolver 参数解析
- ReturnValueHandler 返回值处理spring梳理随笔 ==========
@AutoConfiguration
扫描SPI,读取配置文件,扫描@Conditional来按需加载所有的Bean↓
解析并创建对应的BeanDefinintion,用来记录Bean的元数据(有没有代理,是否单例等等,类似于Class类对象记录类元数据)
依次进行doCreateBean(递归执行,并非异步)
实例化(也就是根据BeanDefinintion创建对象,但是还没有填充属性【空壳】)
初始化
-
(填充属性(@Autowired等,依赖注入)
-
依次走三级缓存,有依赖直接从缓存填充,没有依赖则创建对应依赖)
-
执行Aware接口【让 Bean 能拿到 Spring 容器内部的对象】
-
BeanPostProcessor前置【暴露给外部的hooks】
-
执行@PostConstruct的方法【每个bean独立执行一次】
-
BeanPostProcessor后置【暴露给外部的hooks】
-
创建AOP代理对象
三级缓存
-
一级存完整实例(最终的getBean)【singletonObjects】
-
二级存不完全代理对象【为了解决aop情况下的循环依赖,因为aop需要延迟在初始化后做增强,目的是避免生命周期混乱】(若不存在aop,1级和3级完全能够解决循环依赖问题)【earlySingletonObjects】
-
三级存BeanFactory,lambda生成bean实例(调用一次就会生成一次实例)【singletonFactories】
AOP【基于原始对象做增强】:
-
实现了接口:JDK动态代理
-
没实现接口:CGLIB子类代理
-
扫描@Aspect切面
-
创建Advisor
-
在postProcessAfterInitialization判断bean是否需要增强,增强则返回代理对象【代理对象持有原始对象】,不增强则返回原始对象
-
相关注解:
@Before:前置通知@AfterReturning:正常返回后置@AfterThrowing:异常后置@After:最终后置(finally)@Around:环绕通知(最强,能控制目标方法执不执行)
-
动态代理
在运行时,生成全新的类,替原始对象干活,前后加逻辑
-
jdk代理,必须要有接口,实现类来实现,代理类和原对象平级
-
cglib代理,不需要接口,代理类是原类的子类,通过继承重写方法实现增强
代理对象中持有原始对象,还有拦截器链,在拦截器链中按顺序执行增强。
this导致代理失效,this指向是原始对象,故失效
事务相关
@Transactional本质就是代理包裹Connection(数据库连接)
本质上在你原对象的基础上写一个try catch,设置autocommit=false,手动处理提交/回滚
默认捕获RuntimeException
因为它也是代理,所以想它能够正常使用:
-
方法必须是public
-
必须走代理(不能this.)
-
类必须被Spring管理
传播机制
-
required(默认),有事务加入,无则创建,同一个事务,同生共死
-
required_news,每次创建新事务,内层事务不影响外层(无论内层提交或回滚都不影响外层)各玩各的
-
nested,嵌套事务,内层可以回滚到checkpoint,外层会连带整个内层回滚,(子流程失败不影响主,但是主失败了子也得回退,例如记录日志,日志失败不影响业务),父死子葬
-
supports,有就用,没有就不用
-
not_supports,强制不用事务
-
never,禁止事务,有事务则抛出异常
-
mandatory,必须有事务,没有则抛异常
失效场景
-
使用this,没走代理
-
非public
-
异常被catch
-
类没被spring管理
-
多例/手动new对象
-
错误的传播机制
-
数据库不支持事务
其他使用
@Transactional(readOnly=true),只读事务,出现insert/update等回滚
@Transactional(timeout=x),超时x秒后回滚
IOC:控制反转,统一bean管理,声明自动注入依赖,不用手动new
注入方式:
-
构造器注入
-
set注入
-
@Autowired,@Resource,@Inject
MVC
servlet是一套基于java web开发的规范(接口)
servlet出现之前,java做web开发只能手写socket,出现之后,tomcat,jetty都是对servlet的实现
mvc是对sevlet规范化的上层封装,框架化封装,(中央分发+路由框架)
大体流程
启动时
-
在xml时代,mvc是需要在tomcat配置DispatcherServlet的(本质就是个servlet)
-
在tomcat启动时,加载DispatcherServlet
-
spring容器初始化,加载controller,service等bean
-
扫描所有@RequestMapping路径
-
路径 -> 对应方法建立到映射表HandlerMapping中
-
初始化视图解析器(用于解析视图,不过目前大部分都是restful,估计也很少用模板了)
请求进入
-
发起请求,请求到达tomcat,tomcat接收请求并封装成HttpServletRequest,HttpServletResponse,并交给匹配的sevlet->DispatcherServlet
-
DispatcherServlet拿到请求,根据handlerMapping找对应的映射,包含Controller方法,和一堆拦截器
-
执行(请求拦截器),若false了直接中断请求
-
拦截器执行结束,找HandlerAdapter调用对应的Controller方法
-
HandlerAdapter解析参数,自动转换参数类型,封装成java对象(Bo)
-
执行Controller方法,处理返回值(返回值一般是模板路径)
-
ViewResolver 视图解析器 拼接前后缀,找到对应的模板,并渲染
-
执行postHandle拦截器(响应拦截器)
-
DispatcherServlet 返回响应
如果是RestController(也就是目前主流的方式,目前都是前后端分离,后端返回json数据)
在执行完controller方法后,会直接把返回值通过HttpMessageConverter序列化成json,直接写到响应流(HttpServletResponese)
补充:如果在方法中直接用响应流返回了,(例如导出)走响应拦截器后直接把流返回给前端了
MVC补充
filter和拦截器的区别
-
过滤器是servlet规范,在DispatchServlet之前执行
-
能拿到req,res,拿不到controller
-
适合编码解码,跨域限流,通用请求包裹
-
-
拦截器是MVC组件,在DispatchServlet后,Controller前后
-
能拿到handler(controller方法)
-
适合登录校验,权限,日志,参数记录
-
请求参数如何绑定
HandlerMethodArgumentResolver参数解析器
Spring 内置一堆解析器:
-
RequestParamMethodArgumentResolver
-
PathVariableMethodArgumentResolver
-
RequestResponseBodyMethodProcessor(@RequestBody)
-
ServletRequestMethodArgumentResolver(HttpServletRequest)
返回值怎么绑定
HandlerMethodReturnValueHandler
-
返回 String → 视图解析器
-
返回 Object + @ResponseBody → MessageConverter → JSON
-
返回 ModelAndView → 数据 + 视图
-
返回 void → 认为你自己处理了 response
全局异常
@RestControllerAdvice
-
抛出异常被ExceptionHandler捕获,直接返回json
-
跳过postHandle,会走afterCompletion
静态资源为什么不进controller
ResourceHttpRequestHandler处理静态资源,可在mvcconfig配置放行
MultipartFile 文件上传原理
-
前端form表单指定enctype="multipart/form-data"
-
spring用StandardServletMultipartResolver 解析
-
把文件解析成MultipartFile,接收
mvc核心组件
-
DispatcherServlet 前端控制器
-
HandlerMapping 路径映射
-
HandlerAdapter 执行方法
-
HandlerExceptionResolver 异常解析
-
ViewResolver 视图解析
-
View 视图渲染
-
MessageConverter 消息转换(JSON)
-
ArgumentResolver 参数解析
-
ReturnValueHandler 返回值处理