WinddSnow

Java面试题08Spring框架

字数统计: 3.2k阅读时长: 11 min
2022/10/22

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

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

Spring 的生命周期?

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

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

Spring 支持如下 5 种作用域:

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

BeanFactory 和 ApplicationContext 有什么区别

BeanFactory:

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

ApplicationContext:

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

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

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

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

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

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

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

你知道的 Spring 的通知类型有哪些,分别在什么时候执行?

Spring 的通知类型有四种,分别为:

  1. 前置通知[]before]:在切点运行之前执行
  2. 后置通知[after-returning]:在切点正常结束之后执行
  3. 异常通知[after-throwing]:在切点发生异常的时候执行
  4. 最终通知[after]:在切点的最终执行

Spring 还有一种特殊的通知,叫做环绕通知

环绕通知运行程序员以编码的方式自己定义通知的位置, 用于解决其他通知时序问题

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

  1. 在 spring 中的对象默认是单例的,但是也可以配置为多例。

  2. 单例 bean 对象对应的类存在可变的成员变量并且其中存在改变这个变量的线程时,多线程操作该 bean 对象时会出现线程安全问题。

    原因是:多线程操作如果改变成员变量,其他线程无法访问该 bean 对象,造成数据混乱

    解决办法:在 bean 对象中避免定义可变成员变量;在 bean 对象中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 中。

@Resource 和@Autowired 依赖注入的区别是什么? @Qualifier 使用场景是什么?

  • @Resource

    只能放在属性上,表示先按照属性名匹配 IOC 容器中对象 id 给属性注入值若没有成功,会继续根据当前属性的类型匹配 IOC 容器中同类型对象来注入值若指定了 name 属性@Resource(name = “对象 id”),则只能按照对象 id 注入值。

  • @Autowird

    放在属性上:表示先按照类型给属性注入值如果 IOC 容器中存在多个与属性同类型的对象,则会按照属性名注入值,也可以配合@Qualifier(“IOC 容器中对象 id”)注解直接按照名称注入值。
    放在方法上:表示自动执行当前方法,如果方法有参数,会自动从 IOC 容器中寻找同类型的对象给参数传值,也可以在参数上添加@Qualifier(“IOC 容器中对象 id”)注解按照名称寻找对象给参数传值。

  • @Qualifier 使用场景:

    @Qualifier(“IOC 容器中对象 id”)可以配合@Autowird 一起使用, 表示根据指定的 id 在 Spring 容器中匹配对象

Spring 的常用注解

  • @Component(任何层) @Controller @Service @Repository(dao): 用于实例化对象
  • @Scope : 设置 Spring 对象的作用域
  • @PostConstruct @PreDestroy : 用于设置 Spring 创建对象在对象创建之后和销毁之前要执行的方法
  • @Value: 简单属性的依赖注入
  • @Autowired: 对象属性的依赖注入
  • @Qualifier: 要和@Autowired 联合使用,代表在按照类型匹配的基础上,再按照名称匹配。
  • @Resource 按照属性名称依赖注入
  • @ComponentScan: 组件扫描
  • @Bean: 表在方法上,用于将方法的返回值对象放入容器
  • @PropertySource: 用于引入其它的 properties 配置文件
  • @Import: 在一个配置类中导入其它配置类的内容
  • @Configuration: 被此注解标注的类,会被 Spring 认为是配置类。Spring 在启动的时候会自动扫描并加载所有配置类,然后将配置类中 bean 放入容器
  • @Transactional 此注解可以标在类上,也可以表在方法上,表示当前类中的方法具有事务管理功能。

Spring 的事务传播行为

spring 事务的传播行为说的是,当多个事务同时存在的时候,spring 如何处理这些事务的行为。

备注(方便记忆):

  • propagation 传播
  • require 必须的
  • suppor 支持
  • mandatory 强制托管
  • requires-new 需要新建
  • not -supported 不支持
  • never 从不
  • nested 嵌套的
  1. PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
  2. PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
  3. PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
  4. PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
  5. PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  6. PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
  7. PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按 REQUIRED 属性执行。

Spring 中的隔离级别

  • spring的隔离级别就是数据库的隔离级别
  • ISOLATION_DEFAULT:这是个 PlatfromTransactionManager 默认的隔离级别,
  • ISOLATION_READ_UNCOMMITTED:读未提交,允许另外一个事务可以看到这个事务未提交的数据。
  • ISOLATION_READ_COMMITTED:读已提交,保证一个事务修改的数据提交后才能被另一事务读取,而且能看到该事务对已有记录的更新。解决脏读问题
  • ISOLATION_REPEATABLE_READ:可重复读,保证一个事务修改的数据提交后才能被另一事务读取,但是不能看到该事务对已有记录的更新。行锁
  • ISOLATION_SERIALIZABLE:一个事务在执行的过程中完全看不到其他事务对数据库所做的更新。表锁
CATALOG
  1. 1. Spring的两大核心是什么?谈一谈你对IOC的理解? 谈一谈你对DI的理解?谈一谈你对 AOP 的理解?
    1. 1.0.0.1. Spring 的两大核心是:IOC(控制反转)和 AOP(面向切面编程) DI(依赖注入)
    2. 1.0.0.2. IOC 的意思是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和时机是由程序员自己把控的,而现在这种权力转移到 Spring 容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。最直观的表达就是,IOC 让对象的创建不用去 new 了,可以由 spring 根据我们提供的配置文件自动生产,我们需要对象的时候,直接从 Spring 容器中获取即可.Spring 的配置文件中配置了类的字节码位置及信息, 容器生成的时候加载配置文件识别字节码信息, 通过反射创建类的对象.
    3. 1.0.0.3. Spring 的 IOC 有三种注入方式 :构造器注入, setter 方法注入, 根据注解注入。
    4. 1.0.0.4. DI 的意思是依赖注入,和控制反转是同一个概念的不同角度的描述,即应用程序在运行时依赖 Io c 容器来动态注入对象需要的外部资源。
    5. 1.0.0.5. AOP,一般称为面向切面编程,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect). SpringAOP 使用的动态代理,所谓的动态代理就是说 AOP 框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个 AOP 对象,这个 AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
    6. 1.0.0.6. Spring AOP 中的动态代理主要有两种方式,JDK 动态代理和 CGLIB 动态代理:
      1. 1.0.0.6.1. JDK 动态代理只提供接口代理,不支持类代理,核心 InvocationHandler 接口和Proxy 类,InvocationHandler 通过 invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起,Proxy 利用 InvocationHandler 动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
      2. 1.0.0.6.2. 如果代理类没有实现 InvocationHandler 接口,那么 Spring AOP 会选择使用CGLIB 来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现 AOP。CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final,那么它是无法使用 CGLIB 做动态代理的。
  • 2. Spring 的生命周期?
    1. 2.0.0.1. 实例化一个 Bean,也就是我们通常说的 new
    2. 2.0.0.2. 按照 Spring 上下文对实例化的 Bean 进行配置,也就是 IOC 注入
    3. 2.0.0.3. 如果这个 Bean 实现 dao 了 BeanNameAware 接口,会调用它实现的setBeanName(String beanId)方法,此处传递的是 Spring 配置文件中 Bean 的 ID
    4. 2.0.0.4. 如果这个 Bean 实现了 BeanFactoryAware 接口,会调用它实现的 setBeanFactory(),传递的是 Spring 工厂本身(可以用这个方法获取到其他 Bean)
    5. 2.0.0.5. 如果这个 Bean 实现了 ApplicationContextAware 接口,会调用setApplicationContext(ApplicationContext)方法,传入 Spring 上下文,该方式同样可以实现步骤 4,但比 4 更好,以为 ApplicationContext 是 BeanFactory 的子接口,有更多的实现方法
    6. 2.0.0.6. 如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor 经常被用作是 Bean 内容的更改,并且由于这个是在 Bean 初始化结束时调用 After 方法,也可用于内存或缓存技术
    7. 2.0.0.7. 如果这个 Bean 在 Spring 配置文件中配置了 init-method 属性会自动调用其配置的初始化方法
    8. 2.0.0.8. 如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用postAfterInitialization(Object obj, String s)方法
    9. 2.0.0.9. 注意:以上工作完成以后就可以用这个 Bean 了,那这个 Bean 是一个 single 的,所以一般情况下我们调用同一个 ID 的 Bean 会是在内容地址相同的实例
    10. 2.0.0.10. 当 Bean 不再需要时,会经过清理阶段,如果 Bean 实现了 DisposableBean 接口,会调用其实现的 destroy 方法
    11. 2.0.0.11. 最后,如果这个 Bean 的 Spring 配置中配置了 destroy-method 属性,会自动调用其配置的销毁方法
  • 3. Spring 支持 bean 的作用域有几种吗? 每种作用域是什么样的?
  • 4. BeanFactory 和 ApplicationContext 有什么区别
  • 5. Spring 框架中都用到了哪些设计模式?
  • 6. Spring 事务的实现方式和实现原理
  • 7. 你知道的 Spring 的通知类型有哪些,分别在什么时候执行?
  • 8. Spring 的对象默认是单例的还是多例的? 单例 bean 存不存在线程安全问题呢?
  • 9. @Resource 和@Autowired 依赖注入的区别是什么? @Qualifier 使用场景是什么?
  • 10. Spring 的常用注解
  • 11. Spring 的事务传播行为
  • 12. Spring 中的隔离级别