Spring IoC管理的bean預設scope為singleton。若bean為有狀態(stateful)時應設為prototype才能使每一次產生的bean實例不同。而當singleton bean要取得依賴的prototype bean的實例時,可使用@Lookup
annotation來取得不同的prototype bean的實例。
例如下面是singleton bean SingletonBean
,其依賴於prototype bean PrototypeBean
並以@Autowired
注入至其成員變數prototype
。PrototypeBean
為有狀態count
的bean
SingletonBean.count()
被呼叫時會呼叫PrototypeBean.addCount()
累加狀態count
的值並回傳。
SingletonBean
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
@Component
public class SingletonBean {
@Autowired
private PrototypeBean prototypeBean;
public int count() {
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
PrototypeBean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class PrototypeBean {
private int count;
public void addCount() {
count++;
}
public int getCount() {
return count;
}
}
下面測試方法中呼叫了兩次SingletonBean.count()
,可看到回傳的count
的值是被累加的,代表SingletonBean.prototypeBean
每次呼叫時都是同一個實例。
SingletonBeanTests
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class SingletonBeanTests {
@Autowired
private SingletonBean singletonBean;
@Test
public void getSamePrototypeBeanCount() {
int count = singletonBean.count();
System.out.println(count); // 1
Assertions.assertEquals(1, count);
count = singletonBean.count();
System.out.println(count); // 2
Assertions.assertEquals(2, count);
}
}
若需要每次SingletonBean.count()
使用的都是新的PrototypeBean
實例,則可改用@Lookup
的方式取得。
修改SingletonBean
如下,新增一返回型態為PrototypeBean
的公開方法並在前面加上@Lookup
,則執行時Spring會利用CGLIB技術產生SingletonBean
的子類別並覆寫此方法來返回指定形態的bean實例。
SingletonBean
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
@Component
public class SingletonBean {
// @Autowired
// private PrototypeBean prototypeBean;
public int count() {
PrototypeBean prototypeBean = getPrototypeBean();
prototypeBean.addCount();
return prototypeBean.getCount();
}
@Lookup
public PrototypeBean getPrototypeBean() { // this method wiil be override by Spring CGLIB generated subclass at runtime and return correspondent prototype bean instance
return null;
}
}
下面測試方法中呼叫了兩次SingletonBean.count()
,可看到回傳的count
的值沒有累加,代表SingletonBean.count()
每次被呼叫時取得的PrototypeBean
實例是不同的。
SingletonBeanTests
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class SingletonBeanTests {
@Autowired
private SingletonBean singletonBean;
@Test
public void getDifferentPrototypeBeanCount() {
int count = singletonBean.count();
System.out.println(count); // 1
Assertions.assertEquals(1, count);
count = singletonBean.count();
System.out.println(count); // 1
Assertions.assertEquals(1, count);
}
}
注意@Lookup
方法無法在配置類中以@Bean
註冊的bean中使用,因為bean實例不是由容器產生。
例如把SingletonBean
改以@Bean
的方式註冊。
DemoApplication
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
@Bean
public SingletonBean getSingletonBean() {
return new SingletonBean();
}
}
則呼叫getPrototypeBean()
返回null。
SingletonBean
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
//@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
//@Component
public class SingletonBean {
// @Autowired
// private PrototypeBean prototypeBean;
public int count() {
PrototypeBean prototypeBean = getPrototypeBean(); // NullPointerException
prototypeBean.addCount();
return prototypeBean.getCount();
}
@Lookup
public PrototypeBean getPrototypeBean() {
return null;
}
}
沒有留言:
張貼留言