Spring基礎教程

Spring IOC 容器

IoC容器

Spring容器是Spring框架的核心。容器創建對象,把他們連載一起,配置他們,並且管理他們的整個生命週期從創建到銷燬。Spring容器使用以來注入(DI)來管理組成應用程序的組件。這些對象被稱爲Spring Beans,

Spring提供了兩種不同類型的容器。

序號

容器&描述

1

Spring BeanFactory 容器

它是最簡單的容器,給 DI 提供了基本的支持,它用 org.springframework.beans.factory.BeanFactory 接口來定義。BeanFactory 或者相關的接口,如 BeanFactoryAwareInitializingBeanDisposableBean,在 Spring 中仍然存在具有大量的與 Spring 整合的第三方框架的反向兼容性的目的

2

Spring ApplicationContext 容器

該容器添加了更多的企業特定的功能,例如從一個屬性文件中解析文本信息的能力,發佈應用程序事件給感興趣的事件監聽器的能力。該容器是由 org.springframework.context.ApplicationContext 接口定義。

ApplicationContext 容器包括 BeanFactory 容器的所有功能,所以通常建議超過 BeanFactory。BeanFactory 仍然可以用於輕量級的應用程序,如移動設備或基於 applet 的應用程序,其中它的數據量和速度是顯著。

Spring BeanFactory 容器

Sping 的 BeanFactory容器

這是一個最簡單的容器,它主要的功能是爲依賴注入 (DI) 提供支持,這個容器接口在 org.springframework.beans.factory.BeanFactor 中被定義。 BeanFactory 和相關的接口,比如BeanFactoryAware、 DisposableBean、InitializingBean,仍舊保留在 Spring 中,主要目的是向後兼容已經存在的和那些 Spring 整合在一起的第三方框架。

在 Spring 中,有大量對BeanFactory 接口的實現。其中,最常被使用的是 XmlBeanFactory 類。這個容器從一個 XML 文件中讀取配置元數據,由這些元數據來生成一個被配置化的系統或者應用。

在資源寶貴的移動設備或者基於 applet 的應用當中, BeanFactory 會被優先選擇。否則,一般使用的是 ApplicationContext,除非你有更好的理由選擇 BeanFactory。

在主程序當中,我們需要注意以下兩點:

第一步利用框架提供的 XmlBeanFactory() API 去生成工廠 bean 以及利用 ClassPathResource() API 去加載在路徑 CLASSPATH 下可用的 bean 配置文件。XmlBeanFactory() API 負責創建並初始化所有的對象,即在配置文件中提到的 bean。

第二步利用第一步生成的 bean 工廠對象的 getBean() 方法得到所需要的 bean。 這個方法通過配置文件中的 bean ID 來返回一個真正的對象,該對象最後可以用於實際的對象。一旦得到這個對象,就可以利用這個對象來調用任何方法。

Spring ApplicationContext 容器

Application Context 是 spring 中較高級的容器。和 BeanFactory 類似,它可以加載配置文件中定義的 bean,將所有的 bean 集中在一起,當有請求的時候分配 bean。 另外,它增加了企業所需要的功能,比如,從屬性文件中解析文本信息和將事件傳遞給所指定的監聽器。這個容器在 org.springframework.context.ApplicationContext interface 接口中定義。

ApplicationContext 包含 BeanFactory 所有的功能,一般情況下,相對於 BeanFactory,ApplicationContext 會更加優秀。當然,BeanFactory 仍可以在輕量級應用中使用,比如移動設備或者基於 applet 的應用程序。

最常被使用的 ApplicationContext 接口實現:

FileSystemXmlApplicationContext:該容器從 XML 文件中加載已被定義的 bean。在這裏,你需要提供給構造器 XML 文件的完整路徑。

ClassPathXmlApplicationContext:該容器從 XML 文件中加載已被定義的 bean。在這裏,你不需要提供 XML 文件的完整路徑,只需正確配置 CLASSPATH 環境變量即可,因爲,容器會從 CLASSPATH 中搜索 bean 配置文件。

WebXmlApplicationContext:該容器會在一個 web 應用程序的範圍內加載在 XML 文件中已被定義的 bean。

我們已經在 Spring Hello World Example章節中看到過 ClassPathXmlApplicationContext 容器,並且,在基於spring 的 web 應用程序這個獨立的章節中,我們討論了很多關於 XmlWebApplicationContext。所以,接下來,讓我們看一個關於FileSystemXmlApplicationContext 的例子。

public class TestHello {

       publicstatic void main(String[] args) {

              //直接調用的方式

   Hello hello = new Hello();

              hello.setMessage("測試");

              System.out.println(hello.toString());

              //通過Spring

              ApplicationContextcontext = newClassPathXmlApplicationContext("com/imnu/test/bean.xml");

              Hellohe = (Hello)context.getBean("hello");

              System.out.println(he.toString());

             

              //通過BeanFactory

              BeanFactorybeanFactory = new XmlBeanFactory(newClassPathResource("com/imnu/test/bean.xml"));

              Helloh = (Hello) beanFactory.getBean("hello");

              System.out.println(h.toString());

       }

}

Spring Bean 定義

Bean 定義

被稱作 bean 的對象是構成應用程序的支柱也是由 Spring IoC 容器管理的。bean 是一個被實例化,組裝,並通過 Spring IoC 容器所管理的對象。這些 bean 是由用容器提供的配置元數據創建的,例如,已經在先前章節看到的,在 XML 的表單中的 定義。

bean 定義包含稱爲配置元數據的信息,下述容器也需要知道配置元數據:

如何創建一個 bean

bean 的生命週期的詳細信息

bean 的依賴關係

上述所有的配置元數據轉換成一組構成每個 bean 定義的下列屬性。

Class

這個屬性是強制性的,並且指定用來創建 bean 的 bean 類。

Name

這個屬性指定唯一的 bean 標識符。在基於 XML 的配置元數據中,你可以使用 ID 和/或 name 屬性來指定 bean 標識符。

Scope

這個屬性指定由特定的 bean 定義創建的對象的作用域,它將會在 bean 作用域的章節中進行討論。

Constructor-arg

它是用來注入依賴關係的,並會在接下來的章節中進行討論。

properties

它是用來注入依賴關係的,並會在接下來的章節中進行討論。

autowiring mode

它是用來注入依賴關係的,並會在接下來的章節中進行討論。

lazy-initialization mode

延遲初始化的 bean 告訴 IoC 容器在它第一次被請求時,而不是在啓動時去創建一個 bean 實例。

initialization

在 bean 的所有必需的屬性被容器設置之後,調用回調方法。它將會在 bean 的生命週期章節中進行討論。

destruction

當包含該 bean 的容器被銷燬時,使用回調方法。它將會在 bean 的生命週期章節中進行討論。

Spring 配置元數據

Spring IoC 容器完全由實際編寫的配置元數據的格式解耦。有下面三個重要的方法把配置元數據提供給 Spring 容器:

基於 XML 的配置文件。

基於註解的配置

基於 Java 的配置

你已經看到了如何把基於 XML 的配置元數據提供給容器,但是讓我們看看另一個基於 XML 配置文件的例子,這個配置文件中有不同的 bean 定義,包括延遲初始化,初始化方法和銷燬方法的:

<?xml version="1.0"encoding="UTF-8"?>

<beansxmlns="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-3.0.xsd">

  <!-- A simple bean definition -->

  <bean id="..." class="...">

      <!-- collaborators and configuration for this bean go here -->

  </bean>

  <!-- A bean definition with lazy init set on -->

  <bean id="..." class="..."lazy-init="true">

      <!-- collaborators and configuration for this bean go here -->

  </bean>

  <!-- A bean definition with initialization method -->

  <bean id="..." class="..."init-method="...">

      <!-- collaborators and configuration for this bean go here -->

  </bean>

  <!-- A bean definition with destruction method -->

  <bean id="..." class="..."destroy-method="...">

      <!-- collaborators and configuration for this bean go here -->

  </bean>

  <!-- more bean definitions go here -->

</beans>

Spring Bean 作用域

Bean 的作用域

當在 Spring 中定義一個bean 時,你必須聲明該 bean 的作用域的選項。例如,爲了強制 Spring 在每次需要時都產生一個新的 bean 實例,你應該聲明 bean 的作用域的屬性爲 prototype。同理,如果你想讓 Spring 在每次需要時都返回同一個bean實例,你應該聲明 bean 的作用域的屬性爲 singleton。

Spring 框架支持以下五個作用域,如果你使用 web-aware ApplicationContext 時,其中三個是可用的。

作用域

描述

singleton

在spring IoC容器僅存在一個Bean實例,Bean以單例方式存在,默認值

prototype

每次從容器中調用Bean時,都返回一個新的實例,即每次調用getBean()時,相當於執行newXxxBean()

request

每次HTTP請求都會創建一個新的Bean,該作用域僅適用於WebApplicationContext環境

session

同一個HTTP Session共享一個Bean,不同Session使用不同的Bean,僅適用於WebApplicationContext環境

global-session

一般用於Portlet應用環境,改作用於僅適用於WebApplicationContext環境

singleton 作用域:

 

當一個bean的作用域爲Singleton,那麼Spring IoC容器中只會存在一個共享的bean實例,並且所有對bean的請求,只要id與該bean定義相匹配,則只會返回bean的同一實例。

Singleton是單例類型,就是在創建起容器時就同時自動創建了一個bean的對象,不管你是否使用,他都存在了,每次獲取到的對象都是同一個對象。注意,Singleton作用域是Spring中的缺省作用域。你可以在 bean 的配置文件中設置作用域的屬性爲 singleton,如下所示:

<!-- A bean definition with singletonscope -->

<bean id="..."class="..." scope="singleton">

   <!-- collaborators and configuration for this bean go here -->

</bean>

         <bean id="hello"class="com.imnu.test.Hello" scope="singleton">

       <property name="message" value="wangqing"/>

   </bean>

   

              BeanFactorybeanFactory = new XmlBeanFactory(newClassPathResource("com/imnu/test/bean.xml"));

              Helloh = (Hello) beanFactory.getBean("hello");

              h.setMessage("nihao");

              Hellos = (Hello) beanFactory.getBean("hello");

              System.out.println(h.toString());

              System.out.println(s.toString());

             

              輸出:Hello [message=nihao]

         Hello [message=nihao]

prototype 作用域

 

當一個bean的作用域爲Prototype,表示一個bean定義對應多個對象實例。Prototype作用域的bean會導致在每次對該bean請求(將其注入到另一個bean中,或者以程序的方式調用容器的getBean()方法)時都會創建一個新的bean實例。Prototype是原型類型,它在我們創建容器的時候並沒有實例化,而是當我們獲取bean的時候纔會去創建一個對象,而且我們每次獲取到的對象都不是同一個對象。根據經驗,對有狀態的bean應該使用prototype作用域,而對無狀態的bean則應該使用singleton作用域。

爲了定義 prototype 作用域,你可以在 bean 的配置文件中設置作用域的屬性爲 prototype,如下所示:

<!-- A bean definition with singletonscope -->

<bean id="..." class="..."scope="prototype">

  <!-- collaborators and configuration for this bean go here -->

</bean>

<bean id="hello"class="com.imnu.test.Hello" scope="prototype">

    <property name="message" value="wangqing"/>

</bean>

   

              BeanFactorybeanFactory = new XmlBeanFactory(newClassPathResource("com/imnu/test/bean.xml"));

              Helloh = (Hello) beanFactory.getBean("hello");

              h.setMessage("nihao");

              Hellos = (Hello) beanFactory.getBean("hello");

              System.out.println(h.toString());

              System.out.println(s.toString());

             

              輸出:Hello [message=nihao]

              Hello [message=wangqing]

Spring Bean 生命週期

理解Spring bean的生命週期相當容易。當一個bean被實例化時,它可能要執行一些初始化使他轉成可用的狀態。童謠,當bean

爸在需要的時候,並且從容器中移除時,可能需要做一些清理工作。

儘管有一些在bean的實例化和銷燬直接的活動,但是本章只討論兩個重要的生命週期的回到方法,他們在bean的初始化方法中和銷燬的時候是必須的。

爲了定義安裝和拆卸一個bean,我們只要聲明帶有 init-method 和/或destroy-method 參數的 。init-method 屬性指定一個方法,在實例化 bean 時,立即調用該方法。同樣,destroy-method 指定一個方法,只有從容器中移除 bean 之後,才能調用該方法。

初始化回調

 

org.springframework.beans.factory.InitializingBean接口指定一個單一的方法:

void afterPropertiesSet() throws Exception;

因此,你可以簡單地實現上述接口和初始化工作可以在afterPropertiesSet() 方法中執行,如下所示:

public class ExampleBean implementsInitializingBean {

  public void afterPropertiesSet() {

     // do some initialization work

   }

}

在基於 XML 的配置元數據的情況下,你可以使用 init-method 屬性來指定帶有 void 無參數方法的名稱。例如:

<bean id="exampleBean"

        class="examples.ExampleBean" init-method="init"/>

下面是類的定義:

public class ExampleBean {

  public void init() {

     // do some initialization work

   }

}

銷燬回調

 

org.springframework.beans.factory.DisposableBean接口指定一個單一的方法:

void destroy() throws Exception;

因此,你可以簡單地實現上述接口並且結束工作可以在 destroy() 方法中執行,如下所示:

public class ExampleBean implementsDisposableBean {

  public void destroy() {

     // do some destruction work

   }

}

在基於 XML 的配置元數據的情況下,你可以使用 destroy-method 屬性來指定帶有 void 無參數方法的名稱。例如:

<bean id="exampleBean"

        class="examples.ExampleBean"destroy-method="destroy"/>

下面是類的定義:

public class ExampleBean {

  public void destroy() {

     // do some destruction work

   }

}

如果你在非 web 應用程序環境中使用Spring 的 IoC 容器;例如在豐富的客戶端桌面環境中;那麼在 JVM 中你要註冊關閉 hook。這樣做可以確保正常關閉,爲了讓所有的資源都被釋放,可以在單個 beans 上調用 destroy 方法。

建議你不要使用 InitializingBean 或者 DisposableBean 的回調方法,因爲 XML 配置在命名方法上提供了極大的靈活性。

 

public class Hello {

       privateString message;

 

       publicString getMessage() {

              returnmessage;

       }

 

       publicvoid setMessage(String message) {

              this.message= message;

       }

 

       publicvoid init() {

              System.out.println("Beanis going through init.");

       }

 

       publicvoid destroy() {

              System.out.println("Beanwill destroy now.");

       }

 

       @Override

       publicString toString() {

              return"Hello [message=" + message + "]";

       }

 

}

 

       <beanid="hello" class="com.imnu.test.Hello"scope="singleton" init-method="init"destroy-method="destroy">

              <propertyname="message" value="wangqing" />

       </bean>

      

       publicclass TestHello {

 

       publicstatic void main(String[] args) {

              //直接調用的方式

/*           Hellohello = new Hello();

              hello.setMessage("測試");

              System.out.println(hello.toString());*/

             

              //通過Spring

              ApplicationContextcontext = newClassPathXmlApplicationContext("com/imnu/test/bean.xml");

              Hellohe = (Hello)context.getBean("hello");

              System.out.println(he.toString());

              ((AbstractApplicationContext)context).registerShutdownHook();

             

              //通過BeanFactory

/*           BeanFactorybeanFactory = new XmlBeanFactory(newClassPathResource("com/imnu/test/bean.xml"));

              Helloh = (Hello) beanFactory.getBean("hello");

              h.setMessage("nihao");

              Hellos = (Hello) beanFactory.getBean("hello");

              System.out.println(h.toString());

              System.out.println(s.toString());*/

       }

}

Spring Bean 後置處理器

 beanPostProcessor接口定義回調方法,你可以實現該方法來提供知己的實例化邏輯,依賴解析邏輯等。你

 也可以在Spring容器通過插入一個或多個 BeanPostProcessor 的實現來完成實例化,配置和初始化一個bean之後實現一些自定義邏輯回調方法。

你可以配置多個 BeanPostProcesso r接口,通過設置 BeanPostProcessor 實現的 Ordered 接口提供的 order 屬性來控制這些 BeanPostProcessor 接口的執行順序。

BeanPostProcessor 可以對 bean(或對象)實例進行操作,這意味着 Spring IoC 容器實例化一個 bean 實例,然後 BeanPostProcessor 接口進行它們的工作。

ApplicationContext 會自動檢測由 BeanPostProcessor 接口的實現定義的 bean,註冊這些 bean 爲後置處理器,然後通過在容器中創建 bean,在適當的時候調用它。

public class InitHelloWorld implementsBeanPostProcessor {

 

       @Override

       publicObject postProcessAfterInitialization(Object bean, String beanName) throwsBeansException {

              System.out.println("BeforeInitialization: " + beanName);

              returnbean; // you can return any other object as well

       }

 

       @Override

       publicObject postProcessBeforeInitialization(Object bean, String beanName) throwsBeansException {

              System.out.println("AfterInitialization: " + beanName);

              returnbean; // you can return any other object as well

 

       }

}

      

      

       <beanid="hello" class="com.imnu.test.Hello"scope="singleton"

              init-method="init"destroy-method="destroy">

              <propertyname="message" value="wangqing" />

       </bean>

       <!--<bean id="hello" class="com.imnu.test.Hello"scope="singleton"> <property

              name="message"value="wangqing" /> </bean> -->

       <beanclass="com.imnu.test.InitHelloWorld" />

 

              //通過Spring

              ApplicationContextcontext = newClassPathXmlApplicationContext("com/imnu/test/bean.xml");

              Hellohe = (Hello)context.getBean("hello");

              System.out.println(he.toString());

              ((AbstractApplicationContext)context).registerShutdownHook();

             

              打印

              AfterInitialization: hello

Bean is going through init.

BeforeInitialization : hello

Hello [message=wangqing]

七月 02, 2018 2:04:44 下午 org.springframework.context.support.AbstractApplicationContextdoClose

信息: Closingorg.springframework.context.support.ClassPathXmlApplicationContext@c4437c4:startup date [Mon Jul 02 14:04:43 CST 2018]; root of context hierarchy

Bean will destroy now.

 

 

在Spring機制中可以指定後置處理器調用順序,通過讓BeanPostProcessor接口實現類實現Ordered接口getOrder方法,該方法返回一整數,默認值爲 0,優先級最高,值越大優先級越低

public class InitHelloWorld implementsBeanPostProcessor,Ordered {

       @Override

       publicObject postProcessAfterInitialization(Object bean, String beanName) throwsBeansException {

              System.out.println("BeforeInitialization: " + beanName);

              returnbean; // you can return any other object as well

       }

       @Override

       publicObject postProcessBeforeInitialization(Object bean, String beanName) throwsBeansException {

              System.out.println("AfterInitialization: " + beanName);

              returnbean; // you can return any other object as well

       }

 

       @Override

       publicint getOrder() {

              //TODO Auto-generated method stub

              return1;

       }

}

Spring Bean 定義繼承

Bean 定義繼承

bean 定義可以包含很多的配置信息,包括構造函數的參數,屬性值,容器的具體信息例如初始化方法,靜態工廠方法名,等等。

子 bean 的定義繼承父定義的配置數據。子定義可以根據需要重寫一些值,或者添加其他值。

Spring Bean 定義的繼承與Java 類的繼承無關,但是繼承的概念是一樣的。你可以定義一個父 bean 的定義作爲模板和其他子 bean 就可以從父 bean 中繼承所需的配置。

當你使用基於 XML 的配置元數據時,通過使用父屬性,指定父 bean 作爲該屬性的值來表明子 bean 的定義。

       <beanid="hello" class="com.imnu.test.Hello"scope="prototype"

              init-method="init"destroy-method="destroy">

              <propertyname="message1" value="wangqing" />

              <propertyname="message2" value="wangqing12321" />

       </bean>

       <beanid="helloWq" class="com.imnu.test.HelloWq"parent="hello">

           <property name="message2">

              <value>"haodd"</value>

           </property>

       </bean>

Bean 定義模板

你可以創建一個 Bean 定義模板,不需要花太多功夫它就可以被其他子 bean 定義使用。在定義一個 Bean 定義模板時,你不應該指定類的屬性,而應該指定帶 true 值的抽象屬性,如下所示:

       <beanid="hello" abstract="true" scope="prototype"

              init-method="init"destroy-method="destroy">

              <propertyname="message1" value="wangqing" />

              <propertyname="message2" value="wangqing12321" />

       </bean>

       <beanid="helloWq" class="com.imnu.test.HelloWq"parent="hello">

           <property name="message2">

              <value>"haodd"</value>

           </property>

       </bean>

父 bean 自身不能被實例化,因爲它是不完整的,而且它也被明確地標記爲抽象的。當一個定義是抽象的,它僅僅作爲一個純粹的模板 bean 定義來使用的,充當子定義的父定義使用。

Spring 依賴注入

依賴注入

每個基於應用程序的 java 都有幾個對象,這些對象一起工作來呈現出終端用戶所看到的工作的應用程序。當編寫一個複雜的 Java 應用程序時,應用程序類應該儘可能獨立於其他 Java 類來增加這些類重用的可能性,並且在做單元測試時,測試獨立於其他類的獨立性。依賴注入(或有時稱爲佈線)有助於把這些類粘合在一起,同時保持他們獨立。

假設你有一個包含文本編輯器組件的應用程序,並且你想要提供拼寫檢查。標準代碼看起來是這樣的:

public class TextEditor {

  private SpellChecker spellChecker; 

  public TextEditor() {

     spellChecker = new SpellChecker();

   }

}

在這裏我們所做的就是創建一個 TextEditor 和 SpellChecker 之間的依賴關係。在控制反轉的場景中,我們反而會做這樣的事情:

public class TextEditor {

  private SpellChecker spellChecker;

  public TextEditor(SpellChecker spellChecker) {

     this.spellChecker = spellChecker;

   }

}

在這裏,TextEditor 不應該擔心 SpellChecker 的實現。SpellChecker 將會獨立實現,並且在 TextEditor 實例化的時候將提供給 TextEditor,整個過程是由 Spring 框架的控制。

在這裏,我們已經從 TextEditor 中刪除了全面控制,並且把它保存到其他地方(即 XML 配置文件),且依賴關係(即 SpellChecker 類)通過類構造函數被注入到 TextEditor 類中。因此,控制流通過依賴注入(DI)已經“反轉”,因爲你已經有效地委託依賴關係到一些外部系統。

依賴注入的第二種方法是通過 TextEditor 類的 Setter 方法,我們將創建 SpellChecker 實例,該實例將被用於調用 setter 方法來初始化 TextEditor 的屬性。

因此,DI 主要有兩種變體和下面的兩個子章將結合實例涵蓋它們:

序號

依賴注入類型 & 描述

1

Constructor-based dependency injection

當容器調用帶有多個參數的構造函數類時,實現基於構造函數的 DI,每個代表在其他類中的一個依賴關係。

2

Setter-based dependency injection

基於 setter 方法的 DI 是通過在調用無參數的構造函數或無參數的靜態工廠方法實例化 bean 之後容器調用 beans 的 setter 方法來實現的。

 

你可以混合這兩種方法,基於構造函數和基於 setter 方法的 DI,然而使用有強制性依存關係的構造函數和有可選依賴關係的 sette r是一個好的做法。

代碼是 DI 原理的清洗機,當對象與它們的依賴關係被提供時,解耦效果更明顯。對象不查找它的依賴關係,也不知道依賴關係的位置或類,而這一切都由 Spring 框架控制的。

依賴注入:讓調用類對某一接口實現類的依賴關係由第三方注入,以移除調用類對某一接口實現類的依賴。

接下來將詳細的向大家介紹Spring容器支持的三種依賴注入的方式以及具體配置方法:

•    屬性注入方法

•    構造函數注入方法

•    工廠方法注入方法

屬性注入

屬性注入即通過setXXX()方法注入Bean的屬性值或者依賴對象,由於屬性注入方式具有可選擇性和靈活高的優點,因此屬性注入是實際中最常採用的注入方式。

Spring首先會調用bean的默認構造函數實例化bean對象,然後再通過反射的方法來調用set方法來注入屬性值。

屬性注入要求bean提供一個默認的構造函數,並且得爲需要注入的屬性提供set方法。

 

注意:所謂默認構造函數是指不帶參的構造函數

JAVA中定義:

如果類中沒有定義任何的構造函數,則JAVA虛擬機自動爲其生成一個默認的構造函數。反之,如果類中顯式的定義了構造函數,則JAVA虛擬機便不會在爲其生成構造函數。

其中每一個屬性值對應一個property標籤,name爲屬性的名稱。

在bean實現類中擁有與其對應的實現方法setBrand()。

注意:Spring只會檢查bean中是否有setter方法,而是否有對應的屬性變量則不做具體要求,但按照約定俗成的規則我們最好爲其設定相應的屬性變量。

補充: Spring <property>元素的命名規範:

Spring配置文件中<property>元素所指定的屬性名和Bean實現類的Setter方法滿足SunJavaBean的屬性命名規範,即xxx的屬性對應setXxx()的方法。一般情況下,java的屬性變量名都以小寫字母開頭,但考慮到一些特殊意義的英文縮略詞,javabean也允許一些大寫字母開頭的變量名。但必須滿足特點的條件:

變量的前兩個字母要麼全部大寫,要麼全部小寫

但以編程經驗來說:最好屬性名全部使用小寫字母,方便編程。

       <beanid="helloWq" class="com.imnu.test.HelloWq"parent="hello">

           <property name="message2">

              <value>"haodd"</value>

           </property>

       </bean>

      

       publicclass Hello {

       privateString message1;

       privateString message2;

       publicString getMessage1() {

              returnmessage1;

       }

       publicvoid setMessage1(String message1) {

              this.message1= message1;

       }

       publicString getMessage2() {

              returnmessage2;

       }

       publicvoid setMessage2(String message2) {

              this.message2= message2;

       }

       }

構造函數注入

構造函數注入是除屬性注入之外的另一種常用的注入方式,它保證一些必要的屬性在Bean實例化時就得到了設置,並在實例化後就可以使用。

使用構造函數注入的前提是:bean必須提供帶參的構造函數。

對於構造函數注入,配置文件可以有以下幾種方式:

按類型匹配入參

按索引匹配入參

聯合使用類型和索引匹配入參

通過自身類型反射匹配入參

按類型匹配入參

public class Car {

       privateString brand;

       privatedouble price;

       publicCar(String brand,double price) {

              this.brand= brand;

              this.price= price;

       }

       @Override

       publicString toString() {

              return"Car [brand=" + brand + ", price=" + price + "]";

       }

}

bean

 

       <beanid="car" class="com.imnu.test.Car">

           <constructor-argtype="String">

               <value>紅旗</value>

           </constructor-arg>

           <constructor-argtype="double">

               <value>999999999</value>

           </constructor-arg>

       </bean>

Spring的配置文件採用和元素標籤順序無關的配置策略。這種策略可以在一定程度上保證配置信息的確定性。

那麼當bean中的構造函數的多個參數類型一樣時,按照類型匹配入參的這種方式便會產生混淆,那麼我們便來看一看另外一種方式:按照索引匹配入參。

按照索引匹配入參方法

 

<bean id="Car"class="cn.lovepi.chapter02.reflect.Car">

       <constructor-arg index="0" value="紅旗CA72"/>

       <constructor-arg index="1" value="中國一汽"/>

       <constructor-arg index="2" value="2666"/>

</bean>

注意:

在屬性注入時,Spring按javabean 的規範確定配置屬性和對應的setter方法,並使用java反射機制調用屬性的setter方法完成屬性注入。但java反射機制並不會記住構造函數的入參名。因此我們不能通過指定構造函數的入參名稱來進行構造函數的配置,所以我們只能通過入參的類型及其索引來間接的完成構造函數的屬性注入。

 

聯合使用類型和索引匹配入參

在某些複雜的配置文件當中,需要使用type和index同時出馬才能完成構造函數的參數注入。那麼接下里我們看一下下面的這個例子:   

<bean id="Car"class="cn.lovepi.chapter02.reflect.Car">

       <constructor-arg index="0" type="String">

           <value>紅旗CA72</value>

       </constructor-arg>

       <constructor-arg index="1" type="String">

           <value>中國一汽</value>

       </constructor-arg>

       <constructor-arg index="2" type="int">

           <value>200</value>

       </constructor-arg>

</bean>

可以看到其實重點在於第三個入參的類型,所以我們在配置文件中對其指定了索引和類型,這樣便使得Spring可以知道對那個構造函數進行參數注入了。

注意:

假如我們的配置文件中存在歧義問題,Spring容器是可以正常啓動的。並不會報錯,他將隨機採用一個匹配的構造函數實例化bean。而隨機選擇的構造函數可能並不是用戶所需要的,所以我們在編程時必須小心避免出現這種歧義情況的出現。

通過自身類型反射匹配入參

如果bean構造函數入參的類型是可辨別的,由於java反射機制可以獲取構造函數入參的類型,即使構造函數的注入不提供類型和索引的信息,Spring依舊可以完成構造函數信息的注入。

 

下面的示例代碼當中的Boss類的構造函數的入參類型就是可以辨別的:

public class Boss {

   private String name;

   private Car car;

   private Office office;

   public Boss(String name, Car car, Office office) {

       this.name = name;

       this.car = car;

       this.office = office;

    }

}

<bean id="Boss"class="cn.lovepi.chapter03.scope.Boss">

       <constructor-arg>

           <value>wang</value>

       </constructor-arg>

       <constructor-arg>

           <ref bean="car"/>

       </constructor-arg>

       <constructor-arg>

           <ref bean="office"/>

       </constructor-arg>

</bean>

以上的幾種方法都可以實現構造函數參數的注入,但是爲了避免問題的發生,建議還是使用顯示的index和type來配置構造函數的入參信息。

工廠方法注入方法

工廠方法是應用中被經常使用的設計模式,也是控制反轉和單實例設計思想的主要實現方法。

工廠類負責創建一個或多個工廠類實例,工廠類方法一般以接口或抽象類變量的形式返回目標類實例。

工廠類對外屏蔽了目標類的實例化步驟,調用者甚至根本不用指定具體的目標類是什麼。

由於Spring容器以框架的方法提供工廠方法的功能,並以透明的方式開放給開發者。因此很少需要手工編寫工程方法。但在一些遺留系統或第三方類庫中還是會碰到工廠方法,此時便可以使用Spring工廠注入的方法來進行Spring的注入。

Spring工廠注入的方法可以分爲靜態和非靜態兩種:

非靜態工廠方式

有些工廠方法是非靜態的,則必須實例化工廠類後才能調用工廠方法。

下面來看一個代碼示例:

public class CarFactory {

   public Car createHonQi() {

           Car car = new Car("紅旗", 99932424);

           return car;

    }

}

 

       <beanid="carfactory"class="com.imnu.test.CarFactory"></bean>

       <beanid="car" factory-bean="carfactory"factory-method="createHonQi"></bean>

由於carFactory的工廠方法不是靜態的,所以首先需要定義一個工廠類的bean,然後通過factory-bean這個屬性來引用工廠bean實例。在通過屬性factory-method來指定對應的工廠方法。

靜態工廠方法

很多工廠類方法都是靜態的,這意味在無須創建工廠類實例的情況下就可以調用工廠類方法。

因此靜態工廠方法比非靜態工廠方法的調用更加方便。

那麼我們來看靜態工廠方法的bean代碼:

public class CarFactory {

   public static Car createHonQi() {

           Car car = new Car("紅旗", 99932424);

           return car;

    }

}

<bean id="car"class="com.imnu.test.CarFactory"factory-method="createHonQi"></bean>

總結:

Spring提供了三種可供選擇的注入方式,在實際應用中,我們究竟應該選擇哪種注入方式,並沒有統一的標準,如下是一些可以參考的理由:

構造函數注入理由:

構造函數保證重要屬性預先設置

無需提供每個屬性Setter方法,減少類的方法個數

可更好的封裝類變量,避免外部錯誤調用

屬性注入理由:

屬性過多時,構造函數變的臃腫可怕

構造函數注入靈活性不強,有時需要爲屬性注入null值

多個構造函數時,配置上產生歧義,複雜度升高

構造函數不利於類的繼承和擴展

構造函數注入會引起循環依賴的問題

 

其實,Spring爲我們注入參數提供了這些多方法那麼他們必然有他們在某一問題上的優勢性,那麼我們只需按照我們具體的使用需求選擇適合我們的方法來使用就好了,但在這裏不推薦的方法就是工廠方法注入。

Bean與Bean之間的依賴

public class Car {

       privateHello hello;

       privateString brand;

       privatedouble price;

       publicCar(String brand, double price,Hello hello ) {

              this.brand= brand;

              this.price= price;

              this.hello= hello;

       }

       @Override

       publicString toString() {

              return "Car [hello=" +hello.toString()+ ", brand=" + brand + ", price=" + price +"]";

       }

}

public class Hello {

       privateString message1;

       privateString message2;

      

       publicString getMessage1() {

              returnmessage1;

       }

 

       publicvoid setMessage1(String message1) {

              this.message1= message1;

       }

 

       publicString getMessage2() {

              returnmessage2;

       }

 

       publicvoid setMessage2(String message2) {

              this.message2= message2;

       }

 

       publicvoid init() {

              System.out.println("Beanis going through init.");

       }

 

       publicvoid destroy() {

              System.out.println("Beanwill destroy now.");

       }

 

       @Override

       publicString toString() {

              return"Hello [message1=" + message1 + ", message2=" + message2 +"]";

       }

      

}     

         

bean的注入方式

    <bean id="hello"class="com.imnu.test.Hello">

        <property name="message1"value="nifisfids"></property>

        <property name="message2"value="2018"></property>

    </bean>

    <bean id="car"class="com.imnu.test.Car">

        <constructor-arg index="0"value="hongqi"></constructor-arg>

       <constructor-arg index="1"value="1231"></constructor-arg>

        <constructor-arg index="2"ref="hello"></constructor-arg>

    </bean>

             ApplicationContext context = newClassPathXmlApplicationContext("com/imnu/test/bean.xml");

              Carhe = (Car)context.getBean("car");

              System.out.println(he.toString());

              ((AbstractApplicationContext)context).registerShutdownHook();

使用 p-namespace 實現 XML 配置:

 

如果你有許多的設值函數方法,那麼在 XML 配置文件中使用 p-namespace 是非常方便的。讓我們查看一下區別:

以帶有 標籤的標準 XML 配置文件爲例:

<?xml version="1.0" encoding="UTF-8"?>

 

<beansxmlns="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-3.0.xsd">

 

  <bean id="john-classic"class="com.example.Person">

     <property name="name" value="John Doe"/>

     <property name="spouse" ref="jane"/>

  </bean>

 

  <bean name="jane" class="com.example.Person">

     <property name="name" value="John Doe"/>

  </bean>

 

</beans>

上述 XML 配置文件可以使用p-namespace 以一種更簡潔的方式重寫,如下所示:

<?xml version="1.0"encoding="UTF-8"?>

 

<beansxmlns="http://www.springframework.org/schema/beans"

   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

   xmlns:p="http://www.springframework.org/schema/p"

   xsi:schemaLocation="http://www.springframework.org/schema/beans

   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

 

  <bean id="john-classic"class="com.example.Person"

     p:name="John Doe"

     p:spouse-ref="jane"/>

  </bean>

 

  <bean name="jane" class="com.example.Person"

     p:name="John Doe"/>

  </bean>

 

</beans>

在這裏,你不應該區別指定原始值和帶有 p-namespace 的對象引用。-ref 部分表明這不是一個直接的值,而是對另一個 bean 的引用。

 

Spring 注入內部 Beans

 

正如你所知道的 Java 內部類是在其他類的範圍內被定義的,同理,inner beans 是在其他 bean 的範圍內定義的 bean。因此在 或 元素內 元素被稱爲內部bean,如下所示。

<?xml version="1.0"encoding="UTF-8"?>

 

<beansxmlns="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-3.0.xsd">

 

  <bean id="outerBean" class="...">

     <property name="target">

        <bean id="innerBean" class="..."/>

     </property>

  </bean>

 

注入bean的配置

 

  <bean id="textEditor"class="com.tutorialspoint.TextEditor">

     <property name="spellChecker">

        <bean id="spellChecker"class="com.tutorialspoint.SpellChecker"/>

      </property>

  </bean>

Spring 注入集合

你已經看到了如何使用 value 屬性來配置基本數據類型和在你的 bean 配置文件中使用<property>標籤的 ref 屬性來配置對象引用。這兩種情況下處理奇異值傳遞給一個 bean。

現在如果你想傳遞多個值,如 Java Collection 類型 List、Set、Map 和 Properties,應該怎麼做呢。爲了處理這種情況,Spring 提供了四種類型的集合的配置元素,如下所示:

元素

描述

<list>

它有助於連線,如注入一列值,允許重複。

<set>

它有助於連線一組值,但不能重複。

<map>

它可以用來注入名稱-值對的集合,其中名稱和值可以是任何類型。

<props>

它可以用來注入名稱-值對的集合,其中名稱和值都是字符串類型。

public class JavaCollection {

  List addressList;

  Set  addressSet;

  Map  addressMap;

  Properties addressProp;

   ……省略get/set

}

 

<?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-3.0.xsd">

  <!-- Definition for javaCollection -->

  <bean id="javaCollection"class="com.tutorialspoint.JavaCollection">

     <!-- results in a setAddressList(java.util.List) call -->

     <property name="addressList">

        <list>

           <value>INDIA</value>

           <value>Pakistan</value>

           <value>USA</value>

           <value>USA</value>

        </list>

     </property>

     <!-- results in a setAddressSet(java.util.Set) call -->

     <property name="addressSet">

        <set>

           <value>INDIA</value>

           <value>Pakistan</value>

           <value>USA</value>

           <value>USA</value>

       </set>

     </property>

     <!-- results in a setAddressMap(java.util.Map) call -->

     <property name="addressMap">

        <map>

           <entry key="1" value="INDIA"/>

           <entry key="2" value="Pakistan"/>

           <entry key="3" value="USA"/>

           <entry key="4" value="USA"/>

        </map>

     </property>

     <!-- results in a setAddressProp(java.util.Properties) call -->

     <property name="addressProp">

        <props>

           <prop key="one">INDIA</prop>

           <prop key="two">Pakistan</prop>

           <prop key="three">USA</prop>

           <prop key="four">USA</prop>

        </props>

     </property>

  </bean>

</beans>

 

public static void main(String[] args) {

     ApplicationContext context =

            new ClassPathXmlApplicationContext("Beans.xml");

     JavaCollection jc=(JavaCollection)context.getBean("javaCollection");

     jc.getAddressList();

     jc.getAddressSet();

     jc.getAddressMap();

     jc.getAddressProp();

   }

輸出

List Elements :[INDIA, Pakistan, USA, USA]

Set Elements :[INDIA, Pakistan, USA]

Map Elements :{1=INDIA, 2=Pakistan, 3=USA,4=USA}

Property Elements :{two=Pakistan,one=INDIA, three=USA, four=USA}  

注入 null 和空字符串的值

如果你需要傳遞一個空字符串作爲值,那麼你可以傳遞它,如下所示:

<bean id="..."class="exampleBean">

  <property name="email" value=""/>

</bean>

前面的例子相當於 Java 代碼:exampleBean.setEmail("")。

如果你需要傳遞一個 NULL 值,那麼你可以傳遞它,如下所示:

<bean id="..."class="exampleBean">

  <property name="email"><null/></property>

</bean>

前面的例子相當於 Java 代碼:exampleBean.setEmail(null)。

Spring Beans 自動裝配

Beans 自動裝配

你已經學會如何使用<bean>元素來聲明 bean 和通過使用 XML 配置文件中的<constructor-arg>和<property>元素來注入。

Spring 容器可以在不使用<constructor-arg>和<property> 元素的情況下自動裝配相互協作的 bean 之間的關係,這有助於減少編寫一個大的基於 Spring 的應用程序的 XML 配置的數量。

自動裝配模式

 

下列自動裝配模式,它們可用於指示 Spring 容器爲來使用自動裝配進行依賴注入。你可以使用<bean>元素的 autowire 屬性爲一個 bean 定義指定自動裝配模式。

模式

描述

no

這是默認的設置,它意味着沒有自動裝配,你應該使用顯式的bean引用來連線。你不用爲了連線做特殊的事。在依賴注入章節你已經看到這個了。

byName

由屬性名自動裝配。Spring 容器看到在 XML 配置文件中 bean 的自動裝配的屬性設置爲 byName。然後嘗試匹配,並且將它的屬性與在配置文件中被定義爲相同名稱的 beans 的屬性進行連接。

byType

由屬性數據類型自動裝配。Spring 容器看到在 XML 配置文件中 bean 的自動裝配的屬性設置爲 byType。然後如果它的類型匹配配置文件中的一個確切的 bean 名稱,它將嘗試匹配和連接屬性的類型。如果存在不止一個這樣的 bean,則一個致命的異常將會被拋出。

constructor

類似於 byType,但該類型適用於構造函數參數類型。如果在容器中沒有一個構造函數參數類型的 bean,則一個致命錯誤將會發生。

autodetect

Spring首先嚐試通過 constructor 使用自動裝配來連接,如果它不執行,Spring 嘗試通過 byType 來自動裝配。

可以使用 byType 或者constructor 自動裝配模式來連接數組和其他類型的集合。

自動裝配的侷限性

當自動裝配始終在同一個項目中使用時,它的效果最好。如果通常不使用自動裝配,它可能會使開發人員混淆的使用它來連接只有一個或兩個 bean 定義。不過,自動裝配可以顯著減少需要指定的屬性或構造器參數,但你應該在使用它們之前考慮到自動裝配的侷限性和缺點。

限制

描述

重寫的可能性

你可以使用總是重寫自動裝配的 <constructor-arg>和 <property> 設置來指定依賴關係。

原始數據類型

你不能自動裝配所謂的簡單類型包括基本類型,字符串和類。

混亂的本質

自動裝配不如顯式裝配精確,所以如果可能的話儘可能使用顯式裝配。

Spring 自動裝配 ‘byName’

這種模式由屬性名稱指定自動裝配。Spring 容器看作 beans,在 XML 配置文件中beans 的 auto-wire 屬性設置爲 byName。然後,它嘗試將它的屬性與配置文件中定義爲相同名稱的 beans 進行匹配和連接。如果找到匹配項,它將注入這些 beans,否則,它將拋出異常。

例如,在配置文件中,如果一個 bean 定義設置爲自動裝配 byName,並且它包含 spellChecker 屬性(即,它有一個 setSpellChecker(...) 方法),那麼 Spring 就會查找定義名爲 spellChecker 的 bean,並且用它來設置這個屬性。你仍然可以使用 <property> 標籤連接其餘的屬性。下面的例子將說明這個概念。

這裏是 TextEditor.java 文件的內容:

package com.tutorialspoint;

public class TextEditor {

  private SpellChecker spellChecker;

  private String name;

  public void setSpellChecker( SpellChecker spellChecker ){

     this.spellChecker = spellChecker;

   }

  public SpellChecker getSpellChecker() {

     return spellChecker;

   }

  public void setName(String name) {

     this.name = name;

   }

  public String getName() {

     return name;

   }

  public void spellCheck() {

     spellChecker.checkSpelling();

   }

}

下面是另一個依賴類文件 SpellChecker.java 的內容:

package com.tutorialspoint;

public class SpellChecker {

  public SpellChecker() {

     System.out.println("Inside SpellChecker constructor." );

   }

  public void checkSpelling() {

     System.out.println("Inside checkSpelling." );

  }  

}

下面是 MainApp.java 文件的內容:

package com.tutorialspoint;

importorg.springframework.context.ApplicationContext;

importorg.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {

  public static void main(String[] args) {

     ApplicationContext context =

            new ClassPathXmlApplicationContext("Beans.xml");

     TextEditor te = (TextEditor) context.getBean("textEditor");

     te.spellCheck();

   }

}

下面是在正常情況下的配置文件 Beans.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-3.0.xsd">

 

  <!-- Definition for textEditor bean -->

  <bean id="textEditor"class="com.tutorialspoint.TextEditor">

      <property name="spellChecker" ref="spellChecker"/>

      <property name="name" value="Generic Text Editor"/>

  </bean>

 

  <!-- Definition for spellChecker bean -->

   <bean id="spellChecker"class="com.tutorialspoint.SpellChecker">

  </bean>

 

</beans>

但是,如果你要使用自動裝配 “byName”,那麼你的 XML 配置文件將成爲如下:

<?xml version="1.0"encoding="UTF-8"?>

 

<beansxmlns="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-3.0.xsd">

 

  <!-- Definition for textEditor bean -->

  <bean id="textEditor" class="com.tutorialspoint.TextEditor"

     autowire="byName">

     <property name="name" value="Generic Text Editor"/>

  </bean>

 

  <!-- Definition for spellChecker bean -->

  <bean id="spellChecker"class="com.tutorialspoint.SpellChecker">

  </bean>

 

</beans>

一旦你完成了創建源代碼和 bean 的配置文件,我們就可以運行該應用程序。如果你的應用程序一切都正常,它將打印下面的消息:

Inside SpellChecker constructor.

Inside checkSpelling.

 

Spring 自動裝配 ‘byType’

 

這種模式由屬性類型指定自動裝配。Spring 容器看作 beans,在 XML 配置文件中beans 的 autowire 屬性設置爲 byType。然後,如果它的 type 恰好與配置文件中 beans 名稱中的一個相匹配,它將嘗試匹配和連接它的屬性。如果找到匹配項,它將注入這些 beans,否則,它將拋出異常。

例如,在配置文件中,如果一個 bean 定義設置爲自動裝配 byType,並且它包含 SpellChecker 類型的 spellChecker 屬性,那麼 Spring 就會查找定義名爲 SpellChecker 的 bean,並且用它來設置這個屬性。你仍然可以使用 <property> 標籤連接其餘屬性。下面的例子將說明這個概念,你會發現和上面的例子沒有什麼區別,除了 XML 配置文件已經被改變。

這裏是 TextEditor.java 文件的內容:

package com.tutorialspoint;

public class TextEditor {

  private SpellChecker spellChecker;

  private String name;

  public void setSpellChecker( SpellChecker spellChecker ) {

     this.spellChecker = spellChecker;

   }

  public SpellChecker getSpellChecker() {

     return spellChecker;

   }

  public void setName(String name) {

     this.name = name;

   }

  public String getName() {

     return name;

   }

  public void spellCheck() {

     spellChecker.checkSpelling();

   }

}

下面是另一個依賴類文件 SpellChecker.java 的內容:

package com.tutorialspoint;

public class SpellChecker {

  public SpellChecker(){

     System.out.println("Inside SpellChecker constructor." );

   }

  public void checkSpelling() {

     System.out.println("Inside checkSpelling." );

  }  

}

下面是 MainApp.java 文件的內容:

package com.tutorialspoint;

importorg.springframework.context.ApplicationContext;

importorg.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {

  public static void main(String[] args) {

     ApplicationContext context =

            new ClassPathXmlApplicationContext("Beans.xml");

     TextEditor te = (TextEditor) context.getBean("textEditor");

     te.spellCheck();

   }

}

下面是在正常情況下的配置文件 Beans.xml 文件:

<?xml version="1.0"encoding="UTF-8"?>

 

<beansxmlns="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-3.0.xsd">

 

  <!-- Definition for textEditor bean -->

  <bean id="textEditor"class="com.tutorialspoint.TextEditor">

     <property name="spellChecker" ref="spellChecker"/>

     <property name="name" value="Generic Text Editor"/>

  </bean>

 

  <!-- Definition for spellChecker bean -->

  <bean id="spellChecker"class="com.tutorialspoint.SpellChecker">

  </bean>

 

</beans>

但是,如果你要使用自動裝配 “byType”,那麼你的 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-3.0.xsd">

 

  <!-- Definition for textEditor bean -->

  <bean id="textEditor"class="com.tutorialspoint.TextEditor"

     autowire="byType">

     <property name="name" value="Generic Text Editor"/>

  </bean>

 

  <!-- Definition for spellChecker bean -->

  <bean id="SpellChecker"class="com.tutorialspoint.SpellChecker">

  </bean>

 

</beans>

一旦你完成了創建源代碼和 bean 的配置文件,我們就可以運行該應用程序。如果你的應用程序一切都正常,它將打印下面的消息:

Inside SpellChecker constructor.

Inside checkSpelling.

 

Spring 由構造函數自動裝配

 

這種模式與 byType 非常相似,但它應用於構造器參數。Spring 容器看作 beans,在XML 配置文件中 beans 的 autowire 屬性設置爲 constructor。然後,它嘗試把它的構造函數的參數與配置文件中 beans 名稱中的一個進行匹配和連線。如果找到匹配項,它會注入這些 bean,否則,它會拋出異常。

例如,在配置文件中,如果一個 bean 定義設置爲通過構造函數自動裝配,而且它有一個帶有 SpellChecker 類型的參數之一的構造函數,那麼 Spring 就會查找定義名爲 SpellChecker 的 bean,並用它來設置構造函數的參數。你仍然可以使用 <constructor-arg> 標籤連接其餘屬性。下面的例子將說明這個概念。

這裏是 TextEditor.java 文件的內容:

package com.tutorialspoint;

public class TextEditor {

  private SpellChecker spellChecker;

  private String name;

  public TextEditor( SpellChecker spellChecker, String name ) {

     this.spellChecker = spellChecker;

     this.name = name;

   }

  public SpellChecker getSpellChecker() {

     return spellChecker;

   }

  public String getName() {

     return name;

   }

  public void spellCheck() {

     spellChecker.checkSpelling();

   }

}

下面是另一個依賴類文件 SpellChecker.java 的內容:

package com.tutorialspoint;

public class SpellChecker {

  public SpellChecker(){

     System.out.println("Inside SpellChecker constructor." );

   }

  public void checkSpelling()

   {

     System.out.println("Inside checkSpelling." );

  } 

}

下面是 MainApp.java 文件的內容:

package com.tutorialspoint;

importorg.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {

  public static void main(String[] args) {

     ApplicationContext context =

            new ClassPathXmlApplicationContext("Beans.xml");

     TextEditor te = (TextEditor) context.getBean("textEditor");

     te.spellCheck();

   }

}

下面是在正常情況下的配置文件 Beans.xml 文件:

<?xml version="1.0"encoding="UTF-8"?>

 

<beansxmlns="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-3.0.xsd">

 

  <!-- Definition for textEditor bean -->

  <bean id="textEditor"class="com.tutorialspoint.TextEditor">

     <constructor-arg  ref="spellChecker"/>

     <constructor-arg value="Generic Text Editor"/>

  </bean>

 

  <!-- Definition for spellChecker bean -->

  <bean id="spellChecker"class="com.tutorialspoint.SpellChecker">

  </bean>

 

</beans>

但是,如果你要使用自動裝配 “by constructor”,那麼你的 XML 配置文件將成爲如下:

<?xml version="1.0"encoding="UTF-8"?>

 

<beansxmlns="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-3.0.xsd">

 

  <!-- Definition for textEditor bean -->

  <bean id="textEditor"class="com.tutorialspoint.TextEditor"

     autowire="constructor">

     <constructor-arg value="Generic Text Editor"/>

  </bean>

 

  <!-- Definition for spellChecker bean -->

  <bean id="SpellChecker"class="com.tutorialspoint.SpellChecker">

  </bean>

 

</beans>

一旦你完成了創建源代碼和 bean 的配置文件,我們就可以運行該應用程序。如果你的應用程序一切都正常,它將打印下面的消息:

Inside SpellChecker constructor.

Inside checkSpelling.

 

Spring 基於註解的配置

由 陳 創建, 最後一次修改 2016-08-12

基於註解的配置

 

從 Spring 2.5 開始就可以使用註解來配置依賴注入。而不是採用 XML 來描述一個 bean 連線,你可以使用相關類,方法或字段聲明的註解,將 bean 配置移動到組件類本身。

在 XML 注入之前進行註解注入,因此後者的配置將通過兩種方式的屬性連線被前者重寫。

註解連線在默認情況下在 Spring 容器中不打開。因此,在可以使用基於註解的連線之前,我們將需要在我們的 Spring 配置文件中啓用它。所以如果你想在 Spring 應用程序中使用的任何註解,可以考慮到下面的配置文件。

<?xml version="1.0"encoding="UTF-8"?>

 

<beansxmlns="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-3.0.xsd

   http://www.springframework.org/schema/context

   http://www.springframework.org/schema/context/spring-context-3.0.xsd">

 

  <context:annotation-config/>

  <!-- bean definitions go here -->

 

</beans>

一旦 被配置後,你就可以開始註解你的代碼,表明 Spring 應該自動連接值到屬性,方法和構造函數。讓我們來看看幾個重要的註解,並且瞭解它們是如何工作的:

序號

註解&描述

1

@Required

@Required 註解應用於 bean 屬性的 setter 方法。

2

@Autowired

@Autowired 註解可以應用到 bean 屬性的 setter 方法,非 setter 方法,構造函數和屬性。

3

@Qualifier

通過指定確切的將被連線的 bean@Autowired @Qualifier 註解可以用來刪除混亂。

4

JSR-250 Annotations

Spring 支持 JSR-250 的基礎的註解,其中包括了 @Resource@PostConstruct @PreDestroy 註解。

 

Spring @Required 註釋

 

@Required 註釋應用於bean 屬性的 setter 方法,它表明受影響的 bean屬性在配置時必須放在 XML 配置文件中,否則容器就會拋出一個BeanInitializationException 異常。下面顯示的是一個使用 @Required 註釋的示例。

下面是 Student.java 文件的內容:

package com.tutorialspoint;

importorg.springframework.beans.factory.annotation.Required;

public class Student {

  private Integer age;

  private String name;

  @Required

  public void setAge(Integer age) {

     this.age = age;

   }

  public Integer getAge() {

     return age;

   }

  @Required

  public void setName(String name) {

     this.name = name;

   }

  public String getName() {

     return name;

   }

}

下面是 MainApp.java 文件的內容:

package com.tutorialspoint;

import org.springframework.context.ApplicationContext;

importorg.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {

  public static void main(String[] args) {

     ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");

     Student student = (Student) context.getBean("student");

     System.out.println("Name : " + student.getName() );

     System.out.println("Age : " + student.getAge() );

   }

}

下面是配置文件 Beans.xml: 文件的內容:

<?xml version="1.0"encoding="UTF-8"?>

 

<beansxmlns="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-3.0.xsd

   http://www.springframework.org/schema/context

   http://www.springframework.org/schema/context/spring-context-3.0.xsd">

 

  <context:annotation-config/>

 

  <!-- Definition for student bean -->

  <bean id="student"class="com.tutorialspoint.Student">

     <property name="name" value="Zara" />

 

     <!-- try without passing age and check the result -->

     <!-- property name="age" value="11"-->

  </bean>

 

</beans>

一旦你已經完成的創建了源文件和 bean 配置文件,讓我們運行一下應用程序。如果你的應用程序一切都正常的話,這將引起 BeanInitializationException 異常,並且會輸出一下錯誤信息和其他日誌消息:

Property 'age' is required for bean'student'

下一步,在你按照如下所示從 “age” 屬性中刪除了註釋,你可以嘗試運行上面的示例:

<?xml version="1.0"encoding="UTF-8"?>

 

<beansxmlns="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-3.0.xsd

   http://www.springframework.org/schema/context

   http://www.springframework.org/schema/context/spring-context-3.0.xsd">

 

  <context:annotation-config/>

 

  <!-- Definition for student bean -->

  <bean id="student"class="com.tutorialspoint.Student">

     <property name="name" value="Zara" />

     <property name="age" value="11"/>

  </bean>

 

</beans>

現在上面的示例將產生如下結果:

Name : Zara

Age : 11

 

Spring @Autowired 註釋

 

@Autowired 註釋對在哪裏和如何完成自動連接提供了更多的細微的控制。

@Autowired 註釋可以在setter 方法中被用於自動連接 bean,就像@Autowired 註釋,容器,一個屬性或者任意命名的可能帶有多個參數的方法。

Setter 方法中的 @Autowired

 

你可以在 XML 文件中的setter 方法中使用 @Autowired 註釋來除去 元素。當 Spring遇到一個在 setter 方法中使用的 @Autowired 註釋,它會在方法中視圖執行 byType 自動連接。

這裏是 TextEditor.java 文件的內容:

package com.tutorialspoint;

importorg.springframework.beans.factory.annotation.Autowired;

public class TextEditor {

  private SpellChecker spellChecker;

  @Autowired

  public void setSpellChecker( SpellChecker spellChecker ){

      this.spellChecker = spellChecker;

   }

  public SpellChecker getSpellChecker( ) {

     return spellChecker;

   }

  public void spellCheck() {

     spellChecker.checkSpelling();

   }

}

下面是另一個依賴的類文件 SpellChecker.java 的內容:

package com.tutorialspoint;

public class SpellChecker {

  public SpellChecker(){

     System.out.println("Inside SpellChecker constructor." );

   }

  public void checkSpelling(){

     System.out.println("Inside checkSpelling." );

  } 

}

下面是 MainApp.java 文件的內容:

package com.tutorialspoint;

importorg.springframework.context.ApplicationContext;

importorg.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {

  public static void main(String[] args) {

     ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");

     TextEditor te = (TextEditor) context.getBean("textEditor");

     te.spellCheck();

   }

}

下面是配置文件 Beans.xml:

<?xml version="1.0"encoding="UTF-8"?>

 

<beansxmlns="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-3.0.xsd

   http://www.springframework.org/schema/context

   http://www.springframework.org/schema/context/spring-context-3.0.xsd">

 

  <context:annotation-config/>

 

  <!-- Definition for textEditor bean without constructor-arg  -->

  <bean id="textEditor"class="com.tutorialspoint.TextEditor">

  </bean>

 

  <!-- Definition for spellChecker bean -->

  <bean id="spellChecker"class="com.tutorialspoint.SpellChecker">

  </bean>

 

</beans>

一旦你已經完成的創建了源文件和 bean 配置文件,讓我們運行一下應用程序。如果你的應用程序一切都正常的話,這將會輸出以下消息:

Inside SpellChecker constructor.

Inside checkSpelling.

 

屬性中的 @Autowired

 

你可以在屬性中使用 @Autowired 註釋來除去 setter 方法。當時使用 爲自動連接屬性傳遞的時候,Spring 會將這些傳遞過來的值或者引用自動分配給那些屬性。所以利用在屬性中 @Autowired 的用法,你的 TextEditor.java 文件將變成如下所示:

package com.tutorialspoint;

import org.springframework.beans.factory.annotation.Autowired;

public class TextEditor {

  @Autowired

  private SpellChecker spellChecker;

  public TextEditor() {

     System.out.println("Inside TextEditor constructor." );

  } 

  public SpellChecker getSpellChecker( ){

     return spellChecker;

  } 

  public void spellCheck(){

     spellChecker.checkSpelling();

   }

}

下面是配置文件 Beans.xml:

<?xml version="1.0"encoding="UTF-8"?>

 

<beansxmlns="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-3.0.xsd

   http://www.springframework.org/schema/context

   http://www.springframework.org/schema/context/spring-context-3.0.xsd">

 

  <context:annotation-config/>

 

  <!-- Definition for textEditor bean -->

  <bean id="textEditor" class="com.tutorialspoint.TextEditor">

  </bean>

 

  <!-- Definition for spellChecker bean -->

  <bean id="spellChecker"class="com.tutorialspoint.SpellChecker">

  </bean>

 

</beans>

一旦你在源文件和 bean 配置文件中完成了上面兩處改變,讓我們運行一下應用程序。如果你的應用程序一切都正常的話,這將會輸出以下消息:

Inside TextEditor constructor.

Inside SpellChecker constructor.

Inside checkSpelling.

 

構造函數中的 @Autowired

 

你也可以在構造函數中使用 @Autowired。一個構造函數 @Autowired 說明當創建 bean 時,即使在 XML 文件中沒有使用 元素配置 bean ,構造函數也會被自動連接。讓我們檢查一下下面的示例。

這裏是 TextEditor.java 文件的內容:

package com.tutorialspoint;

import org.springframework.beans.factory.annotation.Autowired;

public class TextEditor {

  private SpellChecker spellChecker;

  @Autowired

  public TextEditor(SpellChecker spellChecker){

     System.out.println("Inside TextEditor constructor." );

     this.spellChecker = spellChecker;

   }

  public void spellCheck(){

     spellChecker.checkSpelling();

   }

}

下面是配置文件 Beans.xml:

<?xml version="1.0"encoding="UTF-8"?>

 

<beansxmlns="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-3.0.xsd

   http://www.springframework.org/schema/context

   http://www.springframework.org/schema/context/spring-context-3.0.xsd">

 

  <context:annotation-config/>

 

  <!-- Definition for textEditor bean without constructor-arg  -->

  <bean id="textEditor" class="com.tutorialspoint.TextEditor">

  </bean>

 

  <!-- Definition for spellChecker bean -->

  <bean id="spellChecker"class="com.tutorialspoint.SpellChecker">

  </bean>

 

</beans>

一旦你在源文件和 bean 配置文件中完成了上面兩處改變,讓我們運行一下應用程序。如果你的應用程序一切都正常的話,這將會輸出以下消息:

Inside SpellChecker constructor.

Inside TextEditor constructor.

Inside checkSpelling.

 

@Autowired 的(required=false)選項

 

默認情況下,@Autowired 註釋意味着依賴是必須的,它類似於 @Required 註釋,然而,你可以使用 @Autowired 的 (required=false) 選項關閉默認行爲。

即使你不爲 age 屬性傳遞任何參數,下面的示例也會成功運行,但是對於 name 屬性則需要一個參數。你可以自己嘗試一下這個示例,因爲除了只有Student.java 文件被修改以外,它和 @Required 註釋示例是相似的。

package com.tutorialspoint;

importorg.springframework.beans.factory.annotation.Autowired;

public class Student {

  private Integer age;

  private String name;

  @Autowired(required=false)

  public void setAge(Integer age) {

     this.age = age;

  } 

  public Integer getAge() {

     return age;

   }

  @Autowired

  public void setName(String name) {

     this.name = name;

  }  

  public String getName() {

     return name;

   }

}

Spring @Qualifier 註釋

 

可能會有這樣一種情況,當你創建多個具有相同類型的 bean 時,並且想要用一個屬性只爲它們其中的一個進行裝配,在這種情況下,你可以使用 @Qualifier 註釋和 @Autowired 註釋通過指定哪一個真正的 bean 將會被裝配來消除混亂。下面顯示的是使用 @Qualifier 註釋的一個示例。

 

這裏是 Student.java 文件的內容:

package com.tutorialspoint;

public class Student {

  private Integer age;

  private String name;

  public void setAge(Integer age) {

     this.age = age;

  }  

  public Integer getAge() {

     return age;

   }

  public void setName(String name) {

     this.name = name;

  } 

  public String getName() {

     return name;

   }

}

這裏是 Profile.java 文件的內容:

package com.tutorialspoint;

importorg.springframework.beans.factory.annotation.Autowired;

importorg.springframework.beans.factory.annotation.Qualifier;

public class Profile {

  @Autowired

  @Qualifier("student1")

  private Student student;

  public Profile(){

     System.out.println("Inside Profile constructor." );

   }

  public void printAge() {

     System.out.println("Age : " + student.getAge() );

   }

  public void printName() {

     System.out.println("Name : " + student.getName() );

   }

}

下面是 MainApp.java 文件的內容:

package com.tutorialspoint;

importorg.springframework.context.ApplicationContext;

importorg.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {

   public static void main(String[] args) {

     ApplicationContext context = newClassPathXmlApplicationContext("Beans.xml");

     Profile profile = (Profile) context.getBean("profile");

     profile.printAge();

     profile.printName();

   }

}

考慮下面配置文件 Beans.xml 的示例:

<?xml version="1.0"encoding="UTF-8"?>

 

<beansxmlns="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-3.0.xsd

   http://www.springframework.org/schema/context

   http://www.springframework.org/schema/context/spring-context-3.0.xsd">

 

   <context:annotation-config/>

 

  <!-- Definition for profile bean -->

  <bean id="profile"class="com.tutorialspoint.Profile">

  </bean>

 

  <!-- Definition for student1 bean -->

  <bean id="student1"class="com.tutorialspoint.Student">

     <property name="name" value="Zara" />

     <property name="age" value="11"/>

  </bean>

 

  <!-- Definition for student2 bean -->

  <bean id="student2"class="com.tutorialspoint.Student">

     <property name="name" value="Nuha" />

     <property name="age" value="2"/>

  </bean>

 

</beans>

一旦你在源文件和 bean 配置文件中完成了上面兩處改變,讓我們運行一下應用程序。如果你的應用程序一切都正常的話,這將會輸出以下消息:

Inside Profile constructor.

Age : 11

Name : Zara

 

 

Spring 基於 Java 的配置(簡單瞭解)

 

到目前爲止,你已經看到如何使用 XML 配置文件來配置 Spring bean。如果你熟悉使用 XML 配置,那麼我會說,不需要再學習如何進行基於 Java 的配置是,因爲你要達到相同的結果,可以使用其他可用的配置。

基於 Java 的配置選項,可以使你在不用配置 XML 的情況下編寫大多數的 Spring,但是一些有幫助的基於 Java 的註解,解釋如下:

@Configuration 和@Bean 註解

 

帶有 @Configuration 的註解類表示這個類可以使用 Spring IoC 容器作爲 bean 定義的來源。@Bean 註解告訴 Spring,一個帶有 @Bean 的註解方法將返回一個對象,該對象應該被註冊爲在 Spring 應用程序上下文中的 bean。最簡單可行的 @Configuration 類如下所示:

package com.tutorialspoint;

importorg.springframework.context.annotation.*;

@Configuration

public class HelloWorldConfig {

  @Bean

  public HelloWorld helloWorld(){

     return new HelloWorld();

   }

}

上面的代碼將等同於下面的 XML 配置:

<beans>

  <bean id="helloWorld"class="com.tutorialspoint.HelloWorld" />

</beans>

在這裏,帶有 @Bean 註解的方法名稱作爲 bean 的 ID,它創建並返回實際的bean。你的配置類可以聲明多個 @Bean。一旦定義了配置類,你就可以使用 AnnotationConfigApplicationContext 來加載並把他們提供給 Spring 容器,如下所示:

public static void main(String[] args) {

  ApplicationContext ctx =

  new AnnotationConfigApplicationContext(HelloWorldConfig.class);

  HelloWorld helloWorld = ctx.getBean(HelloWorld.class);

  helloWorld.setMessage("Hello World!");

  helloWorld.getMessage();

}

你可以加載各種配置類,如下所示:

public static void main(String[] args) {

  AnnotationConfigApplicationContext ctx =

   newAnnotationConfigApplicationContext();

  ctx.register(AppConfig.class, OtherConfig.class);

  ctx.register(AdditionalConfig.class);

  ctx.refresh();

  MyService myService = ctx.getBean(MyService.class);

  myService.doStuff();

}

例子

 

讓我們在恰當的位置使用 Eclipse IDE,然後按照下面的步驟來創建一個 Spring 應用程序:

步驟       描述

1     創建一個名稱爲 SpringExample 的項目,並且在創建項目的 src 文件夾中創建一個包 com.tutorialspoint。

2     使用 Add External JARs 選項,添加所需的 Spring 庫,解釋見 Spring Hello World Example 章節。

3     因爲你是使用基於 java 的註解,所以你還需要添加來自 Java 安裝目錄的 CGLIB.jar 和可以從 asm.ow2.org 中下載的 ASM.jar 庫。

4     在 com.tutorialspoint 包中創建 Java 類 HelloWorldConfig、HelloWorld 和 MainApp。

5     最後一步是創建的所有 Java 文件和 Bean 配置文件的內容,並運行應用程序,解釋如下所示。

這裏是 HelloWorldConfig.java 文件的內容:

package com.tutorialspoint;

importorg.springframework.context.annotation.*;

@Configuration

public class HelloWorldConfig {

  @Bean

  public HelloWorld helloWorld(){

     return new HelloWorld();

   }

}

這裏是 HelloWorld.java 文件的內容:

package com.tutorialspoint;

 

public class HelloWorld {

  private String message;

 

  public void setMessage(String message){

     this.message  = message;

   }

 

  public void getMessage(){

     System.out.println("Your Message : " + message);

   }

}

下面是 MainApp.java 文件的內容:

package com.tutorialspoint;

 

importorg.springframework.context.ApplicationContext;

importorg.springframework.context.annotation.*;

 

public class MainApp {

  public static void main(String[] args) {

     ApplicationContext ctx =

     new AnnotationConfigApplicationContext(HelloWorldConfig.class);

 

     HelloWorld helloWorld = ctx.getBean(HelloWorld.class);

 

     helloWorld.setMessage("Hello World!");

     helloWorld.getMessage();

   }

}

一旦你完成了創建所有的源文件並添加所需的額外的庫後,我們就可以運行該應用程序。你應該注意這裏不需要配置文件。如果你的應用程序一切都正常,將輸出以下信息:

Your Message : Hello World!

注入 Bean 的依賴性

 

當 @Beans 依賴對方時,表達這種依賴性非常簡單,只要有一個 bean 方法調用另一個,如下所示:

package com.tutorialspoint;

importorg.springframework.context.annotation.*;

@Configuration

public class AppConfig {

  @Bean

  public Foo foo() {

     return new Foo(bar());

   }

  @Bean

   publicBar bar() {

     return new Bar();

   }

}

這裏,foo Bean 通過構造函數注入來接收參考基準。現在,讓我們看到一個正在執行的例子:

例子:

 

讓我們在恰當的位置使用 Eclipse IDE,然後按照下面的步驟來創建一個 Spring 應用程序:

步驟       描述

1     創建一個名稱爲 SpringExample 的項目,並且在創建項目的 src 文件夾中創建一個包 com.tutorialspoint。

2     使用 Add External JARs 選項,添加所需的 Spring 庫,解釋見 Spring Hello World Example 章節。

3     因爲你是使用基於 java 的註解,所以你還需要添加來自 Java 安裝目錄的 CGLIB.jar 和可以從 asm.ow2.org 中下載的 ASM.jar 庫。

4     在 com.tutorialspoint 包中創建 Java 類 TextEditorConfig、TextEditor、SpellChecker 和 MainApp。

5     最後一步是創建的所有 Java 文件和 Bean 配置文件的內容,並運行應用程序,解釋如下所示。

這裏是 TextEditorConfig.java 文件的內容:

package com.tutorialspoint;

importorg.springframework.context.annotation.*;

@Configuration

public class TextEditorConfig {

  @Bean

  public TextEditor textEditor(){

     return new TextEditor( spellChecker() );

   }

  @Bean

  public SpellChecker spellChecker(){

     return new SpellChecker( );

   }

}

這裏是 TextEditor.java 文件的內容:

package com.tutorialspoint;

public class TextEditor {

  private SpellChecker spellChecker;

  public TextEditor(SpellChecker spellChecker){

     System.out.println("Inside TextEditor constructor." );

     this.spellChecker = spellChecker;

   }

  public void spellCheck(){

     spellChecker.checkSpelling();

   }

}

下面是另一個依賴的類文件 SpellChecker.java 的內容:

package com.tutorialspoint;

public class SpellChecker {

  public SpellChecker(){

     System.out.println("Inside SpellChecker constructor." );

   }

  public void checkSpelling(){

     System.out.println("Inside checkSpelling." );

   }

 

}

下面是 MainApp.java 文件的內容:

package com.tutorialspoint;

 

importorg.springframework.context.ApplicationContext;

importorg.springframework.context.annotation.*;

 

public class MainApp {

  public static void main(String[] args) {

     ApplicationContext ctx =

     new AnnotationConfigApplicationContext(TextEditorConfig.class);

 

     TextEditor te = ctx.getBean(TextEditor.class);

 

     te.spellCheck();

   }

}

一旦你完成了創建所有的源文件並添加所需的額外的庫後,我們就可以運行該應用程序。你應該注意這裏不需要配置文件。如果你的應用程序一切都正常,將輸出以下信息:

Inside SpellChecker constructor.

Inside TextEditor constructor.

Inside checkSpelling.

@Import 註解:

 

@import 註解允許從另一個配置類中加載 @Bean 定義。考慮 ConfigA 類,如下所示:

@Configuration

public class ConfigA {

  @Bean

  public A a() {

     return new A();

   }

}

你可以在另一個 Bean 聲明中導入上述 Bean 聲明,如下所示:

@Configuration

@Import(ConfigA.class)

public class ConfigB {

  @Bean

  public B a() {

     return new A();

   }

}

現在,當實例化上下文時,不需要同時指定 ConfigA.class 和 ConfigB.class,只有 ConfigB 類需要提供,如下所示:

public static void main(String[] args) {

  ApplicationContext ctx =

  new AnnotationConfigApplicationContext(ConfigB.class);

   //now both beans A and B will be available...

   Aa = ctx.getBean(A.class);

   Bb = ctx.getBean(B.class);

}

生命週期回調

 

@Bean 註解支持指定任意的初始化和銷燬的回調方法,就像在 bean 元素中 Spring 的XML 的初始化方法和銷燬方法的屬性:

public class Foo {

  public void init() {

     // initialization logic

   }

  public void cleanup() {

     // destruction logic

   }

}

 

@Configuration

public class AppConfig {

  @Bean(initMethod = "init", destroyMethod = "cleanup")

  public Foo foo() {

      return new Foo();

   }

}

指定 Bean 的範圍:

默認範圍是單實例,但是你可以重寫帶有 @Scope 註解的該方法,如下所示:

@Configuration

public class AppConfig {

  @Bean

  @Scope("prototype")

  public Foo foo() {

     return new Foo();

   }

}

Spring 框架的 AOP

AOP(Aspect-Oriented Programming, 面向切面編程): 是一種新的方法論, 是對傳統OOP(Object-Oriented Programming, 面向對象編程) 的補充.

AOP 的主要編程對象是切面(aspect), 而切面模塊化橫切關注點.

在應用 AOP 編程時, 仍然需要定義公共功能, 但可以明確的定義這個功能在哪裏, 以什麼方式應用, 並且不必修改受影響的類. 這樣一來橫切關注點就被模塊化到特殊的對象(切面)裏.

AOP 的好處:

每個事物邏輯位於一個位置, 代碼不分散, 便於維護和升級

業務模塊更簡潔, 只包含核心業務代碼.

AOP術語

切面(Aspect): 橫切關注點(跨越應用程序多個模塊的功能)被模塊化的特殊對象

通知(Advice): 切面必須要完成的工作

目標(Target): 被通知的對象

代理(Proxy): 向目標對象應用通知之後創建的對象

連接點(Joinpoint):程序執行的某個特定位置:如類某個方法調用前、調用後、方法拋出異常後等。連接點由兩個信息確定:方法表示的程序執行點;相對點表示的方位。例如 ArithmethicCalculator#add() 方法執行前的連接點,執行點爲 ArithmethicCalculator#add();方位爲該方法執行前的位置

切點(pointcut):每個類都擁有多個連接點:例如 ArithmethicCalculator 的所有方法實際上都是連接點,即連接點是程序類中客觀存在的事務。AOP 通過切點定位到特定的連接點。類比:連接點相當於數據庫中的記錄,切點相當於查詢條件。切點和連接點不是一對一的關係,一個切點匹配多個連接點,切點通過 org.springframework.aop.Pointcut 接口進行描述,它使用類和方法作爲連接點的查詢條件。

靜態代理

 

靜態代理:由程序員創建或特定工具自動生成源代碼,也就是在編譯時就已經將接口,被代理類,代理類等確定下來。在程序運行之前,代理類的.class文件就已經生成。

 

public interface Person {

       voidgiveMoney();

}

 

public class Student implements Person {

   private String name;

   public Student(String name) {

       this.name = name;

    }

       @Override

       publicvoid giveMoney() {

              System.out.println(name+ "上交班費50元");

 

       }

 

}

 

public class StudentsProxy implementsPerson {

 

       Studentstu;

      

       publicStudentsProxy(Person stu) {

              if(stu.getClass()== Student.class) {

                     this.stu= (Student)stu;

              }

       }

       @Override

       publicvoid giveMoney() {

              stu.giveMoney();

             

       }

 

}

 

public class StaticProxyTest {

 

       publicstatic void main(String[] args) {

              Personzhangsan = new Student("張三");

              Personmontor =  new StudentsProxy(zhangsan);

              montor.giveMoney();

 

       }

 

}

動態代理

動態代理模式主要由四個元素共同構成:

 

1. 接口,接口中的方法是要真正去實現的

2. 被代理類,實現上述接口,這是真正去執行接口中方法的類

3. 代理類,實現InvocationHandler,幫助被代理類去實現方法

4. 測試用例:

public interface ArithmeticCalculator {

   public int add(int i, int j);

    public int sub(int i, int j);

   public int mul(int i, int j);

   public int div(int i, int j);

}

 

public class ArithmeticCalculatorImplimplements ArithmeticCalculator {

 

       @Override

       publicint add(int i, int j) {

              intresult = i + j;

              returnresult;

       }

 

       @Override

       publicint sub(int i, int j) {

              intresult = i - j;

              returnresult;

       }

 

       @Override

       publicint mul(int i, int j) {

              intresult = i * j;

              returnresult;

       }

 

       @Override

       publicint div(int i, int j) {

              intresult = i / j;

              returnresult;

       }

 

}

 

public classArithmeticCalculatorLoggingProxy {

   private ArithmeticCalculator target;

   public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) {

           this.target = target;

    }

public ArithmeticCalculatorgetLoggingProxy() {

           ArithmeticCalculator proxy = null;

           //代理對象由哪一個類加載器加載

           ClassLoader loader =target.getClass().getClassLoader();

           //代理對象類型,即其中有哪些方法

           Class [] interfaces = new Class[]{ArithmeticCalculator.class};

           //當調用代理對象其中方法,該執行代碼

           InvocationHandler h = newInvocationHandler() {

       /**

       *proxy:正在返回的那個代理對象,一般情況下在invoke方法中都不使用該對象

       *method:正在被調用的方法

       *args:調用方法時傳入的參數

       */

       @Override

       publicObject invoke(Object proxy, Method method, Object[] args) throws Throwable {

       Stringmethodname = method.getName();

       System.out.println("Themethod " + methodname + "brgins with" + Arrays.asList(args));

                            Objectresoult = method.invoke(target, args);

                            System.out.println("Themethod " + methodname + "end with" + resoult);

                            returnresoult;

                     }

              };

           proxy = (ArithmeticCalculator)Proxy.newProxyInstance(loader, interfaces, h);

           return proxy;

    }

}

 

public class Main {

 

       publicstatic void main(String[] args) {

              ArithmeticCalculatortarget = new ArithmeticCalculatorImpl();

              ArithmeticCalculatorproxy = new ArithmeticCalculatorLoggingProxy(target).getLoggingProxy();

              intresult = proxy.add(1, 2);

              System.out.println(result);

       }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章