1. 程式人生 > 其它 >Spring-學習筆記

Spring-學習筆記

官網

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 的屬性注入值。

  1. 第一步首先給 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;
    }
}
  1. 在 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配置的方式註冊有以下步驟:

  1. 配置類上加@Configuration註解,這個註解表示這個類不是一個普通類,而是一個配置類,它的作用相當於 applicationContext.xml

  2. 定義方法,方法返回物件,方法上新增@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);

注:配置類需要在專案啟動時載入才生效。