一 : Bean的默认作用域
Bean 默认情况下是单例状态(singleton),所有人使用的都是同一个对象.
举例理解Bean的单例状态 :
假设现在有一个公共的 Bean,提供给 A 用户和 B 用户使用,然而在使用的途中 A 用户却“悄悄”地修改了公共 Bean 的数据,导致 B 用户在使用时发生了预期之外的逻辑错误 .
假设现在有一个IkunBean , 张三和李四和都要使用这个IkunBean .
Ikun.java
package Ikun;
public class Ikun {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Chicken{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
IkunBean
package com;
import Ikun.Ikun;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class IkunBean {
@Bean
public Ikun chicken() {
Ikun ikun = new Ikun();
ikun.setAge(26);
ikun.setName("鲲鲲");
return ikun;
}
}
Controller1.java
package com;
import Ikun.Ikun;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
/**
* 张三取出Bean对象
*/
@Controller
public class Controller1 {
@Autowired
private Ikun ikun;
public void docontroller1() {
System.out.println("张三取出该对象");
System.out.println("原数据:"+ ikun);
Ikun ikun1 = ikun;
ikun1.setName("只因");
System.out.println("现数据:"+ ikun1);
}
}
Controller2.java
package com;
import Ikun.Ikun;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
/**
* 李四取出Bean对象
*/
@Controller
public class Controller2 {
@Autowired
private Ikun ikun;
public void doController2() {
System.out.println("李四取出该对象");
System.out.println(ikun);
}
}
分析 :
总结 :
Bean作用域定义 : Bean对象在整个Spring中的某种行为模式 . 比如单例作用域表示的是Bean对象在Spring(框架)中只有一份 .
二 : 作用域定义
- 变量的作用域 : 限定程序中变量的可用范围叫做作用域,或者说在源代码中定义变量的某个区域就叫做作用域.
- Bean的作用域: 指 Bean 在 Spring 整个框架中的某种行为模式,比如 singleton 单例作用域,就表示 Bean 在整个 Spring 中只有一份,它是全局共享的,当其他人修改了这个值之后,另一个人读取到的就是被修改的值 .
三 : Bean的六种作用域及其说明
3.1 singleton
- 官方说明:(Default) Scopes a single bean definition to a single object instance for each Spring IoC container .
- 描述:该作用域下的Bean在IoC容器中只存在一个实例:获取Bean(即通过applicationContext.getBean等方法获取)及装配Bean(即通过@Autowired注入)都是同一个对象 .
- 场景:通常无状态的Bean使用该作用域。无状态表示Bean对象的属性状态不需要更新 .
- 备注:Spring默认选择该作用域 .
3.2 prototype
- 官方说明:Scopes a single bean definition to any number of object instances .
- 描述:每次对该作用域下的Bean的请求都会创建新的实例:获取Bean(即通过applicationContext.getBean等方法获取)及装配Bean(即通过@Autowired注入)都是新的对象实例 .
- 场景:通常有状态的Bean使用该作用域 , 有状态表示Bean对象的属性状态需要频繁更新 .
3.3 request
- 官方说明:Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
- 描述:每次http请求会创建新的Bean实例,类似于prototype
- 场景:一次http的请求和响应的共享Bean
- 备注:限定SpringMVC中使用
3.4 Session
- 官方 说明:Scopes a single bean definition to the lifecycle of an HTTP Session.Only valid in the context of a web-aware Spring ApplicationContext.
- 描述:在一个http session中,定义一个Bean实例
- 场景 : 用户回话的共享Bean, 如:记录某个用户的登陆信息
- 备注:限定SpringMVC中使用
3.5 application
- 官方说明:Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
- 描述:在一个http servlet Context中,定义一个Bean实例
- 场景:Web应用的上下文信息, 如:记录一个应用的共享信息
- 备注:限定SpringMVC中使用
3.6 websocket
- 官方说明:Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.
- 描述:在一个HTTP WebSocket的生命周期中,定义一个Bean实例
- 场景:WebSocket的每次会话中,保存了一个Map结构的头信息,将用来包裹客户端消息头。第一次初始化后,直到WebSocket结束都是同一个Bean。
- 备注:限定Spring WebSocket中使用 . [注意 : Spring MVC 模式下也可以使用websocket]
- 参考文章 : java WebSocket开发入门WebSocket
仍然是前面的例子 , 此处我们将Bean的作用域设置为多例模式 , 查看运行效果 . 设置多例模式的方法有以下两种 :
运行结果 :
四 : Spring执行流程
五 : Bean的生命周期
BeanComponent.java
package com.component;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
/**
* 演示Bean的生命周期
*/
@Component
public class BeanComponent implements BeanNameAware {
//依赖注入
@Autowired
private UserComponent userComponent;
public BeanComponent(UserComponent userComponent) {
this.userComponent = userComponent;
userComponent.doComponent();
System.out.println("执行BeanComponent的构造方法!");
}
//继承 BeanNameAware接口,执行通知方法
public void setBeanName(String s) {
System.out.println("执行了setBeanName通知 : " + s);
}
//xml中init-method指定的方法
public void initMethod() {
System.out.println("执行了initMethod方法!");
}
@PostConstruct
public void myPostConstruct() {
System.out.println("执行了PostConstruct方法!");
}
//销毁前执行方法
@PreDestroy
public void myPreDestroy() {
System.out.println("执行了PreDestroy方法!");
}
//使用Bean
public void use() {
System.out.println("执行了use方法!");
}
}
Test5.java
import com.component.BeanComponent;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Bean的生命周期
*/
public class Test5 {
public static void main(String[] args) {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
BeanComponent component = (BeanComponent) context.getBean("beanComponent");
component.use();
context.destroy();
}
}
spring-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<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"></content:component-scan>
<bean id="beanComponent" class="com.component.BeanComponent"
init-method="initMethod"></bean>
</beans>
解释 :
Bean的生命周期类似于买房的过程 :
- 先买房(实例化 , 从无到有) ;
- 装修(设置属性) ;
- 买家电 (各种初始化) ;
- 入住(使用Bean) ;
- 卖出去(Bean销毁) .
Q : 为什么要先设置属性(依赖注入)再初始化呢 ?
A : 防止初始化时要用到依赖注入的对象 .
重点 : 掌握Bean的大的执行流程 .