Spring的启动流程?
对于一个web应用,其部署在web容器中,web容器提供一个全局的上下文环境,这个上下文就是ServletContext,为后面的IOC容器提供宿主环境
在web.xml里提供contextLoaderListener,它会监听容器的初始化事件,调用contextInitialized方法启动上下文WebApplicationContext,这个就是IOC容器,初始化完毕后会以key-value的形式存储到ServletContext中
随后开始配置web.xml中配置的Servlet,这样的Servlet可以有多个,经典的比如DIspatcherServlet,它的上下文在初始化的时候会建立自己的IOC上下文,用来持有mvc相关的bean,其过程是:先从ServletContext里拿到根上下文(WebApplicationContext)作为自己上下文的父上下文,在此基础上再初始化自己的上下文,这样以来每个Servlet就都持有了自己的上下文,即拥有量自己bean的独立空间,同时他们共享根上下文中定义的那些bean
Spring和MVC上下文的关系
上下文可以简单的理解为容器,配置文件,web上下文目标对象是所有web应用,spring上下文目标对象是单个web应用,mvc目标对象是单个web应用的spring mvc框架
MVC常用注解以及执行流程
流程:
- 用户发送请求到DispatcherServelt
- DispatcherServelt收到请求调用HandlerMapping
- HandlerMapping找到具体的处理器并且生成处理器对象和处理器拦截器,返回给DispatcherServlet
- DispatcherServlet调用HandlerAdapter
- HandlerAdapter经过适配调用具体的Controller
- Controller被调用返回给HandlerAdapter ModelandView,HandlerAdapter接着返回给DispatcherServlet
- DispatcherServlet调用视图解析器进行解析返回具体的View
- 最后由DispatcherServlet返回渲染的视图并且响应用户
常用注解:
- @Controller:标记在类上,表示是一个Controller对象,其实和Component,Service等注解没什么区别
- @RequestMapping:用于处理请求地址映射的注解
- @Resource和@Autowired:用于注入bean,前者其实是jdk的注解并且默认byname注入,后者byType注入
- @PathVariable:用于取出URL模板中的变量作为参数
- @RequestParam:将请求的参数绑定到方法参数上,可以是多个也可以是Map集合
- @RequestBody:取出请求体里的数据自动封装
Controller是否单例?好处是什么?和多线程的关系?
默认是单例的,所以说不要在Controller去定义属性,这样线程是不安全的,如果需要定义属性需要添加@Scope注解改成多例的模式;
由于MVC是基于方法的开发,都是用形参接受值,一个方法结束参数就销毁了,一个单例对象能做完的事情为什么要交给多个多例对象呢
fast-fail机制
是java集合中的错误检测机制,在迭代集合的过程中该集合在结构上发生改变就有可能会发生fast-fail并且抛出ConcurrentModificationException,比如在ArrayList遍历的过程中,刻意的在某一步迭代中调用集合类的remove方法移除一个元素,或者多线程一个线程迭代一个remove;
底层是集合类迭代的next()方法,在访问元素前都会调用checkForComodification方法,这个方法会对modCount这个变量进行判断,如果被更改了就会抛出异常
异常的类别
Throwable类是最大的底下有Exception和Error两个子类;
- Error一般指的是程序问题,自己无法解决,比如OOM,NoClassFound
- Exception指的是可以预料的异常情况,可以捕获它并对他进行处理,比如数组越界,空指针
Mybatis的二级缓存
二级缓存是mapper级别的缓存,底层是hashmap实现的,不同的mapper都有一个二级缓存,他们之间互不影响,与一级缓存的区别在于二级缓存的范围更大,多个sqlSession可以共享一个mapper中的二级缓存,Mybatis区分二级缓存靠的是mapper的不同namespace,如果两个mapper的namespace相同,他们将使用相同的二级缓存
在二级缓存的设计上Mybatis大量使用了装饰者模式+责任链模式,CachingExecutor以及各种Cache接口的装饰器,以下是装饰者模式的案例:
public class Solution {
public static void main(String[] args) {
Competitor competitor = new Competitor();
CompetitorDecoratorA decoratorA = new CompetitorDecoratorA(competitor);
CompetitorDecoratorB decoratorB = new CompetitorDecoratorB(decoratorA);
decoratorB.sing();//偏偏起舞...我唱唱唱......鼓掌....
}
}
class Competitor {
public void sing(){
System.out.println("我唱唱唱......");}}
class CompetitorDecorator extends Competitor{
private Competitor competitor;
public CompetitorDecorator(Competitor competitor){
this.competitor= competitor;
}
public void sing(){
competitor.sing();
}
}
class CompetitorDecoratorA extends CompetitorDecorator {
public CompetitorDecoratorA(Competitor competitor) {
super(competitor);
}
public void sing()
{
this.dance();
super.sing();
}
private void dance()
{
System.out.println("偏偏起舞....");
}
}
class CompetitorDecoratorB extends CompetitorDecorator {
public CompetitorDecoratorB(Competitor competitor) {
super(competitor);
}
public void sing()
{
super.sing();
guzhang();
}
private void guzhang()
{
System.out.println("鼓掌....");
}
}
责任链模式:获取缓存要记录缓存命中率,在事务中操作等等,只使用Cache的某个实现类是不行的,普通的责任链模式是或者关系,这个不行就交给下一个做
public abstract class Handler {
//后继者,即如果处理不了请求要把任务丢给谁
private Handler successor;
public Handler getSuccessor() {
return successor;
}
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public abstract void HandleRequest(int num);
}
/**
* @author wuyimin
* @create 2021-07-29 19:21
* @description 导师类,最抠门的类
*/
public class Teacher extends Handler {
@Override
public void HandleRequest(int num) {
if(num<=600){
System.out.println("抠门的导师接受了你的请求给了你"+num+"人民币");
}else{
System.out.println("你的导师不想给你钱");
getSuccessor().HandleRequest(num);
}
}
}
/**
* @author wuyimin
* @create 2021-07-29 19:25
* @description 兄弟类
*/
public class Brother extends Handler {
@Override
public void HandleRequest(int num) {
if(num<=2000){
System.out.println("你的好兄弟答应了要借你"+num+"元");
}else{
System.out.println("你的好兄弟没有那么多钱");
getSuccessor().HandleRequest(num);
}
}
}
/**
* @author wuyimin
* @create 2021-07-29 19:26
* @description 爹妈类
*/
public class Parent extends Handler {
@Override
public void HandleRequest(int num) {
System.out.println("你的爹妈借了你"+num+"元");
}
public static void main(String[] args) {
Brother brother = new Brother();
Teacher teacher = new Teacher();
Parent parent = new Parent();
teacher.setSuccessor(brother);
brother.setSuccessor(parent);
teacher.HandleRequest(5000);
}
}
Mybatis的二级缓存是协同关系,就是我处理完了马上就给你处理,把Logging Cache,SynchronizedCache,TransactionalCache等实现类串联起来,走一套流程把事情做完
Linux怎么杀掉一个进程
ps -ef|grep 找到进程id,然后kill pid,加上-9参数是强制杀死,其中9代表信号变量SIGKILL的代号,表示立即终止的意思
讲讲CAS的实现
以AtomicInteger为例,它在rt.jar包下,通过BootStrap加载器进行加载,所以可以通过getUnsafe()方法拿到Unsafe类的实例,其内部方法都是基于Unsafe类实现,Unsafe类是个与底层硬件CPU指令通信的工作累,CAS全名叫compare and swap,其底层原理是Unsafe+自旋锁,其中的getandAddInt方法
//Unsafe类
//获取内存地址为obj+offset的变量值, 并将该变量值加上delta
public final int getAndAddInt(Object obj, long offset, int delta) {
int v;
do {
//通过对象和偏移量获取变量的值
//由于volatile的修饰, 所有线程看到的v都是一样的
v= this.getIntVolatile(obj, offset);
/*
while中的compareAndSwapInt()方法尝试修改v的值,具体地, 该方法也会通过obj和offset获取变量的值
如果这个值和v不一样, 说明其他线程修改了obj+offset地址处的值, 此时compareAndSwapInt()返回false, 继续循环
如果这个值和v一样, 说明没有其他线程修改obj+offset地址处的值, 此时可以将obj+offset地址处的值改为v+delta, compareAndSwapInt()返回true, 退出循环
Unsafe类中的compareAndSwapInt()方法是原子操作, 所以compareAndSwapInt()修改obj+offset地址处的值的时候不会被其他线程中断
*/
} while(!this.compareAndSwapInt(obj, offset, v, v + delta));
return v;
}
ABA问题解决,使用atomicStampedReference,拿到版本号再进行业务
|