Spring的IOC總結
IOC:控制反轉,控制權的轉移,應用程式本身不負責依賴物件的建立與維護,而是由外部容器負責建立和維護。
到底什麼是控制反轉,就是獲得依賴物件的過程被反轉了。控制反轉之後,獲得依賴物件的過程由自身管理變為了由IOC容器主動注入。
DI:依賴注入 ,建立物件並且組裝物件之間的關係。就是由IOC容器在執行期間,動態地將某種依賴關係注入到物件之中。
Spring容器並不是只有一個,它自帶了許多容器實現,可以歸為兩種不同的型別。
bean工廠(org.springframework.beans.factory.BeanFactory)
應用上下文ApplicationContext(org.springframework.context.ApplicationContext介面中定義)
ApplicationContext是spring中的容器應用上下文,可以載入配置檔案中定義的bean,儲存bean物件,全權負責物件的建立與組裝。這個容器在org.springframework.context.ApplicationContext介面中定義。ApplicationContext包含BeanFactory所有的功能,所以一般推薦使用ApplicationContext。spring中自帶了多種應用上下文的實現,他們的不同之處就在於如何載入配置。
ApplicationContext介面的實現方式:
①AnnotationConfigApplicationContext
②檔案方式:從檔案系統下的xml檔案中載入已被定義的bean。提供xml檔案在磁碟的完整路徑
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("F://workspace/appcontext.xml")
③Classpath:從類路徑下的xml檔案中載入已被定義的bean。提供xml檔案的相對路徑(應用程式類路徑下的xml配置檔案)
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-context.xml");
④Web應用中依賴servlet或Listen
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>context</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
應用上下文準備就緒之後,就可以呼叫上下文的getBean()方法從Spring容器中獲取bean了。
在web專案中,系統一旦啟動,web伺服器會初始化spring的上下文的。所以以上獲得spring上下文環境的方法主要是在測試類中使用,系統不啟動的情況下手動初始化spring上下文再獲取物件。
裝配Bean的三種方式:
第一種是通過XML裝配bean,Spring的配置檔案applicationContext.xml
<?xml version="1.0"encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util-2.0.xsd
http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
在配置檔案的頂部需要宣告多個XML模式(XSD)檔案,這些檔案定義了配置Spring的XML元素。
1.宣告一個簡單的bean
在基於XML的Spring配置中宣告一個bean,我們要使用Spring-beans模式中的一個元素<bean>。
<bean id="exampleBean" class="example.ExampleBean">
聲明瞭一個簡單的bean,建立這個bean的類由class屬性來指定,並且要使用全限定的類名。
當Spring發現這個<bean>元素時,會呼叫類的預設構造器來建立bean。等同於ExampleBean eb = new ExampleBean ();
2.注入bean的引用有兩種方式:構造器注入和setter方法注入
Ⅰ構造器注入
<bean id="exampleBean" class="example.ExampleBean">
<constructor-arg ref="***"> //ref是引用其他bean的id
<constructor-arg value="***">//value指的是以字面量的方式注入到構造器之中
</bean>
當spring遇到這個bean時會建立一個ExampleBean的例項,<constructor-arg>元素會告知Spring要將一個id為***的bean引用或者字面量***傳遞到ExampleBean的構造器之中。
如果類中的屬性是列表,注入時使用<list>標籤
<constructor-arg>
<list>
<value>***</value>
或者
<ref bean="***">
</list>
</constructor-arg>
作為替代方案,可以使用Spring的c-名稱空間,c-名稱空間是在Spring3.0中引入的。要使用它的話要在XML的頂部宣告其模式,xmlns:c="http://www.springframework.org/schema/c"。具體用法省略。
Ⅱ setter方法注入
<bean id="exampleBean" class="example.ExampleBean">
<property name=" " ref="***"> //ref是引用其他bean的id
<property name="" value="***">//value指的是以字面量的方式注入到屬性中
</bean>
<property>元素為屬性的setter方法提供功能,這個元素會告知Spring要將一個id為***的bean引用或者字面量***傳遞到setter方法之中。
作為替代方案,可以使用Spring的p-名稱空間,p-名稱空間是在Spring3.0中引入的。要使用它的話要在XML的頂部宣告其模式,xmlns:p="http://www.springframework.org/schema/p"。具體用法省略。
還可以使用Autowiring(在<beans>裡的屬性default-autowire="byName" "byType"等等)
No:不做任何操作
byName:根據屬性名自動裝配。此選項將檢查容器並根據名字查詢與屬性完全一致的bean,並將其與屬性自動裝配。
byType:如果容器中存在一個與指定屬性型別相同的bean,那麼將與該屬性自動裝配;如果存在多給該型別相同的bean,那麼丟擲異常。如果 沒有找到相匹配的bean,則什麼事都不發生。
Constructor:與byType方式相似,不同在於它應用於構造器引數。如果容器中沒有找到與構造器引數型別一致的bean,那麼丟擲異常。
Resources:針對於資原始檔的統一介面
①UrlResource:URL對應的資源,根據一個URL地址即可構建
②ClassPathResource:獲取類路徑下的資原始檔
③FileSystemResource:獲取檔案系統裡面的資源
ResourceLoader是對resource載入的一個類,所有的application contexts實現了ResourceLoader介面
public interface ResourceLoader{
Resource getResource(String location);
}
字首:classpath: file: http: none
//實現ApplicationContextAware介面,獲得ApplicationContext物件,呼叫getResource(String location)方法,獲得資源物件。
public class helloResource implements ApplicationContextAware{
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext)throws BeansException{
this.applicationContext=applicationContext;
}
public void resource(){
Resource resource = applicationContext.getResource("classpath:config.txt");
System.out.println(resource.getFilename());
System.out.println(resource.contentLength());
}
}
第二種是自動化裝配bean
元件掃描:Spring會自動發現應用上下文中所建立的bean
自動裝配:Spring會自動滿足bean之間的依賴
一、元件掃描
@component 表明該類會作為元件類,並告知Spring要為這個類建立bean,即註冊Bean到ApplicationContext中。
@component 是一個通用註解,可用於任何bean
@Repository @Service @Controller是更具有針對性的註解
@Repository通常用於註解DAO類,即持久層
@Service通常用於註解Service類,即服務層
@Controller通常用於註解Controller類,即控制層
但是元件掃描預設是不啟用的。我們還需要顯示配置一下Spring,從而命令它去尋找帶有@component @Repository @Service @Controller註解的類,併為其建立bean。
@ComponentScan註解啟用了元件掃描
@Configuration
@ComponentScan
public class HelloConfig{
}
如果沒有其他配置,@ComponentScan預設會掃描與配置類相同的包及其子包。但是如果想掃描不同的包,就需要設定基礎包@ComponentScan("***"),括號裡指明包的名稱。如果你想設定多個基礎包,可以通過basePackages屬性進行配置,要掃描的包為一個數組
@ComponentScan(basePackages={"***","***"}) 這種所設定的基礎包是以String型別表示的。型別不安全
還可以@ComponentScan(basePackageClasses={***.class,***.class}) 指定為包中所包含的類或介面。
還可以使用XML的配置方式啟用元件掃描,使用Spring Context名稱空間的<context:component-scan>元素。
<context:component-scan base-package="包名"></context:component-scan>
為元件掃描的bean命名
Bean的名稱是由BeanNameGenerator生成的。預設是將類名的第一個字母小寫。如果想指定這個Bean的ID,那麼@component("***"),括號裡為bean的id屬性值。
二、自動裝配
@Autowired註解,可以用於成員變數,setter方法和構造器上。滿足bean的依賴。
如果找不到匹配的bean,將會丟擲異常,可以通過@Autowired(required=false)來避免。這種情況下,如果沒有匹配的bean,Spring就會讓這個bean處於未裝配的狀態。如果在程式碼中沒有進行null檢查,這個處於未裝配狀態的bean就可能會出現NullPointerException。
@Autowired是由Spring BeanPostProcessor處理的,所以不能在自己的BeanPostProcessor或BeanFactoryPostProcessor型別應用這些註解,這些型別必須通過XML或者Spring的@Bean註解載入
處理自動裝配的歧義性
如果有多個bean滿足裝配,會產生歧義性。
標示首選bean,可以使用@Primary和@Component組合用在元件掃描的bean上,也可以與@Bean組合用在Java配置的bean宣告中。
限定自動裝配的bean,可以使用@Qualifier註解縮小範圍,@Qualifier("***")所設定的引數就是想要注入的bean的ID。這種情況下如果對類名稱進行修改,就會導致限定符失效。
所以我們可以建立自定義的限定符,為bean設定自己的限定符,而不是依賴於將bean ID作為限定符。在這裡需要做的就是在bean宣告上新增@Qualifier("***")註解,
第三種是通過Java程式碼裝配bean
有時候如果你想將第三方庫中的元件裝配到應用中,是沒辦法在類上新增@Component和@Autowired註解的,因此不能使用自動化裝配方案。在這種情況下就需要用顯示裝配方式了,有兩種,一種是基於XML的,一種是基於Java的。
建立配置類,在類上新增@Configuration註解,表明這個類是一個配置類。
在java配置類中宣告一個bean,需要用到@Bean註解,編寫一個方法,該方法返回建立所需型別的例項,在該方法上新增@Bean註解。預設情況下,bean的ID與帶有@Bean註解的方法名一樣,如果想指定bean的ID,@Bean(name="***")。
@Configuration
public class AppConfig{
@Bean
public MyService myService(){
return new MyServiceImpl();
}
}
@ImportResource(classpath:/com/properties.xml")引入資原始檔
public class AppConfig{
@Value("${jdbc.url}")從資原始檔中載入資源配置
private String url;
@Value("${jdbc.username}")從資原始檔中載入資源配置
private String username;
@Value("${jdbc.password}")從資原始檔中載入資源配置
private String password;
@Bean
public DataSource dataSource(){
return new DriverManagerDataSource(url,username,password);
}}<bean>的作用域
在預設情況下,Spring應用上下文中所有bean都是作為以單例的形式建立的。也就是說,不管給定的一個bean被注入到其他bean多少次,每次注入的都是同一個例項。如果想改變作用域,可以使用@Scope("***")註解或者<bean id="" class="" scope="">
singleton:單例,在整個應用中,只建立bean的一個例項
prototype:原型,每次注入或者通過Spring應用上下文獲取的時候,都會建立一個新的bean例項。
request:請求,在web應用中,為每個請求都建立一個bean例項,且僅在當前request內有效。
session:會話,在web應用中,為每個會話都建立一個bean例項,當前session內有效。
Bean的生命週期(定義 初始化 使用 銷燬)
初始化:實現org.springframework.beans.factory.InitializingBean介面,覆蓋afterPorpertiesSet方法
或者在<bean>標籤中配置init-method
銷燬:實現org.springframework.beans.factory.DisposableBean介面,覆蓋destroy方法。
或者在<bean>標籤中配置destroy-method
如果bean實現了BeanNameAware介面,Spring將bean的ID傳遞給setBeanName()方法
如果bean實現了BeanFactoryAware介面,Spring將呼叫setBeanFactory()方法,將BeanFactory容器例項傳入
如果bean實現了ApplicationContextAware介面,Spring將呼叫setApplicationContext()方法,將bean所在的應用上下文的引用傳入進來。