说一下SpringMVC的核心架构
1)、tomcat 的工作线程将请求转交给spring mvc框架的 DispatcherServlet
2)、DispatcherServlet查找标注了@Controller
注解的类,此时根据请求的uri,去定位到哪个Controller类来进行处理
3)、根据@RequestMapping去查找,使用这个Controller类的哪个方法来进行请求的处理
4)、在Controller的方法处理完后会有一个返回值,以前的时候,一般来说还是走jsp、模板技术,向DispatcherServlet 返回一个ModelAndView对象;然后会有一个 ViewResolver 结合Model和View,来渲染视图。
5)、现在前后端分离,Controller的方法处理完直接返回一个json串给前端
@ RequestMapping 的作用是什么
RequestMapping 是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径
RequestMapping 注解有六个属性,总共分成三类进行说明
value, method
value:指定请求的实际地址,指定的地址可以是URI Template 模式(后面将会说明); method:指定请求的method类型, GET、POST、PUT、DELETE等;
consumes,produces
consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
params,headers
params: 指定request中必须包含某些参数值是,才让该方法处理。
headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。
谈谈你对IOC的理解
IOC(Inverse of Control:控制反转)是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由Spring容器来管理,也就是说在Spring容器初始化时,根据你的xml配置,或者是注解,去实例化bean对象,底层的核心用的就是反射技术,通过反射直接根据你的类去自己构建对应的对象出来
Spring IOC 解决了系统类与类之间彻底的解耦合,假设有个 MyService 接口,有个 MyServiceImpl 实现类,如果没有IOC技术,则系统需要使用的地方都要通过 MyService myService = new MyServiceImpl()
方式来创建,如果现在有个新的实现类 NewServiceManagerImpl,这时但凡以前创建的地方全都要去改,改动代码成本很大,改动完以后的测试的成本很大
谈谈你对AOP的理解
AOP(Aspect-Oriented Programming:面向切面编程) 能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性
Spring AOP就是基于动态代理的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用Cglib ,生成一个被代理对象的子类来作为代理
Spring如何管理Bean的
工厂模式,Spring IOC 核心的设计模式的思想提现,他自己就是一个大的工厂,Spring容器初始化时,根据你的配置运用反射技术去实例化bean对象,把所有的bean实例都给放在了Spring容器里
如果你要使用bean,就找Spring容器就可以了,你自己不用创建对象了,后续如果有哪个接口的实现类发生改变了,只需要在创建Bean的地方去重新new新的实现类即可,这就是工厂模式的体现
Spring支持几种 bean 的作用域
(1)singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例
(2)prototype:原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例
(3)request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例。
(4)session:对于每次HTTP Session,使用session定义的Bean豆浆产生一个新实例。
(5)globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例
如果不指定Bean的作用域,Spring默认使用singleton作用域。一般来说在开发的时99.99%的时候都是用singleton单例作用域,因为 prototype 作用域Java在创建Bean实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加
而 singleton 作用域的Bean实例一旦创建成功,可以重复使用。 因此,除非必要,否则尽量避免将Bean被设置成prototype作用域
Spring中的Bean是线程安全的吗
Spring Bean默认来说,singleton,都是线程不安全的,在web系统,一般来说很少在Spring Bean里放一些实例变量,一般来说他们都是多个组件互相调用,最终去访问数据库的,所以即使有多个线程并发去访问,也不会造成什么影响
Spring中使用了哪些设计模式
工厂模式
Spring IOC 核心的设计模式的思想提现,他自己就是一个大的工厂,Spring容器初始化时,根据你的配置运用反射技术去实例化bean对象,把所有的bean实例都给放在了Spring容器里
如果你要使用bean,就找Spring容器就可以了,你自己不用创建对象了,后续如果有哪个接口的实现类发生改变了,只需要在创建Bean的地方去重新new新的实现类即可,这就是工厂模式的体现
单例模式
Spring默认来说,对每个bean走的都是一个单例模式,确保说你的一个类在系统运行期间只有一个实例对象,只有一个bean,用到了一个单例模式的思想,保证了每个bean都是单例的
代理模式
Spring AOP 核心的设计模式的思想提现,如果说你要对一些类的方法切入一些增强的代码,会创建一些动态代理的对象,让你对那些目标对象的访问,先经过动态代理对象做一些增强的代码,然后再调用你的目标对象的方法
Spring常用的注入方式有哪些?
Spring通过DI(依赖注入)实现IOC(控制反转),常用的注入方式主要有三种:
(1)构造方法注入
(2)setter注入
(3)基于注解的注入
Spring的事务实现原理是什么?能聊聊你对事务传播机制的理解吗?
事务的ACID特性
原子性(Atomicity)
一系列的操作整体不可拆分,要么同时成功,要么同时失败
一致性(Consistency)
数据在事务的前后,业务整体一致
隔离性(Isolation)
事务之间互相隔离
持久性(Durabilily)
一旦事务成功,数据一定会落盘在数据库
Spring的事务实现原理
如果说你加了一个@Transactional
注解,此时就spring会使用AOP思想,对你的这个方法在执行之前,先去开启事务,执行完毕之后,根据你方法是否报错,来决定回滚还是提交事务
Spring的事务传播行为的理解
Spring的事务传播行为 PROPAGATION 总共有7种,以下例举2种
REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务
REQUIRES_NEW:无论当前存不存在事务,都创建新事务
一般使用最多的是 REQUIRED,也是默认的行为
比如说,我们现在有一段业务逻辑,方法A调用方法B和方法C,我希望的是如果说方法A出错了,此时仅仅回滚方法A和B,不能回滚方法C,那么方法 C 就必须得用 REQUIRES_NEW 传播机制,让它和A、B俩的事务行为是不同的。如果再此基础上方法 A 还设置了事务超时时间为 30 秒,此行为只会传播给 B,即使 B 又设置了超时时间为 5 秒,但还是会以30秒为准,因为 A 事务的所有设置会传播到和它共用一个事务的方法
Spring的事务传播行为的坑
同一个对象内的事务方法互调失效,因为绕过了代理对象
如果按照上述的情况,方法A调用方法B和方法C,此时 A、B、C方法都在同一个类中,这种情况下 B、C做任何设置都没有用,只会和 A 共用同一事务
因为 Spring 事务的原理是基于 AOP,也就是代理对象来完成的,如果只是在同一个类中方法互调,相当于说只是把方法 B、C 进行了一次复制,粘贴,没有走 AOP 的代理,即便使用 this.B()
的方式也一样,this 也是指当前对象,也不会经过代理
解决Spring的事务传播行为的坑,使用代理对象
如果是本类方法互调,需要完成上述的情况,则可以使用 AopContext 生成动态代理对象来调用
注意:这里不能直接通过 @ Autowired
注解来注入本类的 OrderService,这样就会产生 Spring Bean的循环依赖问题
使用 AopContext 步骤
添加 spring-boot-starter-aop
的依赖
在启动类上添加 @ EnableAspectJAutoProxy(exposeProxy = true)
注解,并设置exposeProxy为true
这样就能如图上所展示的方式,拿到本类的代理对象进行调用
⬇⬇⬇更多面试题请看⬇⬇⬇⬇
更多面试题集