1. 程式人生 > 實用技巧 >Spring入門及IoC的概念

Spring入門及IoC的概念

Spring入門

Spring是一個輕量級的Java開發框架,最早由Robd Johnson建立,目的為了解決企業級應用開發的業務邏輯層和其他各層的耦合問題,它是一個分層的JavaSE/EE輕量級開源框架。

Spring簡介

Spring框架已集成了20個模組,這些模組分佈在核心容器、資料訪問/整合層,Web層,AOP,植入模組,訊息傳輸和測試模組中。如圖所示

Spring的由來

Spring的目的是為了解決企業級應用開發的業務邏輯層和其他各層的耦合問題。是一個分層的JavaSE/EE的開源框架。

Spring的體系結構

Spring開發環境搭建

使用IDEA搭建JavaWeb應用

Spring的下載和目錄結構

Spring更新之後官方建議使用Maven下載,對於不使用maven的開發者,可以到官網下載。這裡採用的是spring-framework-5.0.2.RELEASE-dist.zip,解壓後就可以看到spring所有的結構。

目錄中的docs是文件。包含spring的API文件和開發規範,libs目錄包含所有的spring的jar包和原始碼。schema目錄包含Spring應用所需要的schema檔案。這些檔案定義了spring相關配置檔案的約束。

對於初學者,在開發Spring應用只需要將4個基本包和commons-logging-1.2.jar複製到Web工程中即可。

4個基礎包:spring-core

spring-beansspring-contextspring-expression

使用IDEA開發Spring入門程式

  1. 首先匯入jar包,建立maven工程,將4個基本包和一個依賴包匯入。
<dependency>
  <groupId>commons-logging</groupId>
  <artifactId>commons-logging</artifactId>
  <version>1.2</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-beans</artifactId>
  <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-core</artifactId>
  <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-expression</artifactId>
  <version>5.0.2.RELEASE</version>
</dependency>
  1. 建立介面TestDao

Spring解決的是業務邏輯層與其他各層的耦合問題,因此它將面向介面的程式設計思想貫穿整個系統應用。

package dao;
public interface TestDao {
    public void sayHello();
}
  1. 建立介面TestDao的實現類TestDaoImpl
package dao.Impl;
import dao.TestDao;
public class TestDaoImpl implements TestDao {
    @Override
    public void sayHello() {
        System.out.println("Hello Study");
    }
}
  1. 建立配置檔案applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--將指定類TestDaoImpl配置給Spring,讓Spring建立例項-->
    <bean id="test" class="dao.Impl.TestDaoImpl"></bean>
</beans>

配置檔案的名稱可以隨意定義,但習慣上命名為applicationContext.xml,有時候命名為beans.xml。

  1. 建立測試類
package test;
import dao.TestDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
    public static void main(String[] args) {
        //初始化Spring容器ApplicationContext,載入配置檔案
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        TestDao testDao = (TestDao)app.getBean("test");//test為配置檔案中的id。
         testDao.sayHello();
    }
}

在執行後會在控制檯輸出列印結果,在上述main方法沒有使用new運算子建立實體物件,而是通過Spring容器來獲取實體物件。這就是SpringIOC的工作機制。

spring IoC

IoC(控制反轉)是Spring框架的基礎,也是Spring框架的核心理念。

Spring IoC的基本概念

控制反轉(IoC)是一個比較抽象的概念,是Spring框架的核心,用來消減計算機程式的耦合問題,依賴注入(DI)是IoC的另一個說法,只是從不同的角度描述相同的概念。

通過一個例項來解釋IoC和DI。當人們需要一件東西時,第一反應就是找東西,例如想吃麵包。在沒有面包店和有面包店兩種情況下,人們會怎麼考慮?在沒有面包店時,最直觀的做法是按照自己的口味自己製作麵包,也就是一個麵包需要主動製作,然而時至今日,各種網店,實體店盛行,已經沒有必要自己製作,想吃麵包,去網店或實體店把自己的口味告訴店家,一會就可以吃到麵包了,注意,這種方式並沒有製作麵包,而是由店家制作,但是完全符合自己的口味。

上述的例子包含了控制反轉的思想,即把製作麵包的主動權交給商家,下面通過面向物件程式設計思想繼續探討。

當某個物件(呼叫者)需要呼叫另一個物件(被呼叫者),即被依賴物件時,在傳統程式設計方式下,呼叫者採用new 被呼叫者方式去建立物件,這種方式會增大呼叫者與被呼叫者的耦合性,不利於後期程式碼的維護。

當Spring框架出現之後,物件的例項不再由呼叫者來建立,而是由Spring容器來建立,Spring容器會負責控制程式之間的關係。而不是由呼叫者的程式程式碼控制,這樣控制權交給了Spring容器,所以控制權發生反轉。

在上述的麵包例項中,呼叫者是想吃麵包的人,被呼叫者是麵包,Spring容器就是麵包店,new是自己製作麵包。

從Spring容器角度來看,Spring容器負責將被依賴物件賦值給呼叫者的成員變數,相當於為呼叫者注入它所依賴的例項,這就是Spring的依賴注入。

Spring IoC容器

實現控制反轉的是Spring IoC容器,Spring IoC容器的設計主要是BeanFactoryApplicationContext介面。

BeanFactory

BeanFactoryorg.springframework.beans.factory.BeanFactory介面定義,它提供了完整的IoC服務支援,是一個管理Bean的工廠,主要負責初始化各種Bean。BeanFactory介面有多個實現類,其中比較常用的是org.springframework.beans.factory.xml.XmlBeanFactory,該類會根據XML配置檔案的定義來裝配Bean。

在建立BeanFactory例項時需要提供XML的絕對路徑。

package test;
import dao.TestDao;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
public class Test {
    public static void main(String[] args) {
        XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new FileSystemResource("E:\\javaProject\\spring-book\\ch1\\src\\main\\resources\\applicationContext.xml"));
        TestDao testDao = (TestDao)xmlBeanFactory.getBean("test");
        testDao.sayHello();
    }
}

使用BeanFactory例項載入Spring配置檔案在實際開發中很少使用。瞭解即可。

ApplicationContext

ApplicationContext是BeanFactory的子介面,也稱為上下應用文,由org.springframework.context.ApplicationContext介面定義,ApplicationContext介面除了包含BeanFactory的所有功能之外,還添加了對國際化,資源訪問,事件傳播等內容的支援。

建立ApplicationContext介面例項的方法通常有以下3種:

  1. 通過ClassPathXmlApplicationContext建立

ClassPathXmlApplicationContext將從類路徑目錄(src目錄)中尋找指定的配置檔案。

package test;

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

public class Test {
    public static void main(String[] args) {
        //初始化Spring容器ApplicationContext,載入配置檔案
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        TestDao testDao = (TestDao)app.getBean("test");//test為配置檔案中的id。
        testDao.sayHello();
    }
}
  1. 通過FileSystemXmlApplicationContext建立

FileSystemXmlApplicationContext將從指定檔案的絕對路徑尋找xml配置檔案,找到並裝載完成ApplicationContext的例項化.

package test;
import dao.TestDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class Test {
    public static void main(String[] args) {
        //初始化Spring容器ApplicationContext,載入配置檔案
        ApplicationContext app = new FileSystemXmlApplicationContext("E:\\javaProject\\spring-book\\ch1\\src\\main\\resources\\applicationContext.xml");
        TestDao testDao = (TestDao)app.getBean("test");//test為配置檔案中的id。
        testDao.sayHello();
    }
}

採用絕對路徑的載入方式將導致程式的靈活性變差,一般不推薦使用。

  1. 通過web伺服器例項化ApplicationContext容器

在Web工程中,ApplicationContext容器的例項化工作將交給Web伺服器完成。

例項化容器通過寫入web.xml檔案進行配置,只需要在web.xml新增程式碼:

<context-param>
<!--將在src下的applicationContext.xml配置檔案-->
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
  <!--指定以ContextLoaderListener方式啟動Spring容器-->
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

依賴注入的型別

在Spring中實現IoC容器的方法是依賴注入,依賴注入的作用是在使用Spring框架建立物件時動態地將其所依賴的物件(例如屬性值)注入Bean元件中,Spring框架依賴注入有兩種方式,一種是使用構造方法注入,另一種是屬性的setter方法注入。

使用構造方法注入

Spring框架可以採用Java的反射機制,通過用構造方法完成依賴注入,下面通過應用例項描述構造方法注入.

  1. 建立dao包,並且建立TestDIDao介面和其實現類TestDIDaoImpl
package dao;
public interface TestDIDao {
    public void sayHello();
}
package dao.Impl;
import dao.TestDIDao;
public class TestDIDaoImpl implements TestDIDao {
    @Override
    public void sayHello() {
        System.out.println("TestDIDao Hello");
    }
}
  1. 建立service包,並且建立TestDIService介面和其實現類TestDIServiceImpl
package service;
public interface TestDIService {
    public void sayHello();
}
package service.Impl;
import dao.TestDIDao;
import service.TestDIService;
public class TestDIServiceImpl implements TestDIService {
    private TestDIDao testDIDao;
    public TestDIServiceImpl(TestDIDao testDIDao){
        super();
        this.testDIDao=testDIDao;
    }
    @Override
    public void sayHello() {
        testDIDao.sayHello();
        System.out.println("TestDIService構造方法注入");
    }
}
  1. 編寫配置檔案,在applicationContext.xml中,首先將dao.TestDIDaoImpl類託管給Spring,讓Spring為其建立物件,然後將service.TestDIServiceImpl託管給Spring,讓Spring建立其物件,同時給構造方法傳遞引數。
<?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">
    <!--將指定類TestDIDaoImpl配置給Spring,讓Spring為其建立例項-->
    <bean id="myTestDIDao" class="dao.Impl.TestDIDaoImpl"/>
    <!--使用構造方法注入-->
    <bean id="testDIService" class="service.Impl.TestDIServiceImpl">
    <!--將myTestDIDao注入到TestDIServiceImpl類的屬性的TestDIDao上-->
        <constructor-arg index="0" ref="myTestDIDao"/>
    </bean>
</beans>

在配置檔案中,constructor-arg元素用於定義構造方法的引數,index用於定義引數的位置,ref指定某個例項的引用,如果是常量值(不是引用型別),ref由value代替。

package test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.TestDIService;
public class TestDI {
    public static void main(String[] args) {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        TestDIService testDIService = (TestDIService)app.getBean("testDIService");
        testDIService.sayHello();
    }
}

使用屬性的setter方法注入

使用setter方法注入是Spring框架最主流的注入方式,它利用JavaBean規範所定義的setter方法來完成注入,靈活且可讀性高。對於setter方法注入,Spring框架也是使用Java的反射機制實現的。

  1. 建立介面的實現類TestDIServiceImpl_setter
package service.Impl;
import dao.TestDIDao;
import service.TestDIService;
public class TestDIServiceImpl_setter implements TestDIService {
    private TestDIDao testDIDao;

    public void setTestDIDao(TestDIDao testDIDao) {
        this.testDIDao = testDIDao;
    }
    @Override
    public void sayHello() {
        testDIDao.sayHello();
        System.out.println("setter方法注入");
    }
}
  1. 將TestDIServiceImpl_setter託管給Spring
<!--將指定類TestDIDaoImpl配置給Spring,讓Spring為其建立例項-->
<bean id="myTestDIDao" class="dao.Impl.TestDIDaoImpl"/>
<!--使用setter方法注入-->
<bean id="testDIService_setter" class="service.Impl.TestDIServiceImpl_setter">
    <!--呼叫setter方法,將myTestDIDao注入到方法的屬性中-->
    <property name="testDIDao" ref="myTestDIDao"/>
</bean>

property的引數name為屬性名,,type為注入的型別,ref是指定的值(配置檔案的id)。

  1. 測試setter注入
package test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.TestDIService;
public class TestDI_setter {
    public static void main(String[] args) {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        TestDIService testDIService = (TestDIService)app.getBean("testDIService_setter");
        testDIService.sayHello();
    }
}