1. 程式人生 > 實用技巧 >Maven簡介及構建Spring專案, Spring IoC篇二

Maven簡介及構建Spring專案, Spring IoC篇二

使用Maven構建Spring開發環境

Maven簡介

Maven是一個專門管理構建Java工程的工具, 他的主要功能:

  1. 提供一套標準的專案結構

  2. 提供一套標準的構建流程

  3. 提供一套依賴管理機制

使用Maven管理的Java專案, 目錄結構預設如下:

maven-project
├── pom.xml // 專案描述檔案, 包含工程描述, 依賴管理
├── src
│   ├── main
│   │   ├── java //原始碼目錄
│   │   └── resources //資源目錄, 比如xml配置檔案
│   └── test
│       ├── java //測試原始碼目錄
│       └── resources //測試資源
└── target //存放編譯結果、打包後文件

一般主流的開發環境, 都有自己的依賴管理平臺, 比如node.js開發使用npm來進行包管理, OSX以及IOS使用CocoaPods, Python如Anaconda

Maven中央倉庫地址: https://mvnrepository.com

建立專案

Idea建立maven專案(可以選擇從骨架建立web專案)

  • GroupId : 包名, 用來描述工程的分組資訊, 及當前工程是屬於哪個組的, 同組內不允許出現兩個相同ArtifactId的工程

  • ArtifactId : 工程的唯一標識名

  • version : 工程版本資訊

構建Spring開發環境

引用jar檔案

阿里雲映象倉庫地址, maven目錄下conf -> settings.xml中修改

	<mirror>
		<id>alimaven</id>
        <name>aliyun maven</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
        <mirrorOf>central</mirrorOf>        
    </mirror>

pom.xml檔案中新增以下依賴, 儲存後Maven就會自動去中央倉庫根據你的依賴下載Jar包

<dependencies>
 
	<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-context</artifactId>
	    <version>5.2.6.RELEASE</version>
	</dependency>
	
	 <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
	<dependency>
	    <groupId>commons-logging</groupId>
	    <artifactId>commons-logging</artifactId>
	    <version>1.2</version>
	</dependency>
	
	<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
	<dependency>
	    <groupId>org.apache.commons</groupId>
	    <artifactId>commons-lang3</artifactId>
	    <version>3.9</version>
	</dependency>
	
</dependencies>

這裡其實並沒有把Spring基礎的jar檔案全部引用, 當Maven去下載spring-context.jar檔案時, 會把相關起來的其他Jar檔案也全部下載, 最後依賴的commons-lang3.jar中封裝了常用的工具, 後續會使用它進行除錯

建立配置檔案 applicationContext.xml

src/main/resources/目錄下建立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">

</beans>

Idea Project Structure中進行如下配置

Spring IoC

Spring IoC - 控制反轉, 在應用程式中, 物件的建立交給容器, 而不是由開發寫程式碼new出來
Spring框架的一個目的是解耦, 而IoC是解耦的一種方式, 也就是程式中出現的物件都交給Spring去管理

Spring所管理的物件被叫做bean物件, 配置檔案是Spring進行生產,依賴注入以及分發Bean的藍圖, 在配置檔案中寫入一個<bean>就是在配置一個bean物件, id是bean的唯一標識, class是對應類的包路徑.

<bean id="user" class="com.xxn.User"></bean>

為了方便對bean的管理, 我們可以建立多個配置檔案, 用來配置不同型別的bean

在載入過程中, 可以讓Spring只加載一個配置, 而這個配置檔案去引用其他的配置檔案

<import resource="application-service.xml"/>

基於XML的常用依賴注入

依賴注入: Spring可以為建立的bean物件設定屬性, 也就是為他們的成員變數賦值,

該小節記錄了基於XML 的常用注入方式, 實際在開發過程中, 很少使用XML方式注入, 多使用註解的方式自動注入

把上篇博文中的User類擴充套件一下,再新增多種型別的成員變數,已儲存更多的資訊

public class User {
	public String username;
	public String password;
	public String[] address;
	public List<String> orders;
	public Set<String> favours;
	public Map<Integer,String> orderStatus;
	
	public User() {
		super();
	}
	
	public User(String username, String password, String[] address, List<String> orders, Set<String> favours,
			Map<Integer, String> orderStatus) {
		super();
		this.username = username;
		this.password = password;
		this.address = address;
		this.orders = orders;
		this.favours = favours;
		this.orderStatus = orderStatus;
	}

    getter && setter ...

基於構造器的依賴注入

可以使用構造器函式來為User物件設定值

User類中有一個構造器函式, 可以讓Spring使用這個建構函式來為User物件設定值.

不過既然Spring需要一個Order物件賦值給User物件作為成員變數, 那麼Spring就需要有建立Order物件的能力,所以也需要把Order設定為一個bean

<bean id="order" class="com.XX.XX.Order"

接下來讓Spring可以訪問User的構造器, 這裡用<constructor-arg></constructor-arg>標籤,它的作用就是呼叫構造器來注入引數, 我們把它掛在id="user"的bean節點下

<bean id="user" class="com.xx.xx.User">
	<constructor-arg name="username" value="tom"></constructor-arg>
	<constructor-arg name="password" value="123"></constructor-arg>
	<constructor-arg name="order" ref="order"></constructor-arg>
</bean>

通過這個配置檔案,Spring可以建立一個User物件, 並且有初始值, 不需要我們手動new一個order給它了

需要注意的是, 構造器注入就是Spring通過反射來呼叫類的帶引數構造器, 在配置檔案中指定引數時, 可以有以下幾種方法:

  • 指定引數名稱

    <constructor-arg name="username" value="tom"></constructor-arg>
    
  • 指定引數型別:

    <constructor-arg type="String"  value="tom"></constructor-arg>
    
  • 指定索引

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

基於屬性的依賴注入

屬性注入是Spring通過反射呼叫對應成員變數的setter方法實現的

例如,我們希望為User的username, password注入屬性可以用如下方式:

<bean name="user" class="com.xx.xx.User">
    <property name="username" value="tom"></property>
    <property name="password" value="123"></property>
</bean>

若為集合型別成員變數注入, 則用以下方式:

<property name="address">
	<array>
        <value>北京市</value>
        <value>上海市</value>
        <value>青島市</value>
    </array>
</property>

<property name="orders">
	<list>
    	<value>洗手液</value>
        <value>口罩</value>
        <value>圖書</value>
    </list>
</property>

<property name="favours">
    <set>
        <value>洗手液</value>
        <value>口罩</value>
        <value>圖書</value>
    </set>
</property>

<property name="orderStatus">
	<map>
    	<entry key="10001" value="已簽收"></entry>
        <entry key="10002" value="已發貨"></entry>
        <entry key="10003" value="未發貨"></entry>
    </map>
</property>

工廠方式建立Bean

之前我們在配置檔案中編寫bean時, 是關聯到一個類上

事實上Spring允許我們關聯到一個方法上, 準確的說是關聯到一個工廠方法中, 這個方法會根據條件 返回一個物件

設計一個貨物類, 這些貨物類要擁有相同的方法, 並且我們會通過工廠方法構造他們的例項

建立一個名為Goods的介面:

package mzq.pojo;
public interface Goods {
    String getName();
    String getPrice();
}

接著建立第一個介面實現類Book

package mzq.pojo;
public class Book implements Goods {
    private String name = "Java核心技術";
    private String price = "$128.00";
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPrice() {
        return price;
    }
    public void setPrice(String price) {
        this.price = price;
    }
}

然後編寫工廠類來建立物件

此時根據工廠方法是靜態的還是動態的, 有兩種不同的實現方式

//動態工廠
public class GoodsDynamicFactory {
    public Goods getGoods(String name) throws Exception {
        if (name.equals("Book")) {
            return new Book();
        } else {
            throw new Exception("can not create " + name);
        }
    }
}
//靜態工廠
public class GoodsStaticFactory {
    public static Goods getGoods(String name) throws Exception {
        if(name.equals("Book")) {
            return new Book();
        }
        else {
            throw new Exception("can not create " + name);
        }
    }
}

最後來編寫配置檔案

    <!--   Dynamic factory  -->
    <!--  建立factory-bean  -->
    <bean name="goodsFactory" class="mzq.factory.GoodsDynamicFactory"></bean>
    <bean name="goods" factory-bean="goodsFactory" factory-method="getGoods">
        <constructor-arg value="Book"></constructor-arg>
    </bean>

    <!--   Static factory  -->
    <bean name="goodsStatic" class="mzq.factory.GoodsStaticFactory" factory-method="getGoods">
        <constructor-arg value="Book"></constructor-arg>
    </bean>

動態的工廠函式是要通過是物件來呼叫的, 所以需要先把這個工廠類也註冊為bean

然後再註冊一個貨物的bean

此時貨物bean 就不再指向某一個類, 而是指向工廠方法

至於傳遞引數是通過<constructor-arg></constructor-arg>而不是<property></property>, 因為property底層就是反射呼叫了某一屬性的setter方法, 所以它就是用來為屬性賦值的

測試程式碼

public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        Book bookD = (Book) ctx.getBean("goodsDynamic");
        System.out.println(ToStringBuilder.reflectionToString(bookD, ToStringStyle.MULTI_LINE_STYLE));

        Book bookS = (Book) ctx.getBean("goodsStatic");
        System.out.println(ToStringBuilder.reflectionToString(bookS, ToStringStyle.MULTI_LINE_STYLE));
    }

Bean作用域

Bean的作用域是指, 被Spring建立的bean物件的生命週期

Spring為bean提供了6種作用域, 分別是:

  • singleton
  • prototype
  • websocket
  • request
  • session
  • application

可以在配置檔案<bean>標籤中設定scope屬性來指定這個bean的作用域.

singleton是單例模式,也就是自始至終,Spring只會建立一個這個bean物件, 這個是預設值.

prototype是非單例模式, 每次該bean被注入, 或者通過getBean被獲取時,Spring都會建立一個新的物件.

後面四種作用域, 其使用場景都在web伺服器中, 每個bean作用域與對應的生命週期所繫結.

request作用域, 其生命週期就是指一個使用者傳送一次http的請求開始,倒一次請求結束為止, 也就是這個bean的作用域從時序上來看,就是這次請求開始到這次請求結束.

session作用域, session-在伺服器端實現的一種會話追蹤技術,簡單來說就是在伺服器端記錄使用者的某些資訊,以此來識別每次請求的使用者; session作用域是從使用者發過來的第一個請求開始,結束時間通常不能直接判斷, 因為瀏覽器關閉是不會通知伺服器的, 一般我們判定如果使用者在一定時間內沒有反應則結束會話, tomcat預設失效時間20min, 此時bean在這個作用域內生效.

application作用域, 指伺服器一開始執行執行, 直到伺服器關閉為止.

迴圈引用

迴圈引用, A裡面包含B型別的成員變數, B裡面包含A型別的成員變數, 這樣的結構.

如果你的程式碼中不幸出現這種結構, 直接看結論:

  • 若所有迴圈引用的bean都是通過構造器注入, 無論如何Spring都不能建立成功
  • 若所有迴圈引用的bean都是通過屬性注入
    • 若所有的bean都是singlton, Spring可以成功建立bean
    • 若所有的bean都是prototype,Spring不能成功建立bean
    • 若bean既有singlton又有prototype, 當首先獲取singlton的bean時會成功,否則會失敗