Spring面试题总结及答案

Spring面试总结

Spring是什么?

Spring框架是一个java平台,提供全面基础设施支持开发java应用程序。

Spring的主要模块有哪些?

  • Spring Code:基础模块,主要提供IOCDI功能。
  • Spring Aspects:该模块对与AspectJ集成提供支持。
  • Spring AOP:提供面向切面编程的实现。
  • Spring JDBC:提供java数据库连接。
  • Spring JMSjava消息服务。【与远程调用机制以及REST接口类似】
  • Spring ORM:用于支持HibernateORM工具。
  • Spring Web:为创建Web应用程序提供支持。
  • Spring Test:提供了对JUnitTestNG测试支持。

Spring的优点

低侵入式设计,代码污染低,低耦合,复用性高,对主流框架的集成支持

aop的理解

aop是面向切面编程,是面向对象的一种补充,用于将那些与业务无关,却对多个对象有影响的公共行为和逻辑,抽取并封装为一个可重用的公共模块,减少重复代码降低模块间的耦合度,提高系统的可维护性

代理模式

静态代理和动态代理

  • 静态代理AspectJ

    也称编译时增强,静态代理就是编译阶段生成代理类,将切面织入java字节码中,运行后就是增强之后的aop对象

  • 动态代理Spring Aop

    动态代理就是不去修改字节码,而是每次运行时在内存中为方法生成一个aop对象,这个对象包含了目标对象的全部方法,在特定的切点做了增强处理,并回调原对象的方法

    • 动态代理基于JDK动态代理和CJLIB动态代理

      • JDK动态代理

        JDK动态代理只提供接口的代理,不支持类的代理

      • CJLIB动态代理

        CJLB时通过继承的方式做的动态代理,如果某个类被标记为final,是无法使用CJLIB做动态代理

    Spring AOP默认是JDK动态代理。

  • 静态代理和动态代理的区别

    区别在于生成aop代理的时机不同,相对来说AspectJ的静态代理有更好的性能,但是AspectJ需要特定的编译器处理,而Spring Aop不需要特定的编译器处理

IOC的理解

IOC控制反转,不是一种技术,而是一种设计思想。是创建对象的控制权和转移,以前创建对象的时机和主动权是由自己把控的,而现在是把这种权力转移到spring容器中,由容器根据配置文件去创建实例和管理各个实例间的依赖关系,对象与对象的松散耦合,也就是IOC让对象的创建不用去new了,由spring自动生产,利用java的反射机制,根据配置文件运行时动态的去创建对象和管理,并调用对象的方法

  • 使用java反射机制的几种方法
    • 通过全限定类名
    • 获取构造器对象,通过构造器new出来
    • 通过class对象创建一个实例对象
    • 通过class对象获得一个属性对象
    • 通过class对象获得一个方法对象

ioc控制反转理解:

  • 谁控制了谁,控制了什么:

    • ioc控制了对象,控制了外部资源的获取
  • 什么是反转?

    • 传统的程序直接获取依赖对象,也就是正转。
    • 而反转就是ioc容器帮我们创建对象及注入依赖

    为何是反转?

    • 是因为容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象。

DI依赖注入理解:

形象的说,由容器动态的将某个依赖关系注入到组件中。

  • 谁依赖了谁:
    • 应用程序依赖了ioc容器
  • 为什么要依赖:
    • 因为需要ioc容器来提供对象需要的外部资源
  • 谁注入了谁:
    • ioc容器注入了应用程序某个对象,应用程序依赖的对象
  • 注入了什么:
    • 就是注入了某个对象所需要的外部资源(包括对象,资源,常量数据等)

ioc为什么要和DI同时出现

  • 因为他们是同一概念不同角度的描述

IOC的注入

  • 构造器注入
  • setter方法注入
  • 注解注入

【构造器参数实现强制依赖】

【setter方法实现可选依赖】

Springbean的作用域

  • singleton:
    • 唯一bean实例,Spring中的bean默认都是单例。
  • prototype
    • 每次请求都会创建一个新的bean实例。(多例)
  • request
    • 每次HTTP请求都会产生一个新的bean,该bean仅在HTTP Request内有效。
  • session
    • 每次HTTP都会产生一个新的bean,该bean仅在HTTP Session内有效。
  • global-session
    • 全局session作用域,仅仅在基于PortletWeb应用中才有意义,Spring5中已经没有了。

BeanFactoryAppplicationContext的区别?

  1. BeanFactoryspring最底层的接口,包含了各种bean的定义,管理bean的加载,实例化,控制bean的生命周期,维护bean之间的关系,而ApplicationContext接口是BeanFactory的派生,除了有BeanFactory所有的功能外,还有更加完整的框架功能:
    • ApplicationContext继承了MessageSource
    • 统一的文件访问方式
    • 可以在监听器中注册bean的事件
    • 同时加载多个配置文件
    • 载入多个有继承关系的上下文,让每一个上下文都专注与一个特定的层次
  2. BeanFactory是延迟加载的方式来注入bean,如果有异常,只有在使用bean的时候,才会抛出,不能及时发现存在spring配置的问题。而ApplicationContext是容器启动时一次性创建所有bean,如果有问题,在启动时我们就可以发现了,有利于检查所有依赖属性是否注入(ApplicationContext启动后预载入所有的单实例bean
  3. BeanFactory通常是以编程的方式进行创建,而ApplicationContext可以以声明的方式进行创建(如:ContextLoader
  4. BeanFactoryApplictionContext都支持BeanPostProcessorBeanFactoryPostProcessor的使用,但BeanFactory是手动注册,而ApplicationContext是自动注册

Spring Bean的生命周期

  1. Bean容器找到配置文件中Spring Bean的定义
  2. Bean容器利用Java Reflection API创建一个Bean的实例
  3. 如果涉及到属性值,利用set()方法设置一些属性值
  4. 如果Bean实现了BeanNameAware接口,调用setBeanName()方法,传入Bean名字
  5. 如果Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象实例
  6. 如果Bean实现BeanFactoryAware接口,调用setBeanClassFactory()方法,传入ClassLoader对象实例。(如果实现了其他Aware接口,就调用相应的方法)
  7. 如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执行postProcessorBeforeInitialization()方法
  8. 如果Bean实现了InitializingBean接口, 执行afeterPropertiesSet()方法
  9. 如果Bean在配置文件中定义了包含init-method属性,执行指定的方法
  10. 如果有何加载这个Bean的Spring容器相关的BeanPostProcess对象,执行postProcessAfterInitialization()方法
  11. 当要销毁Bean的时候,如果Bean实现了DisposableBena接口,执行destroy()方法
  12. 当要销毁Bean的时候,如果Bean在配置文件中的定义包含destory-method属性,执行指定方法

在这里插入图片描述

怎么重载Bean生命周期重要的方法?

  • init-methoddestroy-method属性,可以用他们来定制初始化方法和注销方法
  • 相应的注解:
    • @PostConstuct
    • @PreDestroy

Spring框架中的单例Bean是否线程安全

大部分时候我们的系统中并没有使用多线程,所以很少去关注这个问题,单例Bean是存在线程问题的,主要是因为当多个线程对这个对象的非静态成员变量进行写操作时会存在线程安全问题。

有常用的俩种解决方案:

  • Bean对象中尽量避免定义可变的成员变量。(并不现实)
  • 在类中定义一个ThreadLoad成员变量,把需要可变的成员变量保存在ThreadLoad中。(推荐的一种方式)

Spring事务的管理方式

  • 编程式事务:在代码中硬编码。(不推荐使用)
  • 声明式事务:在配置文件中配置。(推荐使用)
    • 基于XML的声明式事务。
    • 基于注解的声明式事务。

Spring事务中的隔离级别有哪几种?

TransactionDefinition接口中定义了五个表示隔离级别的常量

  • ISOLATION_DEFAULT:使用后端数据库默认的隔离级别,MySQL默认采用REPEATABLE_READ隔离级别,Oracle默认采用的READ_COMMITTED隔离级别。
  • ISOLATION_READ_UNCOMMITTED:最低级隔离级别,允许读取尚未提交的数据变更,可能会导致脏读,幻读,不可重复读。
  • ISOLATION_READ_COMMITTED:允许读取并发事务已提交的数据,可以阻止脏读,但是幻读,不可重复读仍有可能发生。
  • ISOLATION_REPEATABLE_READ:对同一个字段多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但是幻读仍有可能发生。
  • ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别。所有事务依次逐个执行,这样事务之间就完全不可能产生干扰。可阻止脏读,不可重复读,幻读。但是会严重影响程序的性能通常情况不会用到该级别。

Spring事务中的传播行为

TransactionDefiniton接口中定义了八个表示事务传播行为的常量

支持当前事务的情况:

  • PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务,如果当前没有事务,则创建一个新的事务。
  • PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务,如果当前没有事务,则以非事务的方式继续运行。
  • PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务,如果当前没有事务,则抛出异常。(mandatory:强制性)

不支持当前事务的情况:

  • PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把事务挂起。
  • PROPAGATION_NOT_SUPPORTED:以非事务的方式运行,如果当前存在事务,则把事务挂起。
  • PROPAGATION_NEVER:以非事务的方式运行,如果当前存在事务,则抛出异常。

其他情况:

  • PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行,如果当前没有事务,则取值等价于PROPAGATION_REQUIRED。

Spring中用到了那些设计模式?

  • 工厂设计模式:Spring使用工厂代理模式通过BeanFactoryApplicationContext创建对象。

  • 代理设计模式:Spring AOP功能实现。

  • 单例设计模式:Spring中的Bean,默认都是单例的。

  • 模板方法模式:Spring中的jdbcTempalte等以Template结尾的对数据库操作的类,就使用到了模板模式。

  • 包装器设计模式:当我们的项目需要连接多个数据库的时候,而且不同的客户在每次访问中会去访问不同的数据库,这种模式可以根据客户的需求动态的切换不同的数据源。

  • 观察者模式:Spring事件驱动模型就是观察者模式很经典的一个应用。

    • 什么是事件驱动模型?

      是一种对象间一对多的关系,就像交通信号灯,信号灯是目标(也就是一的一方),行人是多方,行人需要注意观察信号灯的变化,当信号灯变为绿色的时候(也就是目标发送了改变),行人就知道这时候该过马路了(也就是订阅者可以接受到改变)。观察者怎么处理,目标不会干涉,这就松散的他们之间的耦合。就像绿灯的时候行人怎么走,是直着走还是弯着走,是走得快还是走的慢,目标都不会管。

  • 适配器模式:Spring AOP的增强通知使用到了适配器模式,Spring MVC也用到了适配模式来适配Controller

  • 等等…

@Component@Bean的区别是什么?

  • 作用对象不同
    • @Component作用与类。
    • @Bean作用与方法上。
  • 可以用@Component注解扫描来自动侦测和自动装配到Spring容器中。
    • 可以使用@ComponentScan来定义要扫描的路径。
  • @Bean注解通常实在标有该注解的方法中产生这个Bean,告诉Spring容器,当我需要他的时候在给我。
  • @Bean@Component的自定义性强,很多地方只能用@Bean来注册Bean。比如当我们需要使用第三方类库的类的时候,就只能通过@Bean来实现。

将一类声明为SpringBean的注解有哪些?

一般我们用@Autowired注解去自动装配Bean,而想要把一个类标识为可以使用@Autowired注解自动装配的Bean,可以使用如下注解:

  • @Component注解:通用注解,可以标注任意类。
  • @Repository注解:对应持久层,也就是dao层,主要用于数据库的相关操作。
  • @Service注解:对应服务层,也就是service层,主要涉及一些复杂的逻辑,需要用到dao层(@Autowired注入)
  • @Controller注解:对应控制层,也就是Controller层,主要用于接受用户请求并调用service层的方法返回数据给前端页面。

什么是装配?

装配(Bean装配)就是在Spring容器中把Bean组装到一起,前提是容器要知道Bean的依赖关系,如何通过依赖注入把他们装配到一起。

什么是自动装配?

Spring容器能够自动装配相互合作的Bean,容器不需要使用<constructor-arg><property>配置,能通过Bean工厂自动处理Bean之间的协作。

不同方式的自动装配

有五种:

  • no:默认不自动装配,可以通过ref属性来进行装配。
  • byName:通过参数名自动装配。
  • byType:通过参数类型自动装配。
  • construtor:这个方式类似于byType,是需要提供给构造器参数。
  • autodetect:首先尝试使用construtor来自动装配,如果无法工作,则使用byType方式。

自动装配的局限性

  • 重写:需要使和配置定义依赖,总要重写自动装配。
  • 基本数据类型:不能自动装配简单的属性,如基本数据类型,String还有类。
  • 模糊特性:自动装配不如显示装配精确,建议使用显示装配。

可以在Spring中注入一个null和一个空字符串吗?

答:可以。

yType`,是需要提供给构造器参数。

  • autodetect:首先尝试使用construtor来自动装配,如果无法工作,则使用byType方式。

自动装配的局限性

  • 重写:需要使和配置定义依赖,总要重写自动装配。
  • 基本数据类型:不能自动装配简单的属性,如基本数据类型,String还有类。
  • 模糊特性:自动装配不如显示装配精确,建议使用显示装配。

可以在Spring中注入一个null和一个空字符串吗?

答:可以。