@Scope注解的作用詳解
在看公司hbase項目config的配置的時候發現有這個注解,順手百度了一下....然后發現我基礎好差(一般來說,數據庫連接還是使用連接池的,這種多例連接數據庫太耗資源了)
參考:
定義:
@Scope注解是springIoc容器中的一個作用域,在 Spring IoC 容器中具有以下幾種作用域:基本作用域singleton(單例)、prototype(多例),Web 作用域(reqeust、session、globalsession),自定義作用域
singleton
單例模式(默認):全局有且僅有一個實例prototype
原型模式:每次獲取Bean的時候會有一個新的實例request
: request表示該針對每一次HTTP請求都會產生一個新的bean,同時該bean僅在當前HTTP request內有效session
:session作用域表示該針對每一次HTTP請求都會產生一個新的bean,同時該bean僅在當前HTTP session內有效global session
: global session作用域類似於標准的HTTP Session作用域,不過它僅僅在基於portlet的web應用中才有意義
直接使用字符串容易出問題,spring有默認的參數:
ConfigurableBeanFactory.SCOPE_PROTOTYPE
,即“prototype”ConfigurableBeanFactory.SCOPE_SINGLETON
,即“singleton”WebApplicationContext.SCOPE_REQUEST
,即“request”WebApplicationContext.SCOPE_SESSION
,即“session”
使用
直接在bean對象方法上增加@Scope注解就可以
/**
* 定義一個bean對象
* @return
*/
@Scope //@Scope(value = "prototype")
@Bean(value = "user0", name = "user0", initMethod = "initUser", destroyMethod = "destroyUser")
public User getUser() {
System.out.println("創建user實例");
return new User("張三", 26);
}
@Scope注解默認的singleton實例,singleton實例的意思不管你使用多少次在springIOC容器中只會存在一個實例,演示如下只打印了一次創建實例:
AnnotationConfigApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(MainConfig.class);
User bean2 = applicationContext2.getBean(User.class);
System.out.println("實例1 === "+bean2);
User bean3 = applicationContext2.getBean(User.class);
System.out.println("實例2 === "+bean3);
//運行結果
創建user實例
實例1 === User [userName=張三, age=26]
實例2 === User [userName=張三, age=26]
若是改為@Scope(value="prototype")
//運行結果
創建user實例
實例1 === User [userName=張三, age=26]
創建user實例
實例2 === User [userName=張三, age=26]
使用場景
幾乎90%以上的業務使用singleton單實例就可以,所以spring默認的類型也是singleton,singleton雖然保證了全局是一個實例,對性能有所提高,但是如果實例中有非靜態變量時,會導致線程安全問題,共享資源的競爭。
當設置為prototype時:每次連接請求,都會生成一個bean實例,也會導致一個問題,當請求數越多,性能會降低,因為創建的實例,導致GC頻繁,gc時長增加
多例
直接在controller層設置多例
在controller類上設置多例,正常
單例調用多例
在controller是默認的單例,service層是多例的時候,多例失效
雖然Service是多例的,但是Controller是單例的。如果給一個組件加上
@Scope("prototype")
注解,每次請求它的實例,spring的確會給返回一個新的。問題是這個多例對象Service是被單例對象Controller依賴的。而單例服務Controller初始化的時候,多例對象Service就已經注入了;當你去使用Controller的時候,Service也不會被再次創建了(注入時創建,而注入只有一次)。
- 方法1: 不使用
@Autowired
,每次調用多例的時候,直接調用bean - 方法2:spring的解決方法:設置
proxyMode
,每次請求的時候實例化
@Scope
注解添加了一個proxyMode的屬性,有兩個值ScopedProxyMode.INTERFACES
和ScopedProxyMode.TARGET_CLASS
,前一個表示表示Service是一個接口,后一個表示Service是一個類。
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
案例
- 創建bean
@Bean
//@Scope標明模式,默認單例模式. prototype多例模式
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
public User hbaseConnection() {
User user = new User((int) (Math.random() * 100));
System.out.println("調用了hbaseConnection,User:id:" + user.getId());
return user;
}
- controller引入並調用
@RestController
@RequestMapping("/demo1")
public class DemoController {
@Autowired
private User hbaseConnection;
@RequestMapping("/test")
public void test(){
System.out.println("test:user:id:"+hbaseConnection.getId());
System.out.println("test:user:id2:"+hbaseConnection.getId());
}
}
調用接口,結果為
調用了hbaseConnection,User:id:7
test:user:id:7
調用了hbaseConnection,User:id:2
test:user:id2:2
每調用一次注入的對象,就會重新創建一個