专业的企业网站建设,刚做网站做什么网站好点,百度目前的推广方法,域名与空间购买后怎么做网站公众号关注“程序员二哥”#xff0c;设为‘星标’#xff0c;带你学习更多的知识。本文内容 详细介绍5中bean的sope及使用注意点自定义作用域的实现应用中#xff0c;有时候我们需要一个对象在整个应用中只有一个#xff0c;有些对象希望每次使用的时候都重新创建一个设为‘星标’带你学习更多的知识。本文内容 详细介绍5中bean的sope及使用注意点自定义作用域的实现应用中有时候我们需要一个对象在整个应用中只有一个有些对象希望每次使用的时候都重新创建一个spring对我们这种需求也提供了支持在spring中这个叫做bean的作用域xml中定义bean的时候可以通过scope属性指定bean的作用域如bean id class scope作用域 / spring容器中scope常见的有5种下面我们分别来介绍一下。singleton 当scope的值设置为singleton的时候整个spring容器中只会存在一个bean实例通过容器多次查找bean的时候(调用BeanFactory的getBean方法或者bean之间注入依赖的bean对象的时候)返回的都是同一个bean对象singleton是scope的默认值所以spring容器中默认创建的bean对象是单例的通常spring容器在启动的时候会将scope为singleton的bean创建好放在容器中(有个特殊的情况当bean的lazy被设置为true的时候表示懒加载那么使用的时候才会创建)用的时候直接返回。案例bean xml配置bean idsingletonBean classcom.javacode2018.lesson001.demo4.BeanScopeModel scopesingleton constructor-arg index0 valuesingleton/beanBeanScopeModel代码package com.javacode2018.lesson001.demo4;public class BeanScopeModel { public BeanScopeModel(String beanScope) { System.out.println(String.format(create BeanScopeModel,{sope%s},{this%s}, beanScope, this)); }}上面构造方法中输出了一段文字一会我们可以根据输出来看一下这个bean什么时候创建的是从容器中获取bean的时候创建的还是容器启动的时候创建的。测试用例package com.javacode2018.lesson001.demo4;import org.junit.Before;import org.junit.Test;import org.springframework.context.support.ClassPathXmlApplicationContext;/** * 公众号路人甲Java工作10年的前阿里P7分享Java、算法、数据库方面的技术干货坚信用技术改变命运让家人过上更体面的生活! * * bean作用域 */public class ScopeTest { ClassPathXmlApplicationContext context; Before public void before() { System.out.println(spring容器准备启动.....); //1.bean配置文件位置 String beanXml classpath:/com/javacode2018/lesson001/demo4/beans.xml; //2.创建ClassPathXmlApplicationContext容器给容器指定需要加载的bean配置文件 this.context new ClassPathXmlApplicationContext(beanXml); System.out.println(spring容器启动完毕); } /** * 单例bean */ Test public void singletonBean() { System.out.println(---------单例bean每次获取的bean实例都一样---------); System.out.println(context.getBean(singletonBean)); System.out.println(context.getBean(singletonBean)); System.out.println(context.getBean(singletonBean)); }}上面代码中before方法上面有Before注解这个是junit提供的功能这个方法会在所有Test标注的方法之前之前运行before方法中我们对容器进行初始化并且在容器初始化前后输出了一段文字。上面代码中singletonBean方法中3次获取singletonBean对应的bean。运行测试用例spring容器准备启动.....create BeanScopeModel,{sopesingleton},{thiscom.javacode2018.lesson001.demo4.BeanScopeModele874448}spring容器启动完毕---------单例bean每次获取的bean实例都一样---------com.javacode2018.lesson001.demo4.BeanScopeModele874448com.javacode2018.lesson001.demo4.BeanScopeModele874448com.javacode2018.lesson001.demo4.BeanScopeModele874448结论从输出中得到2个结论前3行的输出可以看出BeanScopeModel的构造方法是在容器启动过程中调用的说明这个bean实例在容器启动过程中就创建好了放在容器中缓存着最后3行输出的是一样的说明返回的是同一个bean对象单例bean使用注意单例bean是整个应用共享的所以需要考虑到线程安全问题之前在玩springmvc的时候springmvc中controller默认是单例的有些开发者在controller中创建了一些变量那么这些变量实际上就变成共享的了controller可能会被很多线程同时访问这些线程并发去修改controller中的共享变量可能会出现数据错乱的问题所以使用的时候需要特别注意。prototype 如果scope被设置为prototype类型的了表示这个bean是多例的通过容器每次获取的bean都是不同的实例每次获取都会重新创建一个bean实例对象。案例bean xml配置bean idprototypeBean classcom.javacode2018.lesson001.demo4.BeanScopeModel scopeprototype constructor-arg index0 valueprototype/bean新增一个测试用例ScopeTest中新增一个方法/** * 多例bean */Testpublic void prototypeBean() { System.out.println(---------单例bean每次获取的bean实例都一样---------); System.out.println(context.getBean(prototypeBean)); System.out.println(context.getBean(prototypeBean)); System.out.println(context.getBean(prototypeBean));}运行测试用例spring容器准备启动.....spring容器启动完毕---------单例bean每次获取的bean实例都一样---------create BeanScopeModel,{sopeprototype},{thiscom.javacode2018.lesson001.demo4.BeanScopeModel289d1c02}com.javacode2018.lesson001.demo4.BeanScopeModel289d1c02create BeanScopeModel,{sopeprototype},{thiscom.javacode2018.lesson001.demo4.BeanScopeModel22eeefeb}com.javacode2018.lesson001.demo4.BeanScopeModel22eeefebcreate BeanScopeModel,{sopeprototype},{thiscom.javacode2018.lesson001.demo4.BeanScopeModel17d0685f}com.javacode2018.lesson001.demo4.BeanScopeModel17d0685f结论输出中可以看出容器启动过程中并没有去创建BeanScopeModel对象3次获取prototypeBean得到的都是不同的实例每次获取的时候才会去调用构造方法创建bean实例。多例bean使用注意多例bean每次获取的时候都会重新创建如果这个bean比较复杂创建时间比较长会影响系统的性能这个地方需要注意。下面要介绍的3个request、session、application都是在spring web容器环境中才会有的。request 当一个bean的作用域为request表示在一次http请求中一个bean对应一个实例对每个http请求都会创建一个bean实例request结束的时候这个bean也就结束了request作用域用在spring容器的web环境中这个以后讲springmvc的时候会说spring中有个web容器接口WebApplicationContext这个里面对request作用域提供了支持配置方式bean id class scoperequest /session 这个和request类似也是用在web环境中session级别共享的bean每个会话会对应一个bean实例不同的session对应不同的bean实例springmvc中我们再细说。bean id class scopesession /application 全局web应用级别的作用于也是在web环境中使用的一个web应用程序对应一个bean实例通常情况下和singleton效果类似的不过也有不一样的地方singleton是每个spring容器中只有一个bean实例一般我们的程序只有一个spring容器但是一个应用程序中可以创建多个spring容器不同的容器中可以存在同名的bean但是sopeaplication的时候不管应用中有多少个spring容器这个应用中同名的bean只有一个。bean id class scopeapplication /自定义scope 有时候spring内置的几种sope都无法满足我们的需求的时候我们可以自定义bean的作用域。自定义Scope 3步骤第1步实现Scope接口我们来看一下这个接口定义package org.springframework.beans.factory.config;import org.springframework.beans.factory.ObjectFactory;import org.springframework.lang.Nullable;public interface Scope { /** * 返回当前作用域中name对应的bean对象 * name需要检索的bean的名称 * objectFactory如果name对应的bean在当前作用域中没有找到那么可以调用这个ObjectFactory来创建这个对象 **/ Object get(String name, ObjectFactory objectFactory); /** * 将name对应的bean从当前作用域中移除 **/ Nullable Object remove(String name); /** * 用于注册销毁回调如果想要销毁相应的对象,则由Spring容器注册相应的销毁回调而由自定义作用域选择是不是要销毁相应的对象 */ void registerDestructionCallback(String name, Runnable callback); /** * 用于解析相应的上下文数据比如request作用域将返回request中的属性。 */ Nullable Object resolveContextualObject(String key); /** * 作用域的会话标识比如session作用域将是sessionId */ Nullable String getConversationId();}第2步将自定义的scope注册到容器需要调用org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope的方法看一下这个方法的声明/*** 向容器中注册自定义的Scope*scopeName作用域名称* scope作用域对象**/void registerScope(String scopeName, Scope scope);第3步使用自定义的作用域定义bean的时候指定bean的scope属性为自定义的作用域名称。案例需求下面我们来实现一个线程级别的bean作用域同一个线程中同名的bean是同一个实例不同的线程中的bean是不同的实例。实现分析需求中要求bean在线程中是贡献的所以我们可以通过ThreadLocal来实现ThreadLocal可以实现线程中数据的共享。下面我们来上代码。ThreadScopepackage com.javacode2018.lesson001.demo4;import org.springframework.beans.factory.ObjectFactory;import org.springframework.beans.factory.config.Scope;import org.springframework.lang.Nullable;import java.util.HashMap;import java.util.Map;import java.util.Objects;/** * 自定义本地线程级别的bean作用域不同的线程中对应的bean实例是不同的同一个线程中同名的bean是同一个实例 */public class ThreadScope implements Scope { public static final String THREAD_SCOPE thread;//1 private ThreadLocal beanMap new ThreadLocal() {Overrideprotected Object initialValue() {return new HashMap(); } };Overridepublic Object get(String name, ObjectFactory objectFactory) { Object bean beanMap.get().get(name);if (Objects.isNull(bean)) { bean objectFactory.getObject(); beanMap.get().put(name, bean); }return bean; }NullableOverridepublic Object remove(String name) {return this.beanMap.get().remove(name); }Overridepublic void registerDestructionCallback(String name, Runnable callback) {//bean作用域范围结束的时候调用的方法用于bean清理 System.out.println(name); }NullableOverridepublic Object resolveContextualObject(String key) {return null; }NullableOverridepublic String getConversationId() {return Thread.currentThread().getName(); }}1定义了作用域的名称为一个常量thread可以在定义bean的时候给scope使用BeanScopeModelpackage com.javacode2018.lesson001.demo4;public class BeanScopeModel { public BeanScopeModel(String beanScope) { System.out.println(String.format(线程:%s,create BeanScopeModel,{sope%s},{this%s}, Thread.currentThread(), beanScope, this)); }}上面的构造方法中会输出当前线程的信息到时候可以看到创建bean的线程。bean配置文件beans-thread.xml内容?xml version1.0 encodingUTF-8?beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd bean idthreadBean classcom.javacode2018.lesson001.demo4.BeanScopeModel scopethread constructor-arg index0 valuethread/ beanbeans注意上面的scope是我们自定义的值为thread测试用例package com.javacode2018.lesson001.demo4;import org.springframework.context.support.ClassPathXmlApplicationContext;import java.util.concurrent.TimeUnit;/** * 公众号路人甲Java工作10年的前阿里P7分享Java、算法、数据库方面的技术干货坚信用技术改变命运让家人过上更体面的生活! * * 自定义scope */public class ThreadScopeTest { public static void main(String[] args) throws InterruptedException { String beanXml classpath:/com/javacode2018/lesson001/demo4/beans-thread.xml; //手动创建容器 ClassPathXmlApplicationContext context new ClassPathXmlApplicationContext(); //设置配置文件位置 context.setConfigLocation(beanXml); //启动容器 context.refresh(); //向容器中注册自定义的scope context.getBeanFactory().registerScope(ThreadScope.THREAD_SCOPE, new ThreadScope());//1 //使用容器获取bean for (int i 0; i 2; i) { //2 new Thread(() - { System.out.println(Thread.currentThread() , context.getBean(threadBean)); System.out.println(Thread.currentThread() , context.getBean(threadBean)); }).start(); TimeUnit.SECONDS.sleep(1); } }}注意上面代码重点在1这个地方向容器中注册了自定义的ThreadScope。2创建了2个线程然后在每个线程中去获取同样的bean 2次然后输出我们来看一下效果。运行输出线程:Thread[Thread-1,5,main],create BeanScopeModel,{sopethread},{thiscom.javacode2018.lesson001.demo4.BeanScopeModel4049d530}Thread[Thread-1,5,main],com.javacode2018.lesson001.demo4.BeanScopeModel4049d530Thread[Thread-1,5,main],com.javacode2018.lesson001.demo4.BeanScopeModel4049d530线程:Thread[Thread-2,5,main],create BeanScopeModel,{sopethread},{thiscom.javacode2018.lesson001.demo4.BeanScopeModel87a76da}Thread[Thread-2,5,main],com.javacode2018.lesson001.demo4.BeanScopeModel87a76daThread[Thread-2,5,main],com.javacode2018.lesson001.demo4.BeanScopeModel87a76da从输出中可以看到bean在同样的线程中获取到的是同一个bean的实例不同的线程中bean的实例是不同的。总结 spring容器自带的有2种作用域分别是singleton和prototype还有3种分别是spring web容器环境中才支持的request、session、applicationsingleton是spring容器默认的作用域一个spring容器中同名的bean实例只有一个多次获取得到的是同一个bean单例的bean需要考虑线程安全问题prototype是多例的每次从容器中获取同名的bean都会重新创建一个多例bean使用的时候需要考虑创建bean对性能的影响一个应用中可以有多个spring容器自定义scope 3个步骤实现Scope接口将实现类注册到spring容器使用自定义的sope案例源码 链接https://pan.baidu.com/s/1p6rcfKOeWQIVkuhVybzZmQ 提取码zr99Spring系列 Spring系列第1篇为何要学springSpring系列第2篇控制反转(IoC)与依赖注入(DI)Spring系列第3篇Spring容器基本使用及原理Spring系列第4篇xml中bean定义详解(-)Spring系列第5篇创建bean实例这些方式你们都知道更多好文章 Java高并发系列(共34篇)MySql高手系列(共27篇)Maven高手系列(共10篇)Mybatis系列(共12篇)聊聊db和缓存一致性常见的实现方式接口幂等性这么重要它是什么怎么实现泛型有点难度会让很多人懵逼那是因为你没有看这篇文章感谢大家的阅读也欢迎您把这篇文章分享给更多的朋友一起阅读谢谢﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌﹌如果觉得文章还不错大家可以扫码点个关注和你一起成长学习更多知识。