• 【Spring Bean的生命周期】


    Spring Bean的生命周期

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

    实例化

    构造器实例化

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

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

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

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

    @Component
    public class MyBean {
        // class implementation
    }
    
    • 1
    • 2
    • 3
    • 4
    工厂方法实例化

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

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

    代码如下:

    public class MyBeanFactory {
        public static MyBean createMyBean() {
            return new MyBean();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    属性赋值

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

    XML方式

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

    以下是一个简单的 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-4.3.xsd">
        
        <bean id="person" class="com.example.Person">
            <property name="name" value="John"/>
            <property name="age" value="25"/>
        bean>
    
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    上述 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");
    
    • 1
    • 2

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

    注解方式

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

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

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

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

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

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

    @Controller
    public class UserController {
        @Autowired
        private UserService userService;
    
        public void addUser(String username, String password) {
            userService.addUser(username, password);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    当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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

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

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

    通过@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.");
            }
        }
    }
    
    • 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
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

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

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

    在上面的配置中,我们设置了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
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    使用@PostConstruct注解标注的初始化方法
    public class MyBean {
        @PostConstruct
        public void init() {
            // initialization code
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    销毁

    销毁是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>
    
    • 1
    • 2
    • 3

    在上面的代码中,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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

  • 相关阅读:
    产品与技术的平衡
    java实现十大排序算法
    【亲妈教学】配置Gzip压缩,含前后端步骤
    type=“text/javascript“表示什么意思
    JenkinsNote-服务迁移
    008:字符串交换,输入两个长度为4的字符串,交换这两个字符串的前两个字符后输出
    Taurus.MVC WebAPI 入门开发教程6:全局控制器DefaultController与全局事件。
    国内算力真的紧缺么?
    【学习笔记】深度学习入门:基于Python的理论与实现-神经网络
    kotlin集合(Collections)
  • 原文地址:https://blog.csdn.net/java_wxid/article/details/132868888