Home
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 设计模式
  • JavaSE
  • JVM
  • JUC
  • Netty
  • CPP
  • QT
  • UE
  • Go
  • Gin
  • Gorm
  • HTML
  • CSS
  • JavaScript
  • vue2
  • TypeScript
  • vue3
  • react
  • Spring
  • SpringMVC
  • Mybatis
  • SpringBoot
  • SpringSecurity
  • SpringCloud
  • Mysql
  • Redis
  • 消息中间件
  • RPC
  • 分布式锁
  • 分布式事务
  • 个人博客
  • 弹幕视频平台
  • API网关
  • 售票系统
  • 消息推送平台
  • SaaS短链接系统
  • Linux
  • Docker
  • Git
GitHub (opens new window)
Home
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 设计模式
  • JavaSE
  • JVM
  • JUC
  • Netty
  • CPP
  • QT
  • UE
  • Go
  • Gin
  • Gorm
  • HTML
  • CSS
  • JavaScript
  • vue2
  • TypeScript
  • vue3
  • react
  • Spring
  • SpringMVC
  • Mybatis
  • SpringBoot
  • SpringSecurity
  • SpringCloud
  • Mysql
  • Redis
  • 消息中间件
  • RPC
  • 分布式锁
  • 分布式事务
  • 个人博客
  • 弹幕视频平台
  • API网关
  • 售票系统
  • 消息推送平台
  • SaaS短链接系统
  • Linux
  • Docker
  • Git
GitHub (opens new window)
  • Spring常见面试题
    • AOP&IOC
    • 如何通过注解实现AOP?
    • 依赖注入
    • Bean的生命周期
    • Spring循环依赖问题
    • Spring如何实现优雅停机?
    • IOC初始化过程
  • Spring常用注解与接口
  • 容器与Bean
  • AOP
  • Spring事务
  • Spring中的设计模式
  • 手撸Spring

  • 事务嵌套
  • 事件监听器
  • Spring自定义异常
  • Spring
Nreal
2023-11-21
目录

Spring常见面试题

# AOP&IOC

IOC:控制反转,它是一种思想,将手动创建对象的权力交给Spring,由容器来控制对象的生命周期和对象之间的关系。以前对象是手动创建,现在是向容器索要;

为什么叫控制反转?

控制对象生命周期的不再是引用它的对象,而是容器;

Spring的IOC负责管理bean的生命周期,在底层,这个过程通过BeanPostProcessor接口和BeanFactoryPostProcessor接口来实现;

  • BeanFactoryPostProcessor:在容器加载配置文件时,对配置信息进行修改或扩展;
  • BeanPostProcessor:负责在bean的初始化前后进行一些处理操作,如AOP代理、属性注入等;

AOP:面向切面编程,将一些业务逻辑中相同代码抽取到一个独立的模块中,降低模块耦合,让业务逻辑更加清爽。在使用时,首先自定义一个注解作为切点,再定义一个切面类,并为需要增强的方法添加注解,Spring回将切面逻辑自动注入到这个方法中,从而实现AOP功能;

原理:基于动态代理,如果代理对象实现了某个接口,使用jdk动态代理创建对象;如果没有实现接口,会用cglib生成一个被代理对象的子类作为代理;

# 如何通过注解实现AOP?

  1. 定义切面类;

    创建一个类,添加@Aspect注解,定义具体的通知方法:@Before、@After、@Around;

  2. 定义切点;

    使用@Pointcut注解定义切点,切点是一个表达式,用于匹配需要应用切面的连接点(方法调用、字段访问等);

    编写通知方法时指定切点表达式来确定在哪些连接点应用通知;

  3. 启用自动代理;

    在配置类(通常是一个带有 @Configuration 注解的类)中,添加 @EnableAspectJAutoProxy 注解来启用 Spring 对 AOP 的支持,并自动创建代理对象;

# 依赖注入

  • @Autowired:根据类型匹配;

    如果一个接口有多个实现类,再用@Qualifier根据名称注入;

  • @Resource:根据名称匹配;

# Bean的生命周期

  1. 实例化

    Spring容器根据Bean的定义创建Bean的实例,相当于执行构造方法,也就是 new 一个对象;

  2. 属性填充

    相当于执行 setter 方法为字段赋值;

  3. 初始化

    回调各种Aware接口,调用对象的各种初始化方法;

  4. Bean使用

  5. 销毁

    如果Bean实现了DisposableBean接口,执行destroy()方法;相当于执行了=null,释放资源;

# Spring循环依赖问题

循环依赖:两个Bean互相持有对方引用;

AB 循环依赖,A 实例化的时候,发现依赖 B,创建 B 实例,创建 B 的时候发现需要 A,创建 A1 实例……

Spring只解决了单例模式下,通过setter方法进行依赖注入的情况;

解决方式:三级缓存

一级缓存只保存初始化完成的Bean对象;

三级缓存保存Bean工厂对象,解决Bean早期引用问题,通过Bean工厂创建出Bean的代理对象;当检测到循环依赖时,可以通过这个工厂得到Bean的代理对象,这个代理对象放入二级缓存中;

三级缓存为什么存放ObjectFactory,不直接保存实例?

ObjectFactory对象可以对实例对象进行增强;

二级缓存保存实例化完成但没有初始化好的Bean对象;

为什么需要二级缓存?

ObjectFactory每次创建的实例对象不一样,通过二级缓存拿到相同对象实例;

案例:

假设两个 Bean A 和 B 存在循环依赖:

  1. 实例化 A 的时候把 A 的对象⼯⼚放⼊三级缓存,表示 A 开始实例化了。
  2. 实例化 B,发现依赖 A,就从一级缓存里找 A,但 A 还未完全初始化好,因此 B 转而从二级缓存中获取 A,如果二级缓存也没有,则从三级缓存中获取 A 的工厂对象,通过这个工厂对象获取 A 的早期引用(可能是 A 的代理对象),并将这个早期引用放入二级缓存,同时删除三级缓存中的 A。
  3. 使用 A 的早期引用完成 B 的创建和初始化,然后将 B 放入一级缓存。
  4. 在创建 B 的过程中,B 需要 A 的实例。此时,B 尝试从一级缓存获取 A,但 A 还未完全初始化好,因此 B 转而从二级缓存中获取 A,如果二级缓存也没有,则从三级缓存中获取 A 的工厂对象,通过这个工厂对象获取 A 的早期引用(可能是 A 的代理对象),并将这个早期引用放入二级缓存。
  5. 使用 A 的早期引用完成 B 的创建和初始化,然后将 B 放入一级缓存。
  6. 使用完全初始化好的 B 实例完成 A 的创建和初始化,最后将 A 也放入一级缓存。

# Spring如何实现优雅停机?

Spring也是依托于JVM实现的,它通过JVM的shutdownHook感知到Java进程关闭,然后执行doClose方法:

JVM优雅停机方式是通过Runtime.getRuntime().addShutdownHook(shutdownTask);

doClose方法步骤:

  1. 发布一个容器关闭事件;
  2. 调用Bean生命周期关闭方法;
  3. 销毁所有Bean;
  4. 关闭Bean工厂;
  5. 调用子类关闭函数;
// org.springframework.context.support.AbstractApplicationContext
@Deprecated //Spring 5 即将废弃
public void destroy() {
    close();
}

@Override
public void close() {
    synchronized (this.startupShutdownMonitor) {
        doClose();
        // If we registered a JVM shutdown hook, we don't need it anymore now:
        // We've already explicitly closed the context.
        if (this.shutdownHook != null) {
            try {
                Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
            }
            catch (IllegalStateException ex) {
                // ignore - VM is already shutting down
            }
        }
    }
}

protected void doClose() {
    LiveBeansView.unregisterApplicationContext(this);
    try {
        // Publish shutdown event.
        publishEvent(new ContextClosedEvent(this)); ➀
    }
    // Stop all Lifecycle beans, to avoid delays during individual destruction.
    if (this.lifecycleProcessor != null) {
        try {
            this.lifecycleProcessor.onClose(); ➁
        }
        catch (Throwable ex) {
            logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
        }
    }

    // Destroy all cached singletons in the context's BeanFactory.
    destroyBeans(); ➂

    // Close the state of this context itself.
    closeBeanFactory(); ➃

    // Let subclasses do some final clean-up if they wish...
    onClose(); ➄

    // Reset local application listeners to pre-refresh state.
    if (this.earlyApplicationListeners != null) {
        this.applicationListeners.clear();
        this.applicationListeners.addAll(this.earlyApplicationListeners);
    }

    // Switch to inactive.
    this.active.set(false);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

# IOC初始化过程

在Spring中,IoC容器的初始化过程可以分为以下几个步骤:

  1. 读取配置文件:容器根据配置文件(如XML、注解等)读取bean的定义和其他相关信息。这些配置文件通常包含了bean的名称、类路径、依赖关系等。
  2. 创建BeanDefinition对象:根据读取到的配置信息,容器会创建对应的BeanDefinition对象。BeanDefinition对象保存了每个bean的元数据,包括类型、作用域、属性等。
  3. 解析依赖关系:容器会解析各个bean之间的依赖关系。通过查找BeanDefinition对象的属性或构造函数参数,容器可以确定bean之间的依赖关系。
  4. 实例化Bean对象:根据BeanDefinition的信息,容器会实例化各个bean对象。这通常是通过Java反射机制来创建bean实例。
  5. 属性注入:容器会将配置文件中定义的属性值注入到相应的bean实例中。这可以通过setter方法、字段注入或构造函数参数注入来完成。
  6. Aware接口回调:如果bean实现了Aware接口,容器会调用相应的回调方法,将一些特殊的资源(如ApplicationContext)注入到bean中。
  7. 初始化回调:如果bean实现了InitializingBean接口或定义了init-method回调方法,容器会在bean实例化完成后调用这些方法,完成bean的初始化工作。
  8. BeanPostProcessor处理:在bean初始化过程中,容器会调用注册的BeanPostProcessor实现类的方法,对bean进行一些额外的处理操作。
  9. 容器就绪:所有的bean都经过了初始化和处理后,容器就处于就绪状态,可以通过getBean()方法获取bean实例,并开始使用它们。
  10. 销毁回调:当容器关闭时,会调用bean实现了DisposableBean接口或定义了destroy-method回调方法的销毁方法,完成bean的清理工作。
Spring常用注解与接口

Spring常用注解与接口→

Theme by Vdoing | Copyright © 2021-2024
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式