Spring MVC核心组件
主要组件
- 前端控制器 DispatcherServlet
- 处理器映射器HandlerMapping
- 处理器适配器HandlerAdapter
- 处理器Handler
- 视图解析器 ViewResolver
- 视图View
工作原理
- 用户发送请求至前端控制器DispatcherServlet;
- DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;
- 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生
成)一并返回给DispatcherServlet; - DispatcherServlet 调用 HandlerAdapter处理器适配器;
- HandlerAdapter 经过适配调用 具体处理器(Handler,也叫后端控制器);
- Handler执行完成返回ModelAndView;
- HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;
- DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析
- ViewResolver解析后返回具体View;
- DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
- DispatcherServlet响应用户。
常用注解
注解原理是什么
注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。
Spring MVC常用的注解有哪些?
- @RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径
- @RequestBody:注解实现接收http请求的json数据,将json转换为java对象
- @ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户
- @ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户
@Controller注解的作用
用于标记在一个类上,声明注解的类是一个Controller,将该类交给Spring容器进行管理,可以使Controller定义更加灵活,可以不用实现Controller接口,请求处理的方法也更加灵活。
谈谈你对Spring 的理解
- Spring是一个轻量级Java开发框架,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。
- Spring最根本的使命是解决企业级应用开发的复杂性,即简化Java开发
Spring的俩大核心概念
Spring控制反转(IOC)
什么是Spring IOC 容器
- 控制反转即IOC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。
Spring IOC 负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期。
控制反转(IOC)有什么作用
管理对象的创建和依赖关系的维护。对象的创建并不是一件简单的事,在对象关系比较复杂时,如果依赖关系需要程序猿来维护的话,那是相当头疼的
- 解耦,由容器去维护具体的对象
托管了类的产生过程,比如我们需要在类的产生过程中做一些处理,最直接的例子就是代理,如果有容器程序可以把这部分处理交给容器,应用程序则无需去关心类是如何完成代理的
IOC的优点是什么?
IOC 或 依赖注入把应用的代码量降到最低。
- 它使应用容易测试,单元测试不再需要单例和JNDI查找机制。
Spring IOC 的实现机制
Spring 中的 IOC 的实现原理就是工厂模式加反射机制
什么是Spring的依赖注入?
依赖注入(Dependency Injection),即组件之间的依赖关系由容器在应用系统运行期来决定,也就是由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系
Spring面向切面编程(AOP)
什么是AOP
AOP(Aspect-Oriented Programming),一般称为面向切面编程,作为面向对象的一种补充,用于
将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模
块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理等。
Spring AOP and AspectJ AOP 有什么区别?AOP 有哪些实现方式?
AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为
AspectJ;动态代理则以Spring AOP为代表。
Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次
运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法
JDK动态代理和CGLIB动态代理的区别
- Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
- JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy
类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。 - 如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。
- JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy
静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。
Spring由哪些模块组成?
spring core : 提供了框架的基本组成部分,包括控制反转(Inversion of Control,IOC)和依赖注入(Dependency Injection,DI)功能。
- spring beans :提供了框架的基本组成部分,包括控制反转(Inversion of Control,IOC)和依赖注入(Dependency Injection,DI)功能。
- spring context : 构建于 core 封装包基础上的 context 封装包,提供了一种框架式的对象访问方法
- spring aop :提供了面向切面的编程实现,让你可以自定义拦截器、切点等
Spring 框架中都用到了哪些设计模式?
- 工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
- 单例模式:Bean默认为单例模式。
- 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
- 模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
解释Spring支持的几种bean的作用域
Spring框架支持以下五种bean的作用域:
- singleton : bean在每个Spring ioc 容器中只有一个实例。
- prototype:一个bean的定义可以有多个实例。
- request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。
- session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的
Spring ApplicationContext情形下有效。 - global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
注意: 缺省的Spring bean 的作用域是Singleton。使用 prototype 作用域需要慎重的思考,因为频繁创建和销毁 bean 会带来很大的性能开销。
Spring框架中的单例bean是线程安全的吗?
- 不是,Spring框架中的单例bean不是线程安全的。
- spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。
实际上大部分时候 spring bean 无状态的(比如 dao 类),所有某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于new Bean()了,所以就可以保证线程安全了。
在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处 理,解决线程安全问题。
- ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。
- ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
解释Spring框架中bean的生命周期
其过程主要分为4 个阶段:
- 实例化,简单理解就是使用空参构造器 new 了一个对象
- 属性注入,为实例化中 new 出来的对象填充属性
- 初始化,第 3、4 步为在初始化前执行,第 5、6 步为初始化操作,第 7 步在初始化后执行,该阶段结束,才能被用户使用。第 3、4、7完成了 Spring AOP 功能
-
循环依赖问题
1.什么是循环依赖问题?
简单来说,创建A对象的时候依赖B,然后去创建B的时候又依赖A,然后陷入死循环了。
是在Bean的初始化过程中,给Bean进行属性注入的时候发生的。2.然后解决循环依赖问题?
Spring 通过三级缓存来解决循环依赖问题的,通过三级缓存机制+ 提前曝光(未初始化的对象) 机制,解决了。
2.1 什么是三级缓存?
第一级缓存:单例缓存池 singletonObjects。
第二级缓存:早期提前暴露的对象缓存 earlySingletonObjects。
第三级缓存:singletonFactories 单例对象工厂缓存2.2 Bean的创建过程
首先进入getBean方法,然后调用doGetBean,首选从一级缓存中去找,如果没找到,且对象在创建中,则尝试去二级缓存中找,如果还是没找到,且允许从单例工厂缓存中找,则去三级缓存中找。如果找到了,则从三级缓存中移除该对象到二级缓存中,提前暴露。
- 再根据dependsOn方法,判断这个对象是否有循环依赖的问题,如果有,则直接报异常
- 然后进入createBean方法,执行doCreateBean,真正的创建对象。然后找到createBeanInstance方法,如果创建成功,那么就把你的对象放入三级缓存,并从二级缓存中移除,
- 通过addSingletonFactory,将你创建好的对象放入三级缓存中,并且移除早期暴露的对象
- 通过populateBean方法,给属性赋值,此时A对象会去缓存中拿B对象,这时候会拿到,并赋值。
3.为什么非要使用三级缓存?
只要两个缓存确实可以做到解决循环依赖的问题,但是有一个前提这个bean没被AOP进行切面代理,如果这个bean被AOP进行了切面代理,那么只使用两个缓存是无法解决问题
不可能每次执行singleFactory.getObject()方法都给我产生一个新的代理对象,就会将代理对象的生成提前到初始化之前,对象注入的时候。
Spring支持的事务管理类型, spring 事务实现方式有哪些?
- Spring支持两种类型的事务管理:
说一下Spring的事务传播行为
① PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就
加入该事务,该设置是最常用的设置。
② PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不
存在事务,就以非事务执行。
③ PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前
不存在事务,就抛出异常。
④ PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
⑤ PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前
事务挂起。
⑥ PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
⑦ PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则
按REQUIRED属性执行
说一下 spring 的事务隔离?
spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离
级别和数据库的隔离级别一致:
1. ISOLATION_DEFAULT:用底层数据库的设置隔离级别,数据库设置的是什么我就用什么;
2. ISOLATION_READ_UNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其
他事务读取(会出现幻读、脏读、不可重复读);
3. ISOLATION_READ_COMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成
幻读、不可重复读),SQL server 的默认级别;
4. ISOLATION_REPEATABLE_READ:可重复读,保证多次读取同一个数据时,其值都和事务开
始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级
别;
5. ISOLATION_SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏
读、不可重复读、幻读。
什么时候@Transactional失效
因为Spring事务是基于代理来实现的,所以某个加了@Transactional的⽅法只有是被代理对象调⽤时,那么这个注解才会⽣效,所以如果是被代理对象来调⽤这个⽅法,那么@Transactional是不会⽣效的。
同时如果某个⽅法是private的,那么@Transactional也会失效,因为底层cglib是基于⽗⼦类来实现的,⼦类是不能重载⽗类的private⽅法的,所以⽆法很好的利⽤代理,也会导致@Transactianal失效
拦截器和过滤器了解么?
1.拦截器是基于Java的反射机制的,而过滤器是基于函数回调。
2.拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
3.拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
4.拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
5.在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。