Spring 框架的最核心功能之一是 DI (Dependency Injection), 也就是依賴注入。java
DI 的底層原理是反射技術,對 JavaBean 的屬性進行賦值,從而達到 A 到 B 模塊的解耦。Spring 提供 DI 容器,對須要關聯的 JavaBean、不須要關聯的 JavaBean 的建立、銷燬都要進行統一的調度和管理。git
在咱們的程序中,咱們將不使用 new 關鍵字的建立對象,而是交給 DI 容器管理生命週期,以及多個 JavaBean 之間的注入關係。從技術上說,就是使用反射將接口和實現相分離。github
但在此以前,咱們來看看如何使用 Spring 框架,以及幾種建立 JavaBean 的方式。spring
先建立一個 Java Project,建立一個保存操做的類 Save數組
package save; public class Save { public void save() { System.out.println("save 方法!"); } }
一個測試類 Testapp
package test; import save.Save; public class Test { private Save save = new Save(); public Save getSave() { return save; } public void setSave(Save save) { this.save = save; } public static void main(String[] args) { Test test = new Test(); test.getSave().save(); } }
運行結果:框架
在 src 中建立 applicationContext.xml學習
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userinfo1" class="entity.Userinfo"></bean> </beans>
<bean> 標籤告訴 Spring 要建立 entity 包下的 Userinfo 類的對象,放在 DI 容器裏,在容器裏的 id 是 userinfo1。Userinfo 以下:測試
package entity; public class Userinfo { public Userinfo() { System.out.println("類 Userinfo 被實例化 = " + this); } }
測試類 Test:this
package test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test1 { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); } }
運行結果:
若是 applicationContext.xml 聲明的多個對象屬於同一個類型,好比下面的情形:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userinfo1" class="entity.Userinfo"></bean> <bean id="userinfo2" class="entity.Userinfo"></bean> </beans>
若是經過 getBean(Userinfo.class) 方法獲取對象的話就會拋出 NoUniqueBeanDefinitionException 異常,由於 Spring 找到了多個對象,不知道返回哪個。
package test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import entity.Userinfo; public class TestNoUnique { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Userinfo userinfo = context.getBean(Userinfo.class); } }
拋出如下異常 :
如何解決呢? 不要使用 getBean(Userinfo.class); , 使用 getBean(String id); 這樣就能夠得到 id 對應的惟一對象,而解決異常。
package test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import entity.Userinfo; public class Test1_1 { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Userinfo userinfo1 = (Userinfo)context.getBean("userinfo1"); Userinfo userinfo2 = (Userinfo)context.getBean("userinfo2"); System.out.println(userinfo1); System.out.println(userinfo2); } }
運行結果, 能夠看到經過 id 獲取的對象和 Spring 建立的對象是同一個對象:
總結: getBean(Userinfo.class) 適用於只有一個 Userinfo 類實例對象的狀況,而有多個 Userinfo 類對象的時候就須要使用 getBean(String id) 方法。
使用 <context:component-scan base-package=""> 建立對象
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="entity"></context:component-scan> </beans>
使用 @Component 註解,這樣 Userinfo 能被 <context:component-scan base-package="entity"> 識別並建立實例化對象。
package entity; import org.springframework.stereotype.Component; @Component public class Userinfo { public Userinfo() { System.out.println("Userinfo 構造方法執行了:" + this); } }
測試類 Test 類,運行結果:
package test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); } }
getBean(Userinfo.class) 的方式運行:
package test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import entity.Userinfo; public class Test2 { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Userinfo userinfo = (Userinfo)context.getBean(Userinfo.class); System.out.println("main run " + userinfo); } }
咱們也能夠看到經過 id 獲取的對象和 Spring 建立的對象是同一個對象。
全註解配置法也稱 JavaConfig 配置法。註解 @Configuration 起着和 <beans> 標籤同樣的全局配置做用,這樣咱們就不須要 xml 配置方式就能建立 JavaBean 了。而註解 @Bean 就和 <bean> 標籤的功能是同樣的,用來聲明 JavaBean。
@Bean 註解聲明建立對象的方法是名稱能夠是任意的,但必須有返回值。
package tools; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import entity.Userinfo; @Configuration public class CreateBean { @Bean public Userinfo getUserinfo() { Userinfo userinfo1 = new Userinfo(); System.out.println("建立 userinfo1 = " + userinfo1); return userinfo1; } @Bean public Userinfo createUserinfo() { Userinfo userinfo2 = new Userinfo(); System.out.println("建立 userinfo2 = " + userinfo2); return userinfo2; } }
package entity; import org.springframework.stereotype.Component; public class Userinfo { public Userinfo() { System.out.println("Userinfo 構造方法執行了:" + this); } }
package test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Test4 { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("tools"); } }
運行結果:
使用全註解發生 NoUniqueBeanDefinitionException 異常,以及解決方法
將 Test4 類的代碼加上一行代碼
package test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import entity.Userinfo; public class Test4 { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("tools"); Userinfo userinfo = context.getBean(Userinfo.class); } }
解決辦法是給 @Bean 添加一個 name
package tools; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import entity.Userinfo; @Configuration public class CreateBean { @Bean(name = "userinfo1") public Userinfo getUserinfo() { Userinfo userinfo1 = new Userinfo(); System.out.println("建立 userinfo1 = " + userinfo1); return userinfo1; } @Bean(name = "userinfo2") public Userinfo createUserinfo() { Userinfo userinfo2 = new Userinfo(); System.out.println("建立 userinfo2 = " + userinfo2); return userinfo2; } }
而後經過 getBean(String id) 方法得到對象,和 xml 方式是同樣的:
package test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import entity.Userinfo; public class Test4 { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("tools"); Userinfo userinfo1 = (Userinfo)context.getBean("userinfo1"); Userinfo userinfo2 = (Userinfo)context.getBean("userinfo2"); System.out.println("main userinfo1 = " + userinfo1); System.out.println("main userinfo2 = " + userinfo2); } }
運行結果:
使用 @ComponentScan 註解的 basePackages = 「」 建立對象,它和 <context:component-scan base-package=""> </context:component-scan>
做用是相同的,能夠進行類掃描,而且建立對象。
掃描 entity 包的類 CreateBean
package tools; import java.util.Date; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan(basePackages = "entity") public class CreateBean { @Bean public Date getUserinfo() { Date nowDate = new Date(); System.out.println("建立 nowDate = " + nowDate); return nowDate; } }
package entity; import org.springframework.stereotype.Component; @Component public class Userinfo { public Userinfo() { System.out.println("Userinfo 構造方法執行了:" + this.hashCode()); } }
package test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import tools.CreateBean; public class Test { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CreateBean.class); } }
運行結果:
咱們能夠看到 entity 下的 Userinfo 類建立了一個實例對象。
使用 @ComponentScan(basePackages = "") 掃描多個包
package tools; import java.util.Date; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan(basePackages = {"entity1" , "entity2"}) public class CreateBean { @Bean public Date createDate() { Date nowDate = new Date(); System.out.println("createDate " + nowDate.getTime()); return nowDate; } }
package entity1; import org.springframework.stereotype.Component; @Component public class Userinfo { public Userinfo() { System.out.println("Userinfo 構造方法執行了:" + this); } }
package entity2; import org.springframework.stereotype.Repository; @Repository public class Bookinfo { public Bookinfo() { System.out.println("Bookinfo 的構造方法被調用了 " + this); } }
package test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import tools.CreateBean; public class Test4 { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CreateBean.class); } }
運行結果:
使用 @ComponentScan(basePackageClasses = "") 掃描多個類
package tools; import java.util.Date; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import entity1.entity2.Entity2222; import entity1.entity2.entity3.Userinfo; @Configuration //@ComponentScan(basePackageClasses = { Entity2222.class, Userinfo.class}) @ComponentScan(basePackageClasses = { Userinfo.class}) @ComponentScan(basePackageClasses = { Entity2222.class}) public class CreateBean { @Bean public Date createDate() { Date nowDate = new Date(); System.out.println("createDate " + nowDate.getTime()); return nowDate; } }
package entity1.entity2; import org.springframework.stereotype.Repository; @Repository public class Entity2222 { public Entity2222() { System.out.println("Entity2222 構造方法執行了:" + this.hashCode()); } }
package entity1.entity2.entity3; import org.springframework.stereotype.Repository; @Repository public class Bookinfo { public Bookinfo() { System.out.println("Bookinfo 的構造方法被調用了 " + this.hashCode()); } }
package entity1.entity2.entity3; import org.springframework.stereotype.Component; @Component public class Userinfo { public Userinfo() { System.out.println("Userinfo 構造方法執行了:" + this.hashCode()); } }
package test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import tools.CreateBean; public class Test4 { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CreateBean.class); } }
運行結果:
能夠分行寫,也能夠寫成一個數組
//@ComponentScan(basePackageClasses = { Entity2222.class, Userinfo.class}) @ComponentScan(basePackageClasses = { Userinfo.class}) @ComponentScan(basePackageClasses = { Entity2222.class})
若是隻寫 @ComponentScan, 那麼掃描的將是 @Configuration 註解配置類所在包及其子包下的全部組件。
@Component 註解表明都是一個能夠掃描的組件, @Repository 註解表明也是一個組件,通常表明的是 DAO 層的組件,也就是數據訪問層的組件。它們倆均可以被 @ComponentScan 組件掃描。
總結:這篇博客的例子基原本自 JavaEE 核心框架第二版。之前並無記錄 Spring 建立 JavaBean 的方式,因此記錄下來,方便之後學習。