Maven簡介及構建Spring專案, Spring IoC篇二
使用Maven構建Spring開發環境
Maven簡介
Maven是一個專門管理構建Java工程的工具, 他的主要功能:
-
提供一套標準的專案結構
-
提供一套標準的構建流程
-
提供一套依賴管理機制
使用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時會成功,否則會失敗