美文网首页
挠头~?加了@Scope(value="prototype")为

挠头~?加了@Scope(value="prototype")为

作者: 风中的白乌鸦 | 来源:发表于2020-01-10 16:52 被阅读0次

    挠头~?加了@Scope(value="prototype")为何还是单例?

    • 我们知道spring的Bean的作用域分为singleton,prototype,session,reques,默认作用域为singleton。
    • 但有些时候场景则要求创建新的bean,比如多线程连接服务器执行shell、SFTP服务等,若为单例bean,第一个线程执行完命令后close连接,其他的线程都没法玩了...
    • 作用域为prototype时,每次获取Bean都会有一个新的实例。
    例子
    @Component
    //配置bean作用域为多例
    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public class ShellUtil{
        private static Connection conn = new Connection();
    }
    
    @RestController
    public class ExecController {
    
        @Autowired
        private ShellUtil shell;
        
        @GetMapping("test")
        public void test(){
         System.out.println("Thaad-Name:"+Thread.currentThread().getName());
         System.out.println("ObjHashCode:"+shell.hashCode());
      }
    )
        
    
    执行结果:

    拿到的还是同一个bean

    ???挠头~

    分析:上面的例子中虽然ShellUtil虽然设置为了多例,但是他被单例对象ExecController依赖,ExecController在初始化Bean的时候ShellUtil已经注入,并且注入时只创建一次。

    方法1
    • 让ExecController也变成多例对象
    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @RestController
    public class ExecController {
    
        @Autowired
        private ShellUtil shell;
        
        @GetMapping("test")
        public void test(){
         System.out.println("Thaad-Name:"+Thread.currentThread().getName());
         System.out.println("ObjHashCode:"+shell.hashCode());
      }
    )
    
    执行结果:

    效果是达到了,但是ExecController失去了单例的优势,若bean的调用链很长则每个被依赖的bean都需要配置为多例。

    方法2
    • 通过getBean方法直接从bean工厂获取对象,而不是使用自动注入的方式。
    @RestController
    public class ExecController {
    
        /** 
        * 获取spring上下文 
        */
        @Autowired
        private WebApplicationContext applicationContext;
        
        @GetMapping("test")
         public void test() {
         System.out.println("Thaad-Name:"+Thread.currentThread().getName());
         System.out.println("ObjHashCode:"+shell.hashCode());
      }
    )
    
    
    执行结果:
    方法3
    • 对于此类连接工具我们只关心Connection对象是否多例,因此可以使用TheardLocal装配Connection,让每个线程拥有单独的Connection对象,互不干扰。
    @Component
    public class ShellUtil{
        //使用ThreadLocal装配Connection对象
        private static ThreadLocal<Connection> conn = new ThreadLocal<>();
       
        public void close() {    
        if (conn != null) { 
            conn.get().close()
        //注意:连接关闭后,记得回收ThreadLocal对象
            conn.remove();            
        }
    }
    
    
    
    结论:
    • 使用@Scope(value="prototype")配置多例时要确认是否存在Bean依赖链,若存在则使用getBean的方式,
    • 使用连接类工具可以用TheardLocal装配Connection对象,保证现在拥护单独对象。
    参考链接

    https://www.cnblogs.com/heyanan/p/12054840.html

    相关文章

      网友评论

          本文标题:挠头~?加了@Scope(value="prototype")为

          本文链接:https://www.haomeiwen.com/subject/rvcmactx.html