点击上方 Java学习之道,选择 设为星标
每天12点,干货准时奉上!
Spring Bean的作用域
Spring Bean 的作用域即为对象的作用范围
。Spring Bean的作用域由配置项 scope
来限定。
Scope配置项
作用域限定了 Spring Bean 的作用范围,在 Spring 配置文件定义 Bean 时,通过声明 scope 配置项,可以灵活定义 Bean 的作用范围。
想要容器创建的 bean 为单例模式,就设置 Scope 为 singleton
;想要每次创建的 bean 都是不同的对象则使用 prototype
。
六大作用域
官方文档给出的bean作用域的简单介绍。文档的表格写着是六个作用域,但是很多时候都会被认为只有五个作用域。因为
application
和 websocket
的作用差不多。所以会被认为是一个。
范围 | 描述 |
---|---|
singleton | (默认)将每个Spring IoC容器的单个 bean 定义范围限定为单个对象实例。 |
prototype | 将单个 bean 定义的作用域限定为任意数量的对象实例。 |
request | 将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有一个自己的 bean 实例,它是在单个 bean 定义的后面创建的。仅在可感知网络的 Spring 上下文中有效 ApplicationContext 。 |
session | 将单个 bean 定义的范围限定为 HTTP 的生命周期 Session 。仅在可感知网络的 Spring 上下文中有效 ApplicationContext 。 |
application | 将单个 bean 定义的作用域限定为的生命周期 ServletContext 。仅在可感知网络的Spring上下文中有效 ApplicationContext 。 |
websocket | 将单个 bean 定义的作用域限定为的生命周期 WebSocket 。仅在可感知网络的Spring上下文中有效 ApplicationContext 。 |
本文重点放在 singleton 和 prototype ,后面四个是对于WebMVC
而言的,这里暂不进行详细阐述。
单例作用域
先来说说第一个单例范围
,单例范围即无论使用多少个 Dao 去获取 bean ,每次获取到的都是一个相同的 bean对象。「singleton」 这与我们所说的单例模式基本是相同的,singleton 也是默认的作用域,如果没用指定 bean 对象的作用域则在整个上下文环境中通过 Spring IOC 获取到的 bean 都是同一个实例。
配置 bean 为 singleton 作用域代码:
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
接下来定义一个POJO类 -- User来演示 scope 属性对实例的影响。
public class User {private String name;private int age;public User() {}public User(String name, int age) {this.name = name;this.age = age;}/*toString和getter/setter*/@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
再显示的把该 bean 的作用域设置为 singleton。
<?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:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd "><bean id="user" class="com.javastudyway.pojo.User" p:name="Java学习之道" p:age="4" scope="singleton"/></beans>
最后就是测试类了:
import com.javastudyway.pojo.Student;
import com.javastudyway.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {@Testpublic void testSingleton(){ApplicationContext context = new ClassPathXmlApplicationContext("userbean.xml");User user1 = context.getBean("user",User.class);User user2 = context.getBean("user",User.class);System.out.println(user1 == user2);}
}
测试结果如下:从比较结果可以看出,程序两次从 IOC 容器中获取的 User 实例「地址比较结果相同」,说明 IOC 容器返回的是「同一个实例」。
原型作用域
与 singleton 刚好相反,prototype
,是程序每次从 IOC 容器获取的 Bean 都是一个新的实例。这次把 bean 的配置修改成 「prototype」,再进行测试。
<bean id="user" class="com.javastudyway.pojo.User" p:name="Java学习之道" p:age="4" scope="prototype"/>
在测试类中增加一个测试方法:
@Test
public void testPrototype(){ApplicationContext context = new ClassPathXmlApplicationContext("userbean.xml");User user1 = context.getBean("user", User.class);User user2 = context.getBean("user", User.class);System.out.println("两次对象地址比较结果为:" + (user1 == user2));System.out.println("user1 的hasCode为:" + user1.hashCode());System.out.println("user2 的hasCode为:" + user2.hashCode());
}
测试结果为:可以看到,程序两次从 IOC 容器中获取的 user1 和 user2 实例的 hashCode 不相同,说明 User 添加 prototype 作用域后,IOC 容器每次返回的都是一个新的实例。
❝对于作用域使用场景的总结:
有状态的bean应该使用prototype作用域;
对无状态的bean则应该使用singleton作用域。
❞
使用注解声明bean的作用域
大多数时候,我们都不想很麻烦的去修改 applicationContext.xml
,因为修改文件总是烦躁得让人抗拒。
所以我们可以使用注解来配置 bean 的作用范围。具体步骤为
使用 「@Component」 注解将 POJO 类声明为一个 bean;
使用 「@Scope」 注解声明 bean 的作用域;
修改 「applicationContext.xml」 开启扫描 POJO 包中的所有类的注解。
修改POJO类
在 POJO 类上增加 @Component,让 Spring 将 User 视为一个 bean。
在 POJO 类上增加 @Scope 注解,标识 该 bean 的作用域为 singleton。
package com.javastudyway.pojo;import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component("user")
@Scope("singleton")
public class User {private String name;private int age;public User() {}public User(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
开启注解扫描
增加完注解后我们只需要对 applicationContext.xml 这个 Spring 的配置文件做上一小部分修改,即可实现注解开发了。
开启 com.javastudyway.pojo
包中的所有类的注解扫描。
<?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:context="http://www.springframework.org/schema/context"xmlns:p="http://www.springframework.org/schema/p"xmlns:c="http://www.springframework.org/schema/c"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!-- 开启注解扫描 --><context:component-scan base-package="com.javastudyway.pojo"/></beans>
测试
对 POJO 类加上注解和开启注解扫描后,我们就可以通过简单的注解来设置 bean 的作用域而不再需要去配置文件中慢慢修找修改了。用刚刚 测试原型作用域的方法
来测试用注解修改成 singleton 的 bean。
结果从原来的 「false,hashCode 不相等」变成了 「true,hashCode 相等」了。
-- END --
- | 更多精彩文章 -
[漫画]献给写代码把自己蠢乐的自己
Redis内存爆满了怎么办?
(修订版)Spring对象如何创建与管理,又如何使用巧妙的方法注入属性呢?
Nginx是如何快到让人吃惊又稳定的?
买西瓜懂得了如何教女朋友编程
《Java学习-进阶手册》
公众号后台回复「手册」获取资料
▽加我微信,交个朋友长按/扫码添加↑↑↑