通过spring注解开发,简单测试单例和多例区别
作者:hz90s 发布时间:2023-11-06 09:18:31
通过spring注解开发,测试单例和多例区别
1.注解和配置两种用法形式
配置版:
注解版:
2.在spring框架中,scope作用域默认是单例的
注:以下测试均是注解版
3.实例
(1)多例:
配置类:
@Configuration
public class PersonConfigure {
//给容器中注册一个bean,类型为返回值的类型,id为方法名
@Scope("prototype") //多例
@Bean()
public Person person() {
System.out.println("bean被加载到容器中");
return new Person("张三",23);
}
}
单元测试:
@Test
public void test02() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PersonConfigure.class);
System.out.println("ioc容器加载完成");
Person bean = (Person) context.getBean("person");
bean.setName("lisi");
System.out.println(bean.toString());
Person bean1 = (Person) context.getBean("person");
System.out.println(bean1.toString());
System.out.println(bean==bean1);
}
}
测试结果:
结论:多例情况下,容器创建完成时不调用方法创建对象到容器中,在程序中获取时,才会将对象加载到容器中,而且每次调用生成的都是不同的对象。
(2)单例(注解版)
配置类:
//默认是单例
@Configuration
public class PersonConfigure {
//给容器中注册一个bean,类型为返回值的类型,id为方法名
@Bean()
public Person person() {
System.out.println("bean被加载到容器中");
return new Person("张三",23);
}
}
单元测试:
@Test
public void test02() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PersonConfigure.class);
System.out.println("ioc容器加载完成");
Person bean = (Person) context.getBean("person");
bean.setName("lisi");
System.out.println(bean.toString());
Person bean1 = (Person) context.getBean("person");
System.out.println(bean1.toString());
System.out.println(bean==bean1);
}
}
测试结果:
结论:单例情况下,容器创建时调用方法创建对象到容器中,在程序中调用bean,直接从容器中拿取,且每次拿取的都是同一个对象。如果上一次对bean里的属性做了修改,那下一次拿取的就是修改过的bean。
Spring中单例和多例的理解
1、什么是单例和多例
单例:所有请求用同一个对象来处理。通过单例模式,可以保证系统中一个类只有一个实例。
多例:每个请求用一个新的对象来处理。
2、Spring中的单例与多例
spring ioc容器的bean都是默认单例的,即spring依赖注入Bean实例默认是单例的。
spring提供了5中scope,分别是singleton,prototype,request,session,global session,常用是前两种。点此查看官网介绍。
单例bean与多例(原型)bean的区别:
如果一个bean被声明为单例的时候,在处理多次请求的时候,在spring容器里只实例化出一个bean,后续的请求都公用这个对象,这个对象会保存在一个map里面。当有请求来的时候,会先从缓存(map)里查看有没有,有的话直接使用这个对象,没有的话才实例化一个新的对象,所以这是个单例的。但是对于原型(prototype)bean来说,当每次请求来的时候,会直接实例化新的bean,没有缓存以及缓存查询的过程。
3、单例的优势与劣势
优势:
由于不会创建新的对象,所以有以下几个性能上的优势:
减少新生成实例的消耗。新生成实例包括两个方面,第一,spring会通过反射或者cglib来生成bean实例,这都是耗性能的操作。第二,给对象分配内存也会涉及负责算法。
减少jvm垃圾回收。由于不会给每个请求都生成bean实例,所以回收的对象就少了。
可以快速获取到bean。因为单例获取bean操作,除了第一次生成之外,其余都是从缓存里获取的,所以很快。
劣势:
一个很大的劣势是它不能做到线程安全。由于所有请求都共享一个bean实例,那么如果这个bean是一个有状态的bean的话,在并发场景下就有可能出现问题。
4、spring单例模式与线程安全:
当多用户同时请求一个服务时,容器会给每一个请求分配一个线程,这时多个线程会并发执行该请求所对应的业务逻辑(成员方法),此时就要注意了,如果该处理逻辑中有对该单例状态的修改(体现为该单例的成员属性),则必须考虑线程同步问题(此时该状态就是一个临界资源(共享数据),如果多个线程同时操作(修改)这个临界资源就会诱发线程安全问题)。
线程安全:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行的结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。或者说:一个类或者程序所提供的接口对于线程来说是原子操作,或者多线程之间的切换不会导致该接口的执行结果存在二义性,就是线程安全的。
线程安全问题都是由全局变量及静态变量引起的。
若每个线程中对全局变量,静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。
常量始终是线程安全的,因为只存在读操作;
每次调用方法前都新建一个实例是线程安全的,因为不会访问共享的资源;
局部变量是线程安全的。因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享资源。局部变量包括方法的参数变量和方法内的变量。
在关于spring单例与线程安全的很多文章中,会提到一个概念,即有状态bean和无状态bean。
无状态bean:无状态,就是一次操作,不能保存数据。无状态bean,就是没有实例变量的对象,不能保存数据,是不变类,在线程安全的。
有状态bean:有状态,就是有数据存储功能。有状态bean,就是有实例变量的对象,可以保存数据,是非线程安全的。
如何解决线程安全问题?
(1)使用线程同步机制:通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序缜密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂问题,程序设计和编写难度相对较大。
(2)使用ThreadLocal:为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。
概括起来就是:对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
5、单例如何变多例
Scope声明为prototype,即
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
来源:https://blog.csdn.net/hz90s/article/details/81071722
猜你喜欢
- 如何打印GC日志排查问题在工作当中,有时候我们会需要打印GC的相关信息来定位问题。该如何做呢?先来看个示例public static voi
- 介绍技术之前有用eureka 现在用nacos工作流和gateway接口数据流向数据表新建项目新建cloud-删除src-新建modleEu
- 一、Java并发是什么?用学术定义来说就是并发:同一时间段,多个任务都在执行 (单位时间内不一定同时执行);简单来说就是,同一个时间段,让计
- 一、模糊查询的几种实现方式1.concat函数和#{}拼接的方式student_name like concat('%',#
- Java StringWriter流的使用一、StringWriter流定义API说明:在字符串缓冲区中收集输出的字符流,可用于构造字符串,
- 前言:由于公司的业务,硬生生的把ios开发的我,掰成了android!关于上传文件的需求处理,做了一个Java的简单封装 DocumentM
- 动态 sql 简单来讲就是我们能通过条件的设置生成不同的 sql,MyBatis 中常用的动态 sql 表达式主要是有五种:ifchoose
- 1、不知道为啥process.StartInfo.Arguments = "/c" + "start D:/T
- 安装Java:安装J2SE开发工具包5.0(JDK 5.0)下载:Java官方网站。请确保以下环境变量设置,如下所述:JAVA_HOME:
- 由于今天用Security进行权限管理的时候出现了一些Bug,特此发这篇博客来补习一下对SpringSecurity的理解前言引入当今市面上
- 一.创建Spring boot项目,添加如下依赖<dependency> <gro
- 在servlet中,转发和重定向是由request和response完成的。两者之间的区别请看我之前的文章。那么在springMVC中是如何
- Maven打包一般可以生成两种包一种是可以直接运行的包,一种是依赖包(只是编译包)。Maven默认打包时jar,如果需要修改其他类型。可以修
- Java IO BufferedInputStream概要:BufferedInputStream是缓冲输入流,继承于Filte
- 目录前言准备工作Nacos安装及使用入门准备三个SpringBoot服务,引入Nacos及Kafka业务解读Nacos配置创建配置读取配置监
- java简单模拟微信抢红包功能,本例发100元红包,有10个人抢,为了尽可能的公平,每个人的红包金额都要随机(保证结果的不确定性,本例抢红包
- 代理模式:为其他对象提供一种代理以控制某个对象的访问。用在:在某些情况下,一个客户不想或者不能直接访问另一个对象,而代理对象可以在客户端和目
- 文章描述在前一篇写了如何将一张GIF动态图分解成一帧一帧的图片,这一篇我们就把喝进去的一瓢水给还回去。即把一张又一张的图片去拼合成一张GIF
- 两种基本的输入方式1.使用Scanner类需要java.util包构造Scanner类的对象,附属于标准输入流System.in,之后通过其
- CopyOnWriteArrayList介绍它相当于线程安全的ArrayList。和ArrayList一样,它是个可变数组;但是和Array