Spring

一.前言

帶著這些去驗證自己把!

定義?思想(重點)?

幹啥的?作用於哪一方面?

基本配置,基本操作?

二.個人理解

  • spring作用?
  • 使現有技術更加容易使用,本身是一個大雜燴,整合了現有的技術框架
  • 從它的容器中可以拿出很多資料!

三.核心要素

  • IOC 控制反轉(重點)--面試高頻

    • 依賴注入 ID
  • Spring的配置檔案(重點,難點,要記)
  • AOP 面向切面程式設計(重點,難點,面試高頻)
    • 代理模式(重點,難點)
  • 事務ACID,宣告式的事務特性
  • 整合Mybatis
  • 使用註解開發(重點
  • 設計模式(13種)

四.優點

  • Spring 一個開源的免費的框架(容器)

  • Spring 一個輕量級的,非入侵式的框架。

  • 控制反轉(IOC),面向切面程式設計(AOP)

  • 支援事務處理,對框架整合的支援。

  • 總:spring 就是一個輕量級的控制反轉(IOC)和麵向切面程式設計(AOP)的框架

五.介紹

  • 7大模組

  • SpringBoot

    • 一個快速開發的腳手架
    • 基於SpringBoot可以快速的開發單個微服務
    • 約定大於配置
  • SpringCloud

    • SpringCloud是屬於SpringBoot實現的
  • 現在大多數公司都在使用SpringBoot進行快速開發

    • 學習SpringBoot的前提,完全掌握Spring及SpringMVC
  • 弊端:發展太久之後,違背了原來的理論。配置很繁瑣,人稱“配置地獄”

六.IOC---說白了,空調VS電熱扇

六.1.IOC理論推導

  • 1.UserDao 介面

  • 2.UserDaoImpl 實現類

  • 3.UserService 業務介面

  • 4.UserServiceImpl 業務實現類

  • 在我們之前的業務中,使用者的需求可能會影響我們原來的程式碼,我們需要根據使用者的需求去修改原始碼!

    如果程式程式碼量特別大,修改一次的成本就會特別昂貴

  • 我們使用set介面實現,已經發生了革命性的變化

    • 目的:將程式的控制權由程式(死值)變成了使用者(隨用隨調)
     // 程式主動建立物件,每次換東西都得這改
private UserDao userDao1 = new UserDaoImpl(); // 呼叫時:::每次都得換
private UserDao userDao1 = new UserDaoMysqlImpl(); // 呼叫時:::每次都得換 // 程式被動建立,控制權封裝為一個方法,由使用者呼叫方法進行
private UserDao userDao; // Like-a
// 使用set進行動態實現值的注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
  • 之前,程式是主動建立物件!控制權在程式猿(死程式碼中)手上!
  • 使用set注入後,程式不再具有主動性,而是被動的接收物件。

六.2.思想:

  • 想不通的可以想想設定(手機等等)----根據自己的喜好設定一些功能,控制權在使用者的手上,,,沒有設定功能的就是程式主動性的建立物件

  • 或者空調(冷熱一體,給你個遙控器可調整冷熱。。) 而電熱扇,電風扇---只能死死的使用熱,冷

  • 說到底,目的是為了提高了使用者的體驗,使用者可以依據自己的喜好來,而不是按照裝好的東西來使用

  • 這種思想,從本質上解決了問題,我們程式設計師不用去管理物件的建立了,而是提供一個方法(遙控器),系統耦合性大大降低

    可以更加的專注於業務上!---這是IOC原型

六.3.IOC本質(迴歸IT)

  • 控制反轉IOC(Inversion of Control) DI(依賴注入)是實現IOC的一種方法。

    • 沒有IOC的程式,我們使用的是面向物件的程式設計,物件的建立與物件間的依賴關係完全硬編碼在程式中,物件的建立由程式自己控制,

      控制反轉後將物件的建立轉移到第三方。
    • IOC描述的是物件的事情,DI描述的是物件屬性的事情
  • 採用XML方式配置Bean的時候,Bean的定義資訊是和實現分離的,而採用註解的方式可以將兩者結合在一體,Bean的定義資訊直接以註解的形式定義在實現類中,

    從而達到了零配置的目的

    • 使用XML的時候,它所作的就是創物件,物件屬性賦值---而類的資訊還是由類完成(構造器,屬性,方法的定義)

  • 控制反轉是一種通過描述(XML或註解)並通過第三方去生產或獲取特定物件的方式。在Spring中實現控制反轉的是IOC容器。其實現方法是依賴注入(DI)

Spring code start

一.環境需求

  • 1.mavern管理

  • 2.匯入Spring包就可以了

          <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.9</version>
    </dependency> <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.9</version>
    </dependency>

二.springBean

  • spring 容器,就類似於婚介場,所有資料都在裡面。 啟動之前就註冊了xml

三個Bean要用的標籤

  • 1.Bean的配置
     <!--
id :bean的唯一識別符號
class bean物件的全限定名:包名+型別
name:別名,而且name更高階,可以取多個別名,使用空格,逗號等等都可以區分
--> <bean id = "userT" class="com.zjz.pojo.UserT" name="userT2,userT3">
<property name="name" value="zjz"/>
</bean>
  • 2.別名

    • 推薦使用bean裡的name

          <!--
      id :bean的唯一識別符號
      class bean物件的全限定名:包名+型別
      name:別名,而且name更高階,可以取多個別名,使用空格,逗號等等都可以區分
      -->
      <bean id = "userT" class="com.zjz.pojo.UserT" name="userT2,userT3"> <!--如果添加了別名,我們也可以通過別名獲取到這個物件-->
      <alias name="user" alias="adafadfafadf"/>
  • 3.import

    • 一般用於開發團隊使用,它可以將多個配置檔案,匯入合併為一個

  • 假設,現在專案組有多個人開發,這三人負責不同的類,不同的類需要註冊在不同的bean,我們可以利用import將所有人的beans.xml合併為一個總的

    • 張三
    • 李四
    • 王五
    • applicationContext.xml(核心)
  • 使用的時候,直接使用總的配置就好了

三. spring IOC

三.1.簡單認識一下,構造器注入

  • 1.pojo

    • 定義類的屬性,以及方法

    public class Hello {
    private String str; public String getStr() {
    return str;
    } public void setStr(String str) {
    this.str = str;
    } @Override
    public String toString() {
    return "Hello{" +
    "str='" + str + '\'' +
    '}';
    }
    }
  • 2.beans.xml(核心!)

    • 將物件的建立,物件屬性的賦值,在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">
    <!--使用Spring來建立物件,在Spring這些都稱為Bean-->
    <!--bean就是java物件 , 由Spring建立和管理-->
    <!--
    型別 變數名 = new 型別();
    before: Hello hello = new Hello();
    now : id = 變數名 class = new 的物件; property 相當於給物件的屬性set值
    --> <!--如果要使用其它物件的屬性時:
    第一,要有本類呼叫其它類時的定義: private UserDao userDao; // Like-a
    第二:配置 --- <property name="userDao" ref="userDaoImpl"/>
    property中
    ref :引用spring 容器建立好的物件
    value: 具體的值,基本的資料型別
    --> <bean id="hello" class="com.zjz.pojo.Hello">
    <property name="str" value="Spring"/>
    </bean>
    </beans>
  • 3.MyTest

    • 獲取容器(上下文),然後取資料

    public class MyTest {
    public static void main(String[] args) { // 獲取Spring的上下文物件!
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    // 我們的物件,現在都在spring中管理,我們要使用,直接從裡面取出來就行 Hello hello = (Hello) context.getBean("hello"); System.out.println(hello.toString());
    }
    }

三.2.注意:

怎麼引用其它物件??---目前手動的--

  • 怎麼引用一個其它的pojo或者service或者Dao---

使用ref 首先得把ref要用到的Bean註冊上!!要不報錯

注:定義,宣告都還是在中進行,IOC它負責的只是創物件

    <bean id="userDaoImpl" class="com.zjz.dao.UserDaoImpl">
<bean id="userServiceImpl" class="com.zjz.service.UserServiceImpl">
<!--ref :引用spring 容器建立好的物件
value: 具體的值,基本的資料型別
-->
<property name="userDao" ref="userDaoImpl"/>
// 它的功能就將其它類的屬性||物件||其它--給引入到本類中
</bean> private UserDao userDao; // Like-a

三.3思考

  • Hello 物件是誰建立的 ?

    • 【 hello 物件是由Spring建立的 】
  • Hello 物件的屬性是怎麼設定的 ?

    • 【hello 物件的屬性是由Spring容器設定的 】
  • 這個過程就叫控制反轉 :

    • 控制 : 誰來控制物件的建立, 傳統應用程式的物件是由程式本身控制建立的, 使用Spring後, 物件是由Spring來建立的

    • 反轉 : 程式本身不建立物件, 而變成被動的接收物件 .

    • 依賴注入 : 就是利用set方法來進行注入的.

    • IOC是一種程式設計思想,由主動的程式設計變成被動的接收

  • 我們不需要去程式中去改動了,要實現不同的操作,只需要在xml中配置檔案進行修改,所謂IOC,

    一句話搞定:物件由Spring來建立,管理,裝配

四.IOC建立物件的方式

同java一樣啊,構造器造物件

構造器注入

  • 1.使用無參構建物件,預設

  • 2.假設要使用有參構造建立物件 - 此時物件是有屬性的物件

  • 1.下標賦值

      <!-- 第一種,下標賦值-->
    <!--注意是構造器引數種的下標--> <bean id="user" class="com.zjz.pojo.User">
    <constructor-arg index="0" value="zjzHHH"/>
    </bean>
  • 2.型別賦值

        <!-- 第一種,下標賦值-->
    <!--
    <bean id="user" class="com.zjz.pojo.User">
    <constructor-arg index="0" value="zjzHHH"/>
    </bean>
    --> <!--第二種,通過型別建立,兩個方法都是Sting就不行了,不建議使用!-->
    <!--
    <bean id="user" class="com.zjz.pojo.User">
    <constructor-arg type="java.lang.String" value="zjz"></constructor-arg>
    </bean>
    -->
  • 3.引數名-- 重點,使用

      <!--第三種,直接通過引數名來設定-->
    <bean id="user" class="com.zjz.pojo.User">
    <constructor-arg name="name" value="zjz"/>
    </bean>
  • 總結

    • 在配置檔案載入的時候,容器中的管理物件就已經初始化了

五 DI依賴注入

依賴注入 - 依賴:bean物件的建立依賴於容器!

注入:bean物件的所有屬性,由容器來注入!

說到底,物件的賦值,由容器來進行!

  • 五.1.構造器注入--

    • 特徵,有參的話,直接將構造器的引數賦值。。無參你沒的辦法啊,只能依託property
    • 原理:構造器引數注入。
    • 缺點:只能對有參的構造器的引數進行操作,非常不方便

    <!--使用Spring來建立物件,在Spring這些都稱為Bean-->
    <!--bean就是java物件 , 由Spring建立和管理-->
    <!--
    型別 變數名 = new 型別();
    before: Hello hello = new Hello();
    now : id = 變數名 class = new 的物件; property 相當於給物件的屬性set值
    --> // 無參的
    <bean id="user" class="com.zjz.pojo.User"/>
    // 有參的
    <!--第三種,直接通過引數名來設定-->
    <bean id="user" class="com.zjz.pojo.User">
    <constructor-arg name="name" value="zjz"/>
    </bean>
  • 五.2.set注入(重要)

    • 特徵---各種property
    • 原理:實際上是在構造器注入的基礎上(物件),對屬性進行賦值操作
    • 依賴注入 - 依賴:bean物件的建立依賴於容器!

      注入:bean物件的所有屬性,由容器來注入!
    • 環境搭建--
      • 1.複雜型別
      • 2.真實測試物件
        <?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="address" class="com.zjz.pojo.Address"/>
    <bean id="student" class="com.zjz.pojo.Student"> <!--第一種,普通值注入,直接使用value-->
    <property name="name" value="zjz"></property> <!--第二種,bean注入 ref-->
    <property name="address" ref="Address"/> <!-- 陣列注入,ref-->
    <property name="books">
    <array>
    <value>部落格</value>
    <value>Gitee</value>
    <value>DIY部落格</value>
    </array>
    </property> <!--list -->
    <property name="hobbys">
    <list>
    <value>敲程式碼</value>
    <value>做演算法</value>
    <value>學知識</value>
    </list>
    </property> <!--Map-->
    <property name="card">
    <map>
    <entry key="身份證" value="HHH1"/>
    <entry key="銀行卡" value="HHH2"/>
    <entry key="門禁卡" value="HHH3"/>
    </map>
    </property> <!--Set-->
    <property name="game">
    <set>
    <value>LOL</value>
    <value>CF</value>
    <value>Study</value>
    </set>
    </property> <!-- null-->
    <property name="wife">
    <null></null>
    </property> <!--properties-->
    <property name="info">
    <props>
    <prop key="driver"></prop>
    <prop key="url"></prop>
    <prop key="username"></prop>
    <prop key="password"></prop>
    </props> </property> </bean> </beans>
  • 五.3.拓展方式注入

  • p命名 c命名

    • 作用:方便一些操作
    • 原理:在構造器的基礎上,使用一些其它方式直接set值
    • 使用注意:要匯入約束--xmlns
        <?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:c="http://www.springframework.org/schema/c"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- p 名稱空間注入,可以直接注入屬性的值:property-->
    <bean id="user" class="com.zjz.pojo.User" p:name="user1" p:age="18"/> <!--c名稱空間注入,通過構造器注入,construct-args -->
    <bean id="user2" class="com.zjz.pojo.User" c:name="user2" c:age="99"></bean> </beans>

六.Bean的作用域

  • 六.1.單例模式(Spring預設機制)

    • 目的:減少資源浪費,多個操作從同一容器取值
    • 缺點:併發可能出事
    • <bean id="user" class="com.zjz.pojo.User" p:name="user1" p:age="18" scope="singleton"/>
  • 六.2.原型模式

    • 目的:每次從容器get的時候都會產生一個新的物件!
    • 缺點:特別浪費資源
    • <!-- scope 為prototype 原型模式 --> <bean id="user" class="com.zjz.pojo.User" p:name="user1" p:age="18" scope="prototype"/>
  • 六.3.其餘的request,session,application,,,這些只能在web開發中使用到!

七.Bean的裝配

三種裝配方式

  • 1.在XML中顯示的配置(手動)

  • 2.在java中顯示配置

  • 3.隱式的自動裝配(自動)

  • xml使用注意:

    • ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    • 這句話會解析xml檔案所有的東西,包括不需要用到的---經Test:將其他bean放入進去,然後構造器out下
    • 如何避免,,,你可以將這個方法封裝一層。。。

七.一.xml中裝配

  • 推薦不使用自動裝配xml配置,而使用註解

1.手動裝配,

  • XML中每一個物件,每一個屬性,,都手動配(property,或構造器引數)---之前練習中

2.自動裝配

依據:Bean的特性---

實現方式---Spring滿足bean依賴的一種方式

  • Spring的自動裝配需要從兩個角度來實現,或者說是兩個操作:

      1. 元件掃描(component scanning):spring會自動發現應用上下文中所建立的bean;
      • 疑問? 哪裡的上下文???---已經在bean容器中的東西!!!
      • 所以,還是得把東西塞進去把~~
      1. 自動裝配(autowiring):spring自動滿足bean之間的依賴,也就是我們說的IoC/DI;

基於set方法的自動裝配:

  • 共同:都是要走set方法的,沒set方法怎麼弄資料。。。當然,之前也說了,構造器也可哈~

  • byName

    • 依據:類中set方法 以及xmlBean的id
    • 操作:裝配bean用id的方式。。。編寫主要bean 使用autowire的byName
    • 結果:成功使用上下文中含有 id 中的內容,對映到本身要輸出的
    • 缺點:xml中id必須和類中set後的名字必須一致,否則失敗
        <!--
    byName: 會自動在上下文中查詢,和自己物件set方法後面值對應的beanId
    -->
    <!--手動裝配,id的形式,cat,dog 都是構造器的形式-->
    <bean id="cat" class="com.zjz.pojo.Cat"/>
    <bean id="dog" class="com.zjz.pojo.Dog"/> <!--此時Test 會將cat dog 都打印出來-->
    <bean id="people" class="com.zjz.pojo.People" autowire="byName">
    <property name="name" value="zjz"/>
    </bean>
  • byType

    • 依據:類中set前面的型別去查詢 xml中配上class
    • 操作: 編寫主要bean 使用autowire的byType
    • 缺點:只能針對型別返回,針對的型別只能有一個,多了失敗
    
    
        <!--byType : 會自動在上下文中查詢,和自己物件屬性型別相同的bean-->
    <!--手動裝配,cat,dog 都是構造器的形式-->
    <bean class="com.zjz.pojo.Dog"/>
    <bean class="com.zjz.pojo.Cat"/> <!--此時Test 會將cat dog 都打印出來-->
    <bean id="people" class="com.zjz.pojo.People" autowire="byType">
    <property name="name" value="zjz"></property>
    </bean>

七.二.使用註解---實現自動裝配

  • jdk1.5支援的,Spring2.5就支援了

  • 七.二.1.準備工作:

      1. 在spring配置檔案中引入context檔案頭--三句話
      • 只需要在原有的基礎上覆制過來改下就好了
      1. 開啟屬性註解支援!(重要!)
      1. 在spring配置檔案中引入context檔案頭
    <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"> // 第三句 2.開啟屬性註解支援!
    <context:annotation-config/>
  • 七.二.2. @Autowired

三個註解 都spring的--- @Autowired @Qualifier @Nullable 搭配解決各種問題

用在主類匯入子類的欄位,方法上(Has-a)

  • 1.使用::檢視原始碼--

        @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Autowired {
    boolean required() default true;
    }
  • 可以加的位置有欄位,方法,引數(構造器使用)---括號內可以寫required=true(default)||false

  • false 就是這裡可以為null 同 @Nullable

    1. 機制;
    • 1.優先 byType 其次 byName----可檢視原始碼beans\factory\support\DefaultListableBeanFactory.class

    • 2.如果多個物件,多個型別怎麼處理???

      • 使用@Qualifier(value="value")來指定bean的id值 -- Qualifier不能單獨使用
  • 3.使用AutoWired我們就可以不用編寫set方法了,前提是你這個自動裝配的屬性在IOC(spring)容器中存在

  • 前提--你得確保是引入對應的bean

  • 使用@Nullable 標註一個欄位,說明這個欄位可以為null,不會null指標

  • 特別的是java的註解 同樣可以完成自動裝配 @Resource

@Autowired與@Resource異同

    1. @Autowired與@Resource都可以用來裝配bean。都可以寫在欄位上,或寫在setter方法上。
    1. @Autowired預設按型別裝配(屬於spring規範),預設情況下必須要求依賴物件必須存在,如果

      要允許null 值,可以設定它的required屬性為false,如:@Autowired(required=false) ,如果我

      們想使用名稱裝配可以結合@Qualifier註解進行使用
    1. @Resource(屬於J2EE復返),預設按照名稱進行裝配,名稱可以通過name屬性進行指定。如果

      沒有指定name屬性,當註解寫在欄位上時,預設取欄位名進行按照名稱查詢,如果註解寫在

      setter方法上預設取屬性名進行裝配。 當找不到與名稱匹配的bean時才按照型別進行裝配。但是

      需要注意的是,如果name屬性一旦指定,就只會按照名稱進行裝配。
  • 它們的作用相同都是用註解方式注入物件,但執行順序不同。@Autowired先byType,@Resource先 byName。

    • 為啥@Autowired快,,因為名字肯定比型別多--
    • 思想,先判斷少的再判斷多的,提高速率 -- 兩層for也是

總:

-------------------------更多java學習: https://zhangjzm.gitee.io/self_study