上篇文章介绍了Spring更简单的对象存储和取出,这篇文章我们将会介绍Bean 作用域和生命周期


目录

  • 1.作用域
  • 1.1引例
  • 1.2Bean的六种作用域
  • 1.3设置作用域
  • 2.生命周期
  • 2.1Spring 执行流程
  • 2.2Bean的生命周期
  • 结束语


1.作用域

1.1引例

首先这里的作用域和我们之前java学到变量的作用域可能有点不一样。我们之前学习的变量作用域指的是该变量的可使用范围,比如:

{
	{
		int a=0;
	}//2
}//1

a变量只在括号2中生效。但是Bean的作用域指的是:


具体什么意思呢,我们先看例子:

有一个公共的Bean供A和B两个类使用,假设这个Bean里面有个属性为value=ikun,A第一次使用时拿到value=ikun,待B使用时他偷偷改成了value=只因,A下一次使用时就拿到了value=只因,但是预期value=ikun啊,从而导致了程序出现错误。
说好一起当ikun,你却偷偷开了团

用代码怎么演示呢?

@Controller
@Data
public class Ikun {
    private String name="ikun";
}

@Controller
public class A {
    @Autowired
    private Ikun ikun;
    public void show(){
        System.out.println(ikun.getName());//我是ikun
    }
}

@Controller
public class B {
    @Autowired
    private Ikun ikun;
    public void show(){
        ikun.setName("只因");//偷偷开团
    }
}

public class App {
    public static void main(String[] args) {
        ApplicationContext context=
                new ClassPathXmlApplicationContext("spring-config.xml");
        A a=context.getBean("a",A.class);
        System.out.println("A第一次使用Ikun:");
        a.show();
        B b=context.getBean("b",B.class);
        System.out.println("B偷偷开团,修改Ikun");
        b.show();
        System.out.println("A第二次使用Ikun:");
        a.show();
    }
}

注意:这里使用了@Data注解,属于lombok插件,可以帮我们处理set,get等等方法,喜欢可以去了解以下

运行得到结果:

javabean作用域区别 javabean的四个作用域_java-ee


首先怎么处理这样的问题呢?我们需要先知道原因。


原因是Bean默认的作用域是

单例状态

,也就是所有类用的都是同一个对象。可能会有人问,为什么默认是单例模式呢?因为对于单例模式(以前学过懒汉,饿汉等等),我们知道可以提高代码的性能。那怎么修改Bean的模式呢?修改之前,我们要先知道有哪些模式。

1.2Bean的六种作用域

  1. singleton:单例作⽤域
  2. prototype:原型作⽤域(多例作⽤域)
  3. request:请求作⽤域
  4. session:回话作⽤域
  5. application:全局作⽤域
  6. websocket:HTTP WebSocket 作⽤域

后四种都属于SpringMVC里面的值,普通的Spring只有前两种,本文只说前两种。
第一种已经说明,着重解释第二种

prototype:原型作⽤域(多例作⽤域)
这个与单例作用域恰恰相反,每一次装配(即@AutoWired)都是新的对象实例,也就是一个类修改Bean不会影响另一个类的Bean。
那怎么修改上面例子的Bean为该模式呢?

1.3设置作用域

使用@Scope 标签就可以⽤来声明 Bean 的作⽤域
为Ikun类添加注解

//或者 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Scope("prototype")
@Controller
@Data
public class Ikun {
    private String name="ikun";
}
得到结果:

javabean作用域区别 javabean的四个作用域_java-ee_02


很明显,开团失败了

小结:
@Scope 标签既可以修饰⽅法也可以修饰类(上面就是修饰类)
@Scope 有两种设置⽅式:

  1. 直接设置值:@Scope(“prototype”)//适合记得住单词
  2. 使⽤枚举设置:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)//idea会有提示,更方便

2.生命周期

解释生命周期之前,必须要先了解Spring的执行流程

2.1Spring 执行流程

这个我JavaEE第二课就介绍过,这里我直接给出流程:

Bean 执行流程(Spring 执行流程):启动 Spring 容器 -> 实例化 Bean(读取配置文件,分配内存空间,初始化) -> Bean 注册到 Spring 中(存操作) -> 将 Bean 装配到需要的类中(取操作)

这里可能会出一道面试题:

javabean作用域区别 javabean的四个作用域_javabean作用域区别_03

2.2Bean的生命周期

生命周期指的是有个对象从诞生到销毁的整个生命过程,Bean的生命周期注意分为以下五部分:

  1. 实例化Bean(为Bean分配内存空间)ex:拿到毛坯房
  2. 设置属性(Bean注入和装配)ex:购买装修材料
  3. 进行初始化工作
    实现了各种 Aware 通知的方法,如 BeanNameAware、BeanFactoryAware、
    ApplicationContextAware 的接口方法;ex:通知装修工人
    执行 BeanPostProcessor 初始化前置方法;ex:工人制定装修方案
    执行 @PostConstruct 初始化方法,依赖注入操作之后被执行;
    执行自己指定的 init-method 方法(如果有指定的话);ex:开始装修
    执行 BeanPostProcessor 初始化后置方法;ex:处理装修后现场
  4. 使用Bean ex:住房
  5. 销毁Bean ex:拆迁,房子被击碎

个人而言,对于第二点设置属性(Bean注入和装配)如果有疑问的。看以下代码便知:

@Controller
public class B {
    @Autowired //这一步就是设置属性
    private Ikun ikun;
    public void show(){
        ikun.setName("只因");//偷偷开团
    }
}

看到这里我们可以思考一个问题:

上面五个部分中,2和3能不能颠倒顺序?
显然是不能的,因为可能第三步中会用到第二步的设置好的属性Bean,如果颠倒就会导致出现空指针异常,因为Bean还没有注入和装配。

结束语

那么今天的第四课就到这里,下节课会进入SpringBoot的学习