一幅长文细学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

目录
相关文章
|
27天前
|
安全 Java Spring
Spring框架中的单例Bean是线程安全的吗?
Spring框架中的单例Bean是线程安全的吗?
21 1
|
9天前
|
Java Spring 缓存
Spring Bean循环依赖详解
【6月更文挑战第2天】
18 2
|
13天前
|
存储 Java 数据库
Spring的使用-Bean对象的储存和获取/Bea对象的作用域与生命周期
Spring的使用-Bean对象的储存和获取/Bea对象的作用域与生命周期
|
17天前
|
Java Spring 容器
Spring注解开发,bean的作用范围及生命周期、Spring注解开发依赖注入
Spring注解开发,bean的作用范围及生命周期、Spring注解开发依赖注入
30 1
Spring注解开发,bean的作用范围及生命周期、Spring注解开发依赖注入
|
17天前
|
Java 容器 Spring
Spring的加载配置文件、容器和获取bean的方式
Spring的加载配置文件、容器和获取bean的方式
27 3
Spring的加载配置文件、容器和获取bean的方式
|
17天前
|
Java Spring 容器
Spring核心概念、IoC和DI的认识、Spring中bean的配置及实例化、bean的生命周期
Spring核心概念、IoC和DI的认识、Spring中bean的配置及实例化、bean的生命周期
36 0
|
18天前
|
XML Java 数据格式
Spring框架学习 -- Bean的生命周期和作用域
Spring框架学习 -- Bean的生命周期和作用域
18 2
|
18天前
|
存储 XML Java
Spring框架学习 -- 读取和存储Bean对象
Spring框架学习 -- 读取和存储Bean对象
16 0
Spring框架学习 -- 读取和存储Bean对象
|
27天前
|
消息中间件 安全 Java
在Spring Bean中,如何通过Java配置类定义Bean?
【4月更文挑战第30天】在Spring Bean中,如何通过Java配置类定义Bean?
23 1
|
27天前
|
XML Java 数据格式
Spring Bean
【4月更文挑战第30天】Spring Bean
20 0