Spring-學習筆記
官網
Spring元件
Spring 中的元件,大致上提供瞭如下功能:
IOC
概念
Ioc (Inversion of Control),中文叫做控制反轉。這是一個概念,也是一種思想。控制反轉,實際上就是指對一個物件的控制權的反轉。
Ioc介紹
首先建立一個普通的 Maven 專案,然後引入 spring-context 依賴,如下:
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.9.RELEASE</version> </dependency> </dependencies>
接下來,在 resources 目錄下建立一個 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"> </beans>
在這個檔案中,我們可以配置所有需要註冊到 Spring 容器的 Bean:
<?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 class="org.javaboy.Book" id="book"/> </beans>
class 屬性表示需要註冊的 bean 的全路徑,id 則表示 bean 的唯一標記,也開可以 name 屬性作為 bean 的標記,在超過 99% 的情況下,id 和 name 其實是一樣的,特殊情況下不一樣。
接下來,載入這個配置檔案:
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
}
}
執行 main 方法,配置檔案就會被自動載入,進而在 Spring 中初始化一個 Book 例項。此時,我們顯式的指定 Book 類的無參構造方法,並在無參構造方法中列印日誌,可以看到無參構造方法執行了,進而證明物件已經在 Spring 容器中初始化了。
最後,通過 getBean 方法,可以從容器中去獲取物件:
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Book book = (Book) ctx.getBean("book");
System.out.println(book);
}
}
載入方式,除了ClassPathXmlApplicationContext 之外(去 classpath 下查詢配置檔案),另外也可以使用 FileSystemXmlApplicationContext ,FileSystemXmlApplicationContext 會從作業系統路徑下去尋找配置檔案。
public class Main {
public static void main(String[] args) {
FileSystemXmlApplicationContext ctx = new FileSystemXmlApplicationContext("F:\\workspace5\\workspace\\spring\\spring-ioc\\src\\main\\resources\\applicationContext.xml");
Book book = (Book) ctx.getBean("book");
System.out.println(book);
}
}
通過 Class獲取Bean
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Book book = ctx.getBean(Book.class);
System.out.println(book);
}
}
這種方式有一個很大的弊端,如果存在多個例項,這種方式就不可用,例如,xml 檔案中存在兩個 Bean:
<?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 class="org.javaboy.Book" id="book"/>
<bean class="org.javaboy.Book" id="book2"/>
</beans>
此時,如果通過 Class 去查詢 Bean,會報如下錯誤:
所以,一般建議使用 name 或者 id 去獲取 Bean 的例項。
屬性注入
構造方法注入
通過 Bean 的構造方法給 Bean 的屬性注入值。
- 第一步首先給 Bean 新增對應的構造方法:
public class Book implements Serializable {
private static final long serialVersionUID = 5492270562431552420L;
private Integer id;
private String name;
private Double price;
public Book() {
}
public Book(Integer id, String name, Double price) {
this.id = id;
this.name = name;
this.price = price;
}
}
- 在 xml 檔案中注入 Bean
<?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 id="book" class="cn.sail.training.spring.ioc.Book">
<constructor-arg index="0" value="1"/>
<constructor-arg index="1" value="紅樓夢"/>
<constructor-arg index="2" value="33"/>
</bean>
</beans>
constructor-arg 中的 index 和 Book 中的構造方法引數一一對應。寫的順序可以顛倒,但是 index 的值和 value 要一一對應。
另一種構造方法中的屬性注入,則是通過直接指定引數名來注入:
<?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 id="book1" class="cn.sail.training.spring.ioc.Book">
<constructor-arg name="id" value="2"/>
<constructor-arg name="name" value="西遊記"/>
<constructor-arg name="price" value="40"/>
</bean>
</beans>
如果有多個構造方法,則會根據給出引數個數以及引數型別,自動匹配到對應的構造方法上,進而初始化一個物件。
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.xsd">
<!--set方法注入:可以較直觀的看到定義的屬性,推薦使用-->
<bean id="user" class="cn.sail.training.spring.ioc.User">
<property name="id" value="1"/>
<property name="age" value="27"/>
<property name="name" value="廖航"/>
</bean>
</beans>
set 方法注入,不是根據屬性名對應的值,而是根據 get/set 方法分析出來的屬性名。
如果改變set方法的名稱,就算屬性名不變,也會找不到值而報錯。
p 名稱空間注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="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.xsd">
<!--p名稱空間注入:本質上也是呼叫了set方法,層次結構不清晰,且需要額外引進標籤值,不推薦使用-->
<bean id="book2" class="cn.sail.training.spring.ioc.Book" p:id="2" p:name="西遊記" p:price="33"/>
</beans>
靜態工廠注入
首先提供一個 OkHttpClient 的靜態工廠:
import okhttp3.OkHttpClient;
public class OkHttpUtils {
private static OkHttpClient okHttpClient;
public static OkHttpClient getInstance() {
if (okHttpClient == null) {
okHttpClient = new OkHttpClient.Builder().build();
}
return okHttpClient;
}
}
在 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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--靜態工廠注入-->
<bean id="okHttpUtils" class="cn.sail.training.spring.ioc.OkHttpUtils" factory-method="getInstance"/>
</beans>
這個配置表示 OkHttpUtils 類中的 getInstance 是我們需要的例項,例項的名字就叫 okHttpClient。然後,在 Java 程式碼中,獲取到這個例項,就可以直接使用了。
例項工廠注入
例項工廠就是工廠方法是一個例項方法,這樣,工廠類必須例項化之後才可以呼叫工廠方法。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="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.xsd">
<!--例項工廠注入-->
<bean id="okHttpUtils" class="cn.sail.training.spring.ioc.OkHttpUtils" factory-method="getInstance"/>
<bean id="okHttpClient1" class="okhttp3.OkHttpClient" factory-bean="okHttpUtils"/>
</beans>
複雜屬性注入
物件注入
可以通過 xml 注入物件,通過 ref 來引用一個物件。
<bean id="user1" class="cn.sail.training.spring.ioc.User">
<property name="cat" ref="cat"/>
</bean>
<bean id="cat" class="cn.sail.training.spring.ioc.Cat">
<property name="name" value="小白"/>
<property name="color" value="白色"/>
</bean>
陣列注入
<bean id="user2" class="cn.sail.training.spring.ioc.User">
<property name="favorites">
<array>
<value>足球</value>
<value>籃球</value>
<value>乒乓球</value>
</array>
</property>
</bean>
集合注入
即可以通過 ref 使用外部定義好的 Bean,也可以直接在 list 或者 array 節點中定義 bean。
<bean id="user3" class="cn.sail.training.spring.ioc.User">
<property name="cats">
<list>
<ref bean="cat"/>
<bean id="cat2" class="cn.sail.training.spring.ioc.Cat">
<property name="name" value="小黑"/>
<property name="color" value="黑色"/>
</bean>
</list>
</property>
</bean>
Map注入
<bean id="user4" class="cn.sail.training.spring.ioc.User">
<property name="map">
<map>
<entry key="name" value="sail"/>
<entry key="age" value="27"/>
</map>
</property>
</bean>
Properties注入
<bean id="user5" class="cn.sail.training.spring.ioc.User">
<property name="info">
<props>
<prop key="name">sail</prop>
<prop key="age">27</prop>
</props>
</property>
</bean>
Java配置
在 Spring 中,想要將一個 Bean 註冊到 Spring 容器中,整體上來說,有三種不同的方式。
- XML 注入
- Java 配置(通過 Java 程式碼將 Bean 註冊到 Spring 容器中)
- 自動化掃描
Java配置的方式註冊有以下步驟:
-
配置類上加@Configuration註解,這個註解表示這個類不是一個普通類,而是一個配置類,它的作用相當於 applicationContext.xml
-
定義方法,方法返回物件,方法上新增@Bean註解,表示將這個方法的返回值注入的Spring容器中去。也就是說,@Bean所對應的方法,就相當於applicationContext.xml中的bean節點。
@Configuration
public class Config {
@Bean
User user() {
return new User();
}
}
配置載入
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
User user = ctx.getBean(User.class);
Bean的預設名稱是方法名。如果想自定義方法名,直接在@Bean中進行配置。如下配置表示修改Bean的名字為sail:
@Configuration
public class Config {
@Bean("sail")
User user() {
return new User();
}
}
配置載入
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
User user = ctx.getBean("sail", User.class);
注:配置類需要在專案啟動時載入才生效。