springmvc执行流程
DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制
HandlerMapping 将会把请求映射为HandlerExecutionChain 对象(包含一个Handler 处理器(页面控制器)对象、多个HandlerInterceptor 拦截器)对象,通过这种策略模式,很容易添加新的映射策略
HandlerAdapter 将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;
处理器功能处理方法的调用,HandlerAdapter 将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView 对象(包含模型数据、逻辑视图名);
ViewResolver, ViewResolver 将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;
View——>渲染,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术;
返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束
mybatis执行流程
1.读取xml文件,建立连接
MyConfiguration负责与人交互。待读取xml后,将属性和连接数据库的操作封装在MyConfiguration对象中供后面的组件调用
首先Mybaitis自己的一个Resources类会去调用一个叫getResourceAsStream()的方法
加载配置MyBatis的核心配置文件,得到一个流对象。(本质上还是走的java.lang包下的ClassLoader类加载器的getResourceAsStream)
会先去创建一个SqlSessionFactoryBuilder对象(构建者对象),调用build()方法,传入第一步我们得到的那个流对象。
在这个过程中,首先,MyBatis会先去创建一个XMLConfigBuilder解析器对象
解析我们刚才传进来的那个流对象
解析完成之后,返回一个configuration对象,这个对象里面封装了解析之后我们配置的信息
正好这个configuration对象,就是我们这个方法创建(I)SqlSessionFactory类型对象需要的参数
2.创建SqlSession,搭建Configuration和Executor之间的桥梁
一个Session仅拥有一个对应的数据库连接。类似于一个前段请求Request,它可以直接调用exec(SQL)来执行SQL语句。从流程图中的箭头可以看出,MySqlSession的成员变量中必须得有MyExecutor和MyConfiguration去集中做调配,箭头就像是一种关联关系。我们自己的MySqlSession将有一个getMapper方法,然后使用动态代理生成对象后,就可以做数据库的操作了
有了这个SqlSesionFactory实现类的对象之后 我们调用openSession()的方法,这个过程中需 要 通 过TransactionFactory 生成 Transaction 对象, 并且, 还需要创建核心执行器 Executor 对象,基于这些条件,
最终创建了实现SqlSession接口的DefaultSqlSession对象
3.创建Executor,封装JDBC操作数据库
通过 SqlSession 对象执行相应的操作, 如果执行成功, 调用 commit 方法提交事务; 如果失败, 调用rollback 方法事务回滚. 最后, 调用 close 方法关闭session 资源.
Executor是一个执行器,负责SQL语句的生成和查询缓存(缓存还没完成)的维护,也就是jdbc的代码将在这里完成
4.创建MapperProxy,使用动态代理生成Mapper对象
我们只是希望对指定的接口生成一个对象,使得执行它的时候能运行一句sql罢了,而接口无法直接调用方法,所以这里使用动态代理生成对象,在执行时还是回到MySqlSession中调用查询,最终由MyExecutor做JDBC查询。这样设计是为了单一职责,可扩展性更强。
volatile底层原理
volatile 关键字,使一个变量在多个线程间可见
A B线程都用到一个变量,java默认是A线程中保留一份copy,这样如果B线程修改了该变量,则A线程未必知道
使用volatile关键字,会让所有线程都会读到变量的修改值
volatile是java虚拟机提供的轻量级的同步机制,能够保证内存共享变量的可见性。那么volatile是如何保证可见性的呢?
首先要知道内存屏障是什么,
内存屏障是一个CPU指令,内存屏障是这样的指令
1,确保特定操作执行的顺序
2,影响一些数据的可见性,编译器和CPU可以保证输出结果一样的前提下对指令进行重排序,使得性能优化,当插入一个内存屏障,相当于告诉CPU和编译器,先于这个命令的必须先执行,后于这个命令的必须后执行
3,强制更新一次不同的CPU缓存,比如一个写屏障会把这个屏障前写入的数据刷新到缓存,任何师徒读取改数据的线程将得到最新的值
系统提供的内存屏障:
LoadLoad屏障
对于Load1; LoadLoad; Load2 ,操作系统保证在Load2及后续的读操作读取之前,Load1已经读取。
StoreStore屏障
对于Store1; StoreStore; Store2 ,操作系统保证在Store2及后续的写操作写入之前,Store1已经写入。
LoadStore屏障
对于Load1; LoadStore; Store2,操作系统保证在Store2及后续写入操作执行前,Load1已经读取。
StoreLoad屏障
对于Store1; StoreLoad; Load2 ,操作系统保证在Load2及后续读取操作执行前,Store1已经写入,开销较大,但是同时具备其他三种屏障的效果。
volatile修饰的变量,JMM将在写操作后插入一个写屏障指令,在读操作前插入一个读屏障指令,这代表着:
1,一旦有现成对变量写入了新值,任何访问这个变量的线程都会得到新的值
2,在写入前,也会保证所有之前发生的事情已经发生,并且更新过的数据值也是可见的。内存屏障会把之前的写入值都刷新到缓存
所以Volatile可以保证可见性