单例bean是线程安全的吗
不是线程安全的,Spring框架有一个@Scope注解默认的值就是singleton
一般Spring的bean(像DAO、Service这些)都是无状态的,所以没有线程安全问题,如果bean中定义了可修改的成员变量,是要考虑线程问题的,可以使用多例或者加锁
Spring bean的生命周期
![](https://img-blog.csdnimg.cn/img_convert/876968c12f3eecb9b08af53a462f4fd5.png)
通过BeanDefinition获取bean的定义信息(类名,初始化方法名,作用域等)
通过构造方法实例化bean
bean的依赖注入
处理Aware接口(实现接口中的xxxaware方法实现并且执行对应的方法)
调用bean的后置处理器BeanPostProcesser-前置
调用初始化方法
调用bean的后置处理器BeanPostProcesser-后置,并且进行AOP代理对象的创建
使用bean
根据作用域销毁Bean单例bean在单例池关闭时销毁,多例bean手动调用destory时销毁
Spring-bean的循环依赖
循环依赖是指两个或者两个以上的对象互相持有对方,最终形成闭环,比如A依赖于B,B依赖于A
Spring通过三级缓存解决了Setter注入的循环依赖问题
一级缓存:单例池,缓存已经经历了完整的生命周期,已经初始化完成的bean的ui下
二级缓存:缓存生命周期没有走完的对象(创建了但是还没有注入属性)
三级缓存:缓存对象工厂,用来创建某个对象包括代理对象
前两级缓存只能解决普通对象的循环依赖问题,三级缓存能解决代理对象的循环依赖问题
如果A与B相互依赖且注入的方式是构造方法注入即A在构造方法中初始化B,B在构造方法中初始化A,可以使用@Lazy注解进行懒加载
SpringAOP相关问题
常见的AOP使用场景:
-
记录操作日志
-
添加缓存
-
Spring的事务处理
通过动态代理实现(JDK或CGLIB)默认是JDK
使用JDK动态代理的对象必须实现一个或多个接口CGLIB则不需要
Spring事务失效
1.被事务注解控制的方法抛出检查异常导致事务不能回滚
原因:spring默认只会回滚非检查异常
解决方法:在注解后面加异常参数@Transactional(rollbackFor = xxxException.class)
2.业务方法内自己try-catch异常导致事务不能回滚
原因:事务是基于aop环绕通知,通知只有捕捉到目标抛出的异常,才能进行后续回滚,如果目标自己处理掉异常,事务通知无法知道
解决方法:在catch块中异常原样抛出或者手动设置让事务回滚
3.事务没有加在public方法上
原因:spring默认只能在public方法上加事务
解决方法:把public方法加上,或者去修改配置类中修改配置
4.多线程并发加锁问题
原因:synchronized方法加在了事务切面中,只能保证代码块运行的原子性不能保证commit的原子性
解决方法:把synchronized的作用范围扩大或者在sql语句中加写锁
Spring事务传播形式
其实从数据库看就三种情况,假如A事务在外面B事务在里面
融入外界事务:B事务融入A事务
挂起:执行到B事务时,B事务会挂起A事务,等B事务执行完毕再唤醒A事务
嵌套事务:利用保存点实现A事务和B事务嵌套的情况
Required(默认):B事务融入A事务
supports:如果A有事务B就有事务,如果A无事务,B也无事务
mandatory:B必须传播到有事务的环境,如果A无事务并且在B事务的外面,抛异常
requires_new:如果A有事务,执行到B时会挂起A事务
not_supported:如果A有事务,B会挂起A事务并以非事务形式执行
never:A事务和B事务互相调用,如果调用就抛异常
nested:使用嵌套事务
SpringMVC执行流程
浏览器发出请求到Tomcat,Tomcat会初始化前端控制器,处理器映射器,处理器适配器等
前端控制器会根据请求的路径并通过处理器映射器找到对应Handler
Handler就是加了@RequestMapping注解的方法
方法执行前会使用处理器适配器解析方法的各个参数,比如参数前面加了@RequestParam注解
然后执行方法得到返回值
然后解析返回值看有没有加@ResponseBody注解
如果加了直接把方法值返回给浏览器
如果没有加则根据方法返回值找到页面并进行渲染
Spring有哪些注解?
Spring
![](https://img-blog.csdnimg.cn/img_convert/7b148eb2fa80caeb8b60ce5906a46385.png)
@EableTransactionalManagement 开启声明式事务功能
@Transactional 设置声明式事务
@Order 设置优先级控制bean的执行顺序
@EnableAspectJAutoproxy 开启AOP功能
@Component @Controller @Service @Repository 声明spring管理的bean
@ComponetScan 扫描spring管理的bean
@configuration 声明配置类
@Bean 引入第三方对象让spring管理
@Import 导入其他配置类
@lazy 让类变为懒加载
@PropertySource 读取properties文件
@Autowired @Qualifier @value 依赖注入
SpringMVC
![](https://img-blog.csdnimg.cn/img_convert/dd8460b4fd244635d3f3910d2c77e65b.png)
@RequestMapping 对请求路径和方法进行映射
@GetMapping @PostMapping restful风格的请求映射
@RequestBody 页面传入json数据则参数要加这个注解
@ResponseBody 向页面返回json数据
@ControllerAdvice 统一处理
@ExceptionHanlder 异常处理
@RequestHeader @CookieValue @PathVariable 获取参数
@CrossOrigin 解决ajax跨域问题
SpringBoot
![](https://img-blog.csdnimg.cn/img_convert/945e1604e7cd613685501a06ac27e6f8.png)
@SpringBootApplication 引导类上的注解
@EnableAutoConfig 开启自动配置
@SpringBootConfig 用于加载配置文件
@ComponentScan 用于组件扫描和自动装配
Spring设计模式
单例模式 单例池不是单例模式
建造器模式 beanDefinitionBuilder
工厂方法模式 getBean方法
适配器模式 HadlerAdapt
代理模式 AOP
责任链模式 拦截器
模板方法模式 jdbcTemplate
组合模式
装饰器模式
观察者模式
策略模式
SpringBoot自动配置
@SpringBootApplication中提供了三个注解
![](https://img-blog.csdnimg.cn/img_convert/efc65f8cc8e036be861d331ae7db3a9b.png)
@EnableAutoConfiguration是实现自动配置的注解
它底层封装了@Import注解导入对应的配置选择器,读取了该项目和该项目引用的Jar包的classpath路径下的META-INF/spring.factores文件中所配置类的全类名
这些配置类中所定义的bean会根据条件注解(比如@ConditionOnClass)来决定是否需要将其导入到Spring容器中
Mybatis的优缺点
优点:
基于SQL语句进行编程,使用起来很灵活,可以优化sql语句,sql写在XML中与代码的耦合度低
基于JDBC所以与各种数据库兼容性好,相比于JDBC冗余代码少很多开发效率高
能够与Spring很好的集成
缺点:
sql语句编写工作量较大,对开发人员的sql功底要求较高
sql语句依赖于数据库,导致数据库移植性差,不能随意切换数据库
Mybatis的#{}和${}的区别
#{}是一个占位符,处理时会把它预编译成一个?,调用preparedStatement来赋值
${}是一个拼接符,处理时相当于字符拼接,调用statement来赋值
#{}可以防止sql注入
Mybatis的执行流程
![](https://img-blog.csdnimg.cn/img_convert/2901338f56f9e57726b33a811ee9624c.png)
-
读取Mybatis配置文件:mybatis-config.xml加载运行环境和映射文件
-
构建会话工厂sqlsessionfactory
-
会话工厂创建会话sqlsession(包含了执行sql的所有方法)
-
操作数据库接口(Executor执行器),同时负责查询缓存的维护
-
输入参数的映射,把Java类型的数据变成数据库支持的类型
-
返回结果映射
Mybatis延迟加载
延迟加载的意思是在需要用到数据时才进行加载,不需要数据时不加载。(比如有一个用户表里面有一个订单信息字段要去其他表进行查询,当我不需要订单信息时可以先不去查订单表)
mybatis支持延迟加载,但是默认不开启
在mybatis配置文件中,可以配置是否启用延迟加载
实现原理
使用cglib创建目标对象的代理对象
当调用目标方法时,进入拦截器invoke方法,发现目标方法时null值,执行sql查询
获取数据后调用set方法进行赋值,再继续查询目标方法就有值了
Springcloud有哪些常见组件
注册中心:nacos、eureka
负载均衡:ribbon
远程调用:fegin
服务保护:sentinel
网关:gateway
服务注册和服务发现如何实现
![](https://img-blog.csdnimg.cn/img_convert/23f0a949c8bc6dd5a53c62668df84683.png)
用eureka做服务注册中心。
服务注册:服务提供者需要把自己的信息注册到eureka,由eureka来保存信息,比如服务名、ip、端口号
服务发现:消费者向eureka拉取服务列表信息,如果服务有集群会利用负载均衡策略选择一个调用
服务监控:服务提供者每30秒向eureka发送一次心跳,如果nacos长时间没有收到心跳就删除该服务
nacos和eureka的区别
![](https://img-blog.csdnimg.cn/img_convert/14696a3c3f69832bfe5cf2db8d71a9d5.png)
共同点
-
都支持服务注册和服务拉取
-
都支持服务者做心跳检测
不同点
-
nacos还可以做配置中心
-
nacos有临时实例和非临时实例的概念:非临时实例下nacos主动检测服务提供者,临时实例下被动
-
nacos集群采用AP方式,当集群中存在非临时实例时采用CP方式,eureka只能AP
Ribbon负载均衡
![](https://img-blog.csdnimg.cn/img_convert/d5f33928d581c4a29c8db94e49914dc4.png)
发起远程调用fegin会先找到ribbon,ribbon从注册中心拉取服务然后通过负载均衡策略取访问服务
Ribbon负载均衡策略
轮询
按照权重选择服务器(响应时间越长权重越小)
随机选择一个可用的服务器
以区域可用的服务器为基础进行服务器连接(就近)如果没有区域则轮询(默认)
选择并发数最小的服务器
过滤非健康服务,选择并发数最小的服务器
自己创建类实现IRule接口,然后通过配置类或配置文件可以实现自定义负载均衡策略
微服务保护
服务雪崩:一个服务失败,导致整条链路的服务都失败
服务降级:服务自我保护的一种方式,确保服务不会由于请求暴增变得不可用,确保服务不会崩溃
服务熔断:默认关闭需要手动打开,如果检测到10秒内请求失败率超过50%,就触发熔断机制,之后每隔5秒重新尝试请求服务,如果不能响应走熔断机制,如果服务响应则关闭熔断恢复正常请求
分布式事务
CAP定理
Consistency一致性:访问分布式 任意节点,总能返回一致的结果
Availability可用性:分布式系统总能向客户端返回响应
Partition tolerance分区容忍:当分布式系统节点间通信发生了消息丢失或消息延迟,仍然允许系统继续运行
CAP定理:最多三选二无法兼得,通常在CP或者AP之间做选择
也就是说一致性和可用性不可兼得
BASE理论
BASE理论时对CAP定理的一个补充
Basically Available(基本可用):分布式系统出现故障时,允许损失部分可用性
Soft state(软状态):在一定时间内,允许出现中间状态,比如临时不一致
Eventually Consistency(最终一致性):无法保证强一致性,但是软状态结束后,最终达到一致性