現實開發中,每個應用都會由兩個或多個類組成,這些類之間相互協做完成特定的業務邏輯。根據傳統作法,每一個對象負責管理與本身協做的對象的引用(也就是,每一個對象中使用new實例化對象的方式建立協做的對象)——這將致使==高度耦合和難以測試的代碼==。java
public class ClassA{ private ClassB b;//B類的依賴 public ClassA(){ this.b=new ClassB();//A與B緊耦合 } } public class ClassB{}
DI 的出現就是爲了解決對象之間的依賴關係所帶來的高耦合問題。【依賴注入 (DI,Dependency Injection)】:將所依賴的關係自動交給目標對象,而不是讓對象自己去獲取依賴。依賴注入所關注的是已經建立好的對象如何實現它們之間的依賴關係;至於這些對象怎麼被建立和管理,稍後會講述。web
public class ClassA{ private ClassB b; public ClassA(ClassB b){ this.b=b;//B是被注入進來的 } } public class ClassB{}
DI 的實現所帶來的好處是:和麪向接口實現鬆耦合。一個對象經過接口來代表依賴關係,這樣就能夠在對象不肯定的狀況下,使用不一樣的具體實現進行替換——【鬆耦合】。算法
在 Spring 應用中,一個 Bean 對象對應一個對象,並存儲於 Spring 容器中,Spring 容器負責建立對象,裝配、配置對象,以及管理整個對象的生命週期,從生存到死亡。spring
容器是 Spring 框架的核心。Spring 容器使用 DI 管理構成應用的組件,它會建立相互協做的組件之間的關聯。Spring 自帶多個容器實現,主要分爲兩種類型:sql
org.springframework.beans.factory.BeanFactory
接口定義,是最簡單的容器,提供基本的 DI 支持;org.springframework.context.ApplicationContext
接口定義,基於 BeanFactory 構建,並提供應用框架級別的服務;Spring 自帶了多種類型的應用上下文。數據庫
類型 | 描述 |
---|---|
AnnotationConfigApplication | 從一個或多個基於 java 的配置類中加載 Spring 應用上下文 |
AnnotationConfigWebApplicationContext | 從一個或多個基於 Java 的配置類中加載 Spring Web 應用上下文 |
ClasssPathXmlApplicationContext | 從類路徑下的一個或多個 XML 配置文件中加載上下文定義,把應用上下文的定義文件做爲類資源 |
FileSystemXmlApplicationContext | 從文件系統下的一個或多個XML配置文件中加載上下文定義 |
XmlWebApplicationContext | 從 Web 應用下的一個或多個 XML 配置文件中加載上下文定義 |
Java 中經過 new 實例化的對象,其生命週期是從被建立開始,直到再也不被調用,該對象就由 Java 自動進行垃圾回收。數組
在 Spring 中,Bean 對象的生命週期相對複雜,其包含了如下過程:安全
BeanNameAware
接口,Spring 將 bean 的 ID 傳遞給 setBeanName()
方法;BeanFactoryAware
接口,Spring 將調用 setBeanFactory()
方法,將 BeanFactory 容器傳入;BeanPostProcessor
接口,Spring 將調用 postProcessBeforeInitialization()
方法;InitializingBean
接口,Spring 將調用 afterPropertiesSet()
方法。若是 bean 使用 init-method 聲明初始化方法,該方法也會被調用;DisposableBean
接口,Spring 將調用 destory()
方法。一樣,若是 bean 使用 destory-method
聲明瞭銷燬方法,該方法也會被調用;在 Spring 中,對象無需本身查找或建立與其所關聯的其餘對象。相反,容器負責把須要相互協做的對象引用賦予各個對象。建立應用對象之間協做關係的行爲稱爲【裝配 (wiring)】。session
裝配 bean 的三種機制app
儘管,Spring 中提供了多種方案來配置 bean,咱們在配置時可視狀況進行選擇合適的方式進行裝配咱們的 bean 對象。建議是:儘量使用自動配置機制;顯示配置越少越好。並且,使用選擇顯示配置時,JavaConfig 配置會比 XML 配置更增強大,類型更安全。
Spring 是從兩個方面實現自動裝配:
建立組件類時,經常使用的註解有:
@Component
:建立一個組件類,用於被 Spring 掃描並建立 Bean 對象;
該註解能夠爲當前類設定 ID 值,@Component("ID_value")
。沒有設定 ID 值時,默認爲類名的首字母爲小寫。
@Autowire
:自動裝配,爲 Bean 的屬性注入對象。
Autowire
能夠用在定義屬性的語句上、有參構造方法以及 set()
方法上。使用註解,會在 Spring 應用上下文中尋找匹配的 bean 對象。
在使用 @Autowire
註解時,須要注意兩個問題:
上節簡單講述瞭如何建立一個組件類,以及如何實現自動裝配依賴關係。但這並不表明:在Spring容器中建立了一個 Bean 對象。要想建立一個 Bean 對象,須要配置 Spring 的組件掃描,命令 Spring 尋找帶 @Component
註解的類,並建立 Bean,由於 Spring 中組件掃描功能默認是不啓用。那麼,如何啓用組件掃描呢?——有兩種方式:
基於 Java 的配置
須要建立一個配置類,該類與普通類的區別在於:使用註解 @Configuration
修飾。開啓組件掃描,須要使用另外一個註解 @ComponentScan
。
package soundsystem; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan public class CDPlayerConfig { }
XML 文件配置
在 XML 中配置啓用組件掃描,須要使用 Spring context 命名空間 的 <context:component-scan>
元素。
<?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"> <Context:component-scan base-package="soundsystem"/> </beans>
細心的小夥伴可能發現了,在 XML 文件配置中,base-package
屬性是必須給定的。該屬性是指定組件掃描的基礎包,也就是指定哪些包是須要使用組件掃描。
在 Java 配置中,@ComponentScan
註解中可以使用 basePackages 屬性和 basePackageClasses 屬性來指定組件掃描的基礎包。前者給定值是包路徑的 String 類型,後者是 .class
類文件(類文件所在的包會做爲基礎包)。它們的值能夠是單一值,也能夠是複數形式。
@ComponentScan(basePackages={"package1","package2",...})//使用String類型表示,是類型不安全的;當重構代碼時,容易發生錯誤 //@ComponentScan(basePackageClasses={Xxx1.class,Xxx2.class,...}) public class CDPlayerConfig{ }
大多數狀況下,經過組件掃描和自動裝配實現 Spring 的自動化配置更爲推薦。但有些狀況,好比:將第三方庫中的組件裝配到應用中,使用
@Component
和@Autowired
沒法進行註解,這就必須採用顯式裝配。顯式裝配的方案有:Java 和 XML。
在 2.1.2 組件掃描 中,已經說起如何建立一個 Java 配置類,就不在重複講述。在配置類中,經過方法形式和註解
@Bean
建立 Bean 對象。Java 配置的好處是:在建立 Bean 的過程當中,可使用 Java 代碼。
在 Java 配置類中聲明 Bean,須要編寫一個方法,這個方法會返回 建立所需類型的實例,而後給這個方法添加 @Bean
註解;默認狀況下,@Bean
註解會設定與方法名同樣的 ID 值,可使用 name 屬性指定不一樣的名字。
package cn.book.main; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Bean; @Configuration public class StudentConfig { @Bean //@Bean(name="stu") public Student getStu(){ return new Student(); } }
package cn.book.main; public class Student { private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } public Student() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
這個簡單的例子中,經過無參構造方法建立實例,是聲明 Bean 最簡單的方法,由於沒有爲 Bean 注入依賴關係。在配置類中,實現依賴注入的方式都是經過有參構造方法建立,只是獲取要注入的 Bean 的形式有兩種:
引用配置類中建立 Bean 的方法;
@Bean public String getStuName(){ return "Tom"; } @Bean public int getStuAge(){ return 18; } @Bean public Student getStu(){ return new Student(getStuName(),getStuAge()); }
注意:
經過方法參數傳遞;
Java 或 XML 配置中建立的 Bean 對象、組件掃描發現的 Bean 對象,均可以經過方法參數傳遞並注入。
@Bean public Student getStu(String name,int age){ return new Student(name,age); }
在使用 XML 裝配 Bean 以前,須要建立一個新的配置規範,這意味着要建立一個 XML 文件,而且以 <beans>
元素爲根。下面是最爲簡單的 Spring 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 http://www.springframework.org/schema/context "> </beans>
無參構造器聲明 bean
XML 配置中使用 <bean>
元素來聲明一個 bean,該元素相似於 Java 配置中的 @Bean
註解。
<bean id="" class="" /> <!--這個元素將會調用類的默認構造器來建立 bean-->
有參構造器聲明 bean
要使用有參構造器建立 bean ,須要使用<bean>
的 <constructor-arg>
元素,此方式在聲明 bean 的同時,並注入其它依賴關係。該元素中有五個屬性:
<bean id="" class=""> <constructor-arg name="" value="" type="" index=""/> <constructor-arg name="" type="" index="" ref="" /> </bean>
注意
bean
元素中的參數名稱要與構造方法中的參數名一致;bean
元素中的參數順序能夠與構造方法中的參數順序不一致;可使用 index 屬性指定在構造方法的順序;在 XML 配置文件中,注入 bean 的方式有三種:有參構造器注入 <constructor-arg>
、屬性注入 <property>
以及自動注入 <autowire>
。
屬性注入
屬性注入的實質是:調用 set()
方法。在聲明 bean 的元素中,使用 <property>
元素。
<bean id="" class=" "> <property name="" value=""/> <property name="" ref=""/> </bean>
自動注入
自動注入方式使用 bean
元素中的屬性 autowire
,該屬性有三個值 byName、byType、constructor
,根據提供的值進行自動匹配注入。
byName:在當前 XML 文件中,查找 bean 元素的 id 值與須要注入 bean 的屬性名相同的對象,進行匹配。
byType:在當前 XML 文件中,查找 bean 標籤的對象類型與須要注入 bean 的屬性類型相同的對象,進行匹配;此時,不須要關注 bean 標籤的 id 值是否與須要注入的屬性名一致。
constructor:【1】根據須要注入對象的有參構造器的形參名進行查找 ,找到匹配 bean 的 id 值則注入;不然,【2】根據須要注入對象的有參構造器的形參類型進行查找,找到類型匹配的 bean 標籤則注入。
byName 和 byType
其實是調用 set()
方法賦值;constructor
則是調用有參構造方法;byName 和 byType
能夠結合 property
標籤使用;能夠結合 constructor-org
標籤使用,至關於調用多參的有參構造方法;
Spring 中實現了對集合的裝配,包括:Array、List、Set以及Map,它們對應的元素爲:<array>
、<list>
、<set>
以及<map>
,集合配置方式比較接近,這裏舉例 List 和 Map 集合的配置方式
<list value-type=""><!--建立List,並聲明存儲值的類型--> <value type=""></value><!--集合包含的值,可聲明數據類型--> <ref bean=""/><!--引用bean,使用bean的ID--> </list> <map key-type="" value-type=""><!--建立Map,並聲明存儲鍵-值的類型--> <entry key="" value=""/><!--集合包含的值--> <entry key-ref="" value-ref=""/><!--引用到鍵或值的bean,使用bean的ID--> </map>
當咱們在裝配 bean 時,若是同時採用 JavaConfig 和 XML 配置 bean 時,而它們的 bean 相互關聯,這時,就須要將不一樣的配置文件組合在一塊兒。
多個 Java 配置組合
使用註解 @Import
能夠將其它 JavaConfig 配置類引入,
//在配置類中引用另外一個配置類 @Configuration @Import(XxxConfig1.class) public class Xxxconfig2{ } //固然,也能夠建立一個新的配置類,只用於組合配置類 @Configuration @Import(XxxConfig1.class,XxxConfig2.class) public class Config{ }
JavaConfig 配置中引用 XML 配置
@Configuration @ImportResource("classpath:*/*/*.xml") public class Config{ }
<bean class="*.*.Config" /><!--引入 JavaConfig 配置--> <import resource="*/*/*.xml" /><!--引入 XML 配置-->
應用中存在不一樣的環境,應用在不一樣的環境中須要配置不同的 Bean,若是須要切換環境時,原環境的 Bean 在新環境中不必定可用,這時須要在新環境中配置新的 Bean,在 Spring 中,能夠根據環境建立 Bean 或者不建立 Bean,這個就是 Profile 配置 。
跨環境配置的幾個例子:數據庫配置、加密算法以及外部系統的集成。
在這裏,咱們不討論如何配置不一樣的環境,只關注如何使用 Profile
決定 Bean 的建立。現假設,咱們應用中存在下面三個環境,環境名稱爲:dev、qa、prod。如今,咱們要爲指定的環境裝配 Bean。
JavaConfig 中配置
@Profile("Envionment_name")
註解,括號內指定環境名稱,指定某個 Bean 屬於哪個 Profile。當指定的環境爲激活狀態時,該 Bean 被建立,不然不建立。
@Configuration //@Profile("dev") //profile應用在類上,當環境激活時,該配置類纔會被建立 public class ProfileConfig{ @Bean(destroyMethod="shutdown") //使用在方法級別上,能夠將不一樣環境的 Bean 放在同一配置類中 @Profile("dev") public DataSource dataSource(){ return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .addScript("classpath:schema.sql") .addScript("classpath:test-data.sql") .build(); } }
XML 中配置
在 XML 配置中,能夠經過 <Beans>
元素的 profile
屬性,在 XML 配置 Bean。下面是一個例子
<beans profile="dev"> <!--爲dev配置一個 Bean--> <jdbc:embedded-database id="dataSource" type="H2"> <jdbc:script location="classpath:schema.sql" /> <jdbc:script location="classpath:test-data.sql" /> </jdbc:embedded-database> </beans> <beans profile="prod"> <!--爲prod配置一個 Bean--> <jee:jndi-lookup id="dataSource" lazy-init="true" jndi-name="jdbc/myDatabase" resource-ref="true" proxy-interface="javax.sql.DataSource" /> </beans>
Spring 在肯定哪一個 Profile 處於激活狀態時,須要依賴兩個獨立的屬性:spring.profiles.active
和 spring.profiles.default
。前者會根據指定值來肯定哪一個 Profile 是激活的;後者是當沒有指定 active
屬性的值時,默認激活的 Profile。Spring 中設置這兩個屬性的方式
@ActiveProfiles
註解設置;下面的例子中,使用 DispatcherServlet 的參數將 spring.profiles.default
設置 profile。在 Web 應用中,設置 spring.profiles.default
的 web.xml 文件以下
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!--爲上下文設置默認的 profile--> <context-param> <param-name>spring.profiles.default</param-name> <param-value>dev</param-value> </context-param> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>cn.book.main.servlet.DispatcherServlet</servlet-class> <!--爲Servlet設置默認的 profile--> <init-param> <param-name>spring.profiles.default</param-name> <param-value>dev</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/appServlet</url-pattern> </servlet-mapping> </web-app>
注意
spring.profiles.active
和 spring.profiles.default
屬性中,profile 使用的是複數形式,能夠同時激活多個 Profile——經過列出多個 profile 名稱,並以逗號分隔。
若是咱們定義的 bean ,但不但願它們被 Spring 容器即刻被建立,而是但願當類路徑下包含某個庫,或者是建立了其它 Bean,亦或者要求設置了某個特定環境變量後,該 Bean 才被建立。此時,咱們就須要使用條件化配置。
要實現一個條件化 Bean,在裝配 Bean 的方法上( 使用@Bean
),引用另外一個註解 @Conditional(*.class)
,注意:括號內給定的是一個類文件。該註解會根據括號內給定類的返回結果判斷是否建立 Bean,若是爲true,會建立 Bean,不然不建立。
可是,這只是定義了一個要條件化的 Bean,該 Bean 須要知足怎樣的條件,須要本身實現。上面說到,@Conditional
註解須要傳入一個類文件,該類在建立時,要實現 Condition
接口,並重寫 matches()
方法。下面是一個簡單的例子
package cn.book.main.pojo; //Bean 類 public class TestCondition { public TestCondition() { System.out.println("Bean 被建立了"); } }
該類實現 Condition
接口,並重寫 matches()
方法,在方法內能夠編寫判斷代碼,並返回 boolean 值。
package cn.book.main.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class IfCreatCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { return false; } }
配置類,裝配 Bean。
package cn.book.resource; import cn.book.main.condition.IfCreatCondition; import cn.book.main.pojo.TestCondition; import org.springframework.context.annotation.*; @Configuration public class HumanJobConfig { @Bean @Conditional(IfCreatCondition.class) public TestCondition getCondition(){ return new TestCondition(); } }
測試類,若是 IfCreatCondition 類返回 true,則 Bean 被建立;不然不會被建立。
package cn.book.test; import cn.book.resource.HumanJobConfig; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes=cn.book.resource.HumanJobConfig.class) public class HumanJobTest { @Test public void Test(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(HumanJobConfig.class); } }
上面只是演示了實現條件化 Bean 的流程,咱們的條件能夠再複雜。你們應該注意到了,matches()
中有兩個參數:ConditionContext
和 AnnotatedTypeMetadata
。經過這兩個對象,咱們能夠實現符合 IOC 和 DI 的條件。接下來,就來了解這兩個對象:
ConditionContext
是一個接口,它有如下方法
方法 | 描述 |
---|---|
getRegistry | 返回 BeanDefinitionRegistry 檢查 bean 定義; |
getBeanFactory | 返回 ConfigurableListableBeanFactory 檢查 bean 是否存在,甚至 檢查 bean 的屬性; |
getEnvironment | 返回 Environment 檢查環境變量是否存在以及它的值是什麼; |
getResourceLoader | 返回 ResourceLoader 所加載的資源; |
getClassLoader | 返回 ClassLoader 加載並檢查類是否存在; |
AnnotatedTypeMetadata
也是一個接口,可以檢查帶有 @Bean
註解的方法上還有什麼註解。它有如下方法:
方法 | 描述 |
---|---|
boolean isAnnotated(String annotationType) | 檢查帶 @Bean 的方法上是否存在其它特定的註解 |
Map<String,Object> getAnnotationAttributes(String annotationType) | 得到指定註解的 Bean |
Map<String,Object> getAnnotationAttributes(String annotationType, boolean classValueAsString) | ==未了解== |
MultiValueMap<String,Object> getAllAnnotationAttributes(String annotationType) | 得到指定註解的全部 Bean |
MultiValueMap<String,Object> getAllAnnotationAttributes(String annotationType, boolean classValueAsString) | ==未了解== |
自動化裝配中,僅當只有一個 Bean 知足時,才能裝配成功。當多個 bean 知足裝配時,Spring 會產生異常:NoUniqueBeanDefinitionException
。最多見的狀況是:==當一個接口有多個實現類,調用時使用接口對象引用子類==。
好比:Human接口有兩個實現類:Man類和 Woman類
package cn.book.main.entity; public interface Human { }
package cn.book.main.entity; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; @Component public class Man implements Human { public Man() { System.out.println("I am man"); } }
package cn.book.main.entity; import org.springframework.stereotype.Component; @Component public class Woman implements Human { public Woman() { System.out.println("I am woman"); } }
配置類
package cn.book.resource; import cn.book.main.pojo.TestCondition; import org.springframework.context.annotation.*; @Configuration @ComponentScan("cn.book.main.entity") public class HumanConfig { }
測試類,自動注入一個Human接口。此時,spring會產生:NoUniqueBeanDefinitionException
。
package cn.book.test; import cn.book.main.entity.Human; import cn.book.resource.HumanJobConfig; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes=cn.book.resource.HumanJobConfig.class) public class HumanJobTest { @Autowired private Human human; @Test public void Test(){ System.out.println(human.getClass()); } }
當確實發生裝配的歧義性時,Spring 提供瞭如下方案:
標示首選須要使用關鍵字 primary,它在 JavaConfig 中是註解 @Primary
,在 XML 是 bean
元素中的屬性 primary。
JavaConfig 配置
@Primary
註解配合 @Component
和 @Bean
註解組合使用,在須要設置爲首選的組件類和 Bean 對象上。
與 @Component
註解配合使用
@Component @Primary public class Man{ }
或者,與 @Bean
註解配合使用
@Configuration public class JavaConfig{ @Bean @Primary public Human getMan(){ return new Man(); } }
在 XML 中設置 Bean 爲首選項的配置爲:
<bean id="man" class="Man" primary="true"/>
缺點
限定符 @qualifier
註解,主要做用是在可選的 Bean 進行縮小範圍選擇,直到找到知足的 Bean。它的有兩個做用:
與 @Autowired
和 @Inject
協同使用,在注入的時候指定想要注入的是哪一個 Bean;
@qualifier("")
括號內所設置的參數時要注入 Bean 的 ID 值。
與 @Component
和 @Bean
協同使用,爲 Bean 設定限定符;
@qualifier("")
括號內是爲 Bean 設置的限定符,在注入時使用 qualifier
中引用。
若是使用註解 @qualifier
限定符依舊沒法解決 bean 的裝配歧義性問題時,並且,在 Spring 中沒法重複使用相同的 @qualiifer
註解,在這種狀況下,能夠自定義註解來區分 bean。那麼,如何自定義註解呢?
import org.springframework.beans.factory.annotation.Qualifier; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.CONSTRUCTOR,ElementType.FIELD, ElementType.METHOD,ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface 註解名 { }
注意:
在默認狀況下,Spring 應用上下文中全部的 Bean 都是以單例形式建立的。也就是,無論一個 Bean 被注入多少次,每次注入的 Bean 都是同一個實例。
若是一個實例須要保持無狀態並在應用中重複使用,單例做用域是不可行且不安全的。在 Spring 定義了多種做用域,Spring 會基於這些做用域建立 Bean,這些做用域包括:
單例是默認的做用。若是想要選擇其它做用域,要使用 @Scope
註解。註解內使用如下表示做用域的參數:
ConfigurableBeanFactory.SCOPE_PROTUTYPE
或者 "prototype"
;ConfigurableBeanFactory.SCOPE_SESSION
或者 "session"
;ConfigurableBeanFactory.SCOPE_REQUEST
或者 "request"
;若是使用 XML 配置,在 <bean>
元素中的屬性 scope
設置 bean 的做用域。
==學習到 Web 部份內容再深刻學習==
前面在裝配 Bean,講到在建立 Bean 時,將常量(好比int類型、String類型)直接給定,這是將值硬編碼到 Bean 中。有時,爲了不硬編碼值,想讓這些值在運行時在肯定,Spring 提供了兩種在運行時求值的方式:
回顧一下,在咱們使用 JDBC 時,會建立一個屬性文件 *.properties
文件放置鏈接數據庫所需的配置參數。假設,在 Spring 中該文件依舊存在,咱們如何在配置類或配置文件中解析並取值?
JavaConfig 配置類
@PropertySource
中的value屬性設置屬性文件路徑;@Configuration @PropertySource(value = "classpath:/JDBC.properties") public class JdbcConfig { @Autowired Environment env; @Bean public JdbcParams getJdbc(){ return new JdbcParams( env.getProperty("jdbc.driver"), env.getProperty("jdbc.url"), env.getProperty("jdbc.username"), env.getProperty("jdbc.password") ); } }
public class JdbcParams { private String driver; private String url; private String username; private String password; public JdbcParams() { } public JdbcParams(String driver, String url, String username, String password) { this.driver = driver; this.url = url; this.username = username; this.password = password; } }
Environmen 接口的用法,經過 Environment 接口能夠調用如下方法:
方法 | 描述 |
---|---|
String getProperty(String key) |
根據指定值獲取屬性,屬性沒有定義返回null; |
String getProperty(String key, String defaultValue) |
根據指定值獲取屬性,若是沒有屬性值,則返回defaultValue; |
T getProperty(String key, Class<T> type) |
返回指定類型的屬性值;type爲指定類型的.class |
T getProperty(String key, Class<T> type,T defaultValue) |
返回指定類型的屬性值;type爲指定類型的.class,若是沒有屬性值,則返回defaultValue; |
getRequiredProperty(String key) |
根據指定值獲取屬性,屬性沒有定義拋出異常; |
containProperty(String key) |
檢查屬性文件是否存在某個屬性; |
T getPropertyAsClass(String key,Class<T> type) |
將屬性文件解析爲指定的類文件; |
String[] getActiveProfiles() |
返回激活 profile 名稱的數組; |
String[] getDefaultProfiles() |
返回默認 profile 名稱的數組; |
boolean acceptsProfiles(String... profiles) |
若是 environment 支持給定的 profile 的話,就返回 true; |
Spring 支持將屬性定義到外部的屬性文件中,並使用佔位符將值插入到 Bean 中。在 Spring 裝配中,佔位符的形式爲使用 ${...}
包裝的屬性名稱。
爲了使用佔位符,須要配置一個 PropertySourcePlaceholderConfigurer
Bean,它可以基於 Environment 及其屬性源來解析佔位符。下面來看看,JavaConfig 配置和 XMl 配置中使用佔位符的用法
import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; @Configuration //聲明屬性源,並將屬性文件加載到Spring @PropertySource(value = "classpath:/JDBC.properties") public class StudentCongif { 2、 //(1)使用佔位符解析屬性 @Bean public JdbcParams getJdbc( @Value("${jdbc.driver}") String driver, @Value("${jdbc.url}") String url, @Value("${jdbc.username}") String username, @Value("${jdbc.password}") String password){ return new JdbcParams(driver,url,username,password); } //(2)還須要配置一個PropertySourcesPlaceholderConfigurer 的 bean @Bean public PropertySourcesPlaceholderConfigurer placeholderConfigurer(){ return new PropertySourcesPlaceholderConfigurer(); } }
<!--建立 PropertySourceHolderConfigurer --> <context:property-placeholder location="classpath:/JDBC.properties"/> <!-- 使用佔位符進行值注入--> <bean id="jdbc" class="cn.book.main.valueInject.JdbcParams" c:driver="${jdbc.driver}" c:url="${jdbc.url}" c:username="${jdbc.username" c:password="${jdbc.password}"/>
解析外部屬性可以將值的處理推遲到運行時,但它的關注點在於根據名稱解析來自 Spring Environment 和屬性源的屬性。