面试总结七:Spring


一、Spring 框架

1.1 Spring 是什么

  • Spring 是一个开源的轻量级 Java 开发框架,旨在提高系统的开发效率和可维护性。
  • Spring 的核心思想是不重复造轮子,开箱即用。
  • Spring 的核心模块:Spring Core、Spring Web、Spring AOP、Spring Test、数据访问等。

1.2 Spring 框架的优点

  • 容器化:Spring 是一个容器,包含并且管理应用对象的生命周期。
  • 组件化:Spring 实现了使用简单的组件组合成一个复杂的应用。
  • 非侵入式:基于 Spring 开发的应用中的对象可以不依赖于 Spring 的 API。
  • 依赖注入:DI 依赖注入,控制反转(IoC)最经典的实现。
  • 面向切面编程:Aspect Oriented Programming – AOP。

1.3 Spring 框架用到的设计模式

  • 工厂模式:通过 BeanFactory、ApplicationContext 创建 bean 对象属于工厂模式。
  • 单例/原型模式:创建 bean 对象设置作用域时,就可以声明单例模式或原型模式。
  • 代理模式:Spring AOP 功能的实现。
  • 责任链模式:Spring AOP 拦截器的执行。
  • 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
  • 模板方法模式:Spring 中以 Template 结尾的类就使用到了模板方法模式。
  • 策略模式:在创建代理类时如果代理的是接口使用JDK自身的动态代理,如果不是则使用的是cglib实现动态代理。

1.4 Spring 核心

Spring 的核心是提供了一个容器,通常称为 Spring 应用上下文,它们会创建和管理应用组件。这些组件也可以称为 bean,会在 Spring 应用上下文中装配在一起,从而形成一个完整的应用程序。

将 bean 装配在一起的行为是基于依赖注入模式实现的。此时组件不会再去创建和管理它所依赖的组件,而是由容器来创建和维护所有的组件,并将其注入到需要它们的bean中。

1.5 IoC 是什么

IoC 又叫控制反转,是一种设计思想,而不是一个具体的技术实现。Ioc 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理,有效的降低了代码的耦合度,提高了程序的可维护性。

1.6 DI 是什么

DI 又叫依赖注入,是指 bean 之间的依赖关系由容器在运行时决定,即容器动态的将依赖关系注入到 bean 中,提高了组件的复用性。在 Java 中依赖注入有以下三种实现方式:构造器注入、Setter 方法注入、接口注入。

1.7 IoC 和 DI 的关系

依赖注入(DI )是实现控制反转(IoC)的方法和手段,在 JDK1.3 之后增加了反射机制,允许程序在运行的时动态的生成对象、执行对象的方法、改变对象的属性,Spring 就是通过反射来实现注入的。

1.8 AOP 是什么

AOP 是指面向接口编程,是对面向对象的一种补充,在不改变业务代码的基础上,在程序运行期间,动态的将某段代码插入指定方法的指定位置,使业务逻辑和系统模块解耦,提高了程序的复用性。

1.9 AOP 底层原理

AOP 是基于动态代理实现的,如果被代理的对象实现了接口,那么 AOP 会使用 JDK Proxy 去创建代理对象,如果对象没有实现接口,AOP 会使用 Cglib 生成一个被代理对象的子类。

1.10 JDK Proxy 和 Cglib 区别

  • 实现不同:JDK Proxy 是由 Java 内部的反射机制实现的,Cglib 底层则是借助 ASM 来实现的。
  • 范围不同:JDK Proxy 代理的前提必须是实现接口,Cglib 不能对声明为 final 的类进行代理。
  • 效率不同:JDK1.8 之前 Cglib 代理效率高,JDK1.8 的时候 JDK Proxy 效率更高。
  • 优势不同:反射机制在生成类的过程中比较高效,ASM 在生成类之后的相关操作比较高效。

1.11 Spring AOP 和 AspectJ AOP 有什么区别

  • Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。
  • Spring AOP 基于动态代理,而 AspectJ 基于字节码操作。
  • Spring AOP 相对来说更简单,AspectJ 功能更加强大。
  • 当切面过多时,AspectJ 比 Spring AOP 快很多。

二、Spring Bean

2.1 Bean 的作用域

  • 单例(Singleton):整个应用程序,只创建 bean 的一个实例,Spring 默认是单例的。
  • 原型(Prototype):每次注入都会创建一个新的 bean 实例。
  • 请求(Request):每个请求创建一个 bean 实例,只在 Web 系统中有效。
  • 会话(Session):每个会话创建一个 bean 实例,只在 Web 系统中有效。
  • 公共(global-session):全局 session 作用域,Spring5 已经没有了。

2.2 Bean 的生命周期

  • 实例化 Instantiation:当容器启动时,通过构造器或者工厂方法创建Bean实例。
  • 属性赋值 Populate:创建实例后,容器会通过依赖注入或者其他方式,将配置的属性值和引用注入到Bean中。
  • 初始化 Initialization:在属性设置完成后,容器会调用Bean的初始化方法。
  • 销毁 Destruction:当应用程序关闭或者手动移除Bean时,容器会调用Bean的销毁方法。
    // 1.AbstractAutowireCapableBeanFactory#createBean()
    // 2.AbstractAutowireCapableBeanFactory#createBean()
    // 3.AbstractAutowireCapableBeanFactory#doCreateBean()
    // 忽略了无关代码
    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
        // Instantiate the bean.
        BeanWrapper instanceWrapper = null;
        if (instanceWrapper == null) {
            // 实例化阶段
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        // Initialize the bean instance.
        Object exposedObject = bean;
        try {
            // 属性赋值阶段
            populateBean(beanName, mbd, instanceWrapper);
            // 初始化阶段
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
    }

2.3 Bean 的线程安全问题

当多个线程同时操作对象的的非静态成员变量时,会存在线程安全问题,可以使用同步机制或者 ThradLocal 来解决。

  • 同步机制是通过对象的锁保证同一时间只有一个线程访问变量(时间换空间)。
  • ThreadLocal 为每个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突(空间换时间)。

2.4 Bean 循环依赖

所谓的循环依赖,就是两个或两个以上的 bean 互相依赖对方,最终形成闭环。比如 A 依赖 B 而 B 也依赖 A。单例模式的 Bean 可以通过三级缓存来解决循环依赖问题。

构造器注入构成的循环依赖是无法解决的,只能抛出 BeanCurrentlyInCreationException 异常表示循环依赖。Spring 解决循环依赖依靠的是 bean 的中间态,而中间态指的是已经实例化,但还没初始化的状态。

2.5 Bean 循环依赖示例

以 A、B 对象相互依赖为例说明创建过程:

  • 从容器中获取实例 A 时,因为 A 不存在,所以会走 A 创建流程。
  • 实例化 A 并将其放到二级缓存 earlySingletonObjects 中,然后对 A 进行初始化。
  • 因为实例 A 依赖实例 B,所以从容器中获取实例 B,因为 B 不存在,所以会走 B 创建流程。
  • 实例化 B 然后进行初始化,因为 B 依赖 A 所以从容器中获取 A,因为 A 在二级缓存中,所以 B 可以完成初始化并放在一级缓存中。
  • 因为 B 已经初始化完成并返回,因此 A 也初始化成功,并且从二级缓存移动到一级缓存中。

2.6 三级缓存

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry 类。

  • 一级缓存:singletonObjects 存放已经经历完整生命周期的 Bean 对象。
  • 二级缓存:earlySingletonObjects 存放早期暴露出来的 Bean 对象(属性未初始化)。
  • 三级缓存:singletonFactories 存放可以生成 Bean 的工厂。

三、Spring 事务

3.1 Spring 管理事务的方式

  • 编程式事务:在代码中硬编码,通过 TransactionTemplate 或 TransactionManager 手动管理事务。
  • 声明式事务 : 基于 @Transactional 注解,底层通过 AOP 实现。

3.2 Spring 事务传播行为

支持当前事务:

  • required:如果当前存在事务则加入该事务,如果当前没有事务则创建一个新的事务(默认级别)。
  • supports:如果当前存在事务则加入该事务,如果当前没有事务则以非事务的方式继续运行。
  • mandatory:如果当前存在事务则加入该事务,如果当前没有事务则抛出异常。
    不支持当前事务:
  • requires_new:总是创建一个新的事务,如果当前存在事务,则把当前已存在的事务挂起。
  • not_supported:以非事务方式运行,如果当前存在事务则把当前事务挂起。
  • _never:以非事务方式运行,如果当前存在事务则抛出异常。
    其他情况:
  • nested:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行,如果当前没有事务则创建一个新的事务执行。

3.3 Spring 事务隔离级别

  • default:使用后端数据库默认的隔离级别,默认为此事务级别。
  • read_uncommitted:允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
  • read_committed:允许读取并发事务已经提交的数据,可能会导致幻读或不可重复读。
  • repeatable_read:可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • serializable:最高的隔离级别,所有的事务依次逐个执行,可以防止脏读、不可重复读以及幻读,但是这将严重影响程序的性能。

3.4 @Transactional 原理

@Transactional 的工作机制是基于 AOP 实现的,AOP 又是使用动态代理实现的。如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理,如果目标对象没有实现了接口,会使用 CGLIB 动态代理。

3.5 @Transactional 失效

在同一个类中,没有注解的方法内部调用有注解的方法时,会导致事务会失效,这是由于Spring AOP 代理的原因造成的,因为只有当被注解的方法在类以外被调用的时候,Spring 事务管理才生效。

3.6 @Transactional(rollbackFor = Exception.class)

@Transactional 注解作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,在 @Transactional 注解中如果不配置 rollbackFor 属性,那么事务只会在遇到 RuntimeException 的时候才会回滚,加上 rollbackFor=Exception 可以让事务在遇到非运行时异常时也回滚。


四、Spring Boot

4.1 Spring Boot 理解

Spring Boot 用来简化 Spring 应用程序的初始搭建以及开发过程。

4.2 Spring Boot 优点

  • 自动配置:尽可能自动配置Spring和第三方库,无需其他配置。
  • 独立运行:Spring Boot内嵌了servlet容器,只要打成一个可执行的jar包就能独立运行。
  • 应用监控:Spring Boot提供一系列端点可以监控服务及应用,做健康检测。
  • 无代码生成和XML配置:配置过程中无代码生成,也无需XML配置文件就能完成所有配置。

4.3 SpringBoot 自动装配

SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到 Spring 容器,并执行类中定义的各种操作。

4.4 SpringBoot 如何实现自动装配

SpringBoot 实现自动装配依赖核心注解 SpringBootApplication,这个注解明确表明这是一个Spring Boot应用,并且包含了3个其他的注解:

  • @EnableAutoConfiguration:启用 Spring Boot 自动配置机制。
  • @SpringBootConfiguration:将该类声明为配置类,会为 Spring 应用上下文提供 bean,这个注解实际上是 @Configuration 注解的特殊形式。
  • @ComponentScan:启用组件扫描,Spring 会自动发现 bean 并将它们注册到 Spring 应用上下文中。

4.5 @EnableAutoConfiguration 注解

@EnableAutoConfiguration 通过引入 AutoConfigurationImportSelector 类实现核心功能,该类实现了 ImportSelector.selectImports() 方法,该方法通过 SpringFactoriesLoader 加载 META-INF/spring.factories 配置文件,获取所有符合条件的类,通过动态代理的方式注入到 Spring 容器中,从而实现自动装配。

4.6 @Configuration 注解

@Configuration 注解会告知 Spring 这是一个配置类,会为 Spring 应用上下文提供 bean。配置类的方法使用 @Bean 注解进行了标注,表明这些方法所返回的对象会以 bean 的形式添加到 Spring 的应用上下文中。

4.7 @Autowired 和 @Resource 的区别

两个注解都可以用来装配 bean,都可以写在字段和 setter 方法上。

  • 来源不同:@Autowired 源于 spring,@Resource 源于 Java。
  • 装配方式不同:@Autowired 默认按照类型装配,@Resource 默认按照名称进行装配。
  • 注解参数不同:@Autowired 只包含一个参数 required,表示是否开启自动注入;@Resource 包含七个参数,其中最重要的是 name 和 type。

4.8 如何实现一个 starter

  • 创建一个 spring-boot 项目,引入需要的相关依赖;
  • 创建一个配置类,配置需要注入 spring 的类;
  • 在 resource 目录下创建 META-INF/spring.factories 文件,注入配置类;
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.thread.configuration.ThreadConfiguration
@Configuration
public class ThreadConfiguration {
    @Bean
    @ConditionalOnClass(ThreadPoolExecutor.class)
    public ThreadPoolExecutor threadPoolExecutor() {
        return new ThreadPoolExecutor(10, 10, 10L, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值