Spring筆記(1) - 工廠

QuatumW發表於2020-10-27

Spring筆記(1) - 工廠

前言

此筆記由孫哥的視訊整理而來
作者: Wyt

孫哥B站視訊連結如下:
孫哥說Spring5 全部更新完畢 完整筆記、程式碼看置頂評論連結~學不會Spring? 因為你沒找對人

正文

1. 概述

1.1 EJB(Enterprise Java Bean)

存在的問題:

1. 執行環境苛刻
2. 程式碼移植性差

1.2 Spring是什麼

  • 概念
Spring是分層的 Java SE/EE應用 full-stack 輕量級開源框架,以 IOC(Inverse Of Control:反轉控制)和 AOP(Aspect Oriented Programming:面向切面程式設計)為核心。
  • 優勢

    1. 方便解耦,簡化開發
    2. AOP程式設計的支援
    3. 宣告式事物的支援
    4. 方便程式的測試
    
  • 輕量級

1. 對於執行環境是沒有額外要求的
   開源 tomcat resion jetty 
   收費 weblogic  websphere 
2. 程式碼移植性高
   不需要實現額外介面
  • 分層,多模組

多模組
模組介紹

  • 整合設計模式
1. 工廠
2. 代理
3. 模板
4. 策略

1.3 設計模式

1. 廣義概念
物件導向設計中,解決特定問題的經典程式碼
2. 狹義概念
GOF4人幫定義的23種設計模式:工廠、介面卡、裝飾器、門面、代理、模板...

2. Spring入門

2.1 知識鋪墊

1. javase基礎
2. mysql的基本操作
3. 瞭解jdbc
4. maven

2.2 環境支援

1. JDK
2. Maven
3. IDEA
Spring官方網站: www.spring.io

2.3 Maven中各類檔案的介紹

Maven包名
	src
		main
			java: 用來存放專案主要的程式碼: .java
			resouce: 存放配置檔案: .xml .propertise
		test: 存放測試類: .java
	target: 存放依賴: .jar
	pom.xml: 物件模型(Project Object Model)管理檔案
	包名.iml: IDEA生成的對Module的配置資訊
1. 一般在resouce資料夾新建applicationContext.xml
	用於管理bean物件,aop配置等
	(理論上可以任意命名,放在任意位置)
2. 使用時:
	ApplicationContext ctx = new 		
	ClassPathXmlApplicationContext("/applicationContext.xml");

2.4 重要概念

1. bean:
	Spring容器作為超級大工廠,負責建立、管理所有的Java物件,這些Java物件被稱為Bean。
	在applicationContext.xml中, 
	用<bean id="###" class="相對位置">, 引入某個類並賦值id(唯一), 其他`屬性有如name(別名)等
2. 依賴管理: 
	2.1 Spring容器管理容器中Bean之間的依賴關係,Spring使用一種被稱為“依賴注入”的方式來管理Bean之間的依賴關係。
	2.2 使用依賴注入,不僅可以為Bean注入普通的屬性值,還可以注入其他Bean的引用。依賴注入是一種優秀的解耦方式,其可以讓Bean以配置檔案組織在一起,而不是以硬編碼的方式耦合在一起。

https://www.runoob.com/maven/maven-manage-dependencies.html

3. 反射

https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html

2.5 第一個Spring

1. 配置pom.xml
2. 建立java類物件
3. 配置applicationContext.xml
4. 新增測試程式碼並執行

詳細步驟:

1. 配置pom.xml, 指定編碼格式和JDK版本
<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
        ">
        
	<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.complier.source>15</maven.complier.source>
        <maven.complier.target>15</maven.complier.target>
    </properties>
2. 在pom.xml中引入相關依賴
	<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.4.RELEASE</version>
        </dependency>
	
		<dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    <dependencies>
3. 建立java類物件
	在main.java建立com.hello.HelloWorld.java
	新增方法
	public void hey() {
		System.out.println("HelloWorld");
	}
4. 在resource建立applicationContext.xml
	建立bean
	<bean id="helloworld" class="com.hello.helloworld"/>
5. 在test新建測試類Test01.java
	類中新增測試方法: 
	//import org.junit.Test
	@Test
	public void test01() {
		//獲取工廠物件
		ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
		//根據xml中bean的id,獲取bean物件(強制型別轉換)
        HelloWorld hello = (HelloWorld) ctx.getBean("helloworld");
        //HelloWorld hello = ctx.getBean("helloworld", HelloWord.class);
        
        //呼叫HelloWorld的成員方法
        hello.hey();
	}

2.6 細節分析

  • Spring工廠的相關的方法
//通過這種方式獲得物件,就不需要強制型別轉換
Person person = ctx.getBean("person", Person.class);
System.out.println("person = " + person);
        

//當前Spring的配置檔案中 只能有一個<bean class是Person型別
Person person = ctx.getBean(Person.class);
System.out.println("person = " + person);
        

//獲取的是 Spring工廠配置檔案中所有bean標籤的id值  person person1
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
  System.out.println("beanDefinitionName = " + beanDefinitionName);
}
        

//根據型別獲得Spring配置檔案中對應的id值
String[] beanNamesForType = ctx.getBeanNamesForType(Person.class);
for (String id : beanNamesForType) {
  System.out.println("id = " + id);
}
        

//用於判斷是否存在指定id值得bean
if (ctx.containsBeanDefinition("a")) {
  System.out.println("true = " + true);
}else{
  System.out.println("false = " + false);
}
      

//用於判斷是否存在指定id值得bean
if (ctx.containsBean("person")) {
  System.out.println("true = " + true);
}else{
  System.out.println("false = " + false);
}
  • 配置檔案中的細節
1. 只配置class屬性
<bean  class="com.baizhiedu.basic.Person"/>
a) 上述這種配置 有沒有id值 com.baizhiedu.basic.Person#0
b) 應用場景: 如果這個bean只需要使用一次,那麼就可以省略id值
            如果這個bean會使用多次,或者被其他bean引用則需要設定id值

2. name屬性
作用:用於在Spring的配置檔案中,為bean物件定義別名(小名)
相同:
   1. ctx.getBean("id|name")-->object
   2. <bean id="" class=""
      等效
      <bean name="" class=""
區別:
   1. 別名可以定義多個,但是id屬性只能有一個值
   2. XML的id屬性的值,命名要求:必須以字母開頭,字母 數字 下劃線 連字元 不能以特殊字元開頭 /person
         name屬性的值,命名沒有要求 /person
      name屬性會應用在特殊命名的場景下:/person (spring+struts1)
      
      XML發展到了今天:ID屬性的限制,不存在 /person
   3. 程式碼
       //用於判斷是否存在指定id值得bean,不能判斷name值
       if (ctx.containsBeanDefinition("person")) {
           System.out.println("true = " + true);
       }else{
           System.out.println("false = " + false);
       }


       //用於判斷是否存在指定id值得bean,也可以判斷name值
       if (ctx.containsBean("p")) {
           System.out.println("true = " + true);
       }else{
           System.out.println("false = " + false);
       }

2.7 API介紹-ApplicationContext

* 介面型別,代表應用上下文,可以通過其例項獲得 Spring 容器中的 Bean 物件
	ApplicationContext的實現類:
1. ClassPathXmlApplicationContext 
	它是從類的根路徑下載入配置檔案 推薦使用這種
2. FileSystemXmlApplicationContext 
	它是從磁碟路徑上載入配置檔案,配置檔案可以在磁碟的任意位置。
3. AnnotationConfigApplicationContext
	當使用註解配置容器物件時,需要使用此類來建立 spring 容器。它用來讀取註解。

2.8 思考

問題:未來在開發過程中,是不是所有的物件,都會交給Spring工廠來建立呢?
回答:理論上 是的,但是有特例 :實體物件(entity)是不會交給Spring建立,它是由持久層框架進行建立。

3. 設計模式之工廠模式

https://www.runoob.com/design-pattern/factory-pattern.html

3.1 簡介

1. 概念:
		工廠(如applicationContext.xml)中事先引入bean物件,
		則我們可以通過工廠類,建立物件
        上文中通過new ClassPathXmlApplicationContext()獲取工廠類
3. 好處:解耦合
   耦合:指定是程式碼間的強關聯關係,一方的改變會影響到另一方
   問題:不利於程式碼維護
   簡單:把介面的實現類,硬編碼在程式中

3.2 通用工廠的使用方式

1. 定義型別 (類)
2. 通過配置檔案的配置告知工廠(applicationContext.xml)
   key = value
3. 通過工廠獲得類的物件
   Object ret = BeanFactory.getBean("key")

在這裡插入圖片描述

4. Spring5.x與日誌框架的整合

  • 簡介
Spring與日誌框架進行整合,日誌框架就可以在控制檯中,輸出Spring框架執行過程中的一些重要的資訊。
好處:便於瞭解Spring框架的執行過程,利於程式的除錯
  • Spring如何整合日誌框架
預設
  Spring1.2.3早期都是於commons-logging.jar
  Spring5.x預設整合的日誌框架 logback log4j2

Spring5.x整合log4j 
  1. 引入log4j jar包
  2. 引入log4.properties配置檔案
  1. pom.xml中的配置
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.7.25</version>
</dependency>

<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>
  1. resources中log4j.properties的配置
# resources資料夾根目錄下
### 配置根
log4j.rootLogger = debug,console

### 日誌輸出到控制檯顯示
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

5. 注入(Injection)

5.1 什麼是注入

概念:通過Spring工廠及配置檔案,為所建立物件的成員變數賦值

5.1.1 為什麼要注入

原始方法:直接通過編碼的方式,為物件的成員變數賦值,存在耦合

在這裡插入圖片描述

5.1.2 如何進行注入

  • 類的成員變數提供set get方法
  • 配置spring的配置檔案
 <bean id="person" class="com.hello.Person">
   <property name="id">
     <value>01</value>
   </property>
   <property name="name">
     <value>xiaoming</value>
   </property>
</bean>

5.1.3 注入的優勢

解耦合

5.1.4 Spring注入的原理

Spring通過底層呼叫物件屬性對應的set方法,完成成員變數的賦值,這種方式我們也稱之為set注入

5.1.5 JDK內建型別轉換

1. String
2. 陣列
3. Set
4. List
5. Map
6. Properties(特殊)

6. 構造注入

6.1 相關概念

注入:通過Spring的配置檔案,為成員變數賦值
Set注入:Spring呼叫Set方法 通過配置檔案 為成員變數賦值
構造注入:Spring呼叫構造方法 通過配置檔案 為成員變數賦值

6.2 開發步驟(以Cat類為例)

6.2.1 提供有參構造方法

public class Cat implements Serializable {
    private String name;
    private int age;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

6.2.2 Spring的配置檔案

<bean id="cat" class="com.hello.Cat">
  <constructor-arg>
    <value>Kity</value>
  </constructor-arg>
  <constructor-arg>
    <value>2</value>
  </constructor-arg>
</bean>

6.3 構造方法過載

6.3.1 引數個數不同時

通過控制<constructor-arg>標籤的數量進行區分 

6.3.2 引數個數相同時

通過在標籤引入 type屬性 進行型別的區分 <constructor-arg type="">

6.4 set注入與構造注入的比較

1. 構造注入需要過載,操作麻煩
2. Spring框架底層, 大量應用了 set注入

總結:set注入應用更多

7. IOC與依賴注入(重點)

7.1 反轉控制IOC (Inverse of Control)

1. 控制:對於成員變數賦值的控制權
2. 反轉控制:把對於成員變數賦值的控制權,從程式碼中反轉(轉移)到Spring工廠和配置檔案中完成 (注入)
3. 好處:解耦合, 
		即如需對成員變數賦值, 在配置檔案操作, 而不是在程式碼中操作
		減少了直接對程式碼的操作
4. 底層實現:工廠設計模式

7.2 依賴注入 (Dependency Injection DI)

1. 依賴注入:通過Spring的工廠及配置檔案,為物件(bean,元件)的成員變數賦值
2. 依賴注入:當一個類需要另一個類時,就意味著依賴,一旦出現依賴,就可以把另一			個類作為本類的成員變數,最終通過Spring配置檔案進行注入(賦值)。
3. 好處:解耦合
  • 歷史背景
	Rod Johnson是第一個高度重視以配置檔案來管理Java例項的協作關係的人,他給這種方式起了一個名字:控制反轉(Inverse of Control,IoC)。後來Martine Fowler為這種方式起了另一個名稱:依賴注入(Dependency Injection),因此不管是依賴注入,還是控制反轉,其含義完全相同。當某個Java物件(呼叫者)需要呼叫另一個Java物件(被依賴物件)的方法時,在傳統模式下通常有兩種做法:

1. 原始做法: 呼叫者主動建立被依賴物件,然後再呼叫被依賴物件的方法;

2. 簡單工廠模式: 呼叫者先找到被依賴物件的工廠,然後主動通過工廠去獲取被依賴物件,最後再呼叫被依賴物件的方法。

	注意上面的主動二字,這必然會導致呼叫者與被依賴物件實現類的硬編碼耦合,非常不利於專案升級的維護。使用Spring框架之後,呼叫者無需主動獲取被依賴物件,呼叫者只要被動接受Spring容器為呼叫者的成員變數賦值即可,由此可見,使用Spring後,呼叫者獲取被依賴物件的方式由原來的主動獲取,變成了被動接受——所以Rod Johnson稱之為控制反轉。

	另外從Spring容器的角度來看,Spring容器負責將被依賴物件賦值給呼叫者的成員變數——相當於為呼叫者注入它依賴的例項,因此Martine Fowler稱之為依賴注入。

8. Spring工廠建立複雜物件

8.1 什麼是複雜物件

	在java中,不能通過new關鍵字建立的物件,都稱為複雜物件,如抽象類(abstract,例如Calendar日期類)、介面(interface,JDBC中的Connection連線類)。

8.2 Spring工廠建立複雜物件的三種方式

8.2.1 FactoryBean介面

  • 開發步驟
    1. 實現FactoryBean介面

在這裡插入圖片描述

	2. Spring配置檔案的配置
# 如果Class中指定的型別 是FactoryBean介面的實現類,那麼通過id值獲得的是這個類所建立的複雜物件  Connection
<bean id="conn" class="com.baizhiedu.factorybean.ConnectionFactoryBean"/>
  • 細節

    - 如果就想獲得FactoryBean型別的物件   ctx.getBean("&conn")
      獲得就是ConnectionFactoryBean物件
    
    - isSingleton方法
      返回  true 只會建立一個複雜物件
    
      返回 false 每一次都會建立新的物件
      問題:根據這個物件的特點 ,決定是返回true (SqlSessionFactory) 還是 false  (Connection)
    
    - mysql高版本連線建立時,需要制定SSL證照,解決問題的方式
      url = "jdbc:mysql://localhost:3306/suns?useSSL=false"
      
    - 依賴注入的體會(DI)
      把ConnectionFactoryBean中依賴的4個字串資訊 ,進行配置檔案的注入 
    	好處:解耦合
    <bean id="conn" class="com.baizhiedu.factorybean.ConnectionFactoryBean">
      <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
      <property name="url" value="jdbc:mysql://localhost:3306/suns?useSSL=false"/>
      <property name="username" value="root"/>
      <property name="password" value="123456"/>
    </bean>
    
    • FactoryBean總結
    - Spring中用於建立複雜物件的一種方式,也是Spring原生提供的,後續講解Spring整合其他框架,大量應用FactoryBean
    

8.2.2 例項工廠

  • 物件建立時期
* 工廠在構造方法初始化時,會將類進行例項化放在工廠中
  • 優勢
1. 避免Spring框架的侵入 
2. 整合遺留系統 
  • 開發步驟
 <bean id="connFactory" class="com.hello.factorybean.ConnectionFactory"></bean>

 <bean id="conn"  factory-bean="connFactory" factory-method="getConnection"/>

8.2.3 靜態工廠

  • 物件建立時期
* 工廠初始化之前,工廠中的類已經被例項化放在工廠容器中
  • 開發步驟 (看不懂的先跳過)
public class StaticConnectionFactory {
    public static Connection getConnection() {
        Connection conn = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/資料庫名稱?useSSL=false", "root", "123456");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }

        return conn;
    }
}

<bean id="conn" class="com.hello.factorybean.StaticConnectionFactory" factory-method="getConnection"/>
  • 擴充: https://www.cnblogs.com/dyj-blog/p/8867028.html

8.3 Spring工廠建立物件的總結

Spring工廠

9. 控制Spring工廠建立物件的次數

9.1 如何控制簡單物件的建立次數

  • Bean屬性-scope的使用:
1. 功能:控制簡單物件的建立次數
2. 常用選項:
	sigleton:只會建立一次簡單物件 預設值
	prototype:每一次都會建立新的物件
3. 使用: 在.xml配置檔案中對bean物件操作
	<bean id="account" scope="singleton|prototype" class="xxxx.Account"/>

9.2 如何控制複雜物件的建立次數

* 在物件中新增成員方法

FactoryBean{
   isSingleton(){
      return true  只會建立一次
      return false 每一次都會建立新的
   }

}
* 如沒有isSingleton方法 還是通過scope屬性 進行物件建立次數的控制

9.3 為什麼要控制物件的建立次數

* 好處:節省不別要的記憶體浪費 
  • 什麼樣的物件只建立一次?

    1. SqlSessionFactory
    2. DAO
    3. Service
    
  • 什麼樣的物件 每一次都要建立新的?

    1. Connection
    2. SqlSession | Session
    3. Struts2 Action
    

10. Spring配置和相關API

10.1 applicationContext.xml

  • 在類路徑下(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>

10.2 ApplicationContext

1. ApplicationContext的繼承體系
	applicationContext:介面型別,代表應用上下文,可以通過其例項獲得 Spring 容器中的 Bean 物件
	
2. ApplicationContext的實現類

	1)ClassPathXmlApplicationContext 
	它是從類的根路徑下載入配置檔案 推薦使用這種

	2)FileSystemXmlApplicationContext 
	它是從磁碟路徑上載入配置檔案,配置檔案可以在磁碟的任意位置。

	3)AnnotationConfigApplicationContext
	當使用註解配置容器物件時,需要使用此類來建立 spring 容器。它用來讀取註解。

10.3 配置檔案引數化

把Spring配置檔案中需要經常修改的字串資訊,轉移到一個更小的配置檔案中

1. Spring的配置檔案中存在需要經常修改的字串?
   存在 以資料庫連線相關的引數 代表
2. 經常變化字串,在Spring的配置檔案中,直接修改
   不利於專案維護(修改)
3. 轉移到一個小的配置檔案(.properties)
   利於維護(修改)
   
配置檔案引數化:利於Spring配置檔案的維護(修改)
1. 配置檔案引數的開發步驟
  • 提供一個小的配置檔案(.properities)
名字:隨便 (jdbc.properties)
放置位置:隨便 (resources資料夾下)
以鍵值對方式書寫: key=value

jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/suns?useSSL=false
jdbc.username = root
jdbc.password = 123456
  • Spring的配置檔案(xml)與小配置檔案(propertise)進行整合
1. <beans>屬性中加入
	xmlns:context="http://www.springframework.org/schema/context"
	
2. <beans>屬性選項xsi:schemaLocation中加入
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

3. 載入jdbc.properties
    <context:property-placeholder location="classpath:/jdbc.properties"/>
    
4. 配置資料來源物件
    <bean id="conn" class="com.wyt.factorybean.ConnectionFactoryBean">
    	<propertise name="key1" value="value1"/>
    	<propertise name="key2" value="value2"/>
    	...
    </bean>
  • 在Spring配置檔案中通過${key}獲取小配置檔案中對應的值

properties配置

相關文章