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,证明线程安全;

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值