java后端面试题整理
String
List集合
反射
java8新特性
锁
网络编程;
-
TCP的三次握手:
1.向服务端发起连接请求,等待服务器确认
2.服务端向客户端回收一个相应,通知客户端收到了连接请求
3.客户端再次向服务器发送确认信息,确认连接. -
TCP的四次挥手:
1.客户端向服务器发出取消连接请求
2.服务器向客户端返回一个响应
3.服务器向客户端发出确认取消信息
4.客户端再次发送确认消息
HashMap
- hash碰撞的解决方法
- 线性探测
- 二次探测
- 拉链
- 双重散列
- 多重散列
- 遍历map的几种方法
(1)问题分析:
考官主要考核map集合的四种遍历方式
(2)核心答案讲解:
第一种,通过key取值。(Map.keyset()遍历key和value)。
第二种,通过迭代器取值。(Map.enteySet()使用iterator遍历key和value)。
第三中,通过entryset()。(通过Map.enteySe()遍历key和value)。
第四中,通过map的value方法。(Map.values()遍历所有的value)。
(3)问题扩展:
无
(4)结合项目中使用:
在商城中,通过map记录保存相关商品信息 - map的特性
问题分析:
考官主要考核map中hashmap自身独有的特点。
核心答案讲解:
允许空键和空值(但空键只有一个,且放在第一位)
元素是无序的,而且顺序会不定时改变
key 用 Set 存放,所以想做到 key 不允许重复,key 对应的类需要重写 hashCode 和 equals 方法。
底层实现是 链表数组,JDK 8 后又加了红黑树。
实现了 Map 全部的方法
问题扩展:
红黑树本质上是一种二叉查找树,但它在二叉查找树的基础上额外添加了一个标记(颜色),同时具有一定的规则。
每个节点要么是红色,要么是黑色;
根节点永远是黑色的;
所有的叶节点都是是黑色的(注意这里说叶子节点其实是上图中的 NIL 节点);
每个红色节点的两个子节点一定都是黑色;
从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点
结合项目中使用:
有一个 实体类,HashMap 的 key 想通过 name 和 age 判断 people 是否相等,而不是通过 实体对象的存储地址.从而根据 HashMap 原理,需同时重写equals() 和 hashCode();
RabbitMQ面试题
1. 如何保证消息的顺序性
一个队列只有一个消费者的情况下才能保证顺序,否则只能通过全局ID实现(每条消息都一个msgId,关联的消息拥有一个parentMsgId。可以在消费端实现前一条消息未消费,不处理下一条消息;也可以在生产端实现前一条消息未处理完毕,不发布下一条消息)
2. 使用消息队列会带来什么问题
- 系统可用性降低: 消息可能会丢失或MQ会挂掉;
- 系统复杂性提高: 加入MQ之后,需要保证消息没有被重复消费、处理消息丢失的情况、保证消息传递的顺序性等等问题;
- 一致性问题: 消息队列可以实现异步,消息队列带来的异步确实可以提高系统响应速度。但是却不能保证消费者能够消费到消息;
3. 为什么不应该对所有的 message 都使用持久化机制?
message被可靠持久化的条件是:
- queue和exchange要有durable属性
- message要有persistent属性
首先,必然导致性能的下降,因为写磁盘比写 RAM (内存)慢的多,message 的吞吐量可能有 10 倍的差距。
其次,message 的持久化机制用在 RabbitMQ 的内置 cluster 方案时会出现问题。
矛盾点在于:
-
若 message 设置了persistent(持久化) 属性,但 queue 未设置 durable(持久化) 属性,那么当该 queue 的 owner node 出现异常后,在未重建该 queue 前,发往该 queue 的 message 将被 blackholed ,此queue可在其他node上进行重新声明;
-
若 message 设置了 persistent(持久化) 属性,同时 queue (队列)也设置了 durable 属性,那么当 queue 的 owner node 异常且无法重启的情况下,则该 queue 无法在其他 node 上重建,只能等待其 owner node 重启后,才能恢复该 queue 的使用,而在这段时间内发送给该 queue 的 message 将被 blackholed 。
-
所以,是否要对 message 进行持久化,需要综合考虑性能需要,以及可能遇到的问题。若想达到 10w 条/秒以上的消息吞吐量(单 RabbitMQ 服务器),则要么使用其他的方式来确保 message 的可靠 delivery(投递) ,要么使用非常快速的存储系统以支持全持久化(例如使用 SSD)。另外一种处理原则是:仅对关键消息作持久化处理(根据业务重要程度),且应该保证关键消息的量不会导致性能瓶颈。
4. 什么是blackholed
blackholed是指在向exchange投递message过程中,由于各种原因导致该message丢失,但是发送者不知道;如:
- 向未绑定queue的exchange发送message;
- 在向exchange发消息时,exchange与queue绑定的binding key与发送message使用的routing key不匹配
5. 若 cluster 中拥有某个 queue 的 owner node 失效了,且该 queue 被声明具有 durable 属性,是否能够成功从其他 node 上重新声明该 queue ?
不能,在这种情况下,将得到 404 NOT_FOUND 错误。只能等 queue 所属的 node 恢复后才能使用该 queue 。
但若该 queue 本身不具有 durable 属性,则可在其他 node 上重新声明。
多线程
1. 并发和并行是什么?
- 并发:在同一时刻,多个指令在单个CPU交替执行;
- 并行:在同一时刻,多个指令在多个CPU同时执行;
并发是逻辑上的同时发生,并行是物理上的同时发生;
并行是同时发生的多个并发事件;
注意:单核处理器的计算机不能并行的处理多个任务,只能是多个任务在单个CPU上并发运行。从宏观角度上理解线程是并行运行的,但是从微观角度上分析却是串行运行的,即一个线程一个线程的去运行,当系统只有一个CPU时,线程会以某种顺序执行多个线程,我们把这种情况称之为线程调度。
2. 什么是进程什么是线程?
- 进程: 进程是一个操作系统中正在执行的应用程序;
- 独立性:进程是一个独立运行的基本单位,是系统分配资源和调度的独立单位;
- 动态性:进程即程序的一次执行过程,动态产生,动态消亡;
- 并发性:可以和其他进程一起并发执行;
- 线程: 是进程中的一个执行单元;即执行路径、单个顺序控制流;
一个进程中至少有一个线程,多个线程的进程即多线程程序。
3. 线程的生命周期有哪些状态,并谈谈对这些状态的理解?
- 新建状态: 使用new关键字来新建一个线程时所处的状态;
- 就绪状态: 当线程对象调用start方法后,线程等待CPU调度的状态;
- 运行状态: 处于就绪状态的线程对象获得CPU调度,开始执行run方法所处状态;
- 阻塞状态: 是指线程因为某些原因放弃了CPU的使用权,暂时停止运行;
- 死亡状态: 线程死亡;
4. 进程和线程的区别
进程是操作系统资源分配的基本单位,线程是cpu调度的基本单位
- 进程: 有独立的内存空间,进程中的堆内存和栈内存是独立的,至少有一个线程;
- 线程: 堆内存是共享的,栈内存是独立的,线程消耗的资源比进程小;
扩展:
线程的执行取决于CPU的调度,即多线程的随机性;
java程序中至少有两个线程:一个是main方法,另一个是垃圾回收机制,每当java程序执行了一个类,就会启动一个jvm,每个jvm会在操作系统中启动一个线程,java本身有垃圾回收机制;
一个线程开销比一个进程小,开发多任务运行应该考虑创建多线程而非多进程;
5. 线程调度有几种方式?
- 分时调度: 所有线程轮流使用cpu使用权,平均分配每个线程占用CPU的时间;
- 抢占式调度: CPU会优先调度优先级高的线程,但是不绝对,有随机性;
6. 创建线程的方式有哪些?
- 继承Thread类: 定义一个类继承Thread,并重写run方法;
- 实现Runable接口:
- Callable、FutureTesk 有返回值线程: 这种方式少用,不推荐;
7. 继承方式和实现方式的的优劣
- 继承:使用简单,但可扩展性差;不能抛异常,无返回值,不适合资源共享;
- 实现:避免单继承的局限,可扩展性强,使用较为复杂,代码和线程独立,适合多个相同线程处理同一份资源;
8. run()方法和start()方法的区别?
- run():封装线程执行的代码,直接调用相当于普通方法;
- start():启动线程,由JVM调用此线程的run()方法;一个线程对象只能调用一次start()方法启动,重复调用会抛异常;
9. sleep 与 wait 区别
- sleep(): sleep()是Thread的静态方法,可由类名调用,让程序休眠并自动醒来继续执行程序;
- wait(): 是object的方法,必须由锁对象进行调用
10. 讲一讲守护线程
- 线程分为守护线程和用户线程
- 守护线程是用来服务用户线程的,也是线程的一种,可以通过api把线程设置为守护线程;
- 当普通线程执行完毕,守护线程也随之完毕,但是不会立即停止,因为还持有CPU执行权;
- 两者的区别就是判断JVM何时离开;
11. 线程池的作用及主要特点
- 线程池的主要作用: 主要是控制运行的线程的数量,在过程中把任务放入队列中,在线程创建后启动这些任务,如果线程数量超过了最大数量,超过数量的线程会排队等候,等其他线程执行完毕,再从队列中取出任务来执行;
- 多线程的特点: 线程复用、控制最大并发数、管理线程;
12. 线程池分为哪几个组成部分, 其中构造方法有哪些参数,并解释每个参数的意思
组成部分 | 作用 |
---|---|
线程池管理器 | 用于创建并管理线程池 |
工作线程 | 线程池中的线程 |
任务接口 | 每个任务必须实现的接口 |
任务队列 | 用于存放待处理的任务,提供一种缓冲机制 |
- 构造方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
corePoolSize: 线程池核心线程数
maximumPoolSize: 线程最大数
keepAliveTime: 当线程池线程数量超过核心线程数时,空余线程的存活时间
unit: keepAliveTime的时间单位
workQueue: 任务队列,尚未被执行的任务
threadFactory: 线程工厂,用于创建线程
handle: 拒绝策略,拒绝任务的策略
13. 线程池的工作流程:
- 线程池创建,但线程池创建时内部是没有线程的,通过任务队列(workQueue)来向线程池传输数据,但线程池也不会第一时间执行队列中的人物;
- 调用execute()方法添加任务,任务添加的过程中,线程池会判断:
- 如果运行中的线程数小于核心线程数,那么马上创建线程运行任务
- 如果运行中的线程数大于核心线程数,那么将任务放入任务队列
- 如果任务在放入任务队列的时候队列满了,而正在运行的线程数量小于最大线程数时,那么则创建非核心线程立刻执行这个任务;
- 如果任务在放入任务队列的时候队列满了,而正在运行的线程数量大于最大线程数时,会抛出异常;
- 当一个线程完成任务时,线程池会从队列中继续取出任务来执行;
- 当一个线程处于空闲状态,超过一定时间时,如自己设置的keepAliveTime时,当前运行线程数如果大于核心线程数,此线程会被停掉;
14. threadLocal的作用?说一下应用场景
- ThreadLocal的作用:
- 在java8的时候,每个线程内部都有一块特殊的空间,ThreadLocalMap,这个map集合的key值是当前线程本身的id,value是我们存进去的实例值的变量副本; 每个线程内部的ThreadLocalMap由自己维护,别的线程不能获取当前线程的变量副本,这也形成的线程的隔离;
- ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。
- 应用场景:
- 可以用来Session管理,存储一些共享值;
15. 谈一谈什么是守护线程(从定义,优先级,以及如何设置守护线程三个方面来说明)
- 守护线程:
- 定义: 就是一个后台线程,为用户线程提供公共服务,如果没有用户线程可以服务会自动离开
- 优先级:守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。
- 设置:通过 setDaemon(true)来设置线程为“守护线程”;将一个用户线程设置为守护线程 的方式是在 线程对象创建 之前 用线程对象的 setDaemon 方法。
- 在Daemon线程中产生的新线程也是 Daemon 的。
- 线程则是 JVM 级别的,即使停止了 Web 应用,这个线程 依旧是活跃的。
- example: 垃圾回收线程就是一个经典的守护线程,当我们的程序中不再有任何运行的Thread, 程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是 JVM 上仅剩的线 程时,垃圾回收线程会自动离开。它始终在低级别的状态中运行,用于实时监控和管理系统 中的可回收资源。
16. 聊一聊对volatile关键字的理解(概念、优缺点)
- 概念:一种同步机制,
- 作用:
- 变量可见性: 保证变量对所有线程可见,可见指的是当一个线程修改了变量的值,那么新的值对于其他线程是可以立即获取的,类似于事务一致性;
- 有序性: volatile进制重排序
- 比sychronized更轻量级的同步锁: volatile不会导致线程阻塞;
- 缺点: 只能保证单词读写操作的原子性;
- 在并发环境下的使用条件:
- 对变量的写操作不依赖于当前值(i++),或者单纯的变量赋值(boolean flag=true)
- 不同的volatile变量之间,不能互相依赖,只有在状态真正独立于程序内其他内容时,才能使用volatile;
- 保证高并发满足3个条件:原子性、有序性、可见性;volatile保整了可见性、有序性,通常配合原子操作类一起使用保证原子性;
-
多线程产生死锁的原因和四个必要条件
原因:
系统资源不足
进程运行推进的顺序不合适;
资源分配不当;
必要条件:- 互斥条件:任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请
者等待直到资源被占有者释放。 - 不可剥夺条件:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放。
- 请求和保持条件:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
- 互斥条件:任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请
-
任务拒绝策略有哪些
任务拒绝策略的触发时机:线程池最大线程数+队列容量<提交任务数量
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectExecutionException异常。,默认的策略;
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常,这是不推荐的做法;
ThreadPoolExecutor.DiscardOldest:抛弃队列中等待最久的任务然后把当前任务加入队列中;
ThreadPoolExecutor.CallerRunsPolicy:调用任务的run方法绕过线程池直接执行;
Redis
Spring
1. 什么是Spring IOC容器?
将手动创建对象的工作,交给spring容器完成;
- IOC(inversion of controller)即控制反转,把创建对象的控制器权交给Spring框架进行管理,并会根据配置文件去创建实例、管理各个实例之间的依赖关系、对象之间的松散耦合;
- spring IOC有三种注入方式:构造器注入、setter方法注入、注解
- 作用:
- 管理实例之间的依赖关系;
- 解耦:容器维护对象之间的耦合;
- 托管了类的产生过程。比如代理;
2. 谈谈对AOP的理解
在不改变原来代码的同时,为原来代码添加新的功能
- OOP面向对象,AOP面向切面是面向对象的补充,用于把那些与业务无关,但却对多个对象产生了公共行为和逻辑。抽取并封装为一个可重用的模块,即切面;
- 作用:
- 减少重复代码
- 降低代码的耦合
- 提高系统的可维护性;
- 应用场景:权限认证、日志、事务处理;
3. JDK动态代理和CGLIB动态代理的区别
- JDK动态代理只提供接口的代理,不支持类的代理;因为所有生成的代理类的父类都是Proxy,java中不支持多继承;InvocationHandler接口和Proxy类,InvocationHandler通过invoke()方法反射调用目标类中的代码,然后会创建一个符合某接口的实例,生成目标类的代理对象;
- java动态代理使用java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类执行过程中比较高效;
- 如果代理类没有实现InvocationHandler接口,那么SpringAOP对选择使用CGLB来动态代理目标类,可以在运行时动态生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做动态代理;
静态代理和动态代理区别在于生成AOP对象的时机不同,相对来说AspectJ的静态代理具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理;
InvocationHandler 的 invoke(Object proxy,Method method,Object[] args):proxy是最终生成的代理实例; method 是被代理目标实例的某个具体方法; args 是被代理目标实例某个方法的具体入参, 在方法反射调用时使用。
4. AspectJ和Spring AOP区别
AOP实现关键在于代理模式,AOP代理主要分为静态代理和动态代理;静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。
- AspectJ是静态代理,编译时增强。AOP框架会在编译阶段生成AOP代理类,并将AspectJ(切面)织入到Java字节码中,运行的时候就是增强后的对象;
- Spring AOP使用的是动态代理,所谓的动态代理就是AOP框架不会修改字节码,而每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并在特定的切点做了增强,并回调源对象的方法;
5. Spring支持的事务管理类型,Spring事务实现的方式有哪些
Spring支持两种类型的事务管理:
- 编程式事务管理:灵活度高,但是难维护;
- 声明式事务管理:可以将业务代码和事务管理分离,只需要用注解和XML配置来管理事务;
6. Spring事务的实现方式和实现原理
Spring事务的本质其实就是数据库对事物的支持,没有数据库的事务支持,spring是无法提供事务功能的,真正的数据库底层的事务提交和回滚是通过binlog或redo log实现的;
7. Spring事务传播行为
spring传播行为说的是:当多个事务同时存在时,spring如何处理这些事务的行为;
- PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新的事务;如果当前存在事务,就加入该事务,该设置是最常用的设置;
- PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务就以非事务执行;
- PROPAGATION_MANDATORY:支持当前事务。如果当前存在事务就加入,不存在则抛出异常;
- PROPAGATION_REQUIRES_NEW:创建新事物;无论当前是否存在事务,都会创建新事务;
- PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起;
- PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常;
- PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则按REQUIRED属性;
8. 说一下spring的事务隔离
spring有五大隔离级别,默认值为ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库一致;
① ISOLATION_DEFAULT:这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。
② ISOLATION_READ_UNCOMMITTED:读未提交,允许事务在执行过程中,读取其他事务未提交的数据。
③ ISOLATION_READ_COMMITTED:读已提交,允许事务在执行过程中,读取其他事务已经提交的数据。
④ ISOLATION_REPEATABLE_READ:可重复读,在同一个事务内,任意时刻的查询结果都是一致的。
⑤ ISOLATION_SERIALIZABLE:所有事务逐个依次执行。
脏读:表示一个事务能够读取另一个事务中还未提交的数据,比如:某个事务尝试插入记录A,此事务还未提交,然后另一个事务读取到了记录A;
不可重复读:是指在一个事务内,多次读同一个数据;
幻读:指同一个事务内多次查询返回的结果集不一样,比如同一个事务A第一次插叙拿到时候有n条记录,但是第二次同等条件查询却又n+1条记录;发生幻读的原因是另外一个事务对数据进行了增删改操作,同一个记录的数据被修改,数据行的记录就改变了;
9. Spring通知(Advice)有哪些类型
- 前置通知(Before Advice):在连接点(Join Point)之前执行的通知;
- 后置通知(After Advice):当连接点退出的时候执行的通知(不论是正常返回还是异常退出);
- 环绕通知(Around Advice):包围一个连接点的通知,最强大的通知类型,环绕通知可以在方法调用前后完成自定义的行为,也可以选择是否继续执行连接点或直接返回自己的返回值或抛出异常来结束;
- 返回后通知(AfterReturning Advice):在连接点正常完成后执行的通知(如果连接点抛出异常这不执行);
- 抛出异常后通知(AfterThrowing Advice):在方法抛出异常退出执行的通知;
10. Spring AOP里面的名词概念:
- 连接点(Join point):指程序运行过程中所执行的方法,在Spring AOP中,一个连接点代表一个方法的执行;
- 切面(Aspect): 被抽取出来的公共模块,可以用来横切多个对象,Aspect切面可以看成Pointcut切点和Advice通知的结合,一个切面可以由多个切点和通知组成。
在Spring AOP中,切面可以在类上使用 @AspectJ 注解来实现。 - 切点(Pointcut): 切点用于定义要对哪些连接点进行拦截;
切点分为execution方式和annotation方式;execution方式可以用路径表达式指定对哪些方法拦截,比如指定拦截add、search。
annotation方式可以指定被哪些注解修饰的代码进行拦截; - 通知(Advice) 要在连接点上执行的动作,即增强的逻辑,如:权限检验、日志记录等;通知有各种类型,包括Around、Before、After、After returning、After throwing。
- 目标对象(Target): 包含连接点对象,也称作被通知的对象,由于Spring AOP是通过动态代理实现的,所以这个对象永远是一个代理对象;
- 织入(Weaving): 通过动态代理,在目标对象(Target)的方法(Join point)中执行增强逻辑(Advice)的过程;
- 引入(Introduction): 添加额外的方法或者字段到被通知的类。spring允许引入新的接口到任何被代理的对象。例如:可以使用一个引入来使bean实现isModified,以便简化缓存机制;
11. BeanFactory和ApplicationContext有什么区别?
BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。
(1)BeanFactory是Spring里面最底层的接口,是IoC的核心,定义了IoC的基本功能,包含了各种Bean的定义、加载、实例化,依赖注入和生命周期管理。ApplicationContext接口作为BeanFactory的子类,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:
- 继承MessageSource,因此支持国际化。
- 资源文件访问,如URL和文件(ResourceLoader)。
- 载入多个(有继承关系)上下文(即同时加载多个配置文件) ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。
- 提供在监听器中注册bean的事件。
(2)
①BeanFactroy采用的是延迟加载形式来注入Bean的,只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能提前发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
②ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。
③ApplicationContext启动后预载入所有的单实例Bean,所以在运行的时候速度比较快,因为它们已经创建好了。相对于BeanFactory,ApplicationContext 唯一的不足是占用内存空间,当应用程序配置Bean较多时,程序启动较慢。
(3)BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
(4)BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。
12. Spring Bean的生命周期?
简单来说,Spring Bean的生命周期只有四个阶段:实例化 Instantiation --> 属性赋值 Populate --> 初始化 Initialization --> 销毁 Destruction
13. Spring中bean的作用域:
(1)singleton:默认作用域,单例bean,每个容器中只有一个bean的实例。
(2)prototype:为每一个bean请求创建一个实例。
(3)request:为每一个request请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
(4)session:与request范围类似,同一个session会话共享一个实例,不同会话使用不同的实例。
(5)global-session:全局作用域,所有会话共享一个实例。如果想要声明让所有会话共享的存储变量的话,那么这全局变量需要存储在global-session中。
14. Spring框架中的Bean是线程安全的么?如果线程不安全,那么如何处理?
Spring容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体情况还是要结合Bean的作用域来讨论。
(1)对于prototype作用域的Bean,每次都创建一个新对象,也就是线程之间不存在Bean共享,因此不会有线程安全问题。
(2)对于singleton作用域的Bean,所有的线程都共享一个单例实例的Bean,因此是存在线程安全问题的。但是如果单例Bean是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Controller类、Service类和Dao等,这些Bean大多是无状态的,只关注于方法本身。
有状态Bean(Stateful Bean) :就是有实例变量的对象,可以保存数据,是非线程安全的。
无状态Bean(Stateless Bean):就是没有实例变量的对象,不能保存数据,是不变类,是线程安全的。
15. Spring 框架中都用到了哪些设计模式?
(1)工厂模式:Spring使用工厂模式,通过BeanFactory和ApplicationContext来创建对象
(2)单例模式:Bean默认为单例模式
(3)策略模式:例如Resource的实现类,针对不同的资源文件,实现了不同方式的资源获取策略
(4)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术
(5)模板方法:可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中,用来解决代码重复的问题。比如RestTemplate, JmsTemplate, JpaTemplate
(6)适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式,Spring MVC中也是用到了适配器模式适配Controller
(7)观察者模式:Spring事件驱动模型就是观察者模式的一个经典应用。
(8)桥接模式:可以根据客户的需求能够动态切换不同的数据源。比如我们的项目需要连接多个数据库,客户在每次访问中根据需要会去访问不同的数据库
Spring MVC
- spring mvc启动流程
1.启动服务器, 解析web.xml
2.由于配置了dispatcherServlet,和启动时加载, 所以会创建此类实例
3.Web.xml加载初始化参数: spring-mvc.xml, 找到此文件
4.Spring-mvc.xml扫描包扫描到Controller
5.此类上有Controller注解, 所以会把此类对象存入IOC容器
6.方法上有requestMapping注解, 会记录requestmapping的信息
7.通过浏览器访问发起请求时, dispatcherServlet会拦截我们除开jsp以外的所有请求,
8.获取到请求路径与我们所加载的@requestMapping的信息进行匹配,
9.当匹配到对应的方法时, 就执行方法, 并得到返回值,
10.根据返回值在webapp目录中查找对应的页面, 并响应;