Spring~Bean的作用域、生命周期

作用域

Bean的作用域与变量的作用域不一样,变量的作用域是指这个变量的可用范围,而Bean的作用域指的是某种行为模式,也可以理解为不同类型的Bean实例,比如singleton作用域,指的是这个Bean在整个IoC容器中只有一个,每次操作和返回的都是这个bean。

目前Spring的作用域或者作用范围共有六种:

作用域 描述
singleton 单例作用域,在整个Spring IoC容器仅存在一个Bean实例,Bean以单例方式存在
prototype 原型作用域,每次从容器中调用Bean时,都返回一个新的实例。即每次调用getBean时,相当于执行newXxxBean方法
request 请求作用域,每次Http请求都会创建一个新的Bean
session 会话作用域,同一个Http Session共享一个Bean,不同Session使用不同的Bean
application 全局作用域,在一个Http Servlet Context中,定义一个Bean.
websocket (了解) 在一个WebSocket的生命周期中,定义一个Bean实例

在普通的spring项目中只有前两种作用域,后四种是spring MVC中的值,仅适用于web的Spring
WebApplicationContext环境中。

普通spring环境下:

singleton

被声明为singleton的bean在整个spring IoC容器中只有一个实例,当我们通过ApplicationContext.getBean获取bean或者通过@Autowired注解来装配Bean时,得到的都是同一个实例。

Singleton是单例模式,就是在创建容器时就自动的创建了这个对象,无论是否使用,它都已经存在了,每次获取到的都是同一个对象。而且singleton是spring的默认作用域(缺省作用域)

通常无状态的bean使用该作用域,有状态的bean使用prototype作用域。无状态指bean对象的属性不需要更新。

代码举例:
创建TestSingleton类作为bean:

package com;

import org.springframework.stereotype.Component;

@Component
public class TestSingleton {
    
    
    private String message;
    public void setMessage(String message){
    
    
        this.message  = message;
    }
    public void getMessage(){
    
    
        System.out.println("Your Message : " + message);
    }
}

在测试类中同时尝试获取两个bean,看输出信息是否相同,相同即同一个实例:

import com.Controller.UserController;
import com.TestSingleton;
import com.User.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    
    
    public static void main(String[] args) {
    
    
        ApplicationContext context=new ClassPathXmlApplicationContext("a.xml");
        TestSingleton testSingleton_A=(TestSingleton)context.getBean("testSingleton");
        System.out.println("下面是第一个bean的信息:");
        testSingleton_A.setMessage("This is A!");
        testSingleton_A.getMessage();
        TestSingleton testSingleton_B=(TestSingleton)context.getBean("testSingleton");
        System.out.println("下面是第二个bean的信息:");
        testSingleton_B.getMessage();
    }
}

输出:

下面是第一个bean的信息:
Your Message : This is A!
下面是第二个bean的信息:
Your Message : This is A!

prototype

如果将bean声明为prototype,表示每次请求bean或者将其注入到另一个bean中时都会创建一个新的实例。当我们通过ApplicationContext.getBean获取bean或者通过@Autowired注解来装配Bean时,得到的也是一个新的实例

prototype是原型类型,在创建容器的时候并不会实例化对象,而是当我们尝试去获取一个bean时才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。

有状态的bean通常会声明为prototype,即对象属性需要更新的bean.

代码举例:

创建TestPrototype类作为bean,通过注解Scope将作用域类型设置为prototype:

package com;

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

@Component
@Scope("prototype")
public class TestPrototype {
    
    
    private String message;
    public void setMessage(String message){
    
    
        this.message  = message;
    }
    public void getMessage(){
    
    
        System.out.println("Your Message : " + message);
    }
}

在测试类中获取两个bean,看输出信息是否相同,相同即是同一个实例,不同则是不同的实例:


import com.TestPrototype;


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    
    
    public static void main(String[] args) {
    
    
        ApplicationContext context=new ClassPathXmlApplicationContext("a.xml");
        TestPrototype testSingleton_A=(TestPrototype)context.getBean("testPrototype");
        System.out.println("下面是第一个bean的信息:");
        testSingleton_A.setMessage("This is A!");
        testSingleton_A.getMessage();
        TestPrototype testSingleton_B=(TestPrototype)context.getBean("testPrototype");
        System.out.println("下面是第二个bean的信息:");
        testSingleton_B.getMessage();
    }
}

输出:

下面是第一个bean的信息:
Your Message : This is A!
下面是第二个bean的信息:
Your Message : null

spring web环境下:

requestsessionapplication这三个作用域是基于Spring WebApplicationContext实现的,只有在web环境下才能使用(比如XmlWebApplicationContext中)

如果在普通的spring环境下,即常规的IoC容器(比如ClassPathXmlApplicationContext)中使用这些作用域,那么程序会抛出一个IllegalStateException异常,来表明使用了未知的作用域。

而在web环境下,比如使用Spring中的WebApplicationContext时,我们不仅可以使用singleton和prototype这两个作用域,还可以使用三个独有的作用域:request、session、application.

在使用Web环境相关的作用域时,必须在Web容器中进行一些额外的配置:

  • 如果使用的是Spring MVC框架,就不需这些配置,因为每一次http请求都会通过DispatcherServlet来处理,而DispatcherServlet中已经配置好了相关状态。
  • 如果是低版本的web容器,需要在配置的XML文件中添加如下声明即可:
<web-app>
    ...
    <listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener
        </listener-class>
    </listener>
    ...
</web-app>

还可以使用Spring中的RequestContextFilter或者ServletRequestListener和其他方式,这里不再详细介绍。

request

request是请求作用域

作用域被声明为request的bean,每次有http请求时,都会创建新的实例。

在进行使用时,也是在scope属性中声明:

<bean id="" class="" scope="request"/>

假设这个bean的id是loginAction,Spring容器每一次用loginAction来处理Http请求时都会创建一个新的实例,即loginAction 这个bean的作用域是Http Request级别的。

当Http请求调用作用域级别为Request的bean时,每增加一个Http请求,Spring就会创建一个新的bean,当请求处理完成之后变回销毁这个bean,开发者可以任意的改变一个实例的状态,因为每一个bean之间都是根据独立的请求来的,其他实例根本看不到其他被开发者改变的实例的状态。

session

session是会话作用域

同一个Http Session(会话)之间共享一个bean,而不同的会话使用不同的bean.

在使用时,可以在scope属性中声明:

<bean id="user" class="" scope="session"/>

假设bean的id是user,Spring容器每次调用user的时候,都会在一个会话中创建一个实例,即这个bean是Http Session级别的。

Session中的所有http请求共享一个Bean,每次用Session级别的bean处理会话时,每增加一个会话,都会创建一个新的实例。与上面的request一样,开发者可以随意修改bean的状态,因为每个bean都是根据单独的会话session来创建的,其他的bean无法看到这个bean的状态。

application

application是全局作用域

使用方式:

<bean id="app" class="" scope="application"/>

假设bean的id为app,Spring容器会在整个web应用范围使用到app的时候创建一个新的实例。也就是说,appBean是在ServletContext级别的.

全局作用域和单例作用域不同

作为ServletContext的常规属性,全局作用域和Spring的单例作用域类似,但也有不同之处:

  • 来源不同:singleton来自Spring Core,而application来自Spring Web
  • 使用不同:singleton用于IoC容器,而application用于Servlet容器。

如何声明bean的作用域

通过xml配置文件

在xml配置文件中,直接设置bean元素的scope属性即可。

<?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">
 
     //scope属性
    <bean id="SingletonBean" class="com.spring.demo.SingletonBean" scope="prototype"></bean>
   
</beans>

通过注解

在bean上添加注解:

package com.spring.demo;
 
import org.springframework.context.annotation.Scope;;
import org.springframework.stereotype.Component;
 
@Component("SingletonBean")
@Scope("prototype")
public class SingletonBean {
    
    
    private String message;
    public void setMessage(String message){
    
    
        this.message  = message;
    }
    public void getMessage(){
    
    
        System.out.println("Your Message : " + message);
    }
}

@Component(“SingletonBean”)标识这个类是一个bean,@Scope(“prototype”)标识这个bean的作用域为prototype类型。

生命周期

Bean的生命周期指一个Bean对象从“出生”到“销毁”的过程,可以表达为:
Bean的定义——>Bean的初始化——>Bean的使用——>Bean的销毁.

具体过程如下:

1.实例化Bean,为Bean分配一些内存空间。
2.设置属性,注入或者装配Bean
3.进行初始化:

  • 实现各种通知(Aware)方法,如BeanNameAware、BeanFactoryAware、BeanFactoryAware、ApplicationContextAware等
  • 执行BeanPostProcessor初始化前置方法
  • 执行@PostConstruct初始化方法,注入依赖后执行
  • 执行自定义的inti-method方法(可有可无)
  • 执行BeanPostProcessor初始化后置方法。
    4.使用Bean
    5.销毁Bean

流程图如下:
在这里插入图片描述

初始化和实例化的区别:

实例化和属性设置是Java的系统事件,是不可干扰和修改的,而初始化是开发者自定义的,在实例化完成后,类加载之前会执行这些自定义的初始化内容。

文章参考:https://blog.csdn.net/kongmin_123/article/details/82048392

猜你喜欢

转载自blog.csdn.net/Merciful_Lion/article/details/124082956