简单谈一谈你对Spring的理解
spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,在整个开发流程中,所有的框架生产几乎都依赖Spring,Spring帮我们起到了一个IOC容器的作用,用来承载我们所有的Bean对象,在程序下运行所有对象从创建到销毁的整个生命周期都是由Spring进行管理的,使用Spring的时候可以是配置文件也可以使用注解的方式来进行相关的实现。
程序开始启动之后,将之前定义好的Bean转换成一个BeanDefinition【相当于java里的class】,之后完成BeanDefinition的解析和加载过程,实例化BeanDefinition通过反射的方式创建对象,只在堆里开辟了一块空间并未完成初始化操作,之后还会实现aware接口【在Spring中经常用到一些Spring容器本身的功能资源,这个接口帮助我们实现一些具体功能】的一些相关操作,包括AOP等一些初始化的操作,如果使用Aop的话还要执行BeanPostProcessor【后置处理器】来实现相关的扩展工作,成体完成后对象就可以调用了
IoC
原本由程序员用程序控制创建的对象,现在由Spring容器来创建
依赖注入:当对象中有一个属性依赖另一个对象,把另一个对象实例化后注入到这对象的过程,就是依赖注入
AOP
是面向切面编程,主要实现的机制是动态代理【cglib、jdk】,在不改变原本业务的情况下增强原本的功能【添加事务、日志、安全、缓存等处理】
JDK动态代理和CGLIB字节码生成的区别
- JDK动态代理只能对实现了接口的类生成代理,而不能针对类,因为通过反射的方式实现的,所以性能低
- CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法。因为是继承,所以该类或方法最好不要声明成final
Spring创建对象的三种方式
-
构造方法
无参构造:默认
有参构造:需要配置信息,相同数量参数默认使用最后一个有参构造 - 实例工厂:编写工厂,创建对象时,先创建工厂再通过工厂创建对象
- 静态工厂:编写静态工厂,创建对象时,直接通过工厂创建对象
向对象注入值的两种方式
- 构造方法注入:编写构造器
- 设置注入:对象添加set( )
Bean的生命周期
实例化》属性赋值》初始化》销毁
class--->推断构造方法---->实例化--->对象--->属性填充---->初始化【afterPropertiesSet()/@PostConstruct】---->AOP---->生成代理对象---->Bean--->单例池【一级缓存singletonObject】-->驻留应用上下文中使用--->当上下文被销毁时spring注销bean【DisposableBean接口中的注销方法】-->注销
@Autowired注入问题的顺序
- bytype
- 检测是不是@Bean(isAutowirteCandidate = false):禁止自动注入
- @Qualifier(“xxx”):定义bean的名字为xxx或注入名字为xxxx的bean
- @Primary:定义主Bean【优先注入】
- @Priority(优先等级):设置优先级注入
- byname
Spring事务实现方式及隔离级别
实现方式:编程式【手动写代码控制数据库(begin)】、申明式【@Transactional(只能用在public上)】
隔离级别:
- read_uncommitted【RU,未提交读】:读到了上一个事务未提及的数据
可能出现问题:脏读、不可重复读、幻读 - read_committed【RC,提交读、不可重复读(Oracle默认)】:当是一个读事务,其他事务可以读写,当是一个写事务,禁止其他事务访问
可能出现问题:不可重复读、幻读 - repeatable_read【RR,可重复读(MySQL默认)】:在一个事务进行读的时候,其他事务不能进行写操作
可能出现问题:幻读 - serializable【S,可串行化】:事务只能一个一个执行,不能并发
脏读:所谓的脏读,其实就是读到了别的事务回滚前的脏数据。比如事务B执行过程中修改了数据X,在未提交前,事务A读取了X,而事务B却回滚了,这样事务A就形成了脏读。
也就是说,当前事务读到的数据是别的事务想要修改成为的但是没有修改成功的数据。
不可重复读:事务A首先读取了一条数据,然后执行逻辑的时候,事务B将这条数据改变了,然后事务A再次读取的时候,发现数据不匹配了,就是所谓的不可重复读了。
也就是说,当前事务先进行了一次数据读取,然后再次读取到的数据是别的事务修改成功的数据,导致两次读取到的数据不匹配,也就照应了不可重复读的语义。
幻读:事务A首先根据条件索引得到N条数据,然后事务B改变了这N条数据之外的M条或者增添了M条符合事务A搜索条件的数据,导致事务A再次搜索发现有N+M条数据了,就产生了幻读。
也就是说,当前事务读第一次取到的数据比后来读取到数据条目少。
Spring事务执行流程,原理
- 建立数据库链接 conn
- 关闭自动提交事务的功能 conn.setAutocommit(false)
- 执行 sql
- 提交事务 commit
如何解决Spring事务的传播机制问题(自己总结,参考价值不高)
传播机制:多个事务方法相互调用时,事务如何在这些方法间传播
- REQUIRED(Spring默认):没有事务创建新事务,存在事务加入当前事务
- SUPPORTS:存在事务加入当前事务,没有事务以非事务方式运行
- MANDATORY:存在事务加入当前事务,没有事务抛出异常
- REQUIRES_NEW:创建新事务,存在事务个跑个的
- NOT_SUPPORTED:以非事务方式运行
- NEVER:不使用事务,不会加入任何事务,存在事务抛出异常
- NESTED:存在事务嵌套当前事务成为一个子事务,其他与REQUIRED一样
因为只有代理对象调用方法是才会触发@Transactional注解中的事务机制(isolation、propagation、timeout),所以要自注入
- @Transactional()中添加可能存在的问题的属性【事务隔离级别(isolation)、事务传播行为(propagation)】
- 添加一个当前类的对象并自动注入【俗称:自己注入自己】,之后用该对象调用当前类的方法
简述Spring的循环依赖
当前对象的属性中的子属性存在引用当前类对象的情况
Spring容器中存在一级缓存singletonObject、二级缓存earlySingletonObject、三级缓存singletonFactories,
当开始实例化时,先将这个这个原始对象加入到createingSet中【判断是否正在创建】,之后将原始对象又加入到三级缓存中并添加了一个lambda表达式【为提前Aop做准备】
给属性赋值-->一级缓存中找【没有】--> createimgSet-->发现开始循环【循环依赖】-->二级缓存中找【没有】-->三级缓存中找【有】--->执行lambda表达式【提前Aop】-->生成代理对象-->放入二级缓存
之后再出现类似属性再次直接从二级缓存中找不会再走三级缓存
到了真正执行Aop的时候他会从一个earlyProxyReference中移除当前Aop【是在执行lambda表达式的时候将Aop添加进来的,如果拆除成功说明之前执行过Aop,不再进行Aop,反之执行Aop】
移除createingSet--> 放入单例池【一级缓存singletonObject】
其中:二级缓存和三级缓存中不会同时存在相同的对象,所以再添加到一个缓存中时都要从另一个缓存中移除,并且两个缓存的实质都是HashMap,并添加synchronized保证原子性。一级缓存是ConcurrentHashMap
JavaBean和SpringBean的区别
用处不同:
传统javabean更多地作为值传递参数,而spring中的bean用处几乎无处不在,任何组件都可以被称为bean。
写法不同:
传统javabean作为值对象,要求每个属性都提供getter和setter方法;但spring中的bean只需为接受设值注入的属性提供setter方法。
生命周期不同:
传统javabean作为值对象传递,不接受任何容器管理其生命周期;spring中的bean有spring管理其生命周期行为。
Spring用到那些设计模式及应用位置
简单工厂:根据传过来的一个参数,动态生成要生成的那种产品对象【BeanFactory】
工厂模式:所有的xx工厂都实现一个工厂接口,使用时指定某个工厂来生成产品的配【实现FactoryBean接口】
单例模式:唯一【Bean】
适配器模式:将一个类的接口转换成客户希望的另外一个接口【Controller实现Handler接口】
代理模式:【AOP】
策略模式:根据不同的文件进行不同类型的访问【Spring访问Resource接口】
SpringMVC是什么
Springmvc是Spring对web框架的一个解决方案,提供了一个前端控制器来接受请求,还定义了一组路由策略及适配执行handler,并将handler执行结果交给视图解析器展示给前端
SpringMVC工作流程
用户请求-->前端控制器【DispatcherServlet】-->调用处理器映射器【handlerMapping】返回一个处理后的结果-->
DispatcherServlet-->调用处理器适配器【handlerAdapter】经过适配后找到具体的处理器-->
后端处理器【controller】-->【ModelAndView】-->handlerAdapter将携带这controller执行后的ModelAndView返回给-->
DispatcherServlet-->将ModelAndView传给视图解析器【ViewReslover】-->解析成具体的视图【View】-->DispatcherServlet根据这个View进行渲染-->响应给前端用户
SpringBoot是什么
SpringBoot是Spring提供的一个快速开发工具包,跟快速方便的开发spring+springmvc应用,并简化了配置内置了Tomicat,整个了一些列解决方案(各种starter启动器)
SpringBoot自动装配原理
主入口中执行run方法-->里面有个刷新容器【refreshContext( context)】的过程,解释我们的@SpringBootApplication这个注解-->
里面有@AutoConfigurationPackage【自动注册包(CommponentScan扫描的主启动类同级的包)】、@EnableAutoConfigurtion【开启自动配置】-->
import一个类【AutoConfigurationImportSelector】-->有些核心方法帮我们从META-INF/spring.factories下加载配置配置类-->
这些配置类会有一些ConditinalOnXXX或ConditinalOnMissingXXX来判断是否装配-->最后就实现了我们的自动装配
SpringBoot为什么内置tomcat
tomcat是一个轻量级服务器,在并发访问量不是很多的情况下经常使用,免费,可独立运行,性能稳定,技术先进