Spring面试
Spring 定义
Spring是什么
Spring是一个轻量级的Java框架,解决了业务逻辑和其他各层的耦合问题,简化了Java开发
Spring的框架的核心:IOC控制反转和AOP切面编程
Spring的优点
- 方便解耦合,简化开发:将所有对象的创建和维护交给Spring容器管理
- AOP编程的支持:可以将与业务无关的功能抽取出来进行复用,比如日志打印、权限拦截等功能
- 声明式事务:通过简单的配置和注解可以很方便的完成对事物的管理
- 单元测试:Spring提供了对Junit的支持,可以通过注解方便测试web程序
- 极小的入侵性:我们不需要必须去继承Spring的某些接口来完成功能,而且方便集成其他框架
Spring缺点
Spring利用了很多的反射,会影响性能,但是和它的优点相比,是完全可以接受的
Spring由哪些模块组成

Core:提供了框架的基本组成部分,包括IOC和DI
Beans:提供了BeanFactory,工厂模式的实现,用于管理对象
Context:提供了对象访问方法
JDBC:提供了对JDBC的支持,省去了JDBC繁杂的编码
AOP:提供了切面编程的实现,可以自定义拦截器等
Web:提供了针对Web开发特性,比如文件上传等
Test:提供了测试支持,支持使用Junit方便进行测试
Spring IOC
什么是IOC
IOC(Inversion of Control)即控制反转,把传统的由程序代码手动操控对象的调用权交给容器,通过容器完成对象的创建和管理 ;IOC负责创建对象 、通过DI依赖注入维护对象之间的关系,并且管理对象的整个生命周期
IOC的好处
管理了对象的创建和依赖关系的维护。当对象关系复杂时,手动创建和维护是很麻烦的
解耦,避免硬编码造成的过度耦合
IOC和DI降低了代码量
IOC的实现
IOC的实现原理是工厂模式加反射机制
BeanFactory和ApplicationContext区别
BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。
BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。
ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:
- 继承MessageSource,因此支持国际化。
- 统一的资源文件访问方式。
- 提供在监听器中注册bean的事件。
- 同时加载多个配置文件。
- Refresh刷新整个容器的Bean
Bean的加载方式:
BeanFactory是通过延迟加载的方式来注入Bean的,也就是只有在第一次调用获取bean时,bean才会加载,这样的问题就是我们只有在调用才能发现Spring的配置问题
ApplicationContext在容器启动时,一次性创建了所有Bean,这样在容器启动时我们就会发现Spring的配置哪里有问题,ApplicationContext唯一不足就是当配置的Bean有很多,启动就会比较慢
注册方式:
BeanFactory需要手动注册,而ApplicationContext则是自动注册。
ApplicationContext通常的实现是什么?
ClassPathXmlApplicationContext:加载类路径下的xml文件
FileSystemApplicationContext:加载磁盘任意路径下的xml文件
AnnotationApplicationContext:读取注解
什么是Spring的依赖注入?
组件之间的依赖关系由Spring容器在运行期决定,由容器动态的将某种依赖关系的目标对象实例注入到应用系统各个关联的组件中
依赖注入的基本原则是:应用组件不应该负责查找资源或者其他依赖的协作对象,配置对象的工作应该由IoC容器负责
依赖注入的优势:让容器全权负责依赖查询,需要依赖的组件只需要暴露set方法或者构造函数或者接口即可
依赖注入方式有哪些?
依赖注入有接口注入、set方法注入、构造函数注入;接口注入由于灵活性比较差,在Spring4的时候废弃掉了
set方法注入:容器通过调用无参构造实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入。
构造器注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。
两者对比:
set方法:可以直接使用默认的构造函数创建对象,获取对象时,如果没有注入,会没有值,不严谨
构造函数:创建对象时必须注入数据,否则创建不成功 ,即使有的数据用不到
Spring基于xml注入bean的几种方式
- Set方法注入;
- 构造器注入:①通过index设置参数的位置;②通过type设置参数类型;
- 静态工厂注入;
- 实例工厂;
Spring Bean
如何定义Bean的作用域
对bean的scope属性指定作用域
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是线程安全的吗?
虽然只有一个实例,但并不是线程安全的,因为spring没有对单例做线程安全的处理;
但是在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题
ThreadLocal和线程同步机制都是为了解决多线程并发下相同实例访问冲突的问题;不同的是线程同步机制是采用"时间换空间"的方式,仅提供一份变量,不同的线程访问变量通过锁机制来控制;而ThreadLocal是采用的"空间换时间"的方法,每个线程都有一个独享的变量,从而隔离了多线程下的访问冲突问题;
Bean的生命周期
1.Spring对bean进行实例化;
2.Spring将值和bean的引用注入到bean对应的属性中;
3.如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBean-Name()方法;
4.如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入;
5.如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用上下文的引用传入进来;
6.如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessBeforeInitialization()方法;
7.如果bean实现了InitializingBean接口,Spring将调用它们的after-PropertiesSet()方法。类似地,如果bean使用initmethod声明了初始化方法,该方法也会被调用;
8.如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessAfterInitialization()方法;
此时,bean已经准备就绪,如果被应用程序使用,就会驻留在上下文,直到销毁
1.如果bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用。
什么是bean装配?
指在Spring 容器中把bean组装到一起,前提是容器需要知道bean的依赖关系,如何通过依赖注入来把它们装配到一起
什么是bean的自动装配?
对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象,使用autowire来配置自动装载模式。
spring 自动装配 bean 有哪些方式?
no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。
byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自动装配。
byType:通过参数的数据类型进行自动装配。
constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。
autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配。
@Autowired注解自动装配的过程
使用@Autowired自动装配bean之前需要在配置文件进行配置<context:annotation-config/>
在启动IOC时,容器自动装配了AutowiredAnnotationBeanPostProcessor处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean并装配给该对象的属性。
如果查询的结果刚好为一个,容器就会把@Autowired指定的对象装配该Bean
如果查询的结果不止一个,那么@Autowired会根据名称来查找;
如果上述查找的结果为空,那么会抛出异常。如果不想抛出异常,使用required=false。
自动装配有哪些局限性?
基本数据类型:你不能自动装配简单的属性,如基本数据类型,String字符串,和类。
模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。
依赖注入和装配的关系
依赖注入的本质就是装配,装配时依赖注入的具体行为
Spring注解
如何通过注解自动装配
首先 ,注解装配默认是不开启的,如果需要开启,在配置文件中声明:
<context:annotation-config/>
@Component、@Controller、@Service、@Repository区别
@Component:标记该类为bean,它是一个通用型注解,被标记就可以被注入到IOC容器
@Controller:标记该类是SpringMVC的控制类,Bean同样会被注入到IOC容器
@Service:该注解和@Component没有区别,通常使用在服务层 ,更好的表明了该类的意图
@Repository:该注解和@Component一样,通常用于Dao层
@Required 注解有什么作用
这个注解表明bean的属性必须在配置的时候设置,通过一个bean定义的显式的属性值或通过自动装配,若@Required注解的bean属性未被设置,容器将抛出BeanInitializationException。
@Autowired 注解有什么作用
@Autowired默认是按照类型进行装配注入的;用于在属性、setter方法、构造器之上
@Autowired和@Resource之间的区别
@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。
@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。
@Qualifier 注解有什么作用
当创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,您可以使用@Qualifier 注解和 @Autowired 通过指定应该装配哪个确切的 bean 来消除歧义。其实可以直接使用@Resource
@RequestMapping 注解有什么作用
@RequestMappeing用于对请求的url路径和控制器的方法进行映射,也可以用在类上,用在类上表示该url是方法的url的父路径
Spring数据访问
解释对象/关系映射集成模块
Spring提供了ORM模块,支持我们在JDBC之上使用一个对象/关系映射工具,支持集成主流的ORM框架,比如MyBatis、Hibernate。Spring的事务管理也支持ORM框架和JDBC
Spring对JDBC的支持
Spring提供了JDBCTemplate模版,可以更方便的使用JDBC
Spring支持的事务类型
编程式事务管理:通过编程的方式进行管理事务,有灵活性,但是不易维护,粒度更小
声明式事务管理:通过XML或者注解的方式管理事务,可以将业务代码和事务管理分离解耦,粒度最小为方法级别
Spring事务的实现原理
Spring事务的实现原理还是数据库对事物的支持,如果数据库不支持事务,那Spring事务是没用的
Spring事务的传播行为
1.Required:默认的传播行为,如果当前方法有事务就以事务的方式执行,如果没有事务,就创建一个新的事务
2.Support:随意性、如果当前方法有事务就加入该事务,如果没有事务,也正常执行,用于查询
3.Mandatory:强制性、如果当前方法有事务就加入该事务,如果没有事务,就抛出异常
4.REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
- 如果A类的a方法的事务为REQUIRED,B类的b方法为REQUIRES_NEW;当 a方法出错,b方法同样不会回滚,因为它是一个新的事务
5.NOT_SUPPORTED:以非事务的方式执行,如果当前有事务,会将事务挂起
6.Never:以非事务的方式执行,如果当前有事务,就抛异常
7.Nested:如果当前有事务,就开启子事务;如果主事务提交,会携带主事务一起提交,如果主事务回滚,会携带主 事务一起回滚,如果子事务异常回滚,主事务可回滚与不回滚;如果当前没有事务和REQUIRED效果一样;
Spring 事务失效的情况
1.在同一个类中方法调用,会导致事务失效
2.事务用于非public方法上
3.@Transactional 注解属性 rollbackFor 设置错误;Spring默认是跑出了uncheck异常或者Error才回滚,所以如果抛出了其他异常不会回滚的,如果需要回滚,可以指定rollbackFor类型
4.异常被catch住导致失效;
Spring事务隔离
脏读 :表示一个事务能够读取另一个事务中还未提交的数据。比如,某个事务尝试插入记录 A,此时该事务还未提交,然后另一个事务尝试读取到了记录 A。
不可重复读 :是指在一个事务内,多次读取同一数据不一样。两个事务T1,T2,T1读取了一个字段,T2更新了该字段后,T1再次读取同一个字段,值就不同了
幻读 :指同一个事务内多次查询返回的结果集不一样。两个事务T1,T2,T1从表中读取一个字段,T2新增加了一些数据,T1再去读取,就会出现幻觉
1.DEFAULT:默认,数据库的隔离级别是什么,就设置为什么隔离级别
2.UNCOMMITED:未提交读,事务的最低级别,事务未提交的情况下就会被其他事务读取,会造成幻读、脏读、不可重复读的情况
3.COMMITED:提交读,事务被提交的情况下,才会被其他事务读取。会造成幻读、不可重复读
4.REPEATABLE_READ:可重复读,保证多次读取同一数据时,获取到的值都和事务开始时一致,禁止读取到别的事务读取到的数据,会造成幻读;MySQL默认级别
5.SERIALIZABLE:序列化,最可靠的隔离级别,可以避免脏读、幻读、不可重复读,但是性能非常低
Spring AOP
什么是AOP
OOP是面向对象编程,针对业务处理过程中的实体将属性和行为进行封装为对象,获得清晰的单元划分,它的缺点是无法简单的解决问题,它的关注点是面向所有而不是所有类;
AOP是面向切面编程,它是针对业务处理过程中的切面进行提取为公共模块,这个切面是与业务无关,但是对多个对象产生影响的行为逻辑,它的关注点是具体的某个步骤或者功能;降低业务和系统之间的耦合度
OOP解决了竖向的问题,AOP解决了横向的 问题
OOP与AOP并不是对立,而是互补
Spring AOP and AspectJ AOP 有什么区别?
AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。
1)AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。
2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
SpringAOP动态代理的实现
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。
解释一下Spring AOP里面的几个名词
切面(Aspect):切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @AspectJ 注解来实现。
通知(Advice):在AOP术语中,切面的工作被称为通知。
连接点(Join point):连接点是在应用执行过程中能够插入切面的一个点。
切入点(Pointcut):切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。

浙公网安备 33010602011771号