Spring全家桶--2 Spring bean 的创建过程代码演示


前言

本篇通过代码方式演示spirng bean 完整的生命周期。


一、Spirng bean 的生命周期:

Spring Bean的生命周期主要分为 实例化,属性注入,初始化前处理(Initialization pre-processing,初始化(Initialization),初始化后处理(Initialization post-processing,AOP 代理,bean 的使用,bean 的销毁

二、 示例代码:

2.1 代码参考

代码如下(示例):

// 类1 
package com.example.springmycat.bean;

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

import javax.annotation.PostConstruct;


public class BeanProcess implements InitializingBean, BeanNameAware, DisposableBean {

    private String name;

    public String getName() {

        return name;
    }
    public BeanProcess() {
        System.out.println("-------- 1 对象创建");
    }
    public void setName(String name) {
        System.out.println("-------- 2属性注入");
        this.name = name;
    }

    @Override
    public void setBeanName(String s) {
        System.out.println("-------- 3 BeanNameAware bean 的名称"+s);
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("-------- 5.1 对象初始化InitializingBean");

    }
    @PostConstruct
//    @PreDestroy
    public  void init(){
        System.out.println("-------- 对象PostConstruct");
    }
    public  void myInit(){
        System.out.println("-------- 5.2 对象初始化init-method");
    }




    @Override
    public String toString() {
        System.out.println("-------- 7 对象使用");
        return "BeanProcess{" +
                "name='" + name + '\'' +
                '}';
    }
    @Override
    public void destroy() throws Exception {
        System.out.println("-------- 8 对象销毁DisposableBean");

    }
    public void myDestroy() {
        System.out.println("-------- 9 对象销毁myDestroy");
    }

}
// 类2
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if ("beanProcess".equals(beanName)){
            System.out.println("-------- 4 对象初始化之前");
        }
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if ("beanProcess".equals(beanName)){
            System.out.println("-------- 6 对象初始化之后");
        }
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

// 类3
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BeanProcessTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext= new ClassPathXmlApplicationContext("beanProcess.xml");
        BeanProcess beanProcess= applicationContext.getBean(BeanProcess.class);
        System.out.println(beanProcess);
        ((ClassPathXmlApplicationContext)applicationContext).close();
    }
}

bean 定义:

<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-3.0.xsd">

    <bean id="beanProcess" class="com.example.springmycat.bean.BeanProcess" init-method="myInit" destroy-method="myDestroy" >
        <property name="name" value="John"/>
    </bean>
    <bean id="myBeanPostProcessor" class="com.example.springmycat.bean.MyBeanPostProcessor"></bean>
    
</beans>

2.2 代码执行结果

-------- 1 对象创建
-------- 2属性注入
-------- 3 BeanNameAware bean 的名称beanProcess
-------- 4 对象初始化之前
-------- 5.1 对象初始化InitializingBean
-------- 5.2 对象初始化init-method
-------- 6 对象初始化之后
-------- 7 对象使用
BeanProcess{name='John'}
16:52:59.975 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Closing org.springframework.context.support.ClassPathXmlApplicationContext@2ff5659e, started on Fri Oct 13 16:52:59 CST 2023
-------- 8 对象销毁DisposableBean
-------- 9 对象销毁myDestroy

三、 一些方法的对比:

3.1 BeanNameAware 接口 :

在Spring框架中,BeanNameAware接口是一个特殊的接口,其设计主要用于让·Bean知道并获取到自己在Spring配置文件中定义的id或name

如果一个类实现了BeanNameAware接口,那么在Spring容器初始化该Bean时,容器会调用这个Bean的setBeanName(String name)方法,将该Bean在容器中的id或name作为参数传入。这样,这个Bean就可以在其内部访问到自身在Spring容器中的id或name了。

public class MyBean implements BeanNameAware {
    private String beanName;
 
    @Override
    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }
 
    public void printName(){
        System.out.println("Bean的名称为: " + beanName);
    }
}

在上述代码中,当Spring容器初始化MyBean实例时,会调用setBeanName()方法,将MyBean在容器中的id或name传入。然后在MyBean内部,就可以通过printName()方法打印出自身在Spring容器中的名称了。

使用BeanNameAware接口的情况虽然不多,但在某些场合下,知道Bean的id或name可以帮助我们更灵活地控制和管理Bean。例如,我们可以将Bean的名称用于日志记录,或者基于Bean的名称实现一些特定的业务逻辑。

3.2 bean 的属性注入:

Spring通过对字段添加@Autowired、@Resource、@Value等注解,通过反射的方式 ,自动按类型或按名称进行依赖注入
Spring的依赖注入(Dependency Injection,简称DI)可以对类中的以下属性进行值的填充:

3.2.1 属性的3种填充方式:

  1. 成员变量/字段:Spring通过对字段添加@Autowired、@Resource、@Value等注解,能够自动按类型或按名称进行依赖注入。
@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

}

在上面的示例中,userRepository字段上的@Autowired注解告诉Spring,需要一个UserRepository类型的bean,并将其注入到userRepository字段中。

  1. 构造器参数:Spring也可以通过构造函数来进行依赖注入。当一个类只有构造函数时,Spring 4.3以上版本可以省略@Autowired注解,Spring能够自动完成依赖注入。
@Service
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

}

在上述代码中,UserRepository类型的bean将被自动注入到UserService的构造函数参数中。

  1. Setter方法参数:除了字段和构造函数,Spring也支持通过setter方法进行依赖注入。
@Service
public class UserService {

    private UserRepository userRepository;

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

}

在以上例子中,UserRepository类型的bean将被自动注入到setUserRepository方法的参数中。

总的来说,只要是非静态的字段、构造器或设置器方法,且存在于Spring容器管理的类中,都可以通过Spring的依赖注入进行值填充。

3.2.2 基于字段的属性填充:

基于字段的注入,即直接将依赖注入到类的字段(成员变量)中。Spring 使用 Java 的反射 API 实现这种注入。

在 Spring 的依赖注入过程中,当 Spring IoC 容器识别到一个Bean中的字段上标记了 @Autowired 或 @Resource 等注解,就会触发依赖注入流程。

在依赖注入过程中,Spring 会使用 Java 的反射 API 来访问和修改这个字段。具体来说,Spring 会通过 Field 类的 setAccessible(true) 方法来打开这个字段的访问权限,然后使用 Field 类的 set(Object obj, Object value) 方法来给字段设置值。

这个过程中,Spring 会首先寻找 IoC 容器中匹配的Bean,并根据字段类型或者名字来匹配(具体取决于 @Autowired 或 @Resource 等注解的使用方式)。如果找到了匹配的Bean,就会将这个Bean设置为字段的值。

如果没有找到匹配的Bean,则根据注解的 required 属性(默认为 true),Spring可能会抛出异常,也可能不做任何操作。如果 required 属性设置为 false,当没有找到匹配的Bean时,Spring 就不会做任何操作,字段的值将为null。

例如:

Field field = someObject.getClass().getDeclaredField("fieldName");
field.setAccessible(true);
field.set(someObject, value);

3.3 spring @PostConstruct 和 InitializingBean :

在Spring框架中,有多种方法可以在bean初始化时执行特定的代码。@PostConstruct注解和InitializingBean接口就是两种最常见的方式。

以下是使用这两者时的执行顺序:

  1. 首先,Spring容器会调用bean的构造函数或工厂方法来创建bean实例。

  2. 然后,Spring会使用依赖注入(DI)来设置bean的属性。

  3. 一旦所有的属性被设置,Spring框架就会根据以下顺序调用初始化方法:

    • 如果bean实现了InitializingBean接口,那么Spring会调用它的afterPropertiesSet方法。

    • 如果bean配置了@PostConstruct注解的方法,那么Spring会调用这个注解标记的方法。注意,这个注解标记的方法在afterPropertiesSet方法之后被调用。

    • 如果在Spring的XML配置文件中定义了bean的init-method,那么这个方法将在@PostConstruct注解的方法之后被调用。

如果你在同一个bean中同时使用InitializingBean接口和@PostConstruct注解,那么afterPropertiesSet方法将会在@PostConstruct方法之前运行。这是因为InitializingBean接口是Spring框架的一部分,而@PostConstruct注解是Java EE 5的一部分,Spring在设计之初决定了后者的优先级更低。

3.4 bean 中的 destroy-method=“myDestroy” 和spring DisposableBean

Spring中的destroy-method属性和DisposableBean接口都可以用于自定义Bean的销毁方法,它们都可以在Bean销毁时执行某些清理工作,比如关闭网络连接、释放资源等。

不过,它们之间有一些区别:

  • 如果一个Bean实现了DisposableBean接口,那么Spring容器在销毁这个Bean时会自动调用它的destroy()方法。但是如果你在Bean的定义中同时指定了destroy-method属性,那么Spring将会先调用destroy()方法,然后再调用指定的销毁方法。

  • destroy-method更灵活,它允许你指定任何名称的方法作为销毁方法,而不仅仅是destroy();而且,销毁方法可以有任意的访问权限,可以有参数,也可以抛出异常。

  • 如果一个类实现了DisposableBean接口,那么这个类的所有实例都将有一个销毁方法;而destroy-method只影响定义了此属性的Bean。

总的来说,DisposableBean接口和destroy-method属性都可以用于定制Bean的销毁过程,但destroy-method属性更灵活,更易于使用。不过,如果你希望在销毁Bean时执行的清理工作是通用的,那么实现`DisposableBean接口可能是更好的选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值