(第八天)SpringIoC容器和Bean管理 SSM框架的学习与应用(Spring + Spring MVC + MyBatis)-Java EE企业级应用开发学习记录

SSM框架的学习与应用(Spring + Spring MVC + MyBatis)-Java EE企业级应用开发学习记录(第八天)SpringIoC容器和Bean管理

昨天我们详细的学习了控制反转IoC和依赖注入DI,通过Spring容器(也就是IoC容器)完成组件的实例化,从而将组件之前的依赖关系进行了解耦。然而控制反转和依赖注入都是通过Bean实现的,Bean其实本质上就是注册到Spring容器中的对象实例(可以是任何普通的Java对象)在Spring中被称为Bean是用来说明是以此来说明它们被Spring容器管理(整个生命周期的管理,创建、初始化、销毁等阶段)


那么我们今天要掌握的是

了解Spring IoC容器的原理

掌握Bean标签以及属性的使用

熟悉Bean的实例化

掌握Bean的作用域

掌握Bean的装配方式

熟悉Bean的生命周期


一、Spring IoC容器

​ 我们前俩天的项目和文章有学习到,IoC容器统一创建和管理Bean,并负责解决Bean之间的依赖关系,而不需要应用程序代码去手动创建或查找对象。我们前几天也有学了简单工厂模式,其实在IoC容器中,容器充当了工厂的角色,它负责创建和管理Bean对象,应用程序通过容器来获取Bean,而不需要直接实例化这些对象。


BeanFactory接口

BeanFactory是Spring框架的核心接口之一,它是Spring IoC容器的基础接口BeanFactory负责管理和提供Bean对象,它定义了一系列操作Bean的方法,包括获取Bean、判断Bean是否存在、获取Bean的类型等


XmlBeanFactory接口

BeanFactory接口的主要实现类是XmlBeanFactory,它从XML配置文件中读取Bean的定义信息,并根据需要在运行时实例化Bean对象。XmlBeanFactory在初始化时不会实例化所有的Bean对象,而是在第一次访问某个Bean时才进行实例化,这种延迟加载的方式有助于提高性能和降低资源消耗

它是懒加载的,懒加载是一种优化策略,可以在某些情况下提高性能,但在其他情况下可能需要权衡。它在访问Bean时才会解析配置文件,如果应用程序中有大量的Bean,并且这些Bean的初始化是耗时的操作,那么懒加载可以帮助减轻启动时的负担。但是,如果你的应用程序要求在启动时立即初始化所有Bean以确保后续的快速响应时间,那么懒加载可能不合适。

但是XmlBeanFactory它是Spring早期版本的容器实现,现在的Spring应用程序中不推荐使用,很少使用。Spring2.5版本中被标记为已过时。


ApplicationContext接口

ApplicationContext是Spring框架的高级容器,提供了一种更高级、更强大的 IoC 容器,它能够管理 Bean 的生命周期、依赖注入和预初始化等功能,以提高应用程序的性能和灵活性,它在Spring 2.0版本中作为BeanFactory的一个子接口被引入。它在容器启动时会预初始化单例的 Bean,这意味着容器会在启动时创建并初始化所有单例的 Bean,并且通过<property>元素执行setter方法注入属性值,而不是在第一次访问时才初始化。这有助于提高程序获取 Bean 实例的性能,因为单例 Bean 在容器初始化后就已经准备好了,可以直接使用这是 Spring IoC 容器的一项重要功能,它通过管理 Bean 的生命周期和属性注入来提供更高的性能和便利性

相比XmlBeanFactory,ApplicationContext是一个更完善的IoC容器实现,提供了更多企业级应用所需的支持功能。从Spring 2.0版本开始,ApplicationContext成为了Spring的基本IoC容器

大约在Spring 2.5版本中,XmlBeanFactory被标记为已过时,官方文档也建议使用ApplicationContext来替代XmlBeanFactory


Application接口的特点(优点)
  • 国际化支持(MessageSource):允许应用程序根据用户的区域设置加载不同的资源文件,从而实现多语言支持。
  • 资源访问(ResourceLoader):提供了方便的资源加载功能,可以轻松地加载类路径、文件系统或远程位置的资源,包括文本文件、图像、音频
  • 事件传播(ApplicationEventPublisher):允许应用程序在容器内部和外部发布和监听事件。这对于实现应用程序级别的事件处理非常有用,例如处理用户登录、注销等事件。
  • AOP特性支持:提供了更丰富的 AOP(面向切面编程)支持,允许应用程序更容易地实现横切关注点,例如事务管理、日志记录等。

ApplicationContext接口的常用实现类
类名称描述
ClassPathXmlApplicationContext从类路径加载配置文件,实例化ApplicationContext接口
FileSystemXmlApplicationContext从文件系统加载配置文件,实例化ApplicationContext接口
AnnotationConfigApplicationContext从注解中加载配置文件,实例化ApplicationContext接口
WebApplicationContext在Web应用中使用,从相对于Web根目录的路径中加载配置文件,实例化ApplicationContext接口
ConfigurableWebApplicationContext扩展了WebApplicationContext类,它可以通过读取XML配置文件的方式实例化WebApplicationContext类

二、Bean的配置

​ Spring容器支持XMLProperties两种格式的配置文件,在实际开发中,最常用的是XML格式的配置文件。XML是标准的数据传输和存储格式,方便查看和操作数据。在Spring中,XML配置文件的根元素是<beans><beans>元素包含<bean>子元素,每个<bean>子元素可以定义一个Bean,通过<bean>元素将Bean注册到Spring容器中


<bean>元素的常用属性
属性描述
idid属性是<bean>元素的唯一标识符,Spring容器对Bean的配置和管理通过id属性完成,装配Bean时也需要根据id值获取对象
namename属性可以为Bean指定多个名称,每个名称之间用逗号或分号隔开。
classclass属性可以指定Bean的具体实现类,其属性值为对象所属类的全路径
scopescope属性用于设定Bean实例的作用范围,其属性值有:singleton(单例)、prototype(原型)、request、session和global session

<bean>元素的常用子元素
元素描述
<constructor-arg>通过构造方法注入,可以使用<constructor-arg>元素为Bean的属性指定值
<property><property>元素的作用是调用Bean实例中的setter方法完成属性赋值,从而完成依赖注入
refref是<property><constructor-arg>等元素的属性,可用于指定Bean工厂中某个Bean实例的引用;也可用于指定Bean工厂中某个Bean实例的引用
valuevalue是<property><constructor-arg>等元素的属性,用于直接指定一个常量值;也可以用于直接指定一个常量值
<list><list>元素是<property>等元素的子元素,用于指定Bean的属性类型为List或数组。
<set><set>元素是<property>等元素的子元素,用于指定Bean的属性类型为set。
<map><map>元素是<property>等元素的子元素,用于指定Bean的属性类型为Map。
<entry><entry>元素是<map>元素的子元素,用于设定一个键值对。<entry>元素的key属性指定字符串类型的键。

三、 Spring 中 Bean 实例化的两种主要方式:
  • 通过构造方法实例化: 这种方式是通过 Bean 的构造方法来创建实例。您可以在 Spring 配置文件中使用 <constructor-arg> 元素为构造方法的参数指定值,Spring 容器会根据这些参数值来实例化 Bean
  • 通过setter方法实例化: 这种方式是通过 Bean 的 setter 方法来设置属性值,从而实例化 Bean。您可以在 Spring 配置文件中使用 <property> 元素为属性指定值,Spring 容器会在实例化 Bean 后调用相应的 setter 方法来设置属性值

​ 通常,使用 setter 方法实例化更加灵活,因为它可以在 Bean 实例化后动态设置属性值,而构造方法则用于在实例化时确定属性值。(因为构造方法写死,一次必须全部属性都声明好才可以,然而setter方法可以一直调用,实现动态设置属性值)

​ 具体的实例化我们昨天的文章已经有写过了,今天再复习一下好吧,其实也就是构造方法注入和setter方法注入,静态工厂实例化、实例工厂实例化,这就是今天我们要学习的。


四、Spring 支持以下五种 Bean 的作用域:
  1. singleton(单例)在单例模式下,Spring 容器中只会存在一个共享的Bean实例,所有对Bean的请求,只要请求的id(或name)与Bean的定义相匹配,会返回Bean的同一个实例
  2. prototype(原型): 每次请求都会创建一个新的实例,每个实例都是独立的,不会共享。
  3. request(请求):每个 HTTP 请求都会创建一个新的实例,该实例的生命周期仅限于当前请求,不同请求会有不同的实例。
  4. session(会话):每个 HTTP /用户会话(通常对应一个浏览器会话)都会创建一个新的实例,实例的生命周期与用户的会话保持一致。
  5. application(应用程序): 在 Web 应用程序的生命周期内,创建一个全局共享的 Bean 实例。这意味着在整个应用程序中,只会创建一个该 Bean 的实例。
<!-- 默认的作用域是 singleton -->
<bean id="singletonBean" class="com.example.SingletonBean">
    <!-- 这是一个 singleton Bean,它在容器中只会创建一次,并被缓存供后续请求使用。 -->
</bean>

<!-- prototype 作用域,每次请求都会创建一个新的 Bean 实例 -->
<bean id="prototypeBean" class="com.example.PrototypeBean" scope="prototype">
    <!-- 这是一个 prototype Bean,每次请求都会创建一个新的实例,每个实例都是独立的。 -->
</bean>

<!-- request 作用域,每个 HTTP 请求都会创建一个新的 Bean 实例 -->
<bean id="requestBean" class="com.example.RequestBean" scope="request">
    <!-- 这是一个 request Bean,它的生命周期仅限于当前 HTTP 请求内,不同请求会有不同的实例。 -->
</bean>

<!-- session 作用域,每个 HTTP 会话都会创建一个新的 Bean 实例 -->
<bean id="sessionBean" class="com.example.SessionBean" scope="session">
    <!-- 这是一个 session Bean,它的生命周期与用户的会话保持一致,不同用户的会话会有不同的实例。 -->
</bean>

<!-- application 作用域,全局共享一个 Bean 实例 -->
<bean id="applicationBean" class="com.example.ApplicationBean" scope="application">
    <!-- 这是一个 application Bean,在整个应用程序的生命周期内,只会创建一个实例,全局共享。 -->
</bean>

实际测试每种作用域的使用(这里只测试单例和原型,其他留到将SpringMVC的时候再来)

①singleton作用域-单例模式

在spring-config.xml中找到想要修改的bean,然后进行如下添加。

在这里插入图片描述

   <bean id="apple" class="entity.Apple" scope="singleton">
        <property name="weight" value="3"></property>
    </bean>

**编写测试类:**通过Spring容器获取Fruit类的两个实例,判断两个实例是否为同一个,我们这里声明创建的Bean为单例模式,Spring容器应该只会存在一个共享的Bean实例,因此预期结果是:ture

在这里插入图片描述

​ 可以看到判断结果是:true正确

那么接下来,我们尝试把scope=“singleton”去掉,因为我们前面提到了,默认就是单例模式,那么我们进行尝试

<bean id="apple" class="entity.Apple" >
    <property name="weight" value="3"></property>
</bean>

编写测试类

在这里插入图片描述

结果还是一样的:true;


②prototype原型模式的作用域

修改spring-config.xml中的bean,每次请求都会创建一个新的 Bean 实例,预期结果:false

在这里插入图片描述

输出结果:false,符合预期想法。


五、Bean的装配方式
  • 基于XML的装配: 构造方法注入和setter方法注入(我们昨天的文章已经讲解过了)
  • 基于注解的装配 : 在Spring中,使用XML配置文件可以实现Bean的装配工作,但在实际开发中如果Bean的数量较多,会导致XML配置文件过于臃肿,给后期维护和升级带来一定的困难。为解决此问题,Spring提供了注解,通过注解也可以实现Bean的装配。

Spring的常用注解
注解描述
@Component指定一个普通的Bean,可以作用在任何层次
@Controller指定一个控制器组件Bean,用于将控制层的类标识为Spring中的Bean,功能上等同于@Component
@Service指定一个业务逻辑组件Bean,用于将业务逻辑层的类标识为Spring中的Bean,功能上等同于@Component
@Repository指定一个数据访问组件Bean,用于将数据访问层的类标识为Spring 中的Bean,功能上等同于@Component
@Scope指定Bean实例的作用域
@Value指定Bean实例的注入值
@Autowired指定要自动装配的对象
@Resource指定要注入的对象
@Qualifier指定要自动装配的对象名称,通常与@Autowired联合使用
@PostConstruct指定Bean实例完成初始化后调用的方法
@PreDestroy指定Bean实例销毁前调用的方法

使用注解的方式装配Bean
①在pom中写入依赖,这个引入依赖是为了后面的AOP
 <!--用于引入 Spring Framework 中的 AOP(面向切面编程)模块-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aop</artifactId>
                <version>5.2.8.RELEASE</version>
            </dependency>
②创建applicationContext.xml,在该文件中引入Context约束并启动Bean的自动扫描功能。
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="entity" />
</beans>
这里将 <context:component-scan>base-package 配置为 "entity" 意味着 Spring 只会扫描 entity 包及其子包中的组件,而不会扫描其他包。

所以要解决这个问题,我们可以考虑将 <context:component-scan>base-package 属性配置为一个更高级别的包,以便能够扫描到所有的子包。例如,你可以将它配置为你的主包的父级包,或者更高级别的包。这将确保 Spring 能够扫描到所有的组件类

​ 这就是为什么一般来说我们新建项目的包名有讲究,一般都是com.example.xx包,这个是个倒叙的域名写法,中间的example可以是自己的名字又或者公司名,这样我们需要更换一下,扫描的包,扫描的包,或者更换成更高级别的包不可以是java。因为java是关键字,若是扫描的包变成

<context:component-scan base-package="java" />

上述写法,这样会导致Spring识别不出我们设置的bean,然后报错。

在这里插入图片描述

所以我们这里要设置成如下格式

1:添加所有使用了bean的包,作为扫描的对象

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.steveDash.entity" />
    <context:component-scan base-package="com.steveDash.controller" />
    <context:component-scan base-package="com.steveDash.dao" />
    <context:component-scan base-package="com.steveDash.service" />
</beans>

2.在包命名的时候,严格按照公司规章制度,进行包名的创建和设置,如下,我们后续的项目根目录包名都为www.steveDash( 一般都采用这种)

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.steveDash" />
   

</beans>
③定义实体类:在entity包下创建User实体类。
package entity;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component("user")
@Scope("singleton")
public class User {
    @Value("1")
    private int id;
    @Value("张三")
    private String name;
    @Value("123")
    private String password;

    // getter/setter方法和toString()方法
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String toString(){
        return "用户id:"+id+",用户名:"+name+",密码:"+password;
    }
}
④定义dao层,创建UserDao接口作为数据访问层接口,并在UserDao接口中声明save()方法,用于查询User实体的对象信息
package dao;

public interface UserDao {
    public void save();
}
⑤实现dao层,创建UserDao的实体类,代码如下:完善save方法
package dao;

import entity.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Repository;

@Repository("userDao")
public class UserDaoImpl implements UserDao {
    public void save(){
        ApplicationContext applicationContext=new
                ClassPathXmlApplicationContext("applicationContext.xml");
        User user=(User) applicationContext.getBean("user");
        System.out.println(user);
        System.out.println("执行UserDaoImpl.save()");
    }
}
 
⑥创建service层,创建UserService接口作为业务逻辑层接口,并在UserService接口中定义save()方法。
package com.stevedash.service;

public interface UserService {
    public void save();
}
⑦实现service层:在service包下新建impl包,在包内创建UserServiceImpl作为UserService的实现类,并在UserServiceImpl类中实现UserService接口中的save()方法。
package com.steveDash.service.impl;

import com.stevedash.dao.UserDao;
import com.stevedash.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service("userService")
public class UserServiceImpl implements UserService {
    //使用@Resource注解注入UserDao
    @Resource(name="userDao")
    private UserDao userDao;

    @Autowired
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }
    public void save(){
        this.userDao.save();
        System.out.println("执行UserServiceImpl.save()");
    }
}

⑧创建controller层,在controller包中新建创建UserController类作为控制层
package com.steveDash.controller;

import org.springframework.stereotype.Controller;
import com.steveDash.service.UserService;

import javax.annotation.Resource;

@Controller
public class UserController {
    //使用@Resource注解注入UserService
    @Resource(name="userService")
    private UserService userService;
    public void save(){
        this.userService.save();
        System.out.println("执行UserController.save()");
    }
}
⑨创建测试类进行测试:创建测试类AnnotationTest,在该类中编写测试代码,通过Spring容器加载配置文件并获取UserController实例,然后调用实例中的save()方法。
package Test;

import com.steveDash.controller.UserController;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AnnotationTest {
    @Test
   public void getBeanTest(){
        ApplicationContext applicationContext=new
                ClassPathXmlApplicationContext("applicationContext.xml");
        UserController usercontroller=(UserController)
                applicationContext.getBean("userController");
        usercontroller.save();
    }
}

在这里插入图片描述

输出结果如上:可以看到也是能够正常获取到Bean对象的。


六、Bean的自动装配

自动装配(Automatic Wiring)是 Spring 框架中一种方便管理依赖关系的方式。它允许 Spring 容器自动将一个 Bean 的属性设置成其他 Bean 的引用,从而建立起这些 Bean 之间的依赖关系。

可以使用XML或者使用注解方式实现Bean的自动装配。


使用XML方式实现自动装配:

自动装配可以减少在 Spring 配置文件中手动指定依赖关系的工作,使配置更加简洁和易于维护。 可以在 Spring 配置文件中使用 <bean> 元素的 autowire 属性来指定自动装配的方式

属性值描述
default(默认值)<bean>的上级元素<beans>的default-autowire属性值确定
byName根据<bean>元素id属性的值自动装配
byType根据<bean>元素的数据类型(Type)自动装配,如果一个Bean的数据类型,兼容另一个Bean中的数据类型,则自动装配
constructor根据构造函数参数的数据类型,进行byType模式的自动装配
no默认值,不使用自动装配,Bean依赖必须通过<ref>元素或ref属性定义

具体实例如下:

<bean id="exampleBean" class="com.example.ExampleBean" autowire="byType">
   <!-- 其他配置 -->
</bean>

上述配置中,autowire 属性设置为 "byType",表示使用根据类型自动装配的方式。

PS:自动装配并不适用于所有情况,特别是当有多个 Bean 类型满足自动装配条件时,可能会导致歧义。在这种情况下,我们可能需要显式地配置依赖关系,或者使用限定符(@Qualifier 注解)来消除歧义。


使用注解方式实现自动装配

Spring 提供了一些注解来简化自动装配的配置

  1. @Autowired 注解:可以用在属性、构造函数、setter 方法上,告诉 Spring 在容器中查找匹配类型的 Bean,并自动装配到标记了 @Autowired 的属性或构造函数参数上。例如:

    @Autowired
    private UserService userService;
    

    这会自动装配一个类型为 UserService 的 Bean 到 userService 属性上。

  2. @Resource 注解:与 @Autowired 类似,也用于属性、构造函数、setter 方法上,但它是按名称自动装配。可以通过 name 属性指定要自动装配的 Bean 名称。例如:

    @Resource(name = "userService")
    private UserService userService;
    

    这会自动装配名称为 “userService” 的 Bean 到 userService 属性上。

  3. @Qualifier 注解:通常与 @Autowired@Resource 注解一起使用,用于消除自动装配的歧义。当有多个符合类型匹配条件的 Bean 时,可以使用 @Qualifier 注解指定具体要装配的 Bean 的名称。例如:

    @Autowired
    @Qualifier("userService1")
    private UserService userService;
    

    这会自动装配名称为 “userService1” 的 Bean 到 userService 属性上。

使用注解方式的自动装配通常更加简洁和直观,不需要在 XML 配置中显式指定依赖关系。只需要在相应的类或属性上添加注解,Spring 容器会在扫描到这些注解时自动完成装配工作


七、Bean的生命周期

基本概念

Bean的生命周期是指Bean实例被创建、初始化和销毁的过程


Bean在不同作用域内的生命周期
  1. Singleton(单例)作用域: 在Singleton作用域下,Spring容器只会创建一个Bean的实例,并在容器初始化时创建。这个Bean会一直存在于容器中,直到容器销毁。因此,Singleton作用域的Bean的生命周期与容器相同,从容器创建到容器销毁。
  2. Prototype(原型)作用域: 在Prototype作用域下,Spring容器每次请求一个Bean时都会创建一个新的实例。这意味着每次获取Bean时都会调用构造函数创建一个新的Bean实例。Prototype作用域的Bean不受容器管理,容器不负责销毁这些Bean。Bean的生命周期由应用程序管理。
  3. Request(请求)作用域: Request作用域仅适用于Web应用程序。在每个HTTP请求时,Spring容器会创建一个新的Request作用域的Bean实例,并将其绑定到当前请求。一旦请求处理完成,Request作用域的Bean实例会被销毁
  4. Session(会话)作用域: Session作用域也仅适用于Web应用程序。在用户会话期间,Spring容器会维护一个Session作用域的Bean实例,该实例在用户会话期间保持不变。一旦用户会话结束,Session作用域的Bean实例会被销毁
  5. Application(应用程序)作用域: Application作用域是全局的,Bean实例在整个应用程序的生命周期内保持不变。这意味着在应用程序启动时创建Bean实例,在应用程序关闭时销毁通常用于全局配置或共享资源

Bean生命周期的两个时间节点

​ 在Bean的生命周期中,有两个时间节点尤为重要,分别是Bean实例初始化后和Bean实例销毁前,在这两个时间节点通常需要完成一些指定操作。因此,常常需要对这两个节点进行监控


监控时间节点的方式
  • 使用XML配置文件
  • 使用注解

Spring容器提供了**@PostConstruct用于监控Bean对象初始化节点**,

提供了**@PreDestroy用于监控Bean对象销毁节点**。


俩种监控时间节点的方式的使用
1、使用XML配置文件监控时间节点时:
①在applicationContext.xml编写好Bean的配置

​ 我们可以在Bean定义中通过<bean>元素的init-method属性来指定初始化方法,通过destroy-method属性来指定销毁方法。例如:

 <!--Mybean的实例 单例模式 自动装配Bean通过构造器、还使用@PostConstruct用于监控Bean对象初始化节点,也使用@PreDestroy用于监控Bean对象销毁节点-->
    <bean id="myBean" class="com.steveDash.entity.MyBean" init-method="customInit" destroy-method="customDestroy" autowire="constructor" scope="singleton" ></bean>

​ 上面的配置中,init-method属性指定了初始化方法为initMethoddestroy-method属性指定了销毁方法为destroyMethod。这些方法需要在MyBean类中定义,并由Spring容器在相应的生命周期阶段调用

②编写MyBean类,编写好initMethod和destroy-menthod方法代码如下:
package com.steveDash.entity;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class MyBean {

    public void customInit() {
        // 在这里进行初始化操作
        System.out.println("Bean被初始化了!");
    }
    public void hello(){
        System.out.println("你好!");
    }

    public void customDestroy() {
        // 在这里进行销毁操作
        System.out.println("Bean被销毁了!");
    }
}
③编写测试类:查看效果
package Test;

import com.steveDash.entity.MyBean;
import org.apache.log4j.Logger;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyBeanTest {
    private Logger logger = Logger.getLogger(MyBeanTest.class);
    @Test
    public void getMyBeanTest(){
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        MyBean myBean=(MyBean) context.getBean("myBean");
        myBean.hello();
        // 关闭ApplicationContext容器
        ((ClassPathXmlApplicationContext) context).close();
    }
}

因为我们定义的Bean的作用域为Singleton单例模式,那么他的生命周期与IoC容器相同,若是不关闭IoC容器,那么这个Bean就一直存在,不会被销毁,那么destory-method就没办法调用。

输入结果如下:

在这里插入图片描述

几种颜色对应的输出顺序,都是在对应的生命周期进行前调用,比如创建前调用initMethod,销毁前调用destory-method。一般来说都是在销毁Bean实例前,释放他们所占用的资源。


2、使用注解监控时间节点时:
①编写MyBean1类,代码如下:

我们可以在MyBean1类中使用@PostConstruct@PreDestroy注解来标记初始化方法和销毁方法,例如:

package com.steveDash.entity;

import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
public class MyBean1 {

    @PostConstruct
    public void customInit() {
        // 在这里进行初始化操作
        System.out.println("Bean被初始化了!");
    }

    public void hello() {
        System.out.println("你好!");
    }

    @PreDestroy
    public void customDestroy() {
        // 在这里进行销毁操作
        System.out.println("Bean被销毁了!");
    }
}

这里基本代码不变,就是加上了注解还有修改了类名,因为可以留着方便观看俩种监控时间节点的方式的不同。这些注解会告诉Spring容器在Bean的初始化和销毁阶段分别调用标记的方法。

②编写测试类:
@Test
    public void getMyBeanByAnnotationTest(){
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        MyBean1 myBean1=(MyBean1) context.getBean("myBean1");
        myBean1.hello();
        // 关闭ApplicationContext容器
        ((ClassPathXmlApplicationContext) context).close();

    }

在这里插入图片描述

原因是:Spirng的IoC容器,在创建的同时会同时创建所有的作用域为singleton的Bean实例,那么在创建前就会输出一次initMethod方法,在IoC容器关闭时也会同时调用一次destory-method方法,这里我们实际上创建了俩个单例模式的bean,因此会输出俩次,但是我们只调用了一个单例,所以只输出了一个你好。

其他的作用域下的Bean的生命周期,可以各自去尝试一下,这里就不一一尝试了。


俩种监控时间节点方式的对比:

使用XML配置文件的优势:
  1. 可配置性强: XML配置文件允许您明确指定每个Bean的初始化方法和销毁方法。这使得您可以为不同的Bean定制不同的生命周期事件处理逻辑。
  2. 可读性强: 配置文件中的生命周期事件处理定义通常更容易阅读和理解,因为它们以清晰的XML格式呈现,特别是对于大型或复杂的应用程序。
  3. 独立性: 生命周期事件处理逻辑与代码分离,这意味着您可以在不修改Java源代码的情况下修改Bean的初始化和销毁行为。
使用注解的优势:
  1. 简化配置: 使用注解可以减少XML配置文件的数量,从而简化了配置。您可以将初始化和销毁方法的注解直接添加到Bean类上,而不需要额外的XML配置。
  2. 更紧凑的代码: 注解通常会生成更紧凑的代码,因为它们将生命周期事件处理逻辑与Bean类集成在一起,不需要额外的XML配置文件。
  3. 更直观的代码结构: 通过将注解添加到Bean类上,您可以更直观地了解哪些类具有生命周期事件处理行为,而不必查看单独的配置文件。

无论是XML配置文件还是注解方式,都可以用来监控和管理Bean的生命周期,具体选择取决于项目的需要和个人偏好。硬要来说的话,那就是小型项目或更简单的Bean生命周期事件处理可以使用注解来实现,而大型或更复杂的项目可能会受益于使用XML配置文件,以获得更多的可配置性和清晰度。具体还是得根据实际开发情况,将这两种方式结合使用。


总结

​ 今天我们详细的讲解了,详细讲解Spring IoC容器的原理、掌握了Bean标签以及属性的使用、熟悉Bean的实例化、Bean的几种作用域、掌握Bean的装配方式、自动装配方式XML和注解方式的区别和优势、什么是Bean的生命周期。希望通过今天的学习,各位读者可以对Spring的IoC容器和Bean管理有个大致的了解,为框架开发打下坚实基础。

​ 想要跟着学习的可以去我的资源里面找对应的文件下载,我的md文件也会发上去,项目文件会上传可以自己跟着学习一下。

作者:Stevedash

发表于:2023年9月2日 20点56分

注:本文内容基于个人学习理解,如有错误或疏漏,欢迎指正。感谢阅读!如果觉得有帮助,请点赞和分享。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Stevedash

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值