Spring中Bean的生命 周期与作用域


前言

上文讲了, Spring有关注解的一些处理方式, 那这篇博客将注重讲述 ,Spring中 , Bean对象的生命周期与作用域

Bean的作用域概念

这个作用域是在这是Bean对象中使用, 也就是reponsity
首先, 要明白一个基础概念 ,单例模式
单例模式 :单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象
总结 : 单例模式可以理解为在整个程序运行期间,对这个类进行修改, 将会改变整体这个类的值, 以后访问这个类的值也变了.

而Bean对象在默认情况下就是单例模式 , 单例模式有个好处就是可以提高性能 ,所以在Spring中默认情况下就是单例模式!

Bean 的作⽤域是指 Bean 在 Spring 整个框架中的某种⾏为模式,⽐如 singleton 单例作⽤域,就表示 Bean 在整个 Spring 中只有⼀份,它是全局共享的,那么当其他⼈修改了这个值之后,那么另⼀个⼈读取到的就是被修改的值。

Bean作用域类型

Spring有 6 种作⽤域,最后四种是基于 Spring MVC ⽣效的:在普通的 Spring 项⽬中只有前两种

1. singleton:单例作⽤域

注: 即使在单例模式下, 每次通过ApplicationContext 注入对象时,获取的对象也不是同一个
应用场景:Bean对象的属性状态不需要更新
注:Spring默认选择该作⽤域
作用: 该作⽤域下的Bean在IoC容器中只存在⼀个实例:获取Bean(即通过applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是同⼀个对象

package com.Controller;

import com.Service.USerServices;
import com.model.User;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class UserController {
    // 一般是给前端页面返回对像
    @Autowired
    private USerServices uSerServices;

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-congif.xml");
        UserController userController =( UserController) context.getBean("userController");
       User user = userController.uSerServices.init();
        System.out.println(user.toString());
    }
}

package com.model;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

@Repository
public class User {
    // 一般是构建与SQL兼容的对象
    private int id;
    private String name;

    public int getId() {
        return id;
    }

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

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

    public String getName() {
        return name;
    }

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

package com.Repository;

import com.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

import javax.jws.soap.SOAPBinding;

@Repository
public class UserReponsity {


    public User getUser() {
        User user = new User();
        user.setName("张三");
        return user;
    }
}

package com.Service;

import com.Repository.UserReponsity;
import com.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class USerServices {
    @Autowired
    UserReponsity userReponsity ;
    public User init(){
        return userReponsity.getUser();
    }
}

2. prototype:原型作⽤域(多例作⽤域)

使用场景 : 当Bean对象的属性状态需要更新时
作用 : 每次对该作用域下的Bean对象进行操作的时候 , 都会请求创建一个新的实例, 获取Bean(即通过applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是新的对象实例

package com.Controller;


import com.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);
    }


}

package com.Controller;


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

/**
 * 用户控制器 - 版本3
 * 作者:李四
 */
@Controller
public class UserController3 {

    @Autowired
    private User user;

    public void doMethod() {
        System.out.println("UserController3:user -> " + user);
    }

}

package com.model;

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

/**
 * 公共类
 */
@Component
public class Users {
    /**
     * 公共对象 -> 默认单例模式
     * @return
     */
    @Bean(name = "user")
    @Scope("prototype") // 原型模式|多例模式
    public User getUser(){
        User user = new User();
        user.setId(666);
        user.setName("孙猴子");
        return user;
    }
}

3. request:请求作⽤域

应用场景:⼀次http的请求和响应的共享Bean
描述:每次http请求会创建新的Bean实例,类似于prototype
注:限定SpringMVC中使⽤(Spring-Web)

4. session:回话作⽤域

场景:⽤户回话的共享Bean, ⽐如:记录⼀个⽤户的登陆信息
描述:在⼀个http session中,共享⼀个Bean实例
注:限定SpringMVC中使⽤

5. application:全局作⽤域

场景:Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息
描述:在⼀个http servlet Context中,定义⼀个Bean实例
注:限定SpringMVC中使⽤

6. websocket:HTTP WebSocket 作⽤域

场景:WebSocket的每次会话中,保存了⼀个Map结构的头信息,将⽤来包裹客户端消息头。第⼀次初始化后,直到WebSocket结束都是同⼀个Bean。
描述:在⼀个HTTP WebSocket的⽣命周期中,定义⼀个Bean实例
注:限定Spring WebSocket中使⽤

总结

上面, 可以看到作用域的范围是越来越大的 ,而单例模式的作用域 看起来和全局作用域很像 ,那他俩的区别在哪里?
①singleton 是 Spring 的作⽤域;application 是 Spring Web 中的作⽤域;
②singleton 作⽤于 IoC 的容器,⽽ application 作⽤于 Servlet 容器。

使用作用域

使⽤ @Scope 标签就可以⽤来声明 Bean 的作⽤域,⽐如设置 Bean 的作⽤域,如下代码所示
@Scope 标签既可以修饰⽅法也可以修饰类,@Scope 有两种设置⽅式:

  1. 直接设置值:

@Scope(“prototype”)

  1. 使⽤枚举设置:

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
public class Users {
	 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
	 @Bean(name = "u1")
	 public User user1() {
		 User user = new User();
		 user.setId(1);
		 user.setName("Java"); // 【重点:名称是 Java】
		 return user;
	 }
}

Spring的执行流程

① 启动容器 , 加载配置文件(类加载下面的XML文件)
在这里插入图片描述
②扫描包路径下面, 查看是否有5大类注解,并根据配置完成初始化
在这里插入图片描述

③将扫描到的对象注入到容器中
在这里插入图片描述

④装配Bean的属性(有DI操作才会赋值) - 通过@Autowired 与@Resource
在这里插入图片描述

Bean ⽣命周期

所谓的⽣命周期指的是⼀个对象从诞⽣到销毁的整个⽣命过程,我们把这个过程就叫做⼀个对象的⽣命周期
而Bean的生命周期 ,大体上可以分为这5个部分
1.实例化 Bean(为 Bean 分配内存空间)

相当于Java中的声明一个变量
或者说买了一块地准备盖房子,现在只有地

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

买了钢筋,水泥,找人, 盖房子->这一步结束后, 这个房子是毛坯房,

3.Bean 初始化(实现各种通知方法)
① 执行各种通知
② 初始化的前置方法
③ 初始化方法
⑥ 初始化的后置方法

买各种东西,来装配房子,比如家电等等,这个流程做完后,房子就能用了

4.使⽤ Bean

继续注房子

5.销毁 Bean

房子没产权,噶了

实例化和初始化的区别

实例化 : 就是将一个对象创造出来 ,初始化就是给这个对象附上各种属性值
这里在说明一下 , 生命周期 2 ,3 步骤不能相换 , 原因是: 使用依赖注入是动态的 , 也就是说执行了步骤2 之后 ,在执行步骤3, 一定是可以避免空指针异常的 , 但是如果先执行步骤3的话,就很可能出现空指针异常, 因为属性你并没有给他赋值, 而是使用系统的默认值

Bean对象为什么先设置属性,而后初始化呢?

对于属性来说, 先初始化和先设置属性都一样
但是对于初始化来说, 初始化可能会用到属性,而这时属性还没有注入的话,就可能会报错

这儿的初始化,和Java中的初始化是不一致的,这而初始化是说的对象

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值