Spring
一.前言
- Thinking is more important than learning
- 本文主要講述spring 以及它的 IOC原理
- 程式碼地址:https://gitee.com/zhangjzm/spring.git
帶著這些去驗證自己把!
定義?思想(重點)?
幹啥的?作用於哪一方面?
基本配置,基本操作?
二.個人理解
- 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描述的是物件屬性的事情
- 沒有IOC的程式,我們使用的是面向物件的程式設計,物件的建立與物件間的依賴關係完全硬編碼在程式中,物件的建立由程式自己控制,
採用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的自動裝配需要從兩個角度來實現,或者說是兩個操作:
- 元件掃描(component scanning):spring會自動發現應用上下文中所建立的bean;
- 疑問? 哪裡的上下文???---已經在bean容器中的東西!!!
- 所以,還是得把東西塞進去把~~
- 自動裝配(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.準備工作:
- 在spring配置檔案中引入context檔案頭--三句話
- 只需要在原有的基礎上覆制過來改下就好了
- 開啟屬性註解支援!(重要!)
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.優先 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異同
:
- @Autowired與@Resource都可以用來裝配bean。都可以寫在欄位上,或寫在setter方法上。
- @Autowired預設按型別裝配(屬於spring規範),預設情況下必須要求依賴物件必須存在,如果
要允許null 值,可以設定它的required屬性為false,如:@Autowired(required=false) ,如果我
們想使用名稱裝配可以結合@Qualifier註解進行使用
- @Autowired預設按型別裝配(屬於spring規範),預設情況下必須要求依賴物件必須存在,如果
- @Resource(屬於J2EE復返),預設按照名稱進行裝配,名稱可以通過name屬性進行指定。如果
沒有指定name屬性,當註解寫在欄位上時,預設取欄位名進行按照名稱查詢,如果註解寫在
setter方法上預設取屬性名進行裝配。 當找不到與名稱匹配的bean時才按照型別進行裝配。但是
需要注意的是,如果name屬性一旦指定,就只會按照名稱進行裝配。
- @Resource(屬於J2EE復返),預設按照名稱進行裝配,名稱可以通過name屬性進行指定。如果
它們的作用相同都是用註解方式注入物件,但執行順序不同。@Autowired先byType,@Resource先 byName。
- 為啥@Autowired快,,因為名字肯定比型別多--
- 思想,先判斷少的再判斷多的,提高速率 -- 兩層for也是
總: