IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring BeanScope -> 正文阅读

[Java知识库]Spring BeanScope

1. BeanScope

1.1 Singleton

单例,在Bean容器启动初始化后,SingletonBean只会初始化一次;因此对象是唯一的
查看代码org.springframework.beans.factory.support.DefaultSingletonBeanRegistry# registerSingleton

	@Override
	public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
		Assert.notNull(beanName, "Bean name must not be null");
		Assert.notNull(singletonObject, "Singleton object must not be null");
		synchronized (this.singletonObjects) {
			Object oldObject = this.singletonObjects.get(beanName);
			// 可以剔除旧的文档
			if (oldObject != null) {
				throw new IllegalStateException("Could not register object [" + singletonObject +
						"] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
			}
			addSingleton(beanName, singletonObject);
		}
	}

1.2 Prototype

可以使用@Scope,指定作用scopeName ConfigurableBeanFactory.SCOPE_SINGLETON,ConfigurableBeanFactory.SCOPE_PROTOTYPE,默认情况下使用的是Singleton;在每一次调用的时候都会创建一个新的对象,和Singleton形成明确的对比;

1.3 Singleton和Prototype区别

区别一:
Signleton Bean无论依赖查找还是依赖注入,均为同一对象
Prototype Bean无论依赖查找还是依赖注入,均为新生对象

区别二:
如果依赖注入集合类型的对象,Singleton Bean和Prototype Bean均会存在一个
Prototype Bean 有别于其他的Prototype Bean

区别三:
无论是Singleton Bean 还是Prototype Bean均会执行初始化方法回调
但是只有Singleton Bean会执行销毁方法回调

注重讨论区别三:
Singleton对象会进行初始化和销毁,但是Propototype只会进行初始化
在这里插入图片描述
但是如果想要执行销毁操作,可以通过BeanFactoryPostProcessor操作;

	@Autowired
    private ConfigurableListableBeanFactory beanFactory;  // Resolvable Dependency
     // 在类上实现了DisposableBean接口
     public void destroy() throws Exception {
        System.out.println("当前 BeanScopeDemo正在销毁中 ");
        this.prototypeUser.destroy();
        this.prototypeUser2.destroy();
        // 获取Prototype
        for (Map.Entry<String, User> entry : this.users.entrySet()) {
            String beanName = entry.getKey();
            // 获取到BeanDefinition,对Bean的Singleton属性进行判断,如果需要销毁,直接.destroy()即可;
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
            if (!beanDefinition.isSingleton()) {
                entry.getValue().destroy();
            }
        }
    }

扩展

可以通过BeanPostProcesser解决,但是一般都不使用此方法,仅作参考学习;实现BeanPostProcessor中的postProcessAfterInitialization方法,在初始化后对Bean进行操作,如果取消return bean操作,这个Bean在初始化后就会消失;

		// 添加生命周期管理
        beanContext.addBeanFactoryPostProcessor(beanFactory -> {
            beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
                @Override
                public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                    System.out.printf("%s Bean名称:%s 在初始化后回调。。。%n", bean.getClass().getName(), beanName);
                    return bean;
                }
            });
        });

1.4 自定义Scope

实现一个线程级别的Scope,每次新的线程调用的时候,都会创建一个实例

  1. 实现Spring的Scope接口
package edu.ahau.thinking.in.spring.ioc.scope;

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
import org.springframework.core.NamedThreadLocal;

import java.util.HashMap;
import java.util.Map;

/**
 * ThreadLocal 级别 Scope
 *
 * @author zhangxuna
 * @date 2021-10-24 21:14
 */
public class ThreadLocalScope implements Scope {
	// 定义一个域名
    public static final String SCOPE_NAME = "thread-local";
	// 定义个线程Bean,里面是beanName和bean
    private final NamedThreadLocal<Map<String, Object>> threadLocal = new NamedThreadLocal("thread-local-scope") {
    	// 防止空指针
        @Override
        protected Map<String, Object> initialValue() {
            return new HashMap<String, Object>();
        }
    };
    /* 获取 */
    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        Map<String, Object> context = getContext();
        Object object = context.get(name);

        if (object == null) {
            object = objectFactory.getObject();
            context.put(name, object);
        }
        return object;
    }

    private Map<String, Object> getContext() {
        return threadLocal.get();
    }

    /* 删除 */
    @Override
    public Object remove(String name) {
        Map<String, Object> context = getContext();
        return context.remove(name);
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {

    }

    @Override
    public Object resolveContextualObject(String key) {
        Map<String, Object> context = getContext();
        return context.get(key);
    }

    @Override
    public String getConversationId() {
        Thread thread = Thread.currentThread();

        return String.valueOf(thread.getId());
    }
}

  1. 使用addBeanFactoryPostProcessor注册这个Scope
    2.1 单线程下使用:
annotationConfigApplicationContext.addBeanFactoryPostProcessor(beanFactory -> {
                // 注册自定义Scope
                beanFactory.registerScope(ThreadLocalScope.SCOPE_NAME, new ThreadLocalScope());
            });
public class ThreadLocalScopeDemo {

    @Bean
    @Scope(ThreadLocalScope.SCOPE_NAME)
    public User user() {
        User user = new User();
        user.setAge((int) Thread.currentThread().getId());
        return user;
    }

    public static void main(String[] args) {
        try (AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext()) {
            annotationConfigApplicationContext.register(ThreadLocalScopeDemo.class);
            annotationConfigApplicationContext.addBeanFactoryPostProcessor(beanFactory -> {
                // 注册自定义Scope
                beanFactory.registerScope(ThreadLocalScope.SCOPE_NAME, new ThreadLocalScope());
            });
            annotationConfigApplicationContext.refresh();
//            for (int i = 0; i < 5; i++) {
//                new Thread(()->{
//                    System.out.println(annotationConfigApplicationContext.getBean(User.class).hashCode());
//                }).start();
//            }
            System.out.println(annotationConfigApplicationContext.getBean(User.class).hashCode());
            System.out.println(annotationConfigApplicationContext.getBean(User.class).hashCode());

        }
    }
}

20952182
20952182

2.2 多线程下使用

	public static void main(String[] args) {
        try (AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext()) {
            annotationConfigApplicationContext.register(ThreadLocalScopeDemo.class);
            annotationConfigApplicationContext.addBeanFactoryPostProcessor(beanFactory -> {
                // 注册自定义Scope
                beanFactory.registerScope(ThreadLocalScope.SCOPE_NAME, new ThreadLocalScope());
            });
            annotationConfigApplicationContext.refresh();
            for (int i = 0; i < 5; i++) {
                new Thread(()->{
                    System.out.println("线程号:"+Thread.currentThread().getId()+"--->"+annotationConfigApplicationContext.getBean(User.class).hashCode());
                }).start();
            }
        }
    }

线程号:23—>21028764
线程号:24—>21032245
线程号:22—>21025283
线程号:21—>21021802
线程号:25—>21035726

1.5 改善SimpleDateFormat

SimpleDateFormat是多线程不安全的;

public class SimpleDateFormatScope {
    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private final static ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 60, TimeUnit.MINUTES, new LinkedBlockingQueue<>(100000));

    public static void main(String[] args) {
        while (true) {
            executor.execute(() -> {
                String format = simpleDateFormat.format(new Date());
                Date parseDate = null;
                try {
                        parseDate = simpleDateFormat.parse(format);

                } catch (ParseException e) {
                    e.printStackTrace();
                }
                String dateString2 = simpleDateFormat.format(parseDate);
                System.out.println(format.equals(dateString2));
            });
        }
    }
}

改造以后:

  • 自定义一个Scope
public class SimpleDateFormatScope implements Scope {
    public static final String SCOPE_NAME = "simpleDateFormatScope";

    private static final NamedThreadLocal<Map<String, Object>> simpleDateFormatObject = new NamedThreadLocal<Map<String, Object>>("SimpleDateFormatScope") {
        @Override
        protected Map<String, Object> initialValue() {
            Map<String, Object> map = new HashMap<>();
            map.put("simpleDateFormat", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") );
            return map;
        }
    };


    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        Map<String, Object> context = getContext();
        Object object = context.get(name);
        if (object == null) {
            context.put("simpleDateFormat", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") );
        }
        return object;
    }

    private Map<String, Object> getContext() {
        return simpleDateFormatObject.get();
    }

    @Override
    public Object remove(String name) {
        return null;
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {

    }

    @Override
    public Object resolveContextualObject(String key) {
        return null;
    }

    @Override
    public String getConversationId() {
        return null;
    }
}

  • 创建测试类
public class SimpleDateFormateTest {
   @Bean("simpleDateFormat")
   @Scope(SimpleDateFormatScope.SCOPE_NAME)
   public SimpleDateFormat getSimpleDateFormat() {
       return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
   }
   public static void main(String[] args) {
       try (AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext()) {
         annotationConfigApplicationContext.register(SimpleDateFormateTest.class);
         annotationConfigApplicationContext.addBeanFactoryPostProcessor(beanFactory -> beanFactory.registerScope(SimpleDateFormatScope.SCOPE_NAME, new SimpleDateFormatScope()));
         annotationConfigApplicationContext.refresh();
           for (int i = 0; i < 500; i++) {
               new Thread(()->{
                   SimpleDateFormat simpleDateFormat = annotationConfigApplicationContext.getBean(SimpleDateFormat.class);
                   String format = simpleDateFormat.format(new Date());
                   Date parseDate = null;
                   try {
                       parseDate = simpleDateFormat.parse(format);
                   } catch (ParseException e) {
                       e.printStackTrace();
                   }
                   String dateString2 = simpleDateFormat.format(parseDate);
                   System.out.println(format.equals(dateString2));
               }).start();
           }
       }
   }
}

结果全是true,证明线程安全;

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-10-25 12:24:25  更:2021-10-25 12:25:59 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 0:28:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码