Java面试-Spring篇

1. 什么是spring?

Spring是个Java企业级应用的开源开发框架。Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用。Spring框架目标是简化Java企业级应用开发,并通过POJO为基础的编程模型促进良好的编程习惯。

2. 你们项目中为什么使用Spring框架?

这么问的话,就直接说Spring框架的好处就可以了。比如说Spring有以下特点:

- 轻量:Spring是轻量的,基本的版本大约2MB
- 控制反转:Spring通过控制反转实现了松散耦合,对象们给出他们的依赖,而不是创建或查找依赖的对象们
- 面向切面的编程(AOP):Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开。
- 容器:Spring包含并管理应用中对象的生命周期和配置
- MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品
- 事务管理:Spring提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)
- 异常处理:Spring提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked异常

3. Autowired和Resource关键字的区别?

@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,他的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入

  1. 共同点
    两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法
  2. 不同点
    • @Autowired
      @Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入
      @Autowired注解是按照类型(byType)装配依赖对象,默认情况下他要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想按照名称(byName)来装配,可以结合@Qualifier注解一起使用
    • @Resource
      @Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。
      @Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略
      注:最好是将@Resource放在setter方法上,因为这样更符合面向对象的思想,通过set,get去操作属性,而不是直接去操作属性
    • @Resource装配顺序:
    - 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
    - 如果指定了name,则从上下文中查找名称匹配的bean进行装配,找不到则抛出异常
    - 如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常
    - 如果及没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配
    
    @Resource的作用相当于@Autowired,只不过@Autowired按照byType自动注入

4. 依赖注入的方式有几种,各是什么?

  1. 构造器注入:将被依赖对象通过构造器函数的参数注入给依赖对象,并且在初始化对象的时候注入
- 优点:对象初始化完成后便可获得可使用的对象
- 缺点:当需要注入的对象很多时,构造器参数列表将会很长;不够灵活。若有多种注入方式,每种方式只需注入指定几个依赖,那么就需要提供多个重载的构造函数,麻烦
  1. setter方法注入 IOC Service Provider通过调用成员变量提供的setter函数将被依赖对象注入给依赖类
- 优点:灵活。可以选择性的注入需要的对象。
- 缺点:依赖对象初始化完成后由于尚未注入被依赖对象,因此还不能使用
  1. 接口注入 依赖类必须要指定实现指定的接口,然后实现该接口中的一个函数,该函数就是用于依赖注入。该函数的参数就是要注入的对象。
- 优点:接口注入中,接口的名字,函数的名字都不重要,只要保证函数的参数是要注入的对象类型即可
- 缺点:侵入性太强,不建议使用

什么是侵入行?如果类A要使用别人提供的一个功能,若为了使用这功能,需要在自己的类中增加额外的代码,这就是侵入性

5. 讲一下什么是Spring?

Spring是一个轻量级的IOC和AOP容器框架。是为Java应用程序提供基础型服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。常见的配置方式有三种:基于XML的配置,基于注解的配置,基于Java的配置
主要有以下几个核心模块组成:

Spring Core :核心类库,提供IOC服务;
Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI,定时任务等);
Spring AOP:AOP服务;
Spring DAO:对JDBC的抽象,简化了数据访问异常的处理
Spring ORM:对现有的ORM框架的支持;
Spring Web:提供了基本的面向Web的综合特性,例如多方文件上传;
Spring MVC:提供了面向Web应用的Model-View-Controller实现

6.说说你对Spring MVC的理解

什么是MVC模式
MVC:MVC是一种设计模式
分析:
M-Model模型(完成业务逻辑:有JavaBean构成,service+dao+entity)
V-View视图(做界面的展示jsp,html…)
C-Controller控制器(接受请求->调用模型->根据结果派发页面)
springMVC是一个MVC的开源框架,springMVC=struts2+spring,springMVC就相当于是Struts2加上spring的整合,但是这里有一个疑惑就是,spring MVC和spring是什么样的关系呢?其实spring MVC就是spring的一个子模块,所以根本不需要同spring进行整合。
工作原理

1. 用户发送请求至前端控制器DispatcherServlet
2. DispatcherServlet收到请求调用HandlerMapping处理器映射器
3. 处理器映射器找到具体的处理器(可以根据xml配置,注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4. DispatcherServlet调用HandlerAdapter处理器适配器
5. HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
6. Controller执行完成返回ModelAndView
7. HandlerAdapter将controller之心结果ModelAndView返回给DispatcherServlet
8. Dispatcher Servlet将ModelAndView传给ViewResolver视图解析器
9. ViewReslover解析后返回具体View
10.DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)
10. DispatcherServlet响应用户 

组件说明:
以下组件通常使用框架提供实现:

DispatcherServlet:作为前端控制器,整个流程控制的中心,控制其他组件执行,统一调度,降低组件之间的耦合,提高每个组件的扩展性。
Handler Mapping:通过扩展处理器映射器实行不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
HandlerAdapter:通过扩展处理器适配器,支持更多类型的处理器。
ViewResolver:通过扩展视图解析器,支持更多类型的视图解析,例如:jsp,freemarker,pdf,excel等。

组件:1.前端控制器DispatcherServlet,由框架提供 作用:接受请求,响应结果,相当于转发器,中央处理器,有了DispatcherServlet减少了其他组件之间的耦合度。用户请求到达前端控制器,它就相当于mvc模式中的c,DispatcherServlet是整个流程控制的中心,由他调用其他组件处理用户的请求,DispatcherServlet的存在降低了组件之间的耦合性
②. 处理器映射器HandlerMapping(不需要工程师开发),有框架提供 作用:根据请求的url查找Handler ,HandlerMapping负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
处理器适配器HandlerAdapter 作用:按照特定规则(HandlerAdapter要求的规则) 去执行Handler通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行
处理器Handler(需要工程师开发)注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行HandlerHandler是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。由于Handler涉及到具体的用户业务请求,所以一般情况需要工程师根据业务需求开发Handler
视图解析器View resolver(不需要工程师开发),有框架提供 作用:进行视图解析,根据逻辑视图名解析成真正的视图
⑥ 视图View(需要工程师开发jsp)

7. SpringMVC常用的注解有哪些?

@Request Mapping:用于处理请求url映射的注解,可用于类或方法上。
@RequestBody:注解实现接收http请求的json数据,将json转换为Java对象
@ResponseBody:注解实现将controller方法返回对象转为json对象响应给客户

8. 谈谈你对Spring的AOP理解

  • AOP(面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理,日志管理,权限控制等)封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可扩展性和可维护性
  • SpringAOP是基于动态代理的,如果要代理的对象实现了某个接口,那么SpringAOP就会使用JDK动态代理去创建代理对象;而对于没有实现接口的对象,就无法使用JDK动态代理,转而使用CGlib动态代理生成一个被代理对象的子类来作为代理。
  • 当然也可以使用AspectJ

9. SpringAOP和Aspect J AOP有什么区别?

SpringAOP是属于运行时增强,而AspectJ是编译时增强。SpringAOP基于代理,而Aspect J基于字节码操作

10. 说说你对Spring的IOC是怎么理解的?

  1. IOC就是控制反战,是指创建对象的控制权的转移。以前创建对象的主动权和时机是由自己把控的,而现在这种权力转移到Spring容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系。对象与对象之间松散耦合,也利于功能的复用。DI依赖注入,和控制反转是同一个概念的不同家督的描述,即应用程序在运行时依赖IOC容器来动态注入对象需要的外部资源
  2. 最直观的表达就是,IOC让对象的创建不用去new了,可以由spring自动生产。使用Java的反射机制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法的。
  3. Spring的IOC有三种注入方式:构造器注入,setter方法注入,根据注解注入
    IOC让相互协作的组件保持松散的耦合,而AOP变成允许你把遍布于应用各层的功能分离出来形成可重用的功能组件

11. 解释以下spring bean的生命周期

首先说一下Servlet的生命周期:实例化,初始init,接受请求service,销毁destroy
spring上下文中的bean生命周期也类似,如下:

  1. 实例化Bean:
    对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用creatBean进行实例化。对于ApplicationContext容器,当容器启动结束后,通过BeanDefinitioin对象中的信息,实例化所有的bean。
  2. 设置对象属性(依赖注入)
    实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息以及通过BeanWrapper提供的设置属性的接口完成依赖注入
  3. 处理Aware接口:
    接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:
    ① 如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值;
    ② 如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身
    ③ 如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(AookicationContext)方法,传入Spring上下文;
  4. BeanPostProcessor:
    如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj,String s)方法
  5. InitializingBean与init-method:
    如果Bean在Spring配置文件中配置了init-method属性,则会自动调用其配置的初始化方法。
  6. 如果这个Bean是西安了BeanPostProcessor接口,将会调用postProcessAfterInitalization(Object obj,String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;
    以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了
  7. DisposableBean
    当Bean不再需要时,会经过清理阶段,如果Bean实现了Disposable Bean这个接口,会调用其实现的destroy()方法;
  8. destroy-method:
    最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
    在这里插入图片描述

12. 解释Spring支持的几种bean的作用域?

Spring容器中的bean可以分为5个范围:

  1. singleton:默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。
  2. prototype:为每一个bean请求提供一个实例
  3. request:为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
  4. session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
  5. global-session:全局作用域,global-session和Potlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同

13. Spring基于xml注入bean的几种方式?

  1. Set方法注入
  2. 构造器注入:①通过index设置参数的位置;②通过type设置参数类型
  3. 静态工厂注入
  4. 实例工厂

14. Spring框架中都用到了那些设计模式?

1. 简单工厂模式:Spring中的BeanFactory就是简单工厂模式的体现。根据传入一个唯一的标识来获得Bean对象,但是在传入参数后创建还是传入参数前创建,要根据具体情况来定
2. 工厂模式:Spring中的Factory Bean就是典型的工厂方法模式,实现了Factory Bean接口的bean是一类叫做factory的bean。其特点是,spring在使用getBean()调用获得该bean时,会自动调用该bean的getObject()方法,所以返回的不是factory这个bean,而是这个bean.getObject()方法的返回值
3. 单例模式:在spring中用到的单例模式有:scope="singleton",注册式单例模式,bean存放于Map中。bean name当作key,bean当作value
4. 原型模式:在spring中用到的原型模式有:scope="prototype",每次获取的是通过克隆生成的新实例,对其进行修改时对原有实例对象不造成任何影响。
5. 迭代器模式:在Spring中有个CompositeIterator实现了Iterator,Iterable接口和Iterator接口,这两个都是迭代相关的接口。可以这么认为,实现了Iterable接口,则表示某个对象时刻被迭代的。Iterator接口相当于是一个迭代器,实现了Iterator接口,等于具体定义了这个可被迭代的对象是如何进行迭代的
6. 代理模式:Spring中经典的AOP,就是使用动态代理实现的,分为JDK和CGlib动态代理
7. 适配器模式:Spring中的AOP中AdvisorAdapter类,它有三个实现:
	MethodBeforAdviceAdapter,AfterReturnningAdviceAdapter,ThrowsAdviceAdapter.Spring会根据不同的AOP配置来使用对应的Advice,与策略模式不同的是,一个方法可以同时拥有多个Advice。Spring存在很多以Adapter结尾的,大多数都是适配器模式
8. 观察者模式:Spring中的Event和Listener。Spring事件:ApplicationEvent,该抽象类继承了EventObject类,JDK建议所有的事件都应该继承自EventObject。spring事件监听器:ApplicationListener,该接口继承了EventListener接口,JDK建议所有的事件监听器都应该继承EventListener
9. 模板模式:spring中的org.springframework.jdbc.core.JdbcTemplate就是非常经典的模板模式的应用,里面的execute方法,把整个算法步骤都定义好了 

15. 说说Spring中ApplicationContext和BeanFactory的区别

  1. 类图
    在这里插入图片描述
  2. 包目录不同
    - spring-beans,jar中org.springframework.beans.factory.BeanFactory
    - spring-context.jar中org.springframework.context.ApplicationContext
    
  3. 国际化
    BeanFactory是不支持国际化功能的,因为BeanFactory没有扩展Spring中MessageResource接口。相反,由于ApplicationContext扩展了MessageResource接口,因而具有消息处理的能力(i18N)
  4. 强大的事件机制(Event)
    基本上牵扯到事件(Event)方面的设计,就离不开观察者模式,ApplicationContext的事件机制主要通过ApplicationEvent和ApplicationListener这两个接口来提供的。和Java swing中的事件机制一样。即当ApplicationContext中发布一个事件时,所有扩展了ApplicationListener的Bean都将接收到这个事件,并进行相应的处理。
  5. 底层资源的访问
    ApplicationContext扩展了Resource Loader(资源加载器)接口,从而可以用来加载多个Resource,而BeanFactory是没有扩展ResourceLoader。
  6. 对Web应用的支持
    与BeanFactory通常以编程的方式被创建,Application Context能以声明的方式创建,如使用ContextLoader
    当然你也可以使用ApplicationContext的实现方式之一,以编程的方式创建ApplicationContext实例
  7. 延迟加载
- BeanFactory采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的spring的配置问题。而ApplicationContext则相反,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误
- Bean Factory和Application Context都支持BeanPostProcessor,BeanFactoryPostProcessor的使用。两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册

可以看到,ApplicationContext继承了BeanFactory,BeanFactory是Spring中比较原始的Factory,它不支持AOP,Web等Spring插件。而Application Context不仅包含了BeanFactory的所有功能,还支持Spring的各种插件,还以一种面型框架的方式工作以及对上下文进行分层和实现继承
BeanFactory是Spring框架的基础设施,面向Spring本身;而ApplicationContext面向使用Spring的开发者,相比BeanFactory提供了很对面向实际应用的功能,几乎所有场合都可以直接使用ApplicationContext,而不是底层的BeanFactory。
8. 常用容器
BeanFactory类型的有XmlBeanFactory,它可以根据XML文件中定义的内容,创建相应的Bean

16. Spring框架中的单例Bean是线程安全的么?

Spring框架并没有对单例Bean进行任何多线程的封装处理

- 关于单例Bean的线程安全和并发问题,需要开发者自行去搞定
- 单例的线程安全问题,并不是Spring应该去关心的,Spring应该做的是,提供根据配置,创建单例Bean或多例Bean的功能。

当然,但实际上,大部分的Spring Bean并没有可变的状态,所以在某种程度上说Spring的单例Bean是线程安全的。如果你的Bean有多种状态的话,就需要自行保证线程安全。最浅显的解决办法,就是将多态Bean的作用域(Scope)由Singleton变更为Prototype

17. Spring是怎么解决循环依赖的?

在这里插入图片描述
整个流程大致如下:

- 首先A完成初始化第一步并将自己提前曝光出来(通过ObjectFactory将自己提前曝光),在初始化的时候,发现自己依赖对象B,就会去尝试get(B),这个时候发现B还没有被创建出来
- 然后B就走流程,在B初始化的时候,同样发现自己依赖C,C也没有被创建出来;
- 这个时候C由开始初始化进程,但是在初始化的过程中发现自己依赖A,于是尝试get(A)。这个时候由于A已经添加至缓存中(一般都是添加至三级缓存singletonFactories),通过ObjectFactory提前曝光,可以通过ObjectFactory#getObject()方法来拿到A对象。C拿到A对象后顺利完成初始化,然后将自己添加到一级缓存中
- 回到B,B也可以拿到C对象,完成初始化,A可以顺利拿到B完成初始化。到这里整个链路就已经完成了初始化过程了。

关键字:三级缓存,提前曝光

18. 说说事物的隔离级别

- 未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
- 提交读:只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别(不重复读)
- 可重复读:在同一个事务内的查询都是事务开始时刻一致的,Mysql的InnoDB默认级别
- 可串行化:完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞

在这里插入图片描述
不可重复读和幻读的区别主要是:解决了不可重复读需要锁定了当前满足条件的记录,而解决欢度需要锁定当前满足条件的记录及相近的记录。比如查询某个商品的信息,可重复读事务隔离级别可以保证当前商品信息被锁定

19. 说说事务的传播级别

Spring事务定义了7种传播机制:

- PROPAGATION_REQUIRED:默认的Spring事务传播级别,若当前存在事务,则加入该事务,若不存在事务,则新建一个事务。
- PROPAGATION_REQUIRE_NEW:若当前没有事务,则新建一个事务。若当前存在事务,则新建一个事务,新老事务相互独立。外部事务抛出异常回滚不会影响内部事务的正常提交
- PROPAGATION_NESTED:如果当前存在事务,则嵌套在当前事务中执行如果当前没有事务,则新建一个事务,类似于REQUIER_NEW
- SUPPORTS:支持当前事务,若当前不存在事务,以非事务的方式执行
- NOT_SUPPORTED:以非事务的方式执行,若当前存在事务,则把当前事务挂起。
- MANDATORY:强制事务执行,若当前不存在事务,则抛出异常
- NEVER:以非事务的方式执行,如果当前存在事务,则抛出异常

Spring事务传播级别一般不需要定义,默认就是REQUIRED,除非在嵌套事务的情况下需要重点理解

20. Spring事务实现方式

  1. 编程式事务管理:这意味着你可以通过编程的方式管理事务,这种方式带来了很大的灵活性,但很难维护。
  2. 声明式事务管理:这种方式意味着你可以将事务管理和业务代码分离。你只需要通过注解或者XML配置管理事务。

21. Spring框架的事务管理有哪些优点

它为不同的事务API提供了统一的编程模型。它为编程式事务管理提供了一个简单的API而非一系列复杂的事务API。它支持声明式事务管理。它可以和Spring的多种数据访问技术很好的融合。

22. 事务三要素是什么?

1. 数据源:表示具体的事务性资源,是事务的真正处理者,如MySQL等
2. 事务管理器:像一个大管家,从整体上管理事物的处理过程,如打开,提交,回滚等
3. 事务应用和属性配置:像一个标识符,表明那些方阿飞要参与事务,如何参与事务,以及一些相关属性如隔离级别,超时时间等

23. 事务注解的本质是什么?

  • @Transactional这个注解仅仅是一些(和事务相关的)元数据,在运行时被事务基础设施读取消费,并使用这些元数据来配置bean的事务行为。大致来说具有两方面功能,一是表明该方法要参与事务,二是配置相关属性来定制事务的参与方式和运行行为
  • 声明式事务主要是得益于Spring AOP。使用一个事务拦截器,在方法调用的前后/周围进行事务性增强(advice),来驱动事务完成。
  • @Transaction注解既可以标注在类上,也可以标注在方法上。当在类上时,默认应用到类里的所有方法。如果此时方法上也标注了,则此方法的优先级高。另外注意方法一定要是public的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值