Spring入門之五-------SpringIoC之通過註解實現
一、準備工作
創建一個Class註解@Configuration,如下例子:
@Configuration // 該註解可理解為將當前class等同於一個xml文件 @ComponentScan("com.imooc.springClass5.annotation") // 開啟包掃描 public class BeanConfiguration { }
我們創建了一個Class(類名可隨意)並註解了@Configuration,這樣可以將該Class看做一個spring的xml文件。同時我們增加了@ComponentScan註解開啟了包掃描,在掃描包及其子包下面的所有被註解了@Component、@Controller、@Service、@Repository的Class會自動被實例化。
測試代碼類似之前通過加載xml格式文件:
@Test public void testBean() throws Exception { final AbstractApplicationContext context = new AnnotationConfigApplicationContext(BeanConfiguration.class); // 省略其他代碼... }
二、通過構造方法實例化Bean
直接在Class上面註解@Component即可,當然也可以註解@Controller、@Service、@Repository。@Component為通用型註解,@Controller、@Service、@Repository
@Component // 通過構造方法實例化bean public class Bean1 { }
@Component // 通過構造方法實例化bean public class Bean2 { private final Bean1 bean1;
@Autowired // 自動註入Bean1的實例 public Bean2(Bean1 bean1) { this.bean1 = bean1;} }
在上面的兩個例子中,Bean1有默認構造方法,則Spring會通過默認構造方法實例化Bean1;Bean2的構造方法包含參數Bean1,所以Spring在實例化Bean2的時候回在IoC容器中尋找Class為Bean1的實例並註入到該構造方法中以完成對Bean2的實例化。其中:@Autowired表示自動註入。
三、註入Bean
1. 通過方法註入Bean
(1)可通過構造方法註入Bean,如上面Bean2的例子
(2)可通過Set方法註入Bean,例如:
@Component // 通過構造方法實例化bean public class Bean2 { private Bean1 bean1; @Autowired // 自動註入Bean1的實例 public void setBean1(Bean1 bean1) { this.bean1 = bean1; } // get方法省略...... }
2. 通過屬性註入Bean
@Component // 通過構造方法實例化bean public class Bean2 { @Autowired // 自動註入Bean1的實例 private Bean1 bean1; // get/set方法省略...... }
3. 集合類型Bean的註入
(1)直接註入集合實例
@Component // 通過構造方法實例化bean public class Bean { private List<String> stringList; private Map<String, String> stringMap; public List<String> getStringList() { return stringList; } @Autowired // 通過set方法註入bean public void setStringList(List<String> stringList) { this.stringList = stringList; } public Map<String, String> getStringMap() { return stringMap; } @Autowired // 通過set方法註入bean public void setStringMap(Map<String, String> stringMap) { this.stringMap = stringMap; } }
當然,同時也需要在IoC容器中存在List的Bean和Map的Bean,那麽我們怎麽實例化這兩個Bean呢?修改最開始我們創建的BeanConfiguration即可:
@Configuration // 該註解可理解為將當前class等同於一個xml文件 @ComponentScan("com.imooc.springClass5.annotation") // 開啟包掃描 public class BeanConfiguration { @Bean() // 實例化一個List public List<String> stringList() { List<String> list = new ArrayList<String>(); list.add("aaaaa"); list.add("bbbbb"); return list; } @Bean() // 實例化一個Map public Map<String, String> stringMap() { Map<String, String> map = new HashMap<String, String>(); map.put("aaa", "111"); map.put("bbb", "222"); return map; } }
通常情況下,@Bean用來實例化那些我們不能修改源碼的Class,或者需要多次實例化的Class。而@Component系列的四個註解用來實例化我們自己創建的且只需要一次實例化的Class。
那麽小夥伴會問了,在上面的例子中,如果我們實例化了多個List或多個Map,Spring在為Bean註入的時候會給我註入哪個呢?答案是:Spring會報錯給你看!解決辦法就是在通過@Bean和@Component系列實例化Bean的時候指定BeanId,通過@Autowired註入Bean的時候同時通過@Qualifier指定要註入的BeanId。那麽上面的例子可以改成:
@Component // 通過構造方法實例化bean public class Bean { // ... 省略部分代碼 @Autowired // 通過set方法註入bean @Qualifier("stringList") // 指定註入id為stringList的bean public void setStringList(List<String> stringList) { this.stringList = stringList; } // ... 省略部分代碼 @Autowired // 通過set方法註入bean @Qualifier("stringMap") // 指定註入id為mapString的bean public void setStringMap(Map<String, String> stringMap) { this.stringMap = stringMap; } }
@Configuration // 該註解可理解為將當前class等同於一個xml文件 @ComponentScan("com.imooc.springClass5.annotation") // 開啟包掃描 public class BeanConfiguration { @Bean("stringList") // 實例化一個List,id為stringList public List<String> stringList() { // ... 省略部分代碼 } @Bean("stringMap") // 實例化一個Map,id為stringMap public Map<String, String> stringMap() { // ... 省略部分代碼 } }
(2)將多個泛型的實例註入到集合
Spring支持將多個泛型的實例註入到集合,舉例如下:
@Configuration // 該註解可理解為將當前class等同於一個xml文件 @ComponentScan("com.imooc.springClass5.annotation") // 開啟包掃描 public class BeanConfiguration { @Bean("integer1") // 實例化一個Integer,id為integer1 public Integer integer1() { return 10001; } @Bean("integer2") // 實例化一個Integer,id為integer2 public Integer integer2() { return 10002; } }
在上面的代碼中,我們實例化了兩個Integer類型數據,並為每個實例設定了beanId。然後:
@Component // 通過構造方法實例化bean,類似的還有@Controller、@Service、@Repository public class Bean { private List<Integer> integerList; private Map<String, Integer> integerMap; public List<Integer> getIntegerList() { return integerList; } @Autowired // 通過set方法註入bean,將註入所有已經交由IoC容器管理的Integer類型的bean public void setIntegerList(List<Integer> integerList) { this.integerList = integerList; } public Map<String, Integer> getIntegerMap() { return integerMap; } @Autowired // 通過set方法註入bean,將註入所有已經交由IoC容器管理的Integer類型的bean,其中beanId即為鍵值 public void setIntegerMap(Map<String, Integer> integerMap) { this.integerMap = integerMap; } }
上面兩個@Autowired會將我們在BeanConfiguration中創建的兩個Integer類型的Bean全部註入到集合當中,當註入到Map類型(鍵為String類型)時,beanId即作為鍵值註入。
測試代碼:
@Test public void testBean() throws Exception { final AbstractApplicationContext context = new AnnotationConfigApplicationContext(BeanConfiguration.class); System.out.println("bean.getIntegerList() = " + bean.getIntegerList()); System.out.println("bean.getIntegerMap() = " + bean.getIntegerMap()); }
輸出:
bean.getIntegerList() = [10001, 10002]
bean.getIntegerMap() = {integer1=10001, integer2=10002}
當然,如果你想控制泛型實例在List中的順序,可以通過增加@Order註解的方式實現:
@Configuration // 該註解可理解為將當前class等同於一個xml文件 @ComponentScan("com.imooc.springClass5.annotation") public class BeanConfiguration { @Bean("integer1") // 實例化一個Integer,id為integer1 @Order(5) // 該註解可決定這個bean被註入到list時候的順序,可以不連續 public Integer integer1() { return 10001; } @Bean("integer2") // 實例化一個Integer,id為integer2 @Order(2) // 該註解可決定這個bean被註入到list時候的順序,可以不連續 public Integer integer2() { return 10002; } }
則輸出結果即變為:
bean.getIntegerList() = [10002, 10001]
bean.getIntegerMap() = {integer1=10001, integer2=10002}
4. String、Integer等類型直接賦值
String、Integer等類型直接賦值可用@Value實現,例如:
@Component // 通過構造方法實例化bean,類似的還有@Controller、@Service、@Repository public class Bean { private String string; public String getString() { return string; } @Value("zzzzz") // 直接將zzzzz這個值註入進去 public void setString(String string) { this.string = string; } }
四、設定Bean的作用域scope
可直接通過@Scope來實現,例如:
@Component // 通過構造方法實例化bean @Scope("singleton") // 設定bean作用域 public class Bean1 { }
@Configuration // 該註解可理解為將當前class等同於一個xml文件 @ComponentScan("com.imooc.springClass5.annotation") public class BeanConfiguration { @Bean // 實例化一個Bean1 @Scope("singleton") // 設定bean作用域 public Bean1 bean1() { return new Bean1(); } }
五、Bean的懶加載
可直接通過@Lazy實現,例如:
@Component // 通過構造方法實例化bean @Lazy // 開啟懶加載 public class Bean1 { }
@Configuration // 該註解可理解為將當前class等同於一個xml文件 @Lazy // 開啟懶加載 public class BeanConfiguration { @Bean // 實例化一個Bean1 @Scope("singleton") // 設定bean作用域 public Bean1 bean1() { return new Bean1(); } }
@Configuration // 該註解可理解為將當前class等同於一個xml文件 @Lazy // 開啟懶加載 public class BeanConfiguration { }
註意第三段代碼中的@Lazy註解,這意味著在該Configuration中實例化的Bean都將默認為懶加載模式
六、Bean別名
Spring允許一個Bean擁有多個BeanId,但暫時只能在@Bean中實現,@Component系列四個註解暫不支持,@Bean舉例如下:
@Configuration // 該註解可理解為將當前class等同於一個xml文件 public class BeanConfiguration { @Bean({"bean1_1", "bean1_2"}) // 實例化一個Bean1設定其擁有兩個BeanId,分別為bean1_1和bean1_2 public Bean1 bean1() { return new Bean1(); } }
七、引入其他註解了@Configuration的Class 或 其他xml文件格式配置
可直接通過@Import實現,例如:
@Configuration // 該註解可理解為將當前class等同於一個xml文件 @Import(BeanConfiguration1.class) @ImportResource("classpath:spring.xml") public class BeanConfiguration {
}
並且BeanConfiguration1無需註解@Configuration
public class BeanConfiguration1 {}
八、方法註入
可能存在如下場景:Class A 的某個方法依賴於Class B的實例,Class A使用scope=singleton單例模式,但是Class A每次執行方法的時候都希望獲取一個新的Class B的實例,這個時候就用到了方法註入。舉例:
@Component // 通過構造方法實例化bean @Scope("prototype") // 設定bean作用域 public class Bean3 { }
@Component // 通過構造方法實例化bean,類似的還有@Controller、@Service、@Repository public abstract class Bean { @Lookup protected abstract Bean3 createBean3(); public void printBean3() { System.out.println("createBean3().toString() = " + createBean3().toString()); } }
測試代碼:
public class BeanTest { @Test public void testBean() throws Exception { final AbstractApplicationContext context = new AnnotationConfigApplicationContext(BeanConfiguration.class); Bean bean = context.getBean(Bean.class); bean.printBean3(); bean.printBean3(); bean.printBean3(); } }
輸出:
createBean3().toString() = com.imooc.springClass5.annotation.Bean3@1722011b createBean3().toString() = com.imooc.springClass5.annotation.Bean3@57ad2aa7 createBean3().toString() = com.imooc.springClass5.annotation.Bean3@5b3f61ff
可以看到Bean.printBean3()方法每次拿到的Bean3都是不同的實例
九、@PostConstruct和@PreDestroy
如果需要在Bean實例化完成之後或需要在Bean銷毀之前執行一些邏輯,
- 可通過@PostConstruct和@PreDestroy實現。
- 可通過@Bean的initMethod和destroyMethod實現。
@Component // 通過構造方法實例化bean public class Bean1 { public Bean1() { System.out.println(this.getClass().getSimpleName() + ":" + this.toString() + " has been created"); } @PostConstruct public void postConstruct() { System.out.println(this.getClass().getSimpleName() + ":postConstruct()"); } @PreDestroy public void preDestroy() { System.out.println(this.getClass().getSimpleName() + ":preDestroy()"); } }
public class Bean4 { public Bean4() { System.out.println(this.getClass().getSimpleName() + ":" + this.toString() + " has been created"); } public void init() { System.out.println(this.getClass().getSimpleName() + ":init()"); } public void destroy() { System.out.println(this.getClass().getSimpleName() + ":destroy()"); } }
測試:
@Test public void testBean() throws Exception { final AbstractApplicationContext context = new AnnotationConfigApplicationContext(BeanConfiguration.class); System.out.println("=================context has bean created====================="); context.close(); }
輸出:
Bean1:com.imooc.springClass5.annotation.Bean1@415b0b49 has been created Bean1:postConstruct()
Bean4:com.imooc.springClass5.annotation.Bean4@7ef27d7f has been created =================context has bean created=====================
Bean4:init()
Bean4:destroy() Bean1:preDestroy()
十、SpringIoC容器本身接口實例註入
Spring支持我們直接註入其相關接口實例,例如:ApplicationContext、BeanFactory、Environment、ResourceLoader、ApplicationEventPublisher、MessageSource接口及其實現類,舉例其中一個說明:
@Component // 通過構造方法實例化bean,類似的還有@Controller、@Service、@Repository public class Bean { private ApplicationContext context; public ApplicationContext getContext() { return context; } @Autowired // 可直接將ApplicationContext註入進來,也可以註入BeanFactory、Environment、ResourceLoader、ApplicationEventPublisher、MessageSource及其實現類 public void setContext(ApplicationContext context) { this.context = context; } }
Spring入門之五-------SpringIoC之通過註解實現