注:部分答案引用网络文章

简答题

1、Spring项目启动后的加载流程

(1)使用spring框架的web项目,在tomcat下,是根据web.xml来启动的。web.xml中负责配置启动springmvc和启动spring,其中,在

<servlet>
<servlet-name>springMVC(名字任意)</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>中启动spring mvc并进行资源路径映射

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext.xml</param-value>
</context-param>

中通过ContextLoaderListener启动spring容器,同时自动装配applicationContext.xml中的配置信息。

(2)spring中对一些orm框架的启动,包括Mybatis/hibernate。orm框架的启动基本都是通过sqlsessionFactory bean来启动的,并配置各种bean到ioc容器中。包括datasource等:

<!-- MyBatis配置 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 显式指定Mapper文件位置 -->
<property name="mapperLocations" value="classpath*:/mybatis/*/*Mapper.xml" />
<!-- mybatis配置文件路径 -->
<property name="configLocation" value="classpath:/mybatis/config.xml" />
</bean>

(3)web应用程序中,spring相当于程序运行的平台,spring对整个程序提高供ioc支持和aop支持。spring提供注解如@service @repository @component将各种类注册到ioc容器中。通过设置scan package的方式,spring在启动时候会扫描包下的所有注解,并将它们注册到ioc容器中。并针对@autowired @resource,将一些bean从ioc容器中获取填充到bean的构造属性中(@autowired @resource只针对类成员变量,不针对方法的局部变量)。spring会根据配置自动扫描包中的注解:

<!-- 启用spring mvc注解 -->
<context:annotation-config />
<context:component-scan base-package="com.kuangchi.*" />

即所有的bean注入都在applicationContext.xml中配置的

注:正是spring的ioc支持了controller层注入service,service注入dao。打通了各层之间的桥梁,省去了原来的new service(),new Dao()的方法。

延伸:(1)spring IOC的好处:1方便测试,被测试类的依赖类可以通过spring IOC注入进来,2 方便维护,只要接口不变,重写实现类,修改配置即可 ,3 默认IOC容器管理的bean都是单例的,不会被回收掉,4 面向接口开发,service里直接是接口就可以了,解耦 , 5 用AOP作增强

(2)spring的面向接口编程:由于依赖接口,可通过依赖注入随时替换dao接口的实现类,而应用程序完全不用了解接口与底层数据库操作交互的细节。

2、SpringMVC请求响应流程

SpringMVC框架是一个基于请求驱动的Web框架,并且使用了‘前端控制器’模型来进行设计,再根据‘请求映射规则’分发给相应的页面控制器进行处理。

整体流程:

()1、  首先用户发送请求到前端控制器,前端控制器根据请求信息(如 URL)来决定选择哪一个页面控制器进行处理并把请求委托给它,即以前的控制器的控制逻辑部分;图中的 1、2 步骤;

()2、  页面控制器接收到请求后,进行功能处理,首先需要收集和绑定请求参数到一个对象,这个对象在 Spring Web MVC 中叫命令对象,并进行验证,然后将命令对象委托给业务对象进行处理;处理完毕后返回一个 ModelAndView(模型数据和逻辑视图名);图中的 3、4、5 步骤;

()3、  前端控制器收回控制权,然后根据返回的逻辑视图名,选择相应的视图进行渲染,并把模型数据传入以便视图渲染;图中的步骤 6、7;

()4、  前端控制器再次收回控制权,将响应返回给用户,图中的步骤 8;至此整个结束。

核心流程:

第一步:发起请求到前端控制器(DispatcherServlet)

第二步:前端控制器请求HandlerMapping查找 Handler (可以根据xml配置、注解进行查找)

第三步:处理器映射器HandlerMapping向前端控制器返回Handler,HandlerMapping会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象,多个HandlerInterceptor拦截器对象),通过这种策略模式,很容易添加新的映射策略

第四步:前端控制器调用处理器适配器去执行Handler

第五步:处理器适配器HandlerAdapter将会根据适配的结果去执行Handler

第六步:Handler执行完成给适配器返回ModelAndView

第七步:处理器适配器向前端控制器返回ModelAndView (ModelAndView是springmvc框架的一个底层对象,包括 Model和view)

第八步:前端控制器请求视图解析器去进行视图解析 (根据逻辑视图名解析成真正的视图(jsp)),通过这种策略很容易更换其他视图技术,只需要更改视图解析器即可

第九步:视图解析器向前端控制器返回View

第十步:前端控制器进行视图渲染 (视图渲染将模型数据(在ModelAndView对象中)填充到request域)

第十一步:前端控制器向用户响应结果

总结 核心开发步骤

()1、  DispatcherServlet 在 web.xml 中的部署描述,从而拦截请求到 Spring Web MVC

()2、  HandlerMapping 的配置,从而将请求映射到处理器

()3、  HandlerAdapter 的配置,从而支持多种类型的处理器

注:处理器映射求和适配器使用纾解的话包含在了注解驱动中,不需要在单独配置

()4、  ViewResolver 的配置,从而将逻辑视图名解析为具体视图技术

()5、  处理器(页面控制器)的配置,从而进行功能处理

View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf...)

3、Spring事务什么时候会失效(spring事务失效的原因)

事务就是一系列指令的集合,服从ACID 原则

(1)使用了spring+springmvc,则在 <context:component-scan>重复扫描问题可能会引起事务失效

原因是:spring的context是父子容器,会产生冲突。

由servletContextListener产生的是父容器,springmvc产生的是子容器,子容器controller进行扫描装配时,装配了@service注解的实例,而该实例应由父容器进行初始化以保证事务的增强处理,故而此处得到的将是没有经过事务加强处理、没有事务处理能力的原样service

解决方法:

在主容器applicationContext.xml中将controller的注解排除掉:

<context:component-scan base-package="com"

<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />

</context:component-scan

在springMVC配置文件springmvc中将service注解给去掉:

<context:component-scan base-package="com"

    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> 
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" /> 
</context:component-scan>
(2)@Transactional注解开启配置,如果放到DispatcherServlet的配置里,事务也是不起作用的。必须放到listener里加载
(3)@Transactional注解只能应用到public方法上,若在protected、private或者package-visible的方法上使用,不会报错,但事务会失效
(4)@Transactional注解要用在具体的类或方法上,不要用在类所要实现的任何接口上。
原因:在接口上使用@Transactional注解,只能当你设置了基于接口的代理时才生效。因为注解是不能被继承的,这就意味着如果正在使用基于类的代理时,事务的设置将不能被其识别,而且对象不会被事务代理所包装

4、synchronized和volatile关键字 

java中为了保证多线程读写数据时,保证数据的一致性,采用:

(1)同步:如synchronized关键字,或者锁对象

(2)使用volatile关键字:它能使变量在值发生改变时尽快让其他线程知道;

对volatile关键字的解释:volatile关键字只修饰变量。一般对变量的写操作会先在寄存器或者是CPU缓存上进行,最后才写入内存,这个过程中变量的新值对其他线程是不可见的(在多线程问题中这会引发严重问题)。修改volatile修饰的变量的值时,会将其他缓存中存储的修改前的变量清除,然后重新读取,即修改volatile修饰的变量的值时,会直接更新其他缓存中该变量的值(即达到了尽快通知其他线程该变量值改变的目的)。

volatile与synchronized的比较:

(1)volatile本质是告诉JVM当前变量在寄存器中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞;(volatile不会造成线程阻塞而synchronized会)

(2)volatile仅能修饰变量;synchronized能修饰变量和方法(如单例模式);

(3)volatile仅能实现变量的修改可见性;synchronized可以保证变量的修改可见性和原子性(操作步骤要么全部执行,要么都不执行);

volatile失效的情况:1 当一个域的值依赖于它之前的值(n+=1、n++) 2 当某个域的值受到其他域的值的限制(如Range类的lower和upper边界,需要lower<=upper)

使用volatile而不是synchronized唯一安全的情况是只有一个可变的域

5、画出JMM和JVM模型,并添加注释

JMM:java内存模型,规定了线程和内存之间的关系(线程相关)。

系统存在一个主内存,java所有变量都储存在主存中,对所有线程共享。每条线程有自己的工作内存(即本地内存),其中保存的是主存中的变量的拷贝,线程对变量的操作都是在自己的工作内存中进行的,变量传递需要通过主存完成(涉及问题4)

JVM模型:

classLoader:类加载器,将class文件加载到内存(做数据校验、转换解析、初始化数据)

Runtime data area: 运行时数据区,即JVM管理的内存

program counter register:程序计数器,线程私有,指向下一条要执行的指令,(区分“寄存器”,寄存器是执行指令的)

java stack: 栈,线程私有,生命周期与线程相同。每个方法被执行时都会创建一个栈,用于存储局部变量

heap: 堆,线程共享,存储实例对象及数组,是垃圾收集器管理的主要区域

method area:方法区,线程共享的内存区域,存储类信息、常量、静态变量

6、二分查找法的算法实现(java实现)

二分法要求数组是有序的,查找速度快,适用于不经常变动而查找频繁的有序列表

递归实现:

public static int binarySearch(int[] arr,int key,int low,int high){  //方法最后返回的是查找到的位置

   if(key > arr[high] || key < arr[low] || low > high){ return -1;}

   int mid = (low + high)/2;

   if(key < arr[mid]){

     return binarySearch(arr, key, low, mid-1);

    }else if(key > arr[mid]){

      return binarySearch(arr, key, mid+1, high)

    }else{return mid;}

}

非递归实现(用while循环)

public static int commonBinarySearch(int[] arr, int key){

  int low = 0;  //低位

  int high = arr.length-1;  //高位

  int mid = 0;

  if(key > arr[high] || key < arr[low] || low > high){  return -1; }

  while(low <= high){

    mid = (low+high)/2;

    if(arr[mid] > key){

      high = mid - 1;

    }else if(arr[mid] < key){

      low = mid+1;

    }else{ return mid; }

  }

  return -1;  //最后没有找到,返回-1

}

7、有哪些垃圾收集器,及其使用的垃圾回收算法

一、(1)Serial串行垃圾收集器:采用复制算法、使用单线程进行垃圾回收(执行垃圾回收时,冻结所有应用程序线程),不适合服务器环境,适合简单的命令行程序

(2)Parallel并行垃圾收集器:JVM的默认垃圾收集器,采用复制算法、使用多线程进行垃圾回收(执行垃圾回收时,冻结所有的应用程序线程)

(3)CMS并发标记扫描垃圾收集器(concurrent mark sweep):采用“标记-清除”算法、使用多线程扫描堆内存,标记需要清理的实例并清理被标记过的实例。(会出现“碎片”问题)

当标记的引用对象在tenured区域(heap区下的一个区)或者当垃圾回收的时候堆内存的数据被并发的改变,CMS会持有应用程序所有线程。

相比并行垃圾收集器,CMS使用更多的CPU来确保程序的吞吐量;若为了更好的程序性能分配更多的CPU,那么CMS是第一选择。

(4)G1垃圾收集器(比较新,JDK1.7才正式引入):适用于堆内存很大的情况,将堆内存分割多区域,并且并发的对其进行垃圾回收,回收之后对剩余的堆内存空间进行压缩(G1会优先选择第一块垃圾最多的区域)

二、垃圾回收算法:(1)复制算法:把内存空间分成两等分,垃圾回收时,把当前区域中正在使用的对象复制到另一区域(缺点是需要两倍内存空间)

(2)标记-清除算法:从引用根节点开始标记所有被引用的对象,再遍历整个堆,将未标记的对象清除(缺点是要暂停整个应用,产生内存碎片)

(3)标记-整理算法:结合了(1)(2)的优点,从根节点标记所有被引用的对象,再遍历整个堆,清除未标记对象,并把存活的对象按顺序压缩到堆中一块。避免了碎片问题,也避免了两倍空间问题

8、三个线程:T1输出A,T2输出B,T3输出C,如何保证控制台顺序打印A,B,C?

实现线程顺序输出的三种方式:1、使用join方法,等待线程结束在执行其他;2、使用同步synchronized方法;3、使用锁ReentrantLock

1、最简单的实现方式join方法,思路:线程T1打印A,线程T2等待T1结束,再打印B,线程T3等待T2结束,在打印C

主类   public class MainClass{

  public static void main(String[] args){

    ThreadT1 T1= new ThreadT1 ();

    ThreadT2 T2= new ThreadT2(T1);

    ThreadT3 T3= new ThreadT3(T2);

    T1.start();  T2.start();    T3.start();   //此处三个线程启动的顺序可随意,最终都会顺序打印出ABC

  } 

}

线程类    class ThreadT1 extends Thread{

  public void run(){ system.out.println("A") }

}

class  ThreadT2  extends Thread{

  private ThreadT1  t1;

  public  ThreadT2 (ThreadT1   t){ this.t1 = t; }

  public void run(){

    try{

      t1.join();//等待t1线程结束

    }catch(InterruptedExceptionb e){ e.printStackTrace();  }

    system.out.println("B");

  }

}

//线程T3同上实现

2、使用synchronized同步,思路:主类中一个状态变量、三个synchronized修饰的方法分别打印A\B\C,同步锁在唤醒的过程中,会将同一个锁上的线程都唤醒,故方法中的条件判断中使用while循环

主类   public class PrintABC{

  private int status = 1;

  public void printA(){

    synchronized(this){

      while(status!=1){

        try{  this.wait();   }catch(interruptedException e){  e.printStackTrace();  }

      }

      system.out.println("A");   status = 2;   this.notifyAll();     //打印A后,修改状态变量值,唤醒之前被wait的线程

    }

  }

public void printB(){

    synchronized(this){

      while(status!=2){

        try{  this.wait();   }catch(interruptedException e){  e.printStackTrace();  }

      }

      system.out.println("B");   status = 3;   this.notifyAll();     //打印B后,修改状态变量值,唤醒之前被wait的线程

    }

  }

public void printC(){

    synchronized(this){

      while(status!=3){

        try{  this.wait();   }catch(interruptedException e){  e.printStackTrace();  }

      }

      system.out.println("C");   status = 1;   this.notifyAll();     //打印C后,修改状态变量值,唤醒之前被wait的线程

    }

  }

   public static void main(string[] args){

  PrintABC pabc = new PrintABC();

  Thread printA = new Thread(new RunnableA(pabc));

  Thread printB = new Thread(new RunnableB(pabc));

  Thread printC = new Thread(new RunnableC(pabc));

  printA.start();  printB.start();   printC.start();

}

}

线程类   class   RunnableA  implements Runnable{

  private  PrintABC  p;

  public RunnableA (PrintABC  pp){ 

    super();     

     this.p = pp;

  }

  public void run(){

    p.printA();

   }

}

//RunnableB、RunnableC同上实现

3、使用jdk1.5并发包中引入的ReentrantLock,是在方式2的基础上,主类添加ReentrantLock锁,其他不变,比方式2更灵活,也提供了在获取锁时阻塞的办法

import  java.util.concurrent.locks.Condition;

import  java.util.concurrent.locks.ReentrantLock;

public class PrintABC{

  private int status = 1;

  private ReentrantLock lock = new ReentrantLock();

  private Condition ca = lock.newCondition();

  private Condition cb= lock.newCondition();

  private Condition cc = lock.newCondition();

  public void printA(){

    lock.lock();

    try{

      if(status != 1){  ca.await();  }

      system.out.println("A");

      status = 2;

      cb.signal();

    }catch(){ e.printStackTrace();

    }finally{ lock.unlock();

    }

  }  

public void printB(){

    lock.lock();

    try{

      if(status != 2){  cb.await();  }

      system.out.println("B");

      status = 3;

      cc.signal();

    }catch(){  e.printStackTrace();

    }finally{  lock.unlock();

    }

  }

public void printC(){

    lock.lock();

    try{

      if(status != 3){  cc.await();  }

      system.out.println("C");

      status = 1;

      ca.signal();

    }catch(){  e.printStackTrace();

    }finally{  lock.unlock();

    }

  }

  //主方法同方式2

}

//线程类同方式2

java面试记录二:spring加载流程、springmvc请求流程、spring事务失效、synchronized和volatile、JMM和JVM模型、二分查找的实现、垃圾收集器、控制台顺序打印ABC的三种线程实现的更多相关文章

  1. Spring加载xsd引起的问题小记

    前言 最近要把之前写好的监控系统加上报警功能,就是通过rpc调用发短信发邮件的服务发送报警信息.发短信发邮件的功能是通过dubbo管理提供的.自然使用这些服务就难免用到spring.而我这又是一个st ...

  2. 【Java基础】Java类的加载和对象创建流程的详细分析

    相信我们在面试Java的时候总会有一些公司要做笔试题目的,而Java类的加载和对象创建流程的知识点也是常见的题目之一.接下来通过实例详细的分析一下. 实例问题 实例代码 Parent类 package ...

  3. Spring加载properties文件的两种方式

    在项目中如果有些参数经常需要修改,或者后期可能需要修改,那我们最好把这些参数放到properties文件中,源代码中读取properties里面的配置,这样后期只需要改动properties文件即可, ...

  4. Java类的加载和对象创建流程的详细分析

    相信我们在面试Java的时候总会有一些公司要做笔试题目的,而Java类的加载和对象创建流程的知识点也是常见的题目之一.接下来通过实例详细的分析一下: package com.test; public ...

  5. Dubbo实践(六)Spring加载Bean流程

    根据上一小节对于spring扩展schema的介绍,大概可以猜到dubbo中相关的内容是如何实现的. 再来回顾Dubbo实践(一)中定义的dubbo-provider.xml: <?xml ve ...

  6. 【Java Web开发学习】Spring加载外部properties配置文件

    [Java Web开发学习]Spring加载外部properties配置文件 转载:https://www.cnblogs.com/yangchongxing/p/9136505.html 1.声明属 ...

  7. 你知道 Java 类是如何被加载的吗?

    前言 最近给一个非 Java 方向的朋友讲了下双亲委派模型,朋友让我写篇文章深度研究下JVM 的 ClassLoader,我确实也好久没写 JVM 相关的文章了,有点手痒痒,涂了皮炎平也抑制不住的那种 ...

  8. 监听spring加载完成后事件

    有这个想法是在很早以前了,那时的我没有接触什么缓存技术,只知道hibernate有个二级缓存.没有用过memcache,也没有使用过redis. 只懂得将数据放到数组里或者集合里,一直不去销毁它(只有 ...

  9. Android Phonebook编写联系人UI加载及联系人保存流程(一)

    2014-01-06 17:05:11 将百度空间里的东西移过来. 本文适合ROM定制做Phonebook的童鞋看,其他人飘过即可- Phonebook添加/编辑联系人UI加载及保存联系人流程,是一系 ...

随机推荐

  1. js 细节

    1.js中var a=s=1和var a=1,s=1的区别 function fl() { ; } function fl1() { , s = ; } 看出问题所在了吗? var q=w=1 中的s ...

  2. Blazui 常见问题:我更新了数据,为什么界面没刷新?

    首发于:http://www.blazor.group:8000/topic/reply?tpid=9 开门见山,不介绍,不废话 建议食用本文前先食用 https://www.cnblogs.com/ ...

  3. 多线程笔记 - Master-Worker

    多线程的 Master-Worker 从字面上也是可以理解的. Master 相当于领导, 一个就够了, 如果是多个, 那么听谁的, 是个大问题. Master负责指派任务给 Worker. 然后对每 ...

  4. python3-cookbook笔记:第八章 类与对象

    python3-cookbook中每个小节以问题.解决方案和讨论三个部分探讨了Python3在某类问题中的最优解决方式,或者说是探讨Python3本身的数据结构.函数.类等特性在某类问题上如何更好地使 ...

  5. 11种常用css样式之开篇文本字体学习

    常见css样式:1.字体与颜色2.背景属性3.文本属性4.边框属性5.鼠标光标属性6.列表样式7.定位属性8.内外边距9.浮动和清除浮动10.滚动条11.显示和隐藏 文本:1.letter-spaci ...

  6. everspin最新1Gb容量扩大MRAM吸引力

    everspin提供了8/16-bit的DDR4-1333MT/s(667MHz)接口,但与较旧的基于DDR3的MRAM组件一样,时序上的差异使得其难以成为DRAM(动态随机存取器)的直接替代品.   ...

  7. leetcode-简单-栈-有效的括号

    给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效. 有效字符串需满足:  左括号必须用相同类型的右括号闭合. 左括号必须以正确的顺序闭合. 注意空字符串可被 ...

  8. python-21-生成器又是什么东西?

    前言 生成器,只要含有yield关键字的函数都是生成器函数,但yield不能和return共用且需要写在函数内. 生成器,是返回一个迭代器的函数,说白了生成器也是迭代器. 一.生成器简介 1.只要含有 ...

  9. Linux下使用Tomcat

    切换到root账户. tomcat依赖jdk,先安装jdk,注意tomcat对jdk的版本有要求,要看一下tomcat.jdk的版本是否对应. 1.下载tomcat7 不使用软件源,自己下载安装,这样 ...

  10. Mac-App Store 购买过程中出错 请求超时

    打开终端 输入下面命令回车: defaults delete com.apple.appstore.commerce Storefront 接上步骤,继续输入下面命令回车: defaults writ ...