Spring
spring是什么
是一个集成了IOC、AOP,事务管理等很多模块的集合
IOC 容器
就是用来创建和管理Bean对象的,项目启动后会通过BeanDefinitionReader
类用反射读取配置文件定义的Bean或注解标注的类,将它定义为BeanDefinition
对象,然后会对这个Bean对象用BeanFactoryPostProcessor
类进行增强,如果把${}换成具体的属性值,之后实例化,初始化这个对象
依赖注入:一个对象依赖另一个对象,IOC容器会自动把对应的依赖关系注入
AOP 切面
在不改变原有代码功能的基础上,对功能进行增强,前置增强,后置增强,前后都增强等等,类似拦截器,默认使用的是 jdk动态代理
如果要代理的对象,实现了某个接⼝,那么Spring AOP
会使 ⽤JDK Proxy
,去创建代理对象,⽽对于没有实现接⼝的对象,就⽆法使⽤JDK Proxy
去进⾏代 理了,这时候Spring AOP
会使⽤Cglib
,这时候Spring AOP
会使⽤ Cglib
⽣成⼀个被代理对象的 ⼦类来作为代理
Spring AOP 属于运⾏时增强,⽽ AspectJ 是编译时增强。Spring已经整合了AspectJ
Spring 中的 bean 的作⽤域有哪些
- singleton :默认都是单例的
- prototype : 多例,每次请求都会创建⼀个新的 bean 实例
BeanFactory和ApplicationContext区别
ApplicationCntext
是BeanFactory
的子接口,BeanFactory
采用懒加载的方式加载Bean,ApplicationCntext
是即时加载,一次加载所有的Bean
Spring中用到了哪些设计模式
⼯⼚设计模式 : Spring使⽤⼯⼚模式通过 BeanFactory 、 ApplicationContext 创建 bean 对 象
代理设计模式 : Spring AOP 功能的实现
单例设计模式 : Spring 中的 Bean 默认都是单例的
SpringBean的生命周期 ????????????
- 解析类得到
BeanDefinition
- 使用
BeanFactoryPostProcessor
对Bean进行增强,如把${}
占位符换成具体的值 - 实例化对象
- 回调Aware方法,比如
BeanNameAware
,BeanFactoryAware
- 调用
BeanPostProcessorbefore
方法 - 调用初始化方法
- 调用
BeanPostProcessorafter
方法,在这里会进行AOP - 把Bean放入map中
- 容器关闭时调
用DisposableBean
中destory()
方法
Spring事务实现方式和原理
加上@Transactional,就可以开启事务,方法中所有的sql都会在一个事务中执行,统一成功或失败
spring事务使用的是AOP环绕通知,在一个方法上加了@Transactional注解后,Spring会基于这个类生成一个代理对象,会将这个代理对象 作为bean,当在使用这个代理对象的方法时,如果这个方法上存在@Transactional注解,那么代理逻 辑会先把事务的自动提交设置为false,然后再去执行原本的业务逻辑方法,如果执行业务逻辑方法没有 出现异常,那么代理逻辑中就会将事务进行提交,如果执行业务逻辑方法出现了异常,那么则会将事务 进行回滚
Spring事务的隔离级别和传播行为
隔离级别
控制事务之间影响的程度,多个事务读取同一变量并操作同一变量时会出现先问题,有4个隔离级别
read uncommitted(未提交读) read committed(提交读、不可重复读) repeatable read(可重复读,Spring默认) serializable(可串行化)
传播行为
多个事务方法相互调用时,用哪个事务,以哪个事务为准
(REQUIRED)
REQUIRED:spring默认传播行为,一个方法调用另一个方法时,如果被调用的方法存在事务就是使用当前的事务,如果没有事务,则新建事务
REQUIRES_NEW:创建一个新事务,如果存在当前事务,则挂起该事务
Spring事务什么时候失效
spring事务的原理是AOP,进行了切面增强,那么失效的根本原因是这个AOP不起作用了,常见情况:
@Transactional,只能用于 public 的方法上,否则事务不会失效
异常被吃掉,事务不会回滚
SpringMVC
@RestController 和 @Controller 区别
@RestController = @Controller + @ResponseBody
@Controller一般返回一个view视图,@RestController将对象数据直接以 JSON 形式写⼊Response对象的body中
简述springmvc工作流程
用户发送的请求会先到DispatcherServlet这个请求分发的控制器,然后会调用HandlerMapping找到能够处理该请求的一系列Handler(就是Controller),然后调用HandlerAdapter处理请求,处理完成请求,返回一个ModelAndView,然后调用视图解析器解析视图,解析完返回一个view视图返回给浏览器
SpringBoot
@Import导入的类会被Spring加载到IOC容器中
@Component 注解作⽤于类,⽽ @Bean 注解作⽤于⽅法,一般将第三⽅库中的类注入到容器时,只能通过@Bean 来实现
自动配置流程
如何理解SpringBoot中的starter
在starter中会定义相应的AutoConfiguration,如RedisAutoConfiguration,MybatisAutoConfiguration,在这些configuration中就定义了要用到的第三方Bean对象,然后在 starter包的META-INF/spring.factories中写入该配置类,SpringBoot的自动配置机制加载这个配置类,完成自动配置
什么嵌入式服务器
SpringBoot中内嵌了tomcat,运行main方法时就会去启动tomcat,不用再打个war包,然后放到webapp目录下再运行
Mybatis
通常⼀个 Xml 映射⽂件,都会写⼀个 Dao 接⼝ 与之对应,请问,这个 Dao 接⼝的⼯作原理是什么?Dao 接⼝⾥的 ⽅法,参数不同时,⽅法能重载吗?
工作原理:
⼯作原理是 JDK 动态代理,Mybatis 运⾏时会使⽤ JDK 动态代理为 Dao 接⼝⽣成代 理 proxy 对象,代理对象 proxy 会拦截接⼝⽅法,转⽽执⾏对应的 MappedStatement 所代表的 sql,然后将 sql 执⾏结果返回。每⼀个 <select> <update> <delete>
标签,都会被解析为⼀ 个 MappedStatement 对象
方法不能重载
因为是全限名+⽅法名的寻找策略
当调⽤接⼝⽅法时,接⼝全限名+⽅法名 拼接字符串作为 key 值,从而定位到MappedStatement ,举 例: com.mybatis3.mappers.StudentDao.findStudentById
,可以唯⼀找到Xml中namespace 为 com.mybatis3.mappers.StudentDao 中的 id = findStudentById
的 MappedStatement
mybatis动态sql是做什么的
mybatis提供了9种 动态sql 标签 trim|where|set|foreach|if|choose|when|otherwise|bind,这些sql标签用来完成 逻辑判断 和 动态拼接sql功能
如何实现sql执行结果和目标对象映射的
第⼀种是使⽤
<resultMap>
标签,逐⼀定义列名和对象属性名之间的映射关系第⼆种是使 ⽤ sql 列的别名功能,⽐如 T_NAME as name
Redis
介绍一下redis
Redis 是⼀个使⽤ C 语⾔开发的非关系型数据库,数据存储在内存当中,是一个内存数据库,读写速度非常快,一般用于做缓存
redis常见数据类型
string:set、get、del、incr
list:本质是一个双向链表,rpush,lpop,lpush,rpop,lrange
hash
set
- sorted set:增加了权重参数score,使得集合元素按score进行有序排序
Redis 给缓存数据设置过期时间有啥⽤
redis数据存在内存当中,如果缓存中的所有数据都是⼀直保存的话,会有内存溢出的风险,并且有些数据只需要存在一段时间,如短信验证码、token
java exp key 60 # 数据在 60s 后过期
缓存数据的处理流程是怎样的?
缓存中有就从缓存中拿,缓存中没有就从数据库中拿,然后放入缓存
Redis是如何判断数据是否过期的呢
Redis 通过⼀个叫做过期字典(类似hash表)来保存数据过期的时间。过期字典的键指向数据库中的某个key键,值表示键的过期时间
- 过期字典是存储在redisDb这个结构⾥的:
```c++ typedef struct redisDb { ...
dict *dict; //数据库键空间,保存着数据库中所有键值对 dict *expires // 过期字典,保存着键的过期时间 ... } redisDb; ```
过期的数据的删除策略了解么
- volatile-lru(least recently used):从已设置过期时间的数据中挑选最近最少使⽤的数据淘汰
- volatile-ttl:从已设置过期时间的数据中挑选将要过期的数据淘汰
- volatile-random:从已设置过期时间的数据中任意选择数据淘汰
- allkeys-lru(least recently used):在键空间中移除最近最少使⽤的 key(这个是最常⽤的)
- allkeys-random:从数据集中任意选择数据淘汰
- no-eviction:当内存不⾜时,新写⼊操作会 报错
持久化
Redis 提供了两种持久化机制:RDB 和 AOF
RDB
在指定的时间间隔内将内存中的数据集快照写入磁盘,在配置文件中用save命令可以配置 [(save 60 900) 60秒内有900个key发生变化],会另外创建一个fork线程来持久化,不会影响主进程的
AOF
以独立日志的方式记录每次写命令,并在 Redis 重启时重新执行 AOF 文件中的命令以达到恢复数据的目的。AOF重写,只保留最后一次的修改记录,防止文件过大
redis事务
把命令放在multi
和exec
之间,里面有错误就会报错,其他命令也不会执行
缓存穿透
简单点就是⼤量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本 没有经过缓存这⼀层
解决办法:
- 做好参数校验
缓存雪崩
缓存雪崩是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求
热点缓存失效解决方案:
- 设置不同的失效时间⽐如随机设置缓存的失效时间
- 缓存永不失效
如何保证缓存和数据库数据的⼀致性
- 定时任务