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也是
總: