Spring MVC -- Spring框架入門(IoC、DI以及XML配置文件)

Spring MVC是Spring框架中用於Web應用開發的一個模塊。Spring MVC的MVC是Model-View-Controller的縮寫。它是一個普遍應用於圖像化用戶交互開發中的設計模式,不只常見於Web開發,也普遍應用於如Swing和JavaFX等桌面開發。html

Spring MVC基於Spring框架、Servlet和JSP(JavaServer Page),在掌握這3門技術的基礎上學習Spring MVC將很是容易。java

Spring框架是一個開源的企業應用開發框架,做爲一個輕量級的解決方案,它包含20多個不一樣的解決方法。咱們主要關注Core、Spring Bean、Spring MVC和Spring MVC Test模塊。git

一 下載Spring

一、Spring下載

Spring框架下載官網:http://spring.io/projects,具體能夠參考博客Spring框架下載方法程序員

也能夠直接下載:URL爲最Spring最新5.1.6版本地址,獲取其餘版本只需修改下面連接的5.1.6的版本號信息成想要的版本便可:https://repo.spring.io/webapp/#/artifacts/browse/tree/General/libs-release-local/org/springframework/spring/5.1.6.RELEASEgithub

將下載好的zip解壓到任意目錄,在解壓的目錄中,包含相應的文檔和Java源代碼,其中libs文件下爲基於Spring框架開發應用所須要的jar文件。web

二、Spring依賴包下載

關於spring的spring-framework-3.0.2.RELEASE-dependencies.zip包的下載:http://s3.amazonaws.com/dist.springframework.org/release/SPR/spring-framework-3.0.2.RELEASE-dependencies.zipspring

三、下載Spring源碼

Spring框架是一個開源項目,若是你想要還沒有發佈的最新版本的Spring,可使用在github上下載源代碼:https://github.com/spring-projects/spring-frameworkexpress

二 IoC和DI

Spring框架有兩個重要的概念:控制翻轉、依賴注入。 apache

一、IoC是什麼

Ioc—Inversion of Control,即「控制反轉」,不是什麼技術,而是一種設計思想。在Java開發中,Ioc意味着將你設計好的對象交給容器控制,而不是傳統的在你的對象內部直接控制。如何理解好Ioc呢?理解好Ioc的關鍵是要明確「誰控制誰,控制什麼,爲什麼是反轉(有反轉就應該有正轉了),哪些方面反轉了」,那咱們來深刻分析一下:編程

  • 誰控制誰,控制什麼:傳統Java SE程序設計,咱們直接在對象內部經過new進行建立對象,是程序主動去建立依賴對象;而IoC是有專門一個容器來建立這些對象,即由Ioc容器來控制對象的建立;誰控制誰?固然是IoC 容器控制了對象;控制什麼?那就是主要控制了外部資源獲取(不僅是對象包括好比文件等);
  • 爲什麼是反轉,哪些方面反轉了:有反轉就有正轉,傳統應用程序是由咱們本身在對象中主動控制去直接獲取依賴對象,也就是正轉;而反轉則是由容器來幫忙建立及注入依賴對象;爲什麼是反轉?由於由容器幫咱們查找及注入依賴對象,對象只是被動的接受依賴對象,因此是反轉;哪些方面反轉了?依賴對象的獲取被反轉了。

用圖例說明一下,傳統程序設計如圖,都是主動去建立相關對象而後再組合起來:

當有了IoC/DI的容器後,在客戶端類中再也不主動去建立這些對象了:

2  IoC能作什麼

IoC不是一種技術,只是一種思想,一個重要的面向對象編程的法則,它能指導咱們如何設計出鬆耦合、更優良的程序。傳統應用程序都是由咱們在類內部主動建立依賴對象,從而致使類與類之間高耦合,難於測試;有了IoC容器後,把建立和查找依賴對象的控制權交給了容器,由容器進行注入組合對象,因此對象與對象之間是鬆散耦合,這樣也方便測試,利於功能複用,更重要的是使得程序的整個體系結構變得很是靈活。

其實IoC對編程帶來的最大改變不是從代碼上,而是從思想上,發生了「主從換位」的變化。應用程序本來是老大,要獲取什麼資源都是主動出擊,可是在IoC/DI思想中,應用程序就變成被動的了,被動的等待IoC容器來建立並注入它所須要的資源了。

IoC很好的體現了面向對象設計法則之一—— 好萊塢法則:「別找咱們,咱們找你」;即由IoC容器幫對象找相應的依賴對象並注入,而不是由對象主動去找。

三、IoC和DI

DI—Dependency Injection,即「依賴注入」:是組件之間依賴關係由容器在運行期決定,形象的說,即由容器動態的將某個依賴關係注入到組件之中。依賴注入的目的並不是爲軟件系統帶來更多功能,而是爲了提高組件重用的頻率,併爲系統搭建一個靈活、可擴展的平臺。經過依賴注入機制,咱們只須要經過簡單的配置,而無需任何代碼就可指定目標須要的資源,完成自身的業務邏輯,而不須要關心具體的資源來自何處,由誰實現。

理解DI的關鍵是:「誰依賴誰,爲何須要依賴,誰注入誰,注入了什麼」,那咱們來深刻分析一下:

  • 誰依賴於誰:固然是應用程序依賴於IoC容器;
  • 爲何須要依賴:應用程序須要IoC容器來提供對象須要的外部資源;
  • 誰注入誰:很明顯是IoC容器注入應用程序某個對象,應用程序依賴的對象;
  • 注入了什麼:就是注入某個對象所須要的外部資源(包括對象、資源、常量數據)。

IoC和DI由什麼關係呢?其實它們是同一個概念的不一樣角度描述,因爲控制反轉概念比較含糊(可能只是理解爲容器控制對象這一個層面,很難讓人想到誰來維護對象關係),因此2004年大師級人物Martin Fowler又給出了一個新的名字:「依賴注入」,相對IoC 而言,依賴注入」明確描述了「被注入對象依賴IoC容器配置依賴對象」。

注:若是想要更加深刻的瞭解IoC和DI,請參考大師級人物Martin Fowler的一篇經典文章《Inversion of Control Containers and the Dependency Injection pattern》,原文地址:http://www.martinfowler.com/articles/injection.html

四、依賴注入具體案例

有兩個組件A和B,A依賴於B。假設A是一個類,且A有一個方法importantMethod用到了B,以下:

class B{
    public void usefulMethod() {}
}

public class A{
    public void importantMethod() {
        //get an instance of B
        B b = new B();
        b.usefulMethod();
    }
}

要使用B,類A必須先得到組件B的實例引用。若B是一個具體類,則可經過new 關鍵字之間建立組件B的實例。可是,若是B是接口,且有多個實現,則問題就變得複雜了。咱們當然能夠任意選擇接口B的一個實現類,可是這意味着A的可重用性大大下降了,由於沒法採用B的其餘實現。

依賴注入是這樣處理此類情景的:接管對象的建立工做,並將該對象的引用注入到須要該對象的組件中。以上述狀況爲例,依賴注入框架會分別建立對象A和對象B,而後將對象B注入到對象A中。

爲了能讓框架進行依賴注入,程序員須要編寫特定的set方法或者構造方法。例如,爲了能將B注入到A中,類A會被修改爲以下形式:

public class A{
    private B b;
    public void importantMethod() {                
        b.usefulMethod();
    }
    
    public void setB(B b) {
        this.b = b;
    }
}

修改後的類A新增了一個set方法,該方法將會被框架調用,以注入B的一個實例。因爲對象依賴由依賴注入,類A的importantMethod()方法再也不須要在調用B的usefulMethod()方法前去建立B的一個實例。

固然,也能夠採用構造器方式注入,以下所示:

public class A{
    private B b;
    
    public A(B b) {
        this.b = b;
    }
    
    public void importantMethod() {                
        b.usefulMethod();
    }    
}

本例中,Spring會先建立B的實例,再建立A的實例,而後把B注入到實例中。

注:Spring管理的對象稱爲beans。

經過提供一個Ioc容器(或者說DI容器),Spring爲咱們提供一種能夠「聰明」的管理Java對象依賴關係的方法。其優雅之處在於,程序員無需瞭解Spring框架的存在,更不須要引入任何Spring類型。

五、ApplicationContext接口

使用Spring,程序幾乎將全部重要對象的建立工做移交給Spring,並配置如何注入依賴。Spring支持XML或註解兩種配置方式。此外,還須要建立一個ApplicationContext對象,表明一個Spring IoC容器,org.springframework.context.ApplicationContext接口有不少實現類,包括ClassPathXmlApplicationContext和FileSystemXmlApplicationContext。這兩個實現都須要至少一個包含beans信息的XML文件。

  • ClassPathXmlApplicationContext:嘗試在類加載路徑中加載配置文件;
  • FileSystemXmlApplicationContext:從文件系統中加載配置路徑。
/*
 * Copyright 2002-2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context;

import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.lang.Nullable;

/**
 * Central interface to provide configuration for an application.
 * This is read-only while the application is running, but may be
 * reloaded if the implementation supports this.
 *
 * <p>An ApplicationContext provides:
 * <ul>
 * <li>Bean factory methods for accessing application components.
 * Inherited from {@link org.springframework.beans.factory.ListableBeanFactory}.
 * <li>The ability to load file resources in a generic fashion.
 * Inherited from the {@link org.springframework.core.io.ResourceLoader} interface.
 * <li>The ability to publish events to registered listeners.
 * Inherited from the {@link ApplicationEventPublisher} interface.
 * <li>The ability to resolve messages, supporting internationalization.
 * Inherited from the {@link MessageSource} interface.
 * <li>Inheritance from a parent context. Definitions in a descendant context
 * will always take priority. This means, for example, that a single parent
 * context can be used by an entire web application, while each servlet has
 * its own child context that is independent of that of any other servlet.
 * </ul>
 *
 * <p>In addition to standard {@link org.springframework.beans.factory.BeanFactory}
 * lifecycle capabilities, ApplicationContext implementations detect and invoke
 * {@link ApplicationContextAware} beans as well as {@link ResourceLoaderAware},
 * {@link ApplicationEventPublisherAware} and {@link MessageSourceAware} beans.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @see ConfigurableApplicationContext
 * @see org.springframework.beans.factory.BeanFactory
 * @see org.springframework.core.io.ResourceLoader
 */
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
        MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

    /**
     * Return the unique id of this application context.
     * @return the unique id of the context, or {@code null} if none
     */
    @Nullable
    String getId();

    /**
     * Return a name for the deployed application that this context belongs to.
     * @return a name for the deployed application, or the empty String by default
     */
    String getApplicationName();

    /**
     * Return a friendly name for this context.
     * @return a display name for this context (never {@code null})
     */
    String getDisplayName();

    /**
     * Return the timestamp when this context was first loaded.
     * @return the timestamp (ms) when this context was first loaded
     */
    long getStartupDate();

    /**
     * Return the parent context, or {@code null} if there is no parent
     * and this is the root of the context hierarchy.
     * @return the parent context, or {@code null} if there is no parent
     */
    @Nullable
    ApplicationContext getParent();

    /**
     * Expose AutowireCapableBeanFactory functionality for this context.
     * <p>This is not typically used by application code, except for the purpose of
     * initializing bean instances that live outside of the application context,
     * applying the Spring bean lifecycle (fully or partly) to them.
     * <p>Alternatively, the internal BeanFactory exposed by the
     * {@link ConfigurableApplicationContext} interface offers access to the
     * {@link AutowireCapableBeanFactory} interface too. The present method mainly
     * serves as a convenient, specific facility on the ApplicationContext interface.
     * <p><b>NOTE: As of 4.2, this method will consistently throw IllegalStateException
     * after the application context has been closed.</b> In current Spring Framework
     * versions, only refreshable application contexts behave that way; as of 4.2,
     * all application context implementations will be required to comply.
     * @return the AutowireCapableBeanFactory for this context
     * @throws IllegalStateException if the context does not support the
     * {@link AutowireCapableBeanFactory} interface, or does not hold an
     * autowire-capable bean factory yet (e.g. if {@code refresh()} has
     * never been called), or if the context has been closed already
     * @see ConfigurableApplicationContext#refresh()
     * @see ConfigurableApplicationContext#getBeanFactory()
     */
    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;

}
View Code

下面是從類加載路徑中加載config1.xml和config2.xml的ApplicationContext建立的一個代碼示例:

ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"config1.xml","config2.xml"});

能夠經過調用ApplicationContext的getBean()方法從IoC容器中得到對象:

Product product = context.getBean("product",Product.class);

genBean()方法會在xml配置文件中查詢name(或id)爲product且類型爲Product的bean對象。

注:理想狀況下,咱們只需在測試代碼中建立一個ApplicationContext,應用程序自己無需處理。對於Spring MVC應用,能夠經過一個Spring Servlet來處理ApplicationContext,而無需直接處理。

三 XML配置文件

從1.0版本開始,Spring就支持基於XML的配置;從2.5版本開始,增長了經過註解的配置文件。下面介紹如何配置XML文件,配置文件的根元素一般爲beans:

<?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 ">
       ...
</beans>

若是須要更強的Spring配置能力,能夠在schema location屬性中添加相應的schema,也能夠指定schema版本:http://www.springframework.org/schema/beans/spring-beans-5.1.xsd,不過推薦使用默認的schma,以便升級spring庫時無需修改配置文件。

配置文件既能夠是一份,也能夠分解爲多份,以支持模塊化配置。ApplicationContext的實現類支持讀取多份配置文件。另外一種選擇是,經過一份主配置文件,將該文件導入到其餘配置文件。

下面是導入其餘配置文件的一個示例:

<?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 ">
       <import resource="config1.xml"/>
       <import resource="module2/config2.xml"/>
       <import resource="/resources/config3.xml"/>
       ...
</beans>

bean元素的配置在後面將會詳細介紹。

四 Spring控制反轉(IoC)容器的使用

本節將介紹Spring如何管理bean。

一、經過無參構造器建立一個bean實例

前面已經介紹,經過調用ApplicationContext的getBean()方法能夠獲取一個bean的實例。

下面咱們將建立一個名爲spring-intro的Java Project項目,而後咱們須要導入5個Spring Jar包:

  • 從spring-framework-5.1.6.RELEASE\libs下複製

spring-beans-5.1.6.RELEASE.jar

spring-context-5.1.6.RELEASE.jar

spring-core-5.1.6.RELEASE.jar

spring-expression-5.1.6.RELEASE.jar

到當前項目中,右鍵當前項目JRE System Library——>Build Path——>Configure Build Path——>Add External JARs把這四個包導入;

  • 從spring-framework-3.0.2.RELEASE-dependencies\org.apache.commons\com.springsource.org.apache.commons.logging\1.1.1下複製

com.springsource.org.apache.commons.logging-1.1.1.jar

到當前項目中,右鍵當前項目JRE System Library——>Build Path——>Configure Build Path——>Add External JARs把這一個包導入;

若是是Dynamic Java Web項目,直接將這5個Jar包導入到WebContent/WEB-INF/lib便可。

下面爲代碼的編寫,咱們先建立一個Product類,位於包springintro.bean中:

package springintro.bean;
import java.io.Serializable;

public class Product implements Serializable {
    private static final long serialVersionUID = 748392348L;
    private String name;
    private String description;
    private float price;

    public Product() {
    }

    public Product(String name, String description, float price) {
        this.name = name;
        this.description = description;
        this.price = price;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public float getPrice() {
        return price;
    }
    public void setPrice(float price) {
        this.price = price;
    }
}

下面建立一個名爲spring-config.xml的配置文件,其中定義了一個名爲product的bean,該配置文件位於src文件夾下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd ">
    
    <bean name="product" class="springintro.bean.Product"/>
    
</beans>

該bean的定義告訴Spring,經過默認無參的構造器來初始化Product類。若是不存在該構造器則會拋出一個異常。此外,該無參數的構造器並不要求是public簽名。

注意:應採用id或者name屬性標識一個bean。爲了讓Spring建立一個Product實例,應將bean定義的name值"product"和Product類型做爲參數傳給ApplicationContext的getBean()方法。

在包springintro下建立Main.java文件:

package springintro;

import java.time.LocalDate;
import java.util.Calendar;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import springintro.bean.Employee;
import springintro.bean.Product;

public class Main {
    public static void main(String[] args) {
        //建立IoC容器對象  從類路徑下(/src)加載xml配置文件  容器啓動時就會建立容器中配置的全部對象
        ApplicationContext context =
                new ClassPathXmlApplicationContext(new String[] {"spring-config.xml"});
        Product product1 = context.getBean("product", Product.class);
        product1.setName("Excellent snake oil");
        System.out.println("product1: " + product1.getName());
  }
}

輸出以下:

product1: Excellent snake oil

二、經過靜態工廠方法建立一個bean實例

大部分類能夠經過構造器來實例化。然而,Spring還一樣支持經過調用一個工廠的靜態方法來初始化類

下面的bean定義展現了經過靜態工廠方法來實例化Java.time.LocalData,調用java.time.LocalDate的靜態方法now()建立LocalDate 對象:

    <bean id="localDate" class="java.time.LocalDate"
        factory-method="now"/>

本例中採用了id屬性而非name屬性來標識bean,採用了getBean()方法來獲取LocalData實例:

        LocalDate localDate = context.getBean("localDate", java.time.LocalDate.class);
        System.out.println("today:" + localDate);

輸出以下:

today:2019-04-29

三、銷燬方法的使用(bean元素的destroy-method屬性)

有時,咱們但願一些對象在被銷燬前能執行一些方法。Spring考慮到這樣的需求,能夠在bean定義中配置destroy-method屬性,來指定在銷燬前要執行的方法。

下面的例子中,咱們配置Spring經過java.util.concurrent.Exceutors的靜態方法newCachedThreadPool()來建立一個java.util.concurrent.ExceutorService實例,並指定了destroy-method屬性值爲shutdown()方法。這樣,Spring會在銷燬ExceutorService實例前調用shutdown()方法:

     <bean id="executorService" class="java.util.concurrent.Executors" 
         factory-method="newCachedThreadPool" 
         destroy-method="shutdown">
     </bean>
        ExecutorService executorService = context.getBean("executorService", ExecutorService.class);
        //強制關閉IoC容器,在容器關閉以前會銷燬容器中全部對象  
        ((ClassPathXmlApplicationContext)context).close();

在程序中咱們強制關閉IoC容器,這樣就會銷燬ExceutorService實例,從而會觸發executorService.shutdown()方法的執行。

四、初始化方法的使用(bean元素的init-method屬性)

與銷燬方法相對應的還有一個初始化方法,會在對象實例建立以後調用,能夠在bean定義中配置init-method屬性,來指定初始化要執行的方法:

     <bean id="executorService" class="java.util.concurrent.Executors" 
         factory-method="newCachedThreadPool" 
         init-method="shutdown">
     </bean>

五、bean元素的scope屬性

  • singleton(默認值):單例模式,被標識爲單例的對象,在IoC容器中只會存在一個實例;
  • prototype:多例,被標識爲多例的對象,每次在獲取時纔會建立,每次建立都是新的對象,整合struct2時,ActionBean必須配置爲多例的;

  • request(瞭解):web環境下,與request聲明週期一致;
  • session(瞭解):web環境下,與session聲明週期一致;

以下案例:

 <bean name="product" class="springintro.bean.Product" scope="singleton"/>

 

五 Spring依賴注入(DI)方式

本節將詳細介紹Spring的依賴注入方式。

 一、構造器方式依賴注入

前面已經介紹了使用無參構造函數來初始化類,此外,Spring支持經過帶參數的構造器來初始化類。

咱們仍然以Product類爲例,如下的定義展現瞭如何經過Product類構造函數的參數名傳遞參數:

    <bean name="featuredProduct" class="springintro.bean.Product">
        <constructor-arg name="name" value="Ultimate Olive Oil"/>
        <constructor-arg name="description" value="The purest olive oil on the market"/>
        <constructor-arg name="price" value="9.95"/>
    </bean>

這樣,在IoC容器建立Product實例時,Spring會調用以下構造器:

        Product featuredProduct = context.getBean("featuredProduct", Product.class);
        System.out.println(featuredProduct.getName() + ", " + featuredProduct.getDescription()
                + ", " + featuredProduct.getPrice());
    public Product(String name, String description, float price) {
        this.name = name;
        this.description = description;
        this.price = price;
    }

輸出以下:

Ultimate Olive Oil, The purest olive oil on the market, 9.95

除了經過參數名稱傳遞參數外,Spring還支持經過指數方式來傳遞參數,具體以下:

    <bean name="featuredProduct2" class="springintro.bean.Product">
        <constructor-arg index="0" value="Ultimate Olive Oil"/>
        <constructor-arg index="1" value="The purest olive oil on the market"/>
        <constructor-arg index="2" value="9.95"/>
    </bean>

上面index="0",表示第一個參數傳入"Ultimate Olive Oil",同理...

可是若是還存在一個構造函數以下:

    public Product(String name, String description, String price) {
        this.name = name;
        this.description = description;
        this.price =  Float.parseFloat(price);
    }    

咱們會發現這個參數名和以前的同樣,而且順序也同樣,此時爲了區分構造函數,咱們還須要指定type參數:

    <bean name="featuredProduct3" class="springintro.bean.Product">
        <constructor-arg name="name" value="Ultimate Olive Oil"/>
        <constructor-arg name="description" value="The purest olive oil on the market"/>
        <constructor-arg name="price" value="9.95" type="java.lang.String" index="2"/>
    </bean>

若是構造函數的參數不是指類型,而是引用類型,則須要將value更改成ref。

二、Setter方式依賴注入

下面以Employee類和Address類爲例,介紹setter方式依賴注入。

Employee類代碼以下:

package springintro.bean;

public class Employee {
    private String firstName;
    private String lastName;
    private Address homeAddress;
    
    public Employee() {
    }
    
    public Employee(String firstName, String lastName, Address homeAddress) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.homeAddress = homeAddress;
    }
    
    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Address getHomeAddress() {
        return homeAddress;
    }

    public void setHomeAddress(Address homeAddress) {
        this.homeAddress = homeAddress;
    }

    @Override
    public String toString() {
        return firstName + " " + lastName
                + "\n" + homeAddress;
    }

}

Address類代碼以下:

package springintro.bean;

public class Address {
  private String line1;
    private String line2;
    private String city;
    private String state;
    private String zipCode;
    private String country;
    
    public Address(String line1, String line2, String city,
            String state, String zipCode, String country) {
        this.line1 = line1;
        this.line2 = line2;
        this.city = city;
        this.state = state;
        this.zipCode = zipCode;
        this.country = country;
    }

    public String getLine1() {
        return line1;
    }

    public void setLine1(String line1) {
        this.line1 = line1;
    }

    public String getLine2() {
        return line2;
    }

    public void setLine2(String line2) {
        this.line2 = line2;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public String getZipCode() {
        return zipCode;
    }

    public void setZipCode(String zipCode) {
        this.zipCode = zipCode;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    
    @Override
    public String toString() {
        return line1 + "\n"
                + line2 + "\n"
                + city + "\n"
                + state + " " + zipCode + "\n"
                + country;
    }
    

}

Employee類依賴於Address類,能夠經過以下配置來保證每一個Employee實例都能包含Address實例:

    <bean name="simpleAddress" class="springintro.bean.Address">
        <constructor-arg name="line1" value="151 Corner Street"/>
        <constructor-arg name="line2" value=""/>
        <constructor-arg name="city" value="Albany"/>
        <constructor-arg name="state" value="NY"/>
        <constructor-arg name="zipCode" value="99999"/>
        <constructor-arg name="country" value="US"/>
    </bean>

simpleAddress對象是Address類的一個實例,它經過構造器方式實例化。

    <bean name="employee1" class="springintro.bean.Employee">
        <property name="homeAddress" ref="simpleAddress"/>
        <property name="firstName" value="Junior"/>
        <property name="lastName" value="Moore"/>
    </bean>

employee1對象則經過配置property元素來調用setter方法以設置字段值。須要注意的是,homeAddress屬性配置的是simpleAddress對象的引用。

被引用對象的配置定義無需早於引用其對象的定義,在本例中,employee1對象能夠出如今simpleAddress對象定義以前。

這樣,在IoC容器建立employee1實例時,Spring會調用默認構造器,並經過setter方法設置值:

        Employee employee1 = context.getBean("employee1", Employee.class);
        System.out.println(employee1.getFirstName() + " " + employee1.getLastName());
        System.out.println(employee1.getHomeAddress());

輸出以下:

Junior Moore
151 Corner Street

Albany
NY 99999
US

咱們還能夠經過調用有參構造器建立Employee實例,並將Address對象經過構造器注入:

    <bean name="employee2" class="springintro.bean.Employee">
        <constructor-arg name="firstName" value="Senior"/>
        <constructor-arg name="lastName" value="Moore"/>
        <constructor-arg name="homeAddress" ref="simpleAddress"/>
    </bean>

這樣,在IoC容器建立employee2實例時,Spring會調用有參的構造器,具體是哪一個構造器,由設置的constructor-arg肯定:

        Employee employee2 = context.getBean("employee2", Employee.class);
        System.out.println(employee2.getFirstName() + " " + employee2.getLastName());
        System.out.println(employee2.getHomeAddress());

輸出以下:

Senior Moore
151 Corner Street

Albany
NY 99999
US

三、p名稱空間方式依賴注入

四、spel方式依賴注入、

p名稱空間方式依賴注入和spel方式依賴注入使用很少,所以不作介紹,有興趣能夠參考其餘博客。

五、複雜類型注入

若是類中的字段存在複雜類型,如數組,集合(List,Set,Map),Properties,這時依賴注入的配置文件將會有一些小的變化,具體能夠參考博客:Spring依賴注入之數組,集合(List,Set,Map),Properties的注入

注:本文所使用的程序來自控制反轉和依賴注入的理解(通俗易懂),代碼下載地址:https://github.com/pauldeck/springmvc-2ed

參考文獻

[1]Spring框架下載方法

[2]Spring MVC學習指南

[3]控制反轉和依賴注入的理解(通俗易懂)

[4]Spring MVC(推薦)

[5]Spring裝配Bean---使用xml配置(推薦)

[6]Spring應用上下文中Bean的生命週期

相關文章
相關標籤/搜索