項目地址:https://gitee.com/chuyunfei/learn.git
一、springIOC和DI的概念
:spring的IOC和DI完成了對對象的創建任務和對象之間的依賴關係的管理任務,實現了對象之間的解耦。
1.因爲要對對象,也就是Bean進行管理,所以需一個存放Bean的容器,低級的是:XmlBeanFactory,高級的是:ApplicationContext系列。
2.因爲要管理對象,所以需要一個用來描述對象及其依賴關係的一個配置文件,也就是常見的:applicationContext.xml 用於描述對象之間的依賴關係
3.因爲需要對配置文件進行解析,所以需要一個解析對應配置的解析器,可能是註解解析器,也可能是XML解析器。
4.容器本身還有自己的生命週期管理,包括容器的刷新,容器及其bean的加載、創建、銷燬等。
二、spring容器的構建過程
:spring加載配置文件並創建bean以及維護組件關係的過程,以及bean的生命週期管理
1、加載配置文件-》解析配置文件生成並註冊類信息-》生成bean並依賴注入
2、bean的生命週期:構造-》注入屬性-》注入環境-》初始化相關方法-》銷燬
1. 執行bean的構造函數構建一個bean。
2. 爲bean的屬性注入值
3. 如果實現了BeanNameAware接口,調用setBeanName(String beanId)方法。
4. 如果實現了BeanFactoryAware接口,調用setBeanFactory(),傳遞的是Spring工廠本身。
5. 如果實現了ApplicationContextAware接口,調用setApplicationContext(ApplicationContext)方法,傳入Spring上下文。
6. 如果關聯了BeanPostProcessor接口,調用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor經常被用作是Bean內容的更改,並且由於這個是在Bean初始化結束時調用After方法,也可用於內存或緩存技術
7. 如果在Spring配置文件中配置了init-method屬性會自動調用其配置的初始化方法
8. 如果關聯了BeanPostProcessor接口,會調用postAfterInitialization(Object obj, String s)方法
注意:以上工作完成以後就可以用這個Bean了,那這個Bean是一個single的,所以一般情況下我們調用同一個ID的Bean會是在內容地址相同的實例
9. 當Bean不再需要時,會經過清理階段,如果Bean實現了DisposableBean接口,會調用其實現的destroy方法
10. 最後,如果這個Bean的Spring配置中配置了destroy-method屬性,會自動調用其配置的銷燬方法
三、springIOC的配置文件相關配置基礎知識
:運用配置文件對spring容器的bean進行關係管理,學會使用spring容器
1.配置文件:
-》位置:注意classpath是指編譯過後的classes路徑,不同的框架對這個的定義不同,一定要注意。
-》名稱:最好遵循一些默認規則,但是配置文件的名稱是可以隨便寫的
2.基礎語法如下面的配置文件所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<!--
《bean節點的主要屬性註釋》
id和name屬性的詳細解釋:
https://blog.csdn.net/shjniu3000/article/details/70226182
1、一個bean可以只有id,只有name或多個name,既有id也有name或多個name,還能id和name都沒有(這個bean將是一個匿名的對象,不可見);
2、id只能有0或者1個,name可以有0或多個,但是多個name不能相同;
3、當有id時,id作爲這個bean在容器內的唯一標識符,name作爲這個bean的別名
當沒有id時,第一個name作爲這個bean在容器內的唯一標識符,其他的name作爲這個bean的別名
4、可以通過別名和標識符來獲取一個bean,因爲他們都可以唯一標識一個bean,一個bean可以擁有一個標識符和多個別名
5、可以顯式的爲一個bean起別名:<alias name="[一個已經存在的標識符或者別名]" alias="[新的別名]"/>
6、name可以同時起多個,用 ';',' ',','進行分割,但是id不行,因爲id是唯一的
class屬性的詳細解釋:
1、springIOC容器使用反射的方法註冊bean,所以需要一個默認的無參構造函數(非必須:使用構造注入就不需要默認構造器)
2、全限定類名用於反射
scope屬性詳細解釋:
1、singleton爲默認值,即單例,整個容器裏面只有一個實例,而prototype會在每一次獲取的時候都會創建一個新的實例
2、singleton的生命週期由容器來管理,但是prototype的生命週期得你自己管理
primary屬性詳細解釋:
1、是否首要,當bean在autowired是byType時,如果首選爲true就將其作爲注入的bean,否則,當找到多餘的相同類型的bean時將報錯。
2、如果設置兩個首要時也會報錯,所以要保證在進行同一種的byType的自動注入時只有一個首要的bean
depends-on屬性詳細解釋:
1、標識該bean在進行實例化之前,所依賴的bean必須先實例化,該bean要依賴那個bean,所以當依賴的bean沒有時候實例化失敗
-->
<bean id="person" name="person1;person2 person3,person4" class="springBasic.pojo.basic.Person"
scope="prototype" primary="true" depends-on="environment">
<!--
《使用property節點爲bean的屬性進行注入》
1、簡單賦值可以使用:
-》簡單的數據類型如字符串、數字可以使用value屬性直接進行賦值,name是bean的屬性名稱
-》如果有特殊的字符需要使用特殊的賦值方式:<![CDATA[特殊字符]]>
-》賦值爲null需要一節點的方式進行賦值:<value><null/></value>.
-》value既可以作爲屬性也可以作爲一個子節點:<value>
2、如果使用<property>進行值注入時,必須爲類的相應字段配置setter和getter,將字段變成屬性
-->
<property name="idCard" value="2016211050"/>
<property name="name">
<value><![CDATA[<~^楚雲飛^~>]]></value>
</property>
<property name="age" value="20"/>
</bean>
<!--起別名-->
<alias name="person" alias="person5"/>
<!--直接使用P命名空間進行屬性賦值,值用屬性,引用值用屬性值-ref進行賦值-->
<bean name="personp" class="springBasic.pojo.basic.Person" p:idCard="2016211050" p:name="楚雲飛" p:age="20" />
<!--
1、與student形成依賴閉環
2、自動裝配
-->
<bean name="clazz" class="springBasic.pojo.basic.Clazz" depends-on="student" autowire="byName">
<property name="id" value="03011603"/>
<!--引用一個獨立的集合:將自動的找到容器裏面的域屬性名稱相同的bean給注入進來-->
<!--<property name="students" ref="students"/>-->
<property name="studentMap">
<!--在內部配置map,也可以在外部配置爲一個獨立的map,類似的還有set,property-->
<map>
<!--spel表達式:https://blog.csdn.net/a1610770854/article/details/51908390-->
<entry key="#{student.studentNumber != null?'2016211051':'2016211050'}" value-ref="student"/>
</map>
</property>
</bean>
<bean name="student" class="springBasic.pojo.basic.Student">
<!--
《使用指定的構造函數進行實例的構造》
1、使用<name,value/ref,[index],[type]>屬性座標唯一確定一個構造函數
2、注意依賴迴環,所以clazz使用字段依賴,而不是構造器依賴,否則將形成依賴閉環,導致創建失敗
3、依賴閉環的避免:破壞構造器依賴閉環,spring會使用三級緩存來解決字段閉環依賴,但是無法解決構造器閉環依賴
依賴閉環的解決依賴於提前曝光實例引用的方法,所以必須先構造函數,否則將依賴管理失敗。
https://blog.csdn.net/u010853261/article/details/77940767 //依賴閉環
-->
<!--Student(String name,Long idCard,Integer age,Long studentNumber,Double liveCost)-->
<constructor-arg name="name" value="楚雲飛" index="0" type="java.lang.String"/>
<constructor-arg name="idCard" value="2016211050" index="1" type="java.lang.Long"/>
<constructor-arg name="age" value="20" index="2" type="java.lang.Integer"/>
<constructor-arg name="studentNumber" value="2016211050" index="3" type="java.lang.Long"/>
<constructor-arg name="liveCost" value="1200.20" index="4" type="java.lang.Double"/>
<!--使用字段依賴,因爲和clazz形成了閉環依賴,所以使用字段依賴來解決閉環-->
<property name="clazz" ref="clazz"/>
<!--
爲級聯屬性進行賦值必須先實例化後才能進行級聯賦值:這個修改會對clazz本身產生影響
1、在進行bean註冊時,先解析到clazz實例,但是其依賴student實例,所以轉移到student實例來進行初始化,同時將clazz的
引用放置到三級緩存,在實例student的過程中需要依賴clazz實例,於是去一級緩存裏面找,沒有,於是重新回到clazz實例
的創建過程,並將student實例的引用放入三級緩存,然後clazz將構建完成並將其放置到一級緩存裏面,然後將再次回到student
的構建過程,於是student的clazz字段將引用一個創建完成的clazz實例,在進行如下的級聯賦值時,容器裏面的clazz實例的
屬性也將同時改變,至此,clazz的id屬性爲:03011602而不是03011603
-->
<property name="clazz.id" value="03011602"/>
</bean>
<!--一個獨立的集合,是一個ArrayList,元素是Student-->
<util:list id="students" scope="singleton" list-class="java.util.ArrayList" value-type="springBasic.pojo.basic.Student">
<ref bean="student"/>
</util:list>
<!--通過一個配置文件配置出一個properties
id=2016211050;name=楚雲飛;age=20
-->
<util:properties id="testProperties" location="classpath:test.properties"/>
<!--加載外部配置文件-->
<context:property-placeholder location="classpath:database.properties" order="1" file-encoding="UTF-8"/>
<bean name="database" class="springBasic.pojo.basic.Database">
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
</bean>
</beans>
類如下:
public class Clazz {
private Long id;
private List<Student> students;
private Map<Long,Student> studentMap;
public Clazz(){
}
public Clazz(Long id,List<Student> students,Map<Long,Student> studentMap){
this.id = id;
this.students = students;
this.studentMap = studentMap;
}
/****setter and getter ****/
}
public class Database {
private String user;
private String password;
private String driverClass;
private String jdbcUrl;
/****setter and getter ****/
}
public class Person {
private String name;
private Long idCard;
private Integer age;
public Person(){
}
public Person(String name,Long idCard,Integer age){
this.name = name;
this.idCard = idCard;
this.age = age;
}
/****setter and getter ****/
}
public class Student extends Person {
private Long studentNumber;
private Clazz clazz;
private Double liveCost;
public Student(){
}
public Student(Long studentNumber,Clazz clazz,Double liveCost){
this.studentNumber = studentNumber;
this.clazz = clazz;
this.liveCost = liveCost;
}
public Student(String name,Long idCard,Integer age,Long studentNumber,Clazz clazz,Double liveCost){
super(name,idCard,age);
this.studentNumber = studentNumber;
this.clazz = clazz;
this.liveCost = liveCost;
}
public Student(Long studentNumber,Double liveCost){
this.studentNumber = studentNumber;
this.liveCost = liveCost;
}
public Student(String name,Long idCard,Integer age,Long studentNumber,Double liveCost){
super(name,idCard,age);
this.studentNumber = studentNumber;
this.liveCost = liveCost;
}
/****setter and getter ****/
}