【Spring Bean的生命周期】

简介: 【Spring Bean的生命周期】

Spring Bean的生命周期

Spring Bean的生命周期分为四个阶段:实例化、属性赋值、初始化和销毁。

实例化

构造器实例化

通过Java类的构造函数实例化Bean,利用Java反射机制,调用bean对应类的构造方法进行实例化。

在XML文件中,可以使用标签的class属性指定要实例化的Bean类。当容器启动时,容器会根据class属性的全限定类名使用反射机制实例化Bean。示例代码:

<bean id="myBean" class="com.example.MyBean"/>

在注解方式中,@Component、@Service、@Controller等注解本质上是Java类的元数据,Spring框架在启动时会扫描指定包路径下所有的类,将被标注了这些注解的类通过反射机制实例化,并将实例化后的对象注册到Spring容器中,以供程序使用。例如:

@Component
public class MyBean {
    // class implementation
}
工厂方法实例化

在Spring配置文件中,可以定义一个工厂类,该工厂类中有一个静态方法可以创建Bean对象,并且可以指定返回值和参数,Spring框架会在启动时自动调用该静态方法来创建Bean对象。这种方式类似于单例模式,因为每个Bean对象只会被实例化一次,并且可以在整个应用程序中共享。配置示例代码如下:

<bean id="myBean" class="com.example.MyBeanFactory" factory-method="createMyBean"/>

代码如下:

public class MyBeanFactory {
    public static MyBean createMyBean() {
        return new MyBean();
    }
}

属性赋值

属性赋值是在实例化Bean后通过BeanPostProcessor接口实现对Bean的属性赋值。

XML方式

在XML文件中定义Bean以及其属性的值来配置Bean,在Spring容器启动时,会解析这些XML文件并根据其定义的配置创建相应的Bean实例。

以下是一个简单的 XML 配置文件示例:

<?xml version="1.0" encoding="UTF-8"?>
<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-4.3.xsd">
    <!-- 定义一个名为 person 的 Bean -->
    <bean id="person" class="com.example.Person">
        <property name="name" value="John"/>
        <property name="age" value="25"/>
    </bean>
</beans>

上述 XML 配置文件定义了一个名为 person 的 Bean,它的类为 com.example.Person,并设置了 name 和 age 两个属性的值。

在 Spring 容器启动时,会解析该 XML 配置文件,创建一个名为 person 的 Bean 实例,并将其属性值设置为 name=John,age=25。可以使用以下代码获取该 Bean:

ApplicationContext context = new ClassPathXmlApplicationContext("path/to/config.xml");
Person person = (Person) context.getBean("person");

这样就可以使用 person 对象来访问其属性值了。

注解方式

使用@Autowired或@Value注解进行属性赋值。这些注解的实现原理也是基于BeanPostProcessor接口实现的。

@Autowired注解是根据属性的类型来进行自动注入的,如果Spring容器中存在该类型的Bean,则会自动将其注入到对应的属性中。如果存在多个同类型的Bean,则可以使用@Qualifier注解来指定要注入的Bean的名称。

好的,以下是一个简单的示例:

假设我们有一个接口UserService,有两个实现类UserServiceAUserServiceB

public interface UserService {
    void addUser(String username, String password);
}
@Service("userServiceA")
public class UserServiceA implements UserService {
    public void addUser(String username, String password) {
        System.out.println("User " + username + " is added by UserServiceA");
    }
}
@Service("userServiceB")
public class UserServiceB implements UserService {
    public void addUser(String username, String password) {
        System.out.println("User " + username + " is added by UserServiceB");
    }
}

现在我们有一个需要依赖UserService的类UserController

@Controller
public class UserController {
    @Autowired
    private UserService userService;
    public void addUser(String username, String password) {
        userService.addUser(username, password);
    }
}

当Spring容器扫描到UserController的时候,会发现它有一个属性userService需要注入,然后根据该属性的类型UserService自动从容器中查找对应的Bean。由于容器中有UserServiceAUserServiceB两个实现类,所以Spring会报错,无法决定要使用哪个实现类进行注入。

为了解决这个问题,我们可以使用@Qualifier注解,将要注入的Bean的名称告诉Spring容器。例如:

@Controller
public class UserController {
    @Autowired
    @Qualifier("userServiceB")
    private UserService userService;
    public void addUser(String username, String password) {
        userService.addUser(username, password);
    }
}

这样,Spring容器就会自动将名称为userServiceBUserService实现类注入到UserControlleruserService属性中。

@Value注解则是根据属性的值来进行注入的,可以使用${}或#{ }来引用配置文件中的属性值,也可以直接指定一个固定的值。

@Value("${jdbc.url}")
private String url;

通过@Value注解,Spring会将配置文件中名为"jdbc.url"的属性值注入到url属性中。

初始化

初始化是Spring Bean生命周期的第三个阶段,它包括两个过程:初始化前和初始化后,BeanPostProcessor接口在执行初始化方法之前和之后定义了两个方法:postProcessBeforeInitialization()和postProcessAfterInitialization()。

postProcessBeforeInitialization()和postProcessAfterInitialization()

postProcessBeforeInitialization()方法在执行Bean的初始化方法之前被调用,可以对Bean进行自定义的前处理操作。例如,可以修改Bean的属性值、增加一些代理逻辑等等。这时的Bean还没有执行初始化方法,也就是说Bean还没有完全初始化。这个方法常常用于注册一些事件监听器、给Bean进行数据校验等。

postProcessAfterInitialization()方法在执行Bean的初始化方法之后被调用,可以对Bean进行自定义的后处理操作。例如,可以对Bean做一些额外的检查、修改某些属性值等等。这时的Bean已经执行了初始化方法,并且已经完全初始化。这个方法常常用于增强Bean的能力或者为Bean提供一些额外服务(如数据缓存、资源池等)。

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        //在Bean的初始化方法之前执行的自定义操作
        if (bean instanceof MyBean) {
          ((MyBean)bean).setProperty("new value");
      //注册事件监听器
      if (bean instanceof MyBean) {
        MyBean myBean = (MyBean) bean;
        myBean.addEventListener(new MyEventListener());
      }
      //给Bean进行数据校验
      if (bean instanceof MyBean) {
        MyBean myBean = (MyBean) bean;
        Validator validator = new MyBeanValidator();
        validator.validate(myBean);
      }
        }
        return bean;
    }
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //在Bean的初始化方法之后执行的自定义操作
        if (bean instanceof MyBean) {
            //增强Bean的能力
            //检查
      if (bean instanceof MyBean) {
          MyBean myBean = (MyBean)bean;
          if (myBean.getName() == null) {
              throw new IllegalArgumentException("Name cannot be null");
          }
      }
      //修改某些属性值
      if (bean instanceof MyBean) {
          MyBean myBean = (MyBean)bean;
          myBean.setProperty("new value");
      }
      //数据缓存
      if (bean instanceof MyBean) {
          MyBean myBean = (MyBean)bean;
          CacheManager cacheMgr = CacheManager.getInstance();
          Cache cache = cacheMgr.getCache("myCache");
          cache.put(myBean.getId(), myBean);
      }
      //资源池
      if (bean instanceof MyBean) {
          MyBean myBean = (MyBean)bean;
          ConnectionPool pool = ConnectionPool.getInstance();
          myBean.setConnection(pool.getConnection());
      }
        }
        return bean;
    }
}
//实现了ApplicationListener接口,并重写了onApplicationEvent方法来处理ContextRefreshedEvent事件
public class MyEventListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (event.getSource() instanceof MyBean) {
            MyBean myBean = (MyBean) event.getSource();
            System.out.println("MyBean " + myBean.getName() + " has been refreshed.");
        }
    }
}
InitializingBean接口的afterPropertiesSet()方法

Spring容器在创建Bean实例之后,会自动调用InitializingBean接口的afterPropertiesSet()方法完成Bean的初始化。

以下是一个实现InitializingBean接口的示例代码:

import org.springframework.beans.factory.InitializingBean;
public class MyBean implements InitializingBean {
    private String message;
    public void setMessage(String message) {
        this.message = message;
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        // 在这里初始化Bean
        System.out.println("Initializing MyBean...");
        System.out.println("MyBean message: " + message);
    }
}

在XML配置文件中配置该Bean的属性和初始化方法:

<bean id="myBean" class="com.example.MyBean">
    <property name="message" value="Hello, World!"/>
    <property name="initMethod" value="afterPropertiesSet"/>
</bean>

在上面的配置中,我们设置了Bean的属性message为“Hello, World!”,并设置了initMethod属性为afterPropertiesSet,这样在Bean创建完成后,会自动调用MyBean实现的afterPropertiesSet()方法完成Bean的初始化。

除了实现InitializingBean接口,还可以通过@Bean注解中的initMethod属性,或者使用@PostConstruct注解标注的初始化方法。示例代码:

通过@Bean注解定义的初始化方法
@Configuration
public class AppConfig {
    @Bean(initMethod = "init")
    public MyBean myBean() {
        return new MyBean();
    }
}
public class MyBean {
    public void init() {
        // initialization code
    }
}
使用@PostConstruct注解标注的初始化方法
public class MyBean {
    @PostConstruct
    public void init() {
        // initialization code
    }
}

销毁

销毁是Spring Bean生命周期的最后一个阶段,它是通过实现DisposableBean接口或通过配置destroy-method方法来实现。实现DisposableBean接口,需要实现destroy()方法,该方法会在Bean销毁前被调用。在容器关闭之前,Spring会先销毁Bean,并回调Bean的destroy()方法。

配置destroy-method方法来销毁Bean

在XML文件中,可以使用destroy-method属性指定Bean的销毁方法。Spring容器会在销毁Bean之前调用这个方法。

<bean id="exampleBean" class="com.example.ExampleBean" destroy-method="cleanup">
    <property name="name" value="John" />
</bean>

在上面的代码中,ExampleBean类有一个名为cleanup的方法,它将在Bean销毁时被调用。在XML文件中,通过配置destroy-method属性来指定销毁方法。

实现DisposableBean接口来销毁Bean

通过实现DisposableBean接口,Bean类可以在调用destroy()方法之前实现销毁操作。该方法会在Bean销毁之前调用。销毁的具体过程可以自定义实现。在销毁Bean之前,需要先关闭应用上下文,释放Bean占用的资源。

public class ExampleBean implements DisposableBean {
    private String name;
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public void destroy() throws Exception {
        System.out.println("Cleaning up resources for " + name);
    }
}

在上面的代码中,ExampleBean类实现了DisposableBean接口,并重写了destroy()方法。在该方法中,可以自定义销毁Bean的逻辑。在Bean销毁之前,Spring容器会回调该方法。


相关文章
|
3月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
11天前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
1月前
|
XML Java 数据格式
Spring从入门到入土(bean的一些子标签及注解的使用)
本文详细介绍了Spring框架中Bean的创建和使用,包括使用XML配置文件中的标签和注解来创建和管理Bean,以及如何通过构造器、Setter方法和属性注入来配置Bean。
66 9
Spring从入门到入土(bean的一些子标签及注解的使用)
|
2月前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
205 24
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
|
29天前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
2月前
|
XML Java 数据格式
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!
210 18
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
|
2月前
|
XML Java 数据格式
spring复习02,xml配置管理bean
详细讲解了Spring框架中基于XML配置文件管理bean的各种方式,包括获取bean、依赖注入、特殊值处理、属性赋值、集合类型处理、p命名空间、bean作用域及生命周期和自动装配。
spring复习02,xml配置管理bean
|
1月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细解析Spring Bean的生命周期及其核心概念,并深入源码分析。Spring Bean是Spring框架的核心,由容器管理其生命周期。从实例化到销毁,共经历十个阶段,包括属性赋值、接口回调、初始化及销毁等。通过剖析`BeanFactory`、`ApplicationContext`等关键接口与类,帮助你深入了解Spring Bean的管理机制。希望本文能助你更好地掌握Spring Bean生命周期。
73 1
|
1月前
|
Java Spring
获取spring工厂中bean对象的两种方式
获取spring工厂中bean对象的两种方式
38 1
|
1月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细介绍了Spring框架中的核心概念——Spring Bean的生命周期,包括实例化、属性赋值、接口回调、初始化、使用及销毁等10个阶段,并深入剖析了相关源码,如`BeanFactory`、`DefaultListableBeanFactory`和`BeanPostProcessor`等关键类与接口。通过理解这些核心组件,读者可以更好地掌握Spring Bean的管理和控制机制。
84 1