Java開發學習(八)----IOC/DI設定管理第三方bean、載入properties檔案

2022-07-06 06:01:08

前面的部落格都是基於我們自己寫的類,現在如果有需求讓我們去管理第三方jar包中的類,該如何管理?

一、案例:資料來源物件管理

本次案例將使用資料來源DruidC3P0來設定學習下。

1.1 環境準備

學習之前,先來準備下案例環境:

  • 建立一個Maven專案

  • pom.xml新增依賴

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
    </dependencies>
  • resources下新增spring的組態檔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>
  • 編寫一個執行類App

    public class App {
        public static void main(String[] args) {
            ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        }
    }

1.2 思路分析

在上述環境下,我們來對資料來源進行設定管理,先來分析下思路:

需求:使用Spring的IOC容器來管理Druid連線池物件

1.使用第三方的技術,需要在pom.xml新增依賴

2.在組態檔中將【第三方的類】製作成一個bean,讓IOC容器進行管理

3.資料庫連線需要基礎的四要素驅動連線使用者名稱密碼,【如何注入】到對應的bean中

4.從IOC容器中獲取對應的bean物件,將其列印到控制檯檢視結果

思考:

  • 第三方的類指的是什麼?

  • 如何注入資料庫連線四要素?

1.3 實現Druid管理

步驟1:匯入druid的依賴

pom.xml中新增依賴

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
</dependency>
步驟2:設定第三方bean

在applicationContext.xml組態檔中新增DruidDataSource的設定

<?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">
    <!--管理DruidDataSource物件-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/spring_db"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
</beans>

說明:

  • driverClassName:資料庫驅動

  • url:資料庫連線地址

  • username:資料庫連線使用者名稱

  • password:資料庫連線密碼

  • 資料庫連線的四要素要和自己使用的資料庫資訊一致。

步驟3:從IOC容器中獲取對應的bean物件
public class App {
    public static void main(String[] args) {
       ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
       DataSource dataSource = (DataSource) ctx.getBean("dataSource");
       System.out.println(dataSource);
    }
}
步驟4:執行程式

列印如下結果: 說明第三方bean物件已經被spring的IOC容器進行管理

做完案例後,我們可以將剛才思考的兩個問題答案說下:

  • 第三方的類指的是什麼?

    DruidDataSource
  • 如何注入資料庫連線四要素?

    setter注入

1.4 實現C3P0管理

完成了DruidDataSource的管理,接下來我們再來加深下,這次我們來管理C3P0資料來源,具體的實現步驟是什麼呢?

需求:使用Spring的IOC容器來管理C3P0連線池物件

實現方案和上面基本一致,重點要關注管理的是哪個bean物件`?

步驟1:匯入C3P0的依賴

pom.xml中新增依賴

<dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
</dependency>

對於新的技術,不知道具體的座標該如何查詢?

  • 直接百度搜尋

  • 從mvn的倉庫https://mvnrepository.com/中進行搜尋

步驟2:設定第三方bean

在applicationContext.xml組態檔中新增設定

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="com.mysql.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_db"/>
    <property name="user" value="root"/>
    <property name="password" value="root"/>
    <property name="maxPoolSize" value="1000"/>
</bean>

注意:

  • ComboPooledDataSource的屬性是通過setter方式進行注入

  • 想注入屬性就需要在ComboPooledDataSource類或其上層類中有提供屬性對應的setter方法

  • C3P0的四個屬性和Druid的四個屬性是不一樣的

步驟3:執行程式

程式會報錯,錯誤如下

報的錯為ClassNotFoundException,翻譯出來是類沒有發現的異常,具體的類為com.mysql.jdbc.Driver。錯誤的原因是缺少mysql的驅動包。

分析出錯誤的原因,具體的解決方案就比較簡單,只需要在pom.xml把驅動包引入即可。

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>

新增完mysql的驅動包以後,再次執行App,就可以列印出結果:

注意:

  • 資料連線池在設定屬性的時候,除了可以注入資料庫連線四要素外還可以設定很多其他的屬性,具體都有哪些屬性用到的時候再去查,一般設定基礎的四個,其他都有自己的預設值

  • Druid和C3P0在沒有匯入mysql驅動包的前提下,一個沒報錯一個報錯,說明Druid在初始化的時候沒有去載入驅動,而C3P0剛好相反

  • Druid程式執行雖然沒有報錯,但是當呼叫DruidDataSource的getConnection()方法獲取連線的時候,也會報找不到驅動類的錯誤

二、載入properties檔案

我們已經完成兩個資料來源druidC3P0的設定,但是其中包含了一些問題,我們來分析下:

  • 這兩個資料來源中都使用到了一些固定的常數如資料庫連線四要素,把這些值寫在Spring的組態檔中不利於後期維護

  • 需要將這些值提取到一個外部的properties組態檔中

  • Spring框架如何從組態檔中讀取屬性值來設定就是接下來要解決的問題。

問題提出來後,具體該如何實現?

2.1 第三方bean屬性優化

2.1.1 實現思路

需求:將資料庫連線四要素提取到properties組態檔,spring來載入設定資訊並使用這些資訊來完成屬性注入。

1.在resources下建立一個jdbc.properties(檔案的名稱可以任意)

2.將資料庫連線四要素設定到組態檔中

3.在Spring的組態檔中載入properties檔案

4.使用載入到的值實現屬性注入

其中第3,4步驟是重點,具體如何實現?

2.1.2 實現步驟
步驟1:準備properties組態檔

resources下建立一個jdbc.properties檔案,並新增對應的屬性鍵值對

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db
jdbc.username=root
jdbc.password=root
步驟2:開啟context名稱空間

在applicationContext.xml中開context名稱空間

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
步驟3:載入properties組態檔

在組態檔中使用context名稱空間下的標籤來載入properties組態檔

<context:property-placeholder location="jdbc.properties"/>
步驟4:完成屬性注入

使用${key}來讀取properties組態檔中的內容並完成屬性注入

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    
    <context:property-placeholder location="jdbc.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>

至此,讀取外部properties組態檔中的內容就已經完成。

2.2 讀取單個屬性

2.2.1 實現思路

對於上面的案例,效果不是很明顯,我們可以換個案例來演示下:

需求:從properties組態檔中讀取key為name的值,並將其注入到BookDao中並在save方法中進行列印。

1.在專案中新增BookDao和BookDaoImpl類

2.為BookDaoImpl新增一個name屬性並提供setter方法

3.在jdbc.properties中新增資料注入到bookDao中列印方便查詢結果

4.在applicationContext.xml新增設定完成組態檔載入、屬性注入(${key})

2.2.2 實現步驟
步驟1:在專案中添對應的類

BookDao和BookDaoImpl類,並在BookDaoImpl類中新增name屬性與setter方法

public interface BookDao {
    public void save();
}
​
public class BookDaoImpl implements BookDao {
    private String name;
​
    public void setName(String name) {
        this.name = name;
    }
​
    public void save() {
        System.out.println("book dao save ..." + name);
    }
}
步驟2:完成組態檔的讀取與注入

在applicationContext.xml新增設定,bean的設定管理讀取外部properties依賴注入:

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    
    <context:property-placeholder location="jdbc.properties"/>
    
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
        <property name="name" value="${jdbc.driver}"/>
    </bean>
</beans>
步驟3:執行程式

在App類中,從IOC容器中獲取bookDao物件,呼叫方法,檢視值是否已經被獲取到並列印控制檯

public class App {
    public static void main(String[] args) throws Exception{
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        bookDao.save();
​
    }
}

2.2.3 注意事項

至此,讀取properties組態檔中的內容就已經完成,但是在使用的時候,有些注意事項:

  • 問題一:鍵值對的key為username引發的問題

    1.在properties中設定鍵值對的時候,如果key設定為username

    username=root666

    2.在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"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="
                http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context.xsd">
        
        <context:property-placeholder location="jdbc.properties"/>
        
        <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
            <property name="name" value="${username}"/>
        </bean>
    </beans>

    3.執行後,在控制檯列印的卻不是root666,而是自己電腦的使用者名稱

    4.出現問題的原因是<context:property-placeholder/>標籤會載入系統的環境變數,而且環境變數的值會被優先載入,如何檢視系統的環境變數?

    public static void main(String[] args) throws Exception{
        Map<String, String> env = System.getenv();
        System.out.println(env);
    }

    大家可以自行執行,在列印出來的結果中會有一個USERNAME=XXX[自己電腦的使用者名稱稱]

    5.解決方案

    <?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:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="
                http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context.xsd">
        
        <context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
    </beans>

    system-properties-mode:設定為NEVER,表示不載入系統屬性,就可以解決上述問題。

    當然還有一個解決方案就是避免使用username作為屬性的key

  • 問題二:當有多個properties組態檔需要被載入,該如何設定?

    1.調整下組態檔的內容,在resources下新增jdbc.properties,jdbc2.properties,內容如下:

    jdbc.properties

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db
    jdbc.username=root
    jdbc.password=root

    jdbc2.properties

    username=root666

    2.修改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"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="
                http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context.xsd">
        <!--方式一 -->
        <context:property-placeholder location="jdbc.properties,jdbc2.properties" system-properties-mode="NEVER"/>
        <!--方式二-->
        <context:property-placeholder location="*.properties" system-properties-mode="NEVER"/>
        <!--方式三 -->
        <context:property-placeholder location="classpath:*.properties" system-properties-mode="NEVER"/>
        <!--方式四-->
        <context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>
    </beans>    

    說明:

    • 方式一:可以實現,如果組態檔多的話,每個都需要設定

    • 方式二:*.properties代表所有以properties結尾的檔案都會被載入,可以解決方式一的問題,但是不標準

    • 方式三:標準的寫法,classpath:代表的是從根路徑下開始查詢,但是隻能查詢當前專案的根路徑

    • 方式四:不僅可以載入當前專案還可以載入當前專案所依賴的所有專案的根路徑下的properties組態檔

2.3 載入properties檔案小結

  • 如何開啟context名稱空間

  • 如何載入properties組態檔

    <context:property-placeholder location="" system-properties-mode="NEVER"/>
  • 如何在applicationContext.xml引入properties組態檔中的值

    ${key}