技术面:Spring (bean的生命周期、创建方式、注入方式、作用域)

Spring Bean的生命周期是什么样的?

在Spring容器里一个Bean的从创建到销毁一般都是经历了以下几个阶段:
定义阶段 (Bean元信息配置)=>实例化阶段(创建Bean对象)=>初始化阶段(执行初始化逻辑)=>使用阶段(Bean可用)=>销毁阶段(释放资源)

定义阶段(BeanDefinition解析)

Spring通过配置(XML、注解、Java配置)解析Bean的元数据,生成BeanDefinition对象
BeanDefinition存储了Bean的类名、作用域(scope)、依赖项(depends-on)、初始化方法、销毁方法等元数据。

所有BeanDefinition存储在容器的BeanDefinitionMap(一个HashMap)中,键为Bean名称,值为BeanDefinition对象。
解析器:

  • XML配置:XmlBeanDefinitionReader解析<bean>标签。
  • 注解配置:ClassPathBeanDefinitionScanner扫描@Component等注解。
  • Java配置:ConfigurationClassPostProcessor解析@Bean方法。

实例化阶段(创建Bean实例)

根据BeanDefinition通过反射或工厂方法创建Bean实例(对象),但此时属性未注入

默认通过无参构造方法实例化(若未指定,Spring会强制要求无参构造)。

AbstractAutowireCapableBeanFactory类中的createBeanInstance方法中实现。

属性值填充(依赖注入)

为Bean的属性设置值或注入依赖

  • 通过@Autowired@ValueXML<property>等方式注入属性。
  • 若注入的依赖是其他Bean,会递归触发依赖Bean的生命周期。
  • 循环依赖问题:在属性注入阶段处理循环依赖(通过三级缓存解决)。

AbstractAutowireCapableBeanFactorypopulateBean方法中处理。

Aware接口回调设置

若Bean实现了特定Aware接口,Spring会回调对应方法,注入容器相关对象

  • BeanNameAware:注入Bean在容器中的名称(setBeanName(String beanName))。
  • BeanFactoryAware:注入当前Bean所在的BeanFactory(setBeanFactory(BeanFactory beanFactory))。
  • ApplicationContextAware:若容器是ApplicationContext,注入应用上下文(setApplicationContext(ApplicationContext applicationContext))。

AbstractAutowireCapableBeanFactoryinitializeBean方法中调用。

BeanPostProcessor前置处理

在Bean初始化前,允许自定义BeanPostProcessor对Bean实例进行处理。

主要是调用BeanPostProcessorpostProcessBeforeInitialization方法。
常见的实现类

  • ApplicationContextAwareProcessor:处理ApplicationContextAware接口。
  • InitDestroyAnnotationBeanPostProcessor:处理@PostConstruct注解。

AbstractAutowireCapableBeanFactoryapplyBeanPostProcessorsBeforeInitialization方法执行。

InitializingBean处理以及自定义init-method处理

执行Bean的初始化逻辑。
InitializingBean处理,在所有Bean属性设置完成后进行初始化操作。如果Bean实现了InitializingBean接口,InitializingBeanafterPropertiesSet方法会被调用。

自定义init-method处理,如果Bean在配置文件中定义了初始化方法那么该方法会被调用。

例如:通过XML配置init-method或Java配置@Bean(initMethod="xxx")。

AbstractAutowireCapableBeanFactoryinvokeInitMethods方法中调用

BeanPostProcessor后置处理

在Bean初始化后,允许自定义BeanPostProcessor对Bean实例进行处理。
BeanPostProcessorpostProcessAfterInitialization方法会被调用。

常见用途:AOP代理(如AbstractAutoProxyCreator在此阶段为目标对象创建代理)

AbstractAutowireCapableBeanFactoryapplyBeanPostProcessorsAfterInitialization方法执行

注册DisposableBean回调

如果Bean实现了DisposableBean接口或在Bean定义中指定了自定义的销毁方法,Spring容器会为这些Bean注册一个销毁回调,确保在容器关闭时能够正确地清理资源。

AbstractAutowireCapableBeanFactory类中的registerDisposableBeanlfNecessary方法中实现

Bean使用阶段

Bean已完全初始化,可被应用程序使用。 通过依赖注入获取Bean实例(如@AutowiredApplicationContext.getBean())。

此阶段Bean处于"可用"状态,直到容器关闭。

Bean销毁阶段

容器关闭时,释放Bean资源。

主要步骤:

  • 接口回调:若Bean实现了DisposableBean,调用destroy方法。
  • 注解:若方法标注了@PreDestroy,Spring会调用该方法。
  • 自定义销毁方法:通过XML配置destroy-method或Java配置@Bean(destroyMethod="xxx")
  • 资源释放:如关闭数据库连接、释放文件句柄等。

DisposableBeanAdapterdestroy方法中实现

总结

通过代码出处,可以观察到整个Bean的创建的过程都依赖于AbstractAutowireCapableBeanFactory这个类,而销毁主要依赖DisposableBeanAdapter这个类。
AbstractAutowireCapableBeanFactory 的入口处,doCreateBean的核心代码如下,其中包含了实例化、设置属性值、初始化Bean以及注册销毁回调 的几个核心方法。

这里就不贴代码了,想更深入看细节的可以去看源码。

Spring中创建Bean的方式有哪些?

基于注解的自动扫描

通过注解标记类,并配合组件扫描实现自动注册。

常见的注解有
@Component, @Service, @Repository, @Controller(及其衍生注解)。

例如:当在类上添加@Component时,再在配置类或 XML 中启用组件扫描(@ComponentScan<context:component-scan>)。这个类在服务启动时会自动被扫描到,然后注入到Spring容器。

java 复制代码
@Configuration
@ComponentScan("com.jimoer.service")
public class BeanConfig {

}
java 复制代码
@Service
public class UserService {
    public void hello() {
        System.out.println("Hello from UserService");
    }
}

@Component
public class UserHandler {
    public void hello() {
        System.out.println("Hello from UserHandler");
    }
}

@Repository
public class UserRepository {
    public void hello() {
        System.out.println("Hello from UserRepository");
    }
}

@Controller
public class UserController {
    public void hello() {
        System.out.println("Hello from UserController");
    }
}

使用@Configuration与@Bean 注解

通过 @Configuration 标注的配置类,显式定义 Bean 的创建逻辑。

适用于:需要精确控制 Bean 的初始化逻辑(如依赖其他 Bean 或复杂条件)。

java 复制代码
@Configuration
public class AppConfig {
    @Bean
    public UserService userService() {
        return new UserService();
    }
}

XML 配置文件

通过 xml 的方式来定义 Bean。

在SpringBoot 流行以前,这种方式挺多的, SpringBoot 流行起来之后,这么用的越来越少了。

xml 复制代码
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
    <bean id="userService" class="com.jimoer.demo.UserServiceImpl">
       <property name="message" value="Hello Spring!" />
    </bean>
</beans>
java 复制代码
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = context.getBean("userService");

更适用于遗留项目或需要与非注解配置兼容的场景。

使用@Import注解

@Import注解的作用是快速导入某一个或多个类,使这些类能够被Spring加载到IOC容器中进行管理。

让类被Spring 的 IOC 容器管理,这不也是创建 Bean 么,因此,这种方式也可以算是创建Bean的一种方式。

java 复制代码
@Import({UserServiceImpl.class})
@Configuration
public class UserBeanConfiguration {
}

自定义注解

通过自定义一种注解,然后在 Spring 应用启动过程中,通过自定义的 BeanDefinitionRegistryPostProcessorBeanfactoryPostProcessor 来扫描配置的包路径,识别出带有自定义注解的类。

这些处理器解析注解中的属性(如接口类、版本号、超时时间等),并基于这些信息创建 Spring的 BeanDefinition

例如:Dubbo框架使用的@DubboService注解

java 复制代码
@DubboService("version=1.0.0")
public class UserServiceImpl implements UserFacadeService {

}

动态注册(运行时注册)

在运行时通过 BeanDefinitionRegistry 动态注册 Bean。

java 复制代码
// 获取 BeanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();

// 定义 Bean 的元数据
GenericBeanDefinition userDefinition = new GenericBeanDefinition();
userDefinition.setBeanClass(UserService.class);

// 注册 Bean
beanFactory.registerBeanDefinition("userService", userDefinition);

适用于:根据运行时条件动态生成 Bean(如插件化系统、动态配置)。

Spring Bean的注入方式有哪些?

使用@Autowired注解

@Autowired注解是Spring框架提供的一个注解,支持多种方式自动将Spring的bean注入到其他Bean中。

字段注入

java 复制代码
@Component
public class JimoerUserService {
    @Autowired
    private UserRepository userRepository;
}

构造方法注入

java 复制代码
@Component
public class JimoerUserService {
    private final UserRepository userRepository;

    // Spring 4.3+ 可省略 @Autowired(单构造器)
    @Autowired
    public JimoerUserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

setter注入

java 复制代码
@Component
public class JimoerUserService {
    private UserRepository userRepository;

    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

使用@Resource和@Inject注解

除了Spring提供的注解,JDK也提供了可以互相注入Bean的注解,有@Resource@Inject

java 复制代码
@Component
public class JimoerUserService {
    @Resource
    private UserRepository userRepository;
}

@Component
public class JiomerUserService {
    @Inject
    private UserRepository userRepository;
}

使用XML配置注入

如何不使用注解注入,还可以使用XML文件的配置进行Bean的互相注入。

xml 复制代码
<bean id="userRepository" class="com.jimoer.UserRepository"/>
<!-- 构造方法注入 -->
<bean id="userService" class="com.jiomer.UserService">
    <constructor-arg ref="userRepository"/>
</bean>
<!-- 字段注入 -->
<bean id="jimoerUserService" class="com.jiomer.JimoerUserService">
    <property name="userRepository" ref="userRepository"/>
</bean>

构造方法自动注入

其实从 Spring 4.3 开始,除非一个类中声明了至少两个构造函数,否则不需要用 @Autowired 标注构造函数,这个构造函数也能直接注入 Bean。

java 复制代码
@Component
public class JimoerUserService {
	private UserRepository userRepository;
	public JimoerUserService(UserRepository userRepository){
		this.userRepository=userRepository;
	}
}

Spring Bean的作用域有哪些?

Spring的Bean的作用域,就是指这个Bean在哪个范围内可以被使用。

不同的作用域决定了Bean的创建管理和销毁的方式。

常见的作用域有SingletonPrototypeRequestSessionApplication这五种。

在代码中,可以在定义一个Bean的时候,通过@Scope 注解来指定他的作用域。

如果没有指定Bean的作用域,默认是Singleton(单例)。

Singleton(单例)

  • 周期:Spring 容器启动时创建实例,容器关闭时销毁。
  • 作用域:每个Spring IOC容器,只创建一个Bean实例。
  • 适用于:无状态服务(如工具类、缓存管理器、数据库连接池)。
  • 线程安全:需注意,若 Bean 有可变状态(即Bean中存在线程共享变量),需通过同步机制或线程安全集合处理。
  • 配置方式:
java 复制代码
@Component // 默认即为 singleton
public class SingletonBean {
}

Propertype(原型)

  • 周期:每次调用 getBean() 或注入时创建新实例,容器不负责销毁。
  • 适用于:有状态 Bean(如用户会话数据、临时对象)。
  • 线程安全:实例独立,避免线程安全问题。
  • 配置方式:
java 复制代码
@Component
@Scope("prototype")
public class PrototypeBean {
}

Request(HTTP 请求)

  • 周期:每个 HTTP 请求创建一个实例,请求结束后销毁。
  • 适用于:Web 应用中请求级别的数据共享(如请求日志、上下文信息)。

仅适用于 Web 应用环境。

  • 配置方式:
java 复制代码
@Component
@Scope("request")
public class RequestBean {
}

Session(HTTP 会话)

  • 周期:每个用户会话(HttpSession)创建一个实例,会话结束时销毁。
  • 适用于:用户会话数据(如购物车、用户偏好设置)。

仅适用于 Web 应用环境。

  • 配置方式:
java 复制代码
@Component
@Scope("session")
public class SessionBean {
}

Application(应用)

  • 周期:Web 应用启动时创建实例,应用关闭时销毁。
  • 适用于:全局配置或共享资源(如应用级缓存、配置信息)。类似 singleton,但绑定到 ServletContext

仅适用于 Web 环境

  • 配置方式:
java 复制代码
@Component
@Scope("application")
public class ApplicationBean {
}

Websocket(WebSocket 会话)

  • 周期:WebSocket 连接建立时创建实例,连接关闭时销毁。
  • 适用于:WebSocket 会话上下文数据(如实时通信状态)。

仅适用于 WebSocket 应用。

  • 配置方式:
java 复制代码
@Component
@Scope("websocket")
public class WebSocketBean {
}

自定义作用域

一般情况下,在开发过程中,都是使用Singleton作用域,有时候也会用Propertype,其他几个用的都不多。但是除了上面列举的6个Spring提供作用域以外,还可以自己定义Bean作用域。

自定义一个Spring Bean的作用域,需要实现org.springframework.beans.factory.config.Scope接口,主要是实现如下几个方法来管理Bean的生命周期。

java 复制代码
package org.springframework.beans.factory.config;

import org.springframework.beans.factory.ObjectFactory;

public interface Scope {
    Object get(String var1, ObjectFactory<?> var2);

    Object remove(String var1);

    void registerDestructionCallback(String var1, Runnable var2);

    Object resolveContextualObject(String var1);

    String getConversationId();
}

自定义一个类,然后实现Scope接口,来实现我们自己的Bean作用域。

java 复制代码
public class JimoerScope implements Scope{
    @Override
    public Object get(String s, ObjectFactory<?> objectFactory) {
        // 获取Bean的逻辑
        return objectFactory.getObject();
    }

    @Override
    public Object remove(String s) {
        // 移除Bean的逻辑
        return null;
    }

    @Override
    public void registerDestructionCallback(String s, Runnable runnable) {
        // 注册Bean销毁时的回调
    }

    @Override
    public Object resolveContextualObject(String s) {
        // 解析上下文
        return null;
    }

    @Override
    public String getConversationId() {
        // 获取会话ID
        return "";
    }
}

接下来,我们将Spring配置中注册这个自定义的作用域。

这可以通过ConfigurableBeanFactory.registerScope 方法实现。

java 复制代码
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public JimoerScope jimoerScope(ConfigurableBeanFactory beanFactory) {
        JimoerScope jimoerScope = new JimoerScope();
        beanFactory.registerScope("jimoer", jimoerScope);
        return jimoerScope;
    }

}

此时在Bean定义中使用自定义的作用域的名称jimoer

Spring 容器将会根据你的自定义逻辑来创建和管理这些 Bean。

java 复制代码
@Component
@Scope("jimoer")
public class CustomerScopeTest {
}
相关推荐
We....2 小时前
Java多线程分块下载文件
java·开发语言
不秃的开发媛2 小时前
Java连接池详解:从Oracle到TiDB的随缘之旅
java·oracle·tidb
Pluchon2 小时前
硅基计划3.0 Map类&Set类
java·开发语言·数据结构·算法·哈希算法·散列表
Angelyb3 小时前
微服务保护和分布式事务
java·微服务·架构
七夜zippoe3 小时前
缓存三大劫攻防战:穿透、击穿、雪崩的Java实战防御体系(一)
java·开发语言·缓存
帧栈4 小时前
开发避坑指南(46):Java Stream 对List的BigDecimal字段进行求和
java
重生之我是Java开发战士4 小时前
【数据结构】Java集合框架:List与ArrayList
java·数据结构·list
爱干饭的boy4 小时前
手写Spring底层机制的实现【初始化IOC容器+依赖注入+BeanPostProcesson机制+AOP】
java·数据结构·后端·算法·spring
影子24014 小时前
java jdbc连接sqlserver2008R2版本数据库报错,驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接
java·数据库·ssl