1. 程式人生 > >Spring的IOC總結

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

:從一個或多個基於java配置類中載入應用上下文。

②檔案方式:從檔案系統下的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所在的應用上下文的引用傳入進來。