通过spring注解开发,简单测试单例和多例区别

目录
  • 通过spring注解开发,测试单例和多例区别
    • 1.注解和配置两种用法形式
    • 2.在spring框架中,scope作用域默认是单例的
    • 3.实例
      • (1)多例:
      • (2)单例(注解版)
  • Spring中单例和多例的理解
    • 1、什么是单例和多例
    • 2、Spring中的单例与多例
      • 单例bean与多例(原型)bean的区别:
    • 3、单例的优势与劣势
      • 优势:
      • 劣势:
    • 4、spring单例模式与线程安全:
      • 如何解决线程安全问题?
    • 5、单例如何变多例

通过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)

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Spring框架中 @Autowired 和 @Resource 注解的区别

    Spring框架中 @Autowired 和 @Resource 注解的区别 在 spring 框架中,除了使用其特有的注解外,使用基于 JSR-250 的注解,它包括 @PostConstruct, @PreDestroy 和 @Resource 注释. 首先,咱们简单了解 @PostConstruct 和 @PreDestroy 注释: 为了定义一个 bean 的安装和卸载,我们可以使用 init-method 和 destroy-method 参数简单的声明一下 ,其中 init-meth

  • 浅谈Spring单例Bean与单例模式的区别

    Spring单例Bean与单例模式的区别在于它们关联的环境不一样,单例模式是指在一个JVM进程中仅有一个实例,而Spring单例是指一个Spring Bean容器(ApplicationContext)中仅有一个实例. 首先看单例模式,在一个JVM进程中(理论上,一个运行的JAVA程序就必定有自己一个独立的JVM)仅有一个实例,于是无论在程序中的何处获取实例,始终都返回同一个对象,以Java内置的Runtime为例(现在枚举是单例模式的最佳实践),无论何时何处获取,下面的判断始终为真: // 基

  • Spring中单例和多例的深入理解

    Spring单例和多例的理解 1.什么是单例和多例 单例:所有请求用同一个对象来处理.通过单例模式,可以保证系统中一个类只有一个实例. 多例:每个请求用一个新的对象来处理. 2.Spring中的单例与多例 spring ioc容器的bean都是默认单例的,即spring依赖注入Bean实例默认是单例的. spring提供了5中scope,分别是singleton,prototype,request,session,global session,常用是前两种.点此查看官网介绍. 单例bean与多例

  • 浅谈Spring 的Controller 是单例or多例

    背景:今天写代码遇到一个Controller 中的线程安全问题,那么Spring 的Controller 是单例还是多例的呢?若为单例又如何保证并发安全呢? 一.面试回答 Spring管理的Controller,即加入@Controller 注入的类,默认是单例的,因此建议: 1.不要在Controller 中定义成员变量:(单例非线程安全,会导致属性重复使用) 2.若必须要在Controller 中定义一个非静态成员变量,则通过注解@Scope("prototype"),将其设置为多

  • 通过spring注解开发,简单测试单例和多例区别

    目录 通过spring注解开发,测试单例和多例区别 1.注解和配置两种用法形式 2.在spring框架中,scope作用域默认是单例的 3.实例 (1)多例: (2)单例(注解版) Spring中单例和多例的理解 1.什么是单例和多例 2.Spring中的单例与多例 单例bean与多例(原型)bean的区别: 3.单例的优势与劣势 优势: 劣势: 4.spring单例模式与线程安全: 如何解决线程安全问题? 5.单例如何变多例 通过spring注解开发,测试单例和多例区别 1.注解和配置两种用法

  • Java之Spring注解开发案例详解

    在Spring4之后,要使用注解开发,必须要保证aop的包导入了 使用注解需要导入context约束,增加注解的支持! <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance&

  • Mybatis示例讲解注解开发中的单表操作

    目录 Mybatis注解开发单表操作 MyBatis的常用注解 MyBatis的增删改查 注解开发总结 常用注解 配置映射关系 练习项目代码 Mybatis注解开发单表操作 MyBatis的常用注解 Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper映射文件了.我们先围绕一些基本的CRUD来学习,再学习复杂映射多表操作. 注解 说明 @Insert 实现新增 @Update 实现更新 @Delete 实现删除 @Select 实现查询 @Result 实现结果集封装 @Re

  • Spring注解开发@Bean和@ComponentScan使用案例

    组件注册 用@Bean来注册 搭建好maven web工程 pom加入spring-context,spring-core等核心依赖 创建实例类com.hjj.bean.Person, 生成getter,setter方法 public class Person { private String name; private int age; } 创建com.hjj.config.MainConfig @Configuration //告诉spring是一个配置类 public class Main

  • Spring注解开发生命周期原理解析

    生命周期 initMethod和destroyMethod Bean定义 public class Car { public Car() { System.out.println("car constructor"); } public void init(){ System.out.println("car init"); } public void destroy(){ System.out.println("car destroy"); }

  • Spring入门到精通之注解开发详解

    目录 Spring原始注解 DI 依赖注入的注解实现方式 Spring新注解 @Configuration @ComponentScan @Import @PropertySource @value @Bean Spring整合Junit 原始Junit测试Spring的问题 上述问题解决思路 Spring集成Junit步骤 Spring集成Junit代码实现 Spring原始注解 Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以

  • Spring纯注解开发模式让开发简化更简化

    目录 一.注解开发 二.注解定义Bean 三.衍生注解 四.纯注解开发模式 五.注解实现注入 1.自动装配 2.按名称注入 3.简单数据注入 4.读取properties配置文件 六.Spring整合MyBatis 一.注解开发 以前跟老韩学习SE时他就说: 注解本质是一个继承了Annotation 的特殊接口,其具体实现类是Java 运行时生成的动态代理类. 而我们通过反射获取注解时,返回的是Java 运行时生成的动态代理对象$Proxy1.通过代理对象调用自定义注解(接口)的方法,会最终调用

  • Spring超详细讲解注解开发

    目录 1.使用注解开发 1.1.Bean的实现 1.2.属性注入 1.3.衍生注解 1.4.自动装配注解 1.5.作用域 1.6.小结 2.基于Java类进行配置 1.使用注解开发 说明 在spring4之后,想要使用注解形式,必须得要引入aop的包 在配置文件当中,还得要引入一个context约束 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.spring

  • Java Spring详解如何配置数据源注解开发以及整合Junit

    目录 Spring数据源的配置 数据源(连接池)的作用 数据源的开发步骤 手动创建数据源 Spring注解开发 Spring原始注解 Spring新注解 Spring整合Junit Spring集成Junit步骤 Spring数据源的配置 数据源(连接池)的作用 数据源(连接池)是提高程序性能如出现的 事先实例化数据源,初始化部分连接资源 使用连接资源时从数据源中获取 使用完毕后将连接资源归还给数据源 常见的数据源(连接池):DBCP.C3PO.BoneCP.Druid等 数据源的开发步骤 1.

  • Spring注解@Configuration与@Bean注册组件的使用详解

    目录 原始Spring开发 Person.java pom.xml bean.xml PersonTest.java 注解Spring开发 原始Spring开发 Person.java 准备Person.java类: package com.jektong.spring; public class Person { private String name; private int age; public Person() { super(); } public Person(String nam

随机推荐