JavaEE进阶学习:Bean 作用域和生命周期

1.Bean 作用域

.通过一个案例来看 Bean 作用域的问题

假设现在有一个公共的 Bean,提供给 A 用户和 B 用户使用,然而在使用的途中 A 用户却“悄悄”地修改了公共 Bean 的数据,导致 B 用户在使用时发生了预期之外的逻辑错误。

我们预期的结果是,公共 Bean 可以在各自的类中被修改,但不能影响到其他类。

package com.java.demo.model;

/**
 * @projectName: Demo
 * @package: com.java.demo.model
 * @className: User
 * @author: 王嘉辉
 * @description:
 * @date: 2023/11/28 16:18
 * @version: 1.0
 */
public class User {

    private int id;
    private String name;

    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;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

package com.java.demo.model;

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

/**
 * @projectName: Demo
 * @package: com.java.demo.model
 * @className: Users
 * @author: 王嘉辉
 * @description:
 * @date: 2023/11/28 16:18
 * @version: 1.0
 */

/**
 * 公共类
 */
@Component
public class Users {

    /**
     * 公共对象
     * @return
     */
    @Bean("user")
    public User getUser() {
        User user = new User();
        user.setId(1);
        user.setName("凹凸曼");
        return user;
    }
}

package com.java.demo.controller;

/**
 * @projectName: Demo
 * @package: com.java.demo.controller
 * @className: UserController
 * @author: 王嘉辉
 * @description:
 * @date: 2023/11/26 21:36
 * @version: 1.0
 */

import com.java.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

/**
 * 用户控制器 - 版本
 * 作者:李四
 */
@Controller
public class UserController {
    @Autowired
    private User user;

    public void doMethod() {

        System.out.println("UserController user -> " + user);
    }
}

package com.java.demo.controller;

/**
 * @projectName: Demo
 * @package: com.java.demo.controller
 * @className: UserController2
 * @author: 王嘉辉
 * @description:
 * @date: 2023/11/28 16:23
 * @version: 1.0
 */

import com.java.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

/**
 * 用户控制器 - 版本2
 * 作者:张三
 */
@Controller
public class UserController2 {

    @Autowired
    private User user;

    public void doMethod() {
        User user2 = user;
        System.out.println("UserController2 修改之前: user -> " + user);
        user2.setName("三三");
        System.out.println("UserController2 修改之后: user -> " + user);
    }
}

import com.java.demo.controller.UserController;
import com.java.demo.controller.UserController2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @projectName: Demo
 * @package: com.java.demo
 * @className: App
 * @author: 王嘉辉
 * @description:
 * @date: 2023/11/28 16:31
 * @version: 1.0
 */
public class App {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");

        UserController2 userController2 = context.getBean("userController2",UserController2.class);
        userController2.doMethod();

        UserController userController = context.getBean("userController",UserController.class);
        userController.doMethod();
    }
}

JavaEE进阶学习:Bean 作用域和生命周期_第1张图片

JavaEE进阶学习:Bean 作用域和生命周期_第2张图片
我们的Users 默认是单例模式,想要 公共 Bean 可以在各自的类中被修改,但不能影响到其他类

我们可以加一个作用域

JavaEE进阶学习:Bean 作用域和生命周期_第3张图片

1.什么叫作用域

Bean 作用域指的是 Bean 在 Spring 容器中的某种行为(单例,原型…)

2.Bean 的 6 种作用域

Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作用域。Spring有 6 种作用域,最后四种是基于 Spring MVC 生效的:

  1. singleton:单例作用域
  2. prototype:原型作用域(多例作用域)
  3. request:请求作用域 (只适用于Spring MVC 项目)
  4. session:回话作用域 (一个 HTTP 会话共享一个Bean,只适用于Spring MVC 项目)
  5. application:全局作用域(表示的是一个 Context 容器共享一个作用域,只适用于Spring MVC 项目)
  6. websocket:HTTP WebSocket 作用域

单例模式的 Bean 是线程安全的么?

不是,使用TreadLocal(本地线程变量)

3.设置 Bean 的作用域

1.@Scope(“prototype”)

package com.java.demo.model;

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

/**
 * @projectName: Demo
 * @package: com.java.demo.model
 * @className: Users
 * @author: 王嘉辉
 * @description:
 * @date: 2023/11/28 16:18
 * @version: 1.0
 */

/**
 * 公共类
 */
@Component
public class Users {

    /**
     * 公共对象 -> 默认是单例模式
     * @return
     */
    @Bean(name = "user")
    @Scope("prototype") //原型模式 | 多例模式(每一次使用注解请求的时候,都会 new 一个新的之前对象)
    //@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public User getUser() {
        User user = new User();
        user.setId(1);
        user.setName("凹凸曼");
        return user;
    }
}

2.@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

package com.java.demo.model;

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

/**
 * @projectName: Demo
 * @package: com.java.demo.model
 * @className: Users
 * @author: 王嘉辉
 * @description:
 * @date: 2023/11/28 16:18
 * @version: 1.0
 */

/**
 * 公共类
 */
@Component
public class Users {

    /**
     * 公共对象 -> 默认是单例模式
     * @return
     */
    @Bean(name = "user")
    //@Scope("prototype") //原型模式 | 多例模式(每一次使用注解请求的时候,都会 new 一个新的之前对象)
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public User getUser() {
        User user = new User();
        user.setId(1);
        user.setName("凹凸曼");
        return user;
    }
}

2.Spring 执行流程和 Bean 的生命周期

1.执行流程

JavaEE进阶学习:Bean 作用域和生命周期_第4张图片

Bean 执行流程(Spring 执行流程):启动 Spring 容器 -> 实例化 Bean(分配内存空间,从无到有) -> Bean 注册到 Spring 中(存操作) -> 将 Bean 装配到需要的类中(取操作)。

2.Bean 的生命周期

所谓的生命周期指的是⼀个对象从诞生到销毁的整个生命过程,我们把这个过程就叫做⼀个对象的生命周期。

  1. 实例化(内存分配空间)

  2. 设置 Bean 的属性(进行依赖注入,将依赖的 Bean 赋值到当前类的属性上)

  3. Bean 的初始化

  • 执行各种通知
  • 初始化的前置方法
  • 初始化方法
  • 初始化的后置方法
  1. 使用 Bean

  2. 销毁 Bean

实例化和属性设置是 Java 级别的系统“事件”,其操作过程不可人工干预和修改;而初始化是给开发者提供的,可以在实例化之后,类加载完成之前进行自定义“事件”处理。
JavaEE进阶学习:Bean 作用域和生命周期_第5张图片

import org.springframework.beans.factory.BeanNameAware;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/**
 * @projectName: untitled
 * @package: PACKAGE_NAME
 * @className: BeanLifeComponent
 * @author: 王嘉辉
 * @description:
 * @date: 2023/11/28 21:12
 * @version: 1.0
 */
public class BeanLifeComponent implements BeanNameAware {
    @Override
    public void setBeanName(String s) {
        System.out.println("执行了 BeanNameAware -> " + s);
    }
    @PostConstruct
    public void doPostConstruct() {
        System.out.println("执行了 @PostConstruct");
    }
    public void myinit() {
        System.out.println("执行了 myinit");
    }

    @PreDestroy
    public void doPreDestroy() {
        System.out.println("执行了 @PreDestroy");

    }

    public void useBean() {
        System.out.println("使用 Bean");
    }

}

import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @projectName: untitled
 * @package: PACKAGE_NAME
 * @className: BeanLifeTest
 * @author: 王嘉辉
 * @description:
 * @date: 2023/11/28 21:25
 * @version: 1.0
 */
public class BeanLifeTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("spring-config.xml");
        BeanLifeComponent component =
                context.getBean("mybean",BeanLifeComponent.class);
        component.useBean();
        context.close();
    }
}


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:content="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 https://www.springframework.org/schema/context/spring-context.xsd">
    <content:component-scan base-package="com.java.demo">content:component-scan>
    <bean id="mybean" class="BeanLifeComponent" init-method="myinit" >bean>
beans>

JavaEE进阶学习:Bean 作用域和生命周期_第6张图片

JavaEE进阶学习:Bean 作用域和生命周期_第7张图片
符合上述 Bean初始化 的流程

3.为什么要先设置属性在进行初始化

import com.java.demo.model.User;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/**
 * @projectName: untitled
 * @package: PACKAGE_NAME
 * @className: BeanLifeComponent
 * @author: 王嘉辉
 * @description:
 * @date: 2023/11/28 21:12
 * @version: 1.0
 */
public class BeanLifeComponent implements BeanNameAware {

    @Autowired
    private User user;

    @Override
    public void setBeanName(String s) {
        System.out.println("执行了 BeanNameAware -> " + s);
    }

    @PostConstruct
    public void doPostConstruct() {
        System.out.println("执行了 @PostConstruct");
        System.out.println(user.toString());
    }

    public void myinit() {
        System.out.println("执行了 myinit");
    }

    @PreDestroy
    public void doPreDestroy() {
        System.out.println("执行了 @PreDestroy");

    }

    public void useBean() {
        System.out.println("使用 Bean");
    }

}

JavaEE进阶学习:Bean 作用域和生命周期_第8张图片

在 Bean 的生命周期中,先设置属性再进行初始化的原因是因为在实例化 Bean 对象时,Spring 容器会调用 Bean 的构造方法创建 Bean 对象,并将对象的属性注入到 Bean 中。如果在初始化之前就进行依赖注入,那么被注入的属性可能还没有完全初始化,这样可能会导致对象状态不一致的问题。

当初始化 Bean 时,容器会调用 Bean 的初始化方法。这个时候,Bean 对象已经被完全初始化,可以安全地进行一些初始化操作。因此,先设置属性再进行初始化可以确保 Bean 对象的完整性和正确性。

你可能感兴趣的:(JavaEE进阶学习,java-ee,学习,java)