1. SpringMVC常用注解

1.@RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类 上,则表示类中的所有响应请求的方法都是以该地址作为父路径。
2.@RequestBody:注解实现接收 http 请求的 json 数据,将 json 转换为 java 对象。
3.@ResponseBody:注解实现将 conreoller 方法返回对象转化为 json 对象响应给客 户。
4.@PathVariable 用户从 url 路径上获取指定参数,标注在参数前 @PathVariabel(“ 要获取的参数名”)。 5.@RequestParam: 标注在方法参数之前,用于对传入的参数做一些限制,支持三个 属性: - value:默认属性,用于指定前端传入的参数名称 - required:用于指定此参数是否必传 - defaultValue:当参数为非必传参数且前端没有传入参数时,指定一个默认值
6. @ControllerAdvice 标注在一个类上,表示该类是一个全局异常处理的类。
7. @ExceptionHandler(Exception.class) 标注在异常处理类中的方法上,表示该方法 可以处理的异常类型。

2. SpringMVC 主要组件

  1. 前端控制器 DispatcherServlet:接收请求、响应结果,相当于转发器,有了 DispatcherServlet 就减少了其它组件之间的耦合度。
  2. 处理器映射器 HandlerMapping:根据请求的 URL 来查找 Handler 处理器适配器
  3. HandlerAdapter:负责执行 Handler 处理器 Handler:
  4. 处理业务逻辑的 Java 类 视图解析器 ViewResolver:进行视图的解析,根据视图逻辑名将 ModelAndView 解 析成真正的视图(view)
  5. 视图 View:View 是一个接口,它的实现类支持不同的视图类型,如 jsp,freemarker, pdf 等等


3.谈一下 SpringMVC 的执行流程以及各个组件的作用(必会)

image.png
1. 用户发送请求到前端控制器(DispatcherServlet)
2. 前 端 控 制 器 ( DispatcherServlet ) 收 到 请 求 调 用 处 理 器 映 射 器 (HandlerMapping),去查找处理器(Handler)
3. 处理器映射器(HandlerMapping)找到具体的处理器(可以根据 xml 配置、注解进行 查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet。
4. 前端控制器(DispatcherServlet)调用处理器映射器(HandlerMapping)
5. 处理器适配器(HandlerAdapter)去调用自定义的处理器类(Controller,也叫 后端控制器)。
6.自定义的处理器类(Controller,也叫后端控制器)将得到的参数进行处理并返回结 果给处理器映射器(HandlerMapping)
7. 处 理 器 适 配 器 ( HandlerAdapter ) 将 得 到 的 结 果 返 回 给 前 端 控 制 (DispatcherServlet)
8. DispatcherServlet( 前 端 控 制 器 ) 将 ModelAndView 传 给 视 图 解 析 器
(ViewReslover)
9. 视图解析器(ViewReslover)将得到的参数从逻辑视图转换为物理视图并返回给
前端控制器(DispatcherServlet)
10. 前端控制器(DispatcherServlet)调用物理视图进行渲染并返回
11. 前端控制器(DispatcherServlet)将渲染后的结果返回

4. Spring事务失效的几种情况以及解决方案

  1. 出现检查异常会导致事务失效,数据不会进行回滚
    1. 出现的原因: Spring默认只会回滚非检查异常
    2. 解决: 在@Transactional注解中加上属性@Transactional(rollbackfor=Exception.class)
  2. 自己try catch 时,不抛出异常时,事务回滚会失效
    1. 解决:
      1. try catch 捕获中抛出异常
      2. 手动设置 TransactionInterceptor.currentTransactionStatus.setRollbackOnly
  3. aop切面顺序导致事务失效
    1. 解决
      1. try catch 捕获中抛出异常
      2. 手动设置 TransactionInterceptor.currentTransactionStatus.setRollbackOnly
    2. 原因: 事务切面优先级最低 , 但如果自定义的切面优先级和他一样,则还是自定义切面在内层,这时若自定义切面没有正确抛出异常
  4. 非public 方法会导致事务失效
    1. 原因: 非public 方法无法创建代理,事务通知
    2. 解决: 将public 补上即可 或者使用try catch 抛出异常
  5. 父子容器导致的事务失效
    1. 原因: 子容器扫描范围过大, 把未加事务配置的service扫描进来
    2. 解决: 各扫描各的,不要图简便 || 不要用父子容器,所有bean放在同一容器
  6. 调用本类方法导致传播行为失效
    1. 原因: 本类方法调用不经过代理因此无法增强
    2. 解决:
      1. 依赖注入自己(代理) 使用
      2. 通过aopcontext 拿到代理对象来调用
      3. 通过CTW , LTW实现功能增强
  7. @Transactional没有保证原子行为
    1. 原因:事务的原子性仅涵盖 insert , update , delete , select…for update 语句, select 方法并不阻塞
  8. @Transactional方法导致的synchronized失效
    1. 原因: synchronized保证的仅是目标方法的原子性 , 环绕目标方法的还有commit等操作 ,他们并未处于sync块内
    2. 解决:
      1. synchronized范围应扩大至代理方法调用
      2. 使用select …. for update 替换 select

5. Spring 框架

1.Spring的两大核心是什么? 谈一谈你对IOC的理解? 谈一谈你对DI的理解?谈一谈你对AOP的理解?

  1. spring的两大核心时IOC(控制反转)和AOP(面向切面编程) DI是指依赖注入
  2. IOC的意思时控制反转, 是指创建对象的控制权转移,以前创建对象的主动权和时机是由自己把控的, 而现在这种权力转移到Spring容器中,并把容器根据配置文件去创建实例和管理各个实例之间的依赖关系 , 对象与对象之间松散耦合, 也利于功能的复用, 最直观的表达就是 , IOC让对象的创建不用去new了,可以由spring根据我们提供的配置文件自动生产,我们需要对象的时候,直接从spring容器中获取即可
    1. spring的配置文件中配置了类的字节码位置以及信息 , 容器生成的时候加载配置文件识别字节码信息,通过反射创建类的对象
    2. spring的IOC由三种注入方式: 构造器注入 , setter方法注入 , 根据注释注入
  3. DI的意思是依赖注入 , 和控制反转是同一个概念的不同角度的描述 , 即应用程序在运行时依赖IOC容器来动态注入对象需要的外部资源
  4. AOP , 一般称为面向切面编程 , 作为面向对象的一种补充 , 用于将那些与业务无关 , 但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块 , 这个模块被命名为”切面”(Aspect) , Spring AOP使用的动态代理 ,所谓的动态代理 , 就是说AOP框架不会去修改字节码 ,而是每次运行时在内存中临时为方法生成一个AOP对象 , 这个AOP对象包含了目标对象的全部方法 , 并且在特定的切点 , 做了增强处理, 并回调原对象的方法
  5. spring AOP中的动态代理有两种方式 JDK动态代理和CGLIB动态代理
    1. (1)JDK 动态代理只提供接口代理,不支持类代理,核心 InvocationHandler 接口和 Proxy 类,InvocationHandler 通过 invoke()方法反射来调用目标类中的代码,动态地将 横切逻辑和业务编织在一起,Proxy 利用 InvocationHandler 动态创建一个符合某一接口 的的实例, 生成目标类的代理对象。
    2. (2) 如果代理类没有实现 InvocationHandler 接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库, 可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现 AOP。CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final, 那么它是无法使用 CGLIB 做动态代理的

2. Spring的生命周期?

  1. 实例化一个bean
  2. IOC注入(按照spring上下文对实例化的bean进行配置)
  3. 如果这个bean实现dao了BeanNameAware接口 , 会调用它实现的setBeanName(String beanId) 方法 , 此处传递的是spring配置文件中bean的ID
  4. 如果这个 Bean 实现了 BeanFactoryAware 接口,会调用它实现的 setBeanFactory(), 传递的是 Spring 工厂本身(可以用这个方法获取到其他 Bean)
  5. 如果这个 Bean 实现了 ApplicationContextAware 接口,会调用 setApplicationContext(ApplicationContext)方法,传入 Spring 上下文,该方式同样可 以实现步骤 4,但比 4 更好,以为 ApplicationContext 是 BeanFactory 的子接口,有更多 的实现方法
  6. 如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用 postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor 经常被 用作是 Bean 内容的更改,并且由于这个是在 Bean 初始化结束时调用 After 方法,也可用 于内存或缓存技术
  7. 如果这个 Bean在 Spring 配置文件中配置了 init-method属性会自动调用其配置的初始 化方法
  8. 如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用 postAfterInitialization(Object obj, String s)方法 注意:以上工作完成以后就可以用这个 Bean 了,那这个 Bean 是一个 single 的,所以 一般情况下我们调用同一个 ID 的 Bean 会是在内容地址相同的实例
  9. 当 Bean 不再需要时,会经过清理阶段,如果 Bean 实现了 DisposableBean 接口,会 调用其实现的 destroy 方法
  10. 最后,如果这个 Bean 的 Spring 配置中配置了 destroy-method 属性,会自动调用其 配置的销毁方法

3. Spring支持bean的作用域有几种?每种作用域是什么样的?

Spring支持一下5种作用域:

  1. singleton: 默认作用域 , 单例bean , 每个容器种只有一个bean实例
  2. prototype: 为每一个bean请求创建一个实例
  3. request: 为每一个request请求创建一个实例 , 在请求完成以后 , bean会失效并被垃圾回收器回收
  4. session: 与request范围类似 , 同一个session会话共享一个实例 , 不同会话使用不同的实例
  5. global-session: 全局作用域,所有会话共享一个实例 , 如果想要声明让所有会话共享的存储变量的话 , 那么这全局变量需要存储在global-session种

4. BeanFactory 和 ApplicationContext有什么区别?

BeanFactory:

  • Spring最顶层接口, 实现了spring容器的最基础的一些功能 , 调用起来比较麻烦 , 一般面向spring自身使用
  • BeanFactory在启动的时候不会去实例化Bean , 从容器中拿Bean的时候才会去实例化

ApplicationContext:

  • 是BeanFactory的子接口 , 扩展了其功能 , 一般面向程序员使用
  • ApplicationContext在启动的时候就把所有的Bean全部实例化了

5. Spring框架中都用到了哪些设计模式?

  1. 工厂模式: BeanFactory就是简单的工厂模式的体现 , 用来创建对象的实例
  2. 单例模式: Bean默认为单例模式
  3. 代理模式: Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术
  4. 模板方法: 用来解决代码重复的问题 , 比如: RestTemplate , JmsTemplate , JpaTemolate
  5. 观察者模式: 定义对象键一种一对多的依赖关系 , 当一个对象的状态发生改变时 , 所有依赖于它的对象都会得到通知被动更新 , 如 Spring 中的listener实现 —>ApplicationListener

6. Spring事务的实现方式和实现原理

Spring事务的本质其实就是数据库对事务的支持 , 没有数据库的事务支持,spring是无法提供事务功能的 , 真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的

spring事务实现主要有两种方法:

  1. 编程式 , beginTransaction() , commit() , rollback()等事务管理相关的方法
  2. 声明式 , 利用注解Transactional 或者aop 配置

7. Spring的对象默认是单例的还是多例的? 单例bean存不存在线程安全问题呢?

  1. 在 spring 中的对象默认是单例的,但是也可以配置为多例。
  2. 单例 bean 对象对应的类存在可变的成员变量并且其中存在改变这个变量的线程时,
    多线程操作该 bean 对象时会出现线程安全问题。
    原因是:多线程操作如果改变成员变量,其他线程无法访问该 bean 对象,造成数据混
    乱。
    1. 解决办法:在 bean 对象中避免定义可变成员变量;
      在 bean 对象中定义一个 ThreadLocal 成员变量,将需要的可变成员变量
      保存在 ThreadLocal 中。

8. Spring七大组件

  • 核心容器(Spring core)
  • 核心容器提供Spring框架的基本功能。Spring以bean的方式组织和管理Java应用中的各个组件及其关系。Spring使用BeanFactory来产生和管理Bean,它是工厂模式的实现。BeanFactory使用控制反转(IOC)模式将应用的配置和依赖性规范与实际的应用程序代码分开。BeanFactory使用依赖注入的方式提供给组件依赖。

  • Spring上下文(Spring context)

  • Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,如JNDI、EJB、电子邮件、国际化、校验和调度功能。

  • Spring面向切面编程(Spring AOP)

  • 通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring框架中。所以,可以很容易地使 Spring框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。

  • Spring DAO模块

  • DAO模式主要目的是将持久层相关问题与一般的的业务规则和工作流隔离开来。Spring 中的DAO提供一致的方式访问数据库,不管采用何种持久化技术,Spring都提供一直的编程模型。Spring还对不同的持久层技术提供一致的DAO方式的异常层次结构。

  • Spring ORM模块

  • Spring 与所有的主要的ORM映射框架都集成的很好,包括Hibernate、JDO实现、TopLink和IBatis SQL Map等。Spring为所有的这些框架提供了模板之类的辅助类,达成了一致的编程风格。

  • Spring Web模块

  • Web上下文模块建立在应用程序上下文模块之上,为基于Web的应用程序提供了上下文。Web层使用Web层框架,可选的,可以是Spring自己的MVC框架,或者提供的Web框架,如Struts、Webwork、tapestry和jsf。

  • Spring MVC框架(Spring WebMVC)

  • MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的。Spring的MVC框架提供清晰的角色划分:控制器、验证器、命令对象、表单对象和模型对象、分发器、处理器映射和视图解析器。Spring支持多种视图技术


9. 拦截器和过滤器的区别

过滤器和拦截器的区别:
①拦截器是基于java的反射机制的,而过滤器是基于函数回调。
②拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
③拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
④拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
⑤在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。

⑥拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。

1.过滤器和拦截器触发时机不一样:
过滤器是在请求进入容器后,但请求进入servlet之前进行预处理的。请求结束返回也是,是在servlet处理完后,返回给前端之前。
1.过滤器和拦截器触发时间和地点不一样:
过滤器是在请求进入容器后,但请求进入servlet之前进行预处理的。请求结束返回也是,是在servlet处理完后,返回给前端之前。