一幅长文细学Spring(四)—— bean详解

简介: 本文讲解了Spring的特性

4 bean详解

4.1 bean作用域

引入:在Spring的IOC容器中,bean的创建方式默认采用单例模式;也就是说,创建再多的bean,也是同一个bean。

验证

image-20221001224822790

  1. 新建一个Student类

    package beanDemo;
    
    public class Student {
        private String name;
        private Integer age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    }
  2. 书写配置文件

    <?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.xsd">
        <bean id="Student" class="beanDemo.Student"></bean>
    </beans>
  3. 新建一个StudentDemo测试类

    import beanDemo.Student;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class StudentDemo {
        @Test
        public void testStudent(){
            ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
            Student student1 = ioc.getBean(Student.class);
            Student student2 = ioc.getBean(Student.class);
            System.out.println(student1);
            System.out.println(student2);
        }
    }

    image-20221001224707179

说明:在Spring中可以通过配置bean标签的scope书写来指定bean的作用域范围,各取值含义如下表:

取值 含义 创建对象的时机
singleton 在IOC容器中,这个bean的对象始终为单实例 IOC容器创建初始化时
prototype 这个bean在IOC容器中有多个实例 获取bean时

提示:如果实在WebApplicationContext环境下还会有另外两个作用域,但不常用。

取值 含义
request 在一个请求范围内有效
session 在一个会话范围内有效


4.2 bean的生命周期

生命周期:从创建到消亡的完整过程

bean生命周期:bean从创建到销毁的整体过程

说明:学过Vue3或者Servlet都能理解什么是生命周期;对于Vue3来说,生命周期钩子就是生命周期控制最重要的手段。对于bean的生命周期来说,大体可分为如下几个阶段:

  1. bean对象创建(调用无参构造器)
  2. 给bean对象设置属性
  3. bean对象初始化之前操作(由bean的后置处理器负责)
  4. bean对象初始化(需在配置bean时指定初始化方法)
  5. bean对象初始化之后操作(由bean的后置处理器负责)
  6. bean对象就绪可以使用
  7. bean对象销毁(需在配置bean时指定销毁方法)
  8. IOC容器关闭

提示

  • 若bean的作用域为单例时,生命周期的前三个步骤会在获取IOC容器时执行
  • 若bean的作用域为多例时,生命周期的前三个步骤会在获取bean时执行

步骤演示

  1. 创建一个User类

    package beanDemo;
    
    public class User {
        private Integer id;
        private String username;
        private String password;
        private Integer age;
    
        public User(Integer id, String username, String password, Integer age) {
            this.id = id;
            this.username = username;
            this.password = password;
            this.age = age;
        }
    
        public User(){}
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", password='" + password + '\'' +
                    ", age=" + age +
                    '}';
        }
    
        public void initMethod(){
            System.out.println("生命周期:初始化");
        }
    
        public void destroyMethod(){
            System.out.println("生命周期:销毁");
        }
    }
  2. 书写配置文件

    <?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.xsd">
        <bean id="User" class="beanDemo.User" init-method="initMethod" destroy-method="destroyMethod"></bean>
    </beans>
  3. 创建一个测试类UserDemo

    import beanDemo.User;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class UserDemo {
        @Test
        public static void main(String[] args) {
            ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
            User user = ioc.getBean(User.class);
            System.out.println(user);
        }
    }
    
  4. 如果想关闭容器,可以使用2.2提到的ConfigurableApplicationContext接口中的close方法;修改UserDemo测试类

    import beanDemo.User;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class UserDemo {
        @Test
        public static void main(String[] args) {
            ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
            User user = ioc.getBean(User.class);
            System.out.println(user);
            ioc.close();
        }
    }
  5. 如果需要在生命周期初始化前后添加额外操作,需要实现BeanPostProcessor接口,且配置到IOC容器中;需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是IOC容器中所有bean都会执行。

    package beanDemo;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    
    public class MyBeanPostProcess implements BeanPostProcessor {
    
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("后置处理器的初始化前方法");
            return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("后置处理器的初始化后方法");
            return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
        }
    }
  6. 在容器中配置该bean

    <?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.xsd">
        <bean id="User" class="beanDemo.User" init-method="initMethod" destroy-method="destroyMethod"></bean>
        <bean id="myBeanPostProcessor" class="beanDemo.MyBeanPostProcess"></bean>
    </beans>
  7. 执行

    image-20221002111540430


4.3 FactoryBean

说明:如名字一样,这类bean如同工厂模式一般屏蔽了对象的内部细节,只会把需要的部分作为展示。与普通的bean不同,配置一个 FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是 getObject()方法的返回值;如果想要创建一个FactoryBean类型的Bean,我们必须实现该接口,并且重写其中三个方法中的两个,其中isSingleton方法是可选的。

  • getObject():提供一个对象交给IOC管理
  • getObjectType():设置所提供对象的类型
  • isSingleton():所提供的对象是否单例

提示:将来我们整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。


4.4 基于xml的自动装配

说明:IOC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配。仔细品读这句话,我们可以发现它所说的bean依赖的资源,其实指的就是需要通过ref来引用的资源,如类类型。自动装配方式分为多种,如下:

  • 按类型
  • 按名称
  • 按构造方法
  • 不启用自动装配

依赖自动装配特征

  • 自动装配用于引用类型依赖注入,而不能用于简单类型的依赖注入
  • 使用按类型装配时必须保障容器中具有相同类型的bean唯一,推荐使用
  • 使用按名称装配时必须保障容器中具有指定名称的bean,因变量名与配置文件耦合高,不推荐使用
  • 自动装配优先级低于setter注入和构造器注入,同时出现时自动装配配置失效

步骤演示

  1. 我们采用经典的MVC三层架构来做这么一个演示

    image-20221010103905709

  2. 其中BookDao和BookService是两个接口,内容分别如下:

    package dao;
    
    public interface BookDao {
        void save();
    }
    package service;
    
    public interface BookService {
        void save();
    }
  3. 其中BookDaoImpl和BookServiceImpl是上述两个接口的实现类,而BookServiceImpl中使用了BookDaoImpl的对象。

    package dao.impl;
    
    import dao.BookDao;
    
    public class BookDaoImpl implements BookDao {
        @Override
        public void save() {
            System.out.println("book dao save");
        }
    }
    package service.impl;
    
    import dao.BookDao;
    import service.BookService;
    
    public class BookServiceImpl implements BookService {
        private BookDao bookDao;
    
        public void setBookDao(BookDao bookDao) {
            this.bookDao = bookDao;
        }
    
        @Override
        public void save() {
            System.out.println("book service save");
            bookDao.save();
        }
    }
  4. 按照之前的做法,我们需要在配置文件中声明两个bean,然后在BookService对应的bean来通过ref引用BookDao的bean。

    <?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.xsd">
        <bean id="bookDao" class="dao.impl.BookDaoImpl"></bean>
    
        <bean id="bookService" class="service.impl.BookServiceImpl">
            <property name="bookDao" ref="bookDao"></property>
        </bean>
    </beans>
  5. 学会了自动装配后,我们不需要通过ref引用了,只需使用autowire来自动装配即可;自动装配利用了BookServiceImpl中的set方法来进行注入,故set方法必须存在,否则无法进行自动装配。

    <?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.xsd">
        <bean id="bookDao" class="dao.impl.BookDaoImpl"></bean>
    
        <bean id="bookService" class="service.impl.BookServiceImpl" autowire="byType"/>
    </beans>
  6. 对于采用按类型自动装配来说,其必须要求BookDaoImpl的bean标签对应的class和项目文件中BookDaoImpl的路径相同,如果你在配置文件中使用了两个BookDaoImpl的bean,那么他们的类路径是相同的,此时类类型自动装配无法识别将哪个bean注入到BookServiceImpl的bean中。
  7. 对于采用按名称自动装配来说,其必须要求set方法使用的setBookDao把set去掉后必须和bean中的id名要相同。

    image-20221010105357783

    image-20221010105445638


4.5 容器

说明:这实际上是对前面的学习做一个回顾,让我们来看看之前时如何创建IOC容器的。

创建容器

// 方式一:类路径加载配置文件
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");

// 方式二:文件路径加载配置文件
ApplicationContext ioc2 = new FileSystemXmlApplicationContext("C:\\UserWorkstation\\Code\\Spring\\src\\main\\resources\\applicationContext.xml");

// 方式三:加载多个配置文件
ApplicationContext ioc = new ClassPathXmlApplicationContext("bean1.xml","bean2.xml");


4.6 核心容器总结

  1. beanFactory是IOC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载
  2. ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载
  3. ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能
  4. bean标签中的属性

    image-20221010113227023

  5. 依赖注入相关

    image-20221010113455336

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