美文网首页
挠头~?加了@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