IOC:Inversion of control控制反轉-使用對象時由程序中主動new對象轉為外部提供對象。
? ? ? ? 此過程中對象創建控制權由程序內部轉為外部,此思想稱為控制反轉。
Spring技術對IOC思想提供了實現:
? ? ? ? Spring提供一個容器,稱為IOC容器,用來充當IOC思想的“外部”
? ? ? ? IOC容器負責對象的創建、初始化等工作,被創建的對象在IOC容器中被稱為Bean
適合交給容器管理的對象:表現層對象、業務層對象、數據層對象、工具對象
不適合交給容器管理的對象:封裝實體的域對象
<!-- 1.pom文件中導入Spring對應的坐標spring-context,對應的版本是5.3.21-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.21</version>
</dependency>
<!-- 2.配置bean-->
<bean id="bookDao" class="com.kxg.exercise.dao.impel.BookDaoImpel" name="bookDao1,bokDao2" scope="prototype"/>
bean標簽屬性介紹:
? ? ? ? id屬性:給bean起名字
? ? ? ? class屬性:表示給bean定義類型
? ? ? ? name屬性:給bean起別名。可以定義多個,是用逗號、分號、空格分隔
? ? ? ? scope屬性:定義bean的作用范圍。singleton:單例(默認) prototype:非單例
程序內部獲取bean可以通過id或name,如果兩個都沒有得到將拋出異常NoSuchBeanDefinition
// 3.在程序中獲取容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext.xml");// 可以使用文件系統配置路徑:new ClassPathXmlApplicationContext("絕對路徑")// 一次獲取多個容器:new ClassPathXmlApplicationContext("a.xml","b.xml");
// 4.獲取容器中的bean// 方法一:按照bean名稱獲取
BookDao bookDao1 = (BookDao)ctx.getBean("bookDao");// 方法二:按照bean類型獲取(類型唯一)
BookDao bookDao2 = ctx.getBean("BookDao.class");// 方法三:使用bean名稱獲取指定類型
BookDao bookDao3 = ctx.getBean("bookDao", BookDao.class);
經過上面幾步,對象的創建成功由程序內部轉為程序外部
DI:Dependency Injection-依賴注入
? ? ? ? 在容器中建立bean與bean之間的依賴關系的整個過程,成為依賴注入
<!-- 在spring文件中配置類與類之間的關系:service與dao-->
<bean id="bookService" class="com.kxg.exercise.service.impel.BookServiceImpel"><property name="bookDao" ref="bookDao"/>
</bean>
property標簽:表示配置當前bean的屬性
? ? ? ? name:表示配置哪一個具體的屬性
? ? ? ? ref:表示參照哪一個bean(兩個類的綁定)
依賴注入的目的:充分解耦
通過控制反轉、依賴注入,對象可以直接從外部獲取,并且獲取到的bean已經綁定了所有的依賴
這樣雖然實現了解耦但是同時也出現了很多新問題:
? ? ? ? bean類中的成員變量怎樣實現初始化???
? ? ? ? bean是怎樣進行實例化的???
? ? ? ? bean的生命周期???
? ? ? ? 外部bean怎樣進行管理???
因此Spring也提供了很多解決方案。
依賴注入的方式:setter注入、構造器注入
setter注入:
? ? ? ? 簡單數據類型:在bean類中設置set方法;xml文件中使用property標簽value屬性注入
? ? ? ? 引用數據類型:在bean類中設置set方法;xml文件中使用property標簽ref屬性注入建立關系
<property name="connectNumber" value="10"/>
<property name="bookDao" ref="bookDao"/>
構造器注入:
? ? ? ? 簡單類型:bean類中提供構造方法;xml文件中<constructor-atg>標簽中name、value屬性
? ? ? ? 引用類型:bean類中提供構造方法;xml文件中<constructor-atg>標簽中name、ref屬性
<!-- 引用類型 -->
<constructor-arg name="bookDao" ref="bookDao"/>
<!-- 簡單數據類型 --><!-- 通過參數名稱匹配:沒有達到程序解耦的問題 -->
<constructor-arg name="connectNumber" value="55"/>
<constructor-arg name="databaseName" value="mysql"/><!-- 通過參數值類型進行匹配,試圖實現解耦問題 -->
<constructor-arg type="int" value="55"/>
<constructor-arg type="java.lang.String" value="mysql"/><!-- 通過參數前后位置匹配,試圖實現解耦問題 -->
<constructor-arg index="1" value="55"/>
<constructor-arg index="2" value="mysql"/>
通過依賴注入的不同方式,第一個問題得到了解決
bean的實例化:
構造方法:調用類中的無參構造創建實例對象
靜態工廠:調用工廠類中的靜態方法實例化對象
? ? ? ? 1.添加factory-method屬性創建類,不能僅配工廠名。否則僅僅創建的是工廠類不是bean對象
? ? ? ? 2.factory工廠類中方法添加static才能創建對象
// 靜態工廠-static
public static BookDao getBookDao() {System.out.println("factory setup...");return new BookDaoImpel();
}
<bean id="bookDaoFactory" class="com.kxg.exercise.factory.BookDaoFactory" factory-method="getBookDao"/>
實例工廠:調用靜態類中的非靜態方法
? ? ? ? 容器中配置工廠類
? ? ? ? 容器中factory-bean找到相應的工廠類,factory-method找到對應的方法
// 非靜態工廠
public BookDao getBookDao_() {System.out.println("factory2 setup...");return new BookDaoImpel();
}
<!--非靜態工廠-->
<bean id="bookDaoFactory1" class="com.kxg.exercise.factory.BookDaoFactory"/>
<bean id="bookDao1" factory-method="getBookDao_" factory-bean="bookDaoFactory1"/>
使用FactoryBean實例化
? ? ? ? 創建相關FactoryBean類實現FactoryBean接口,實現相關的創建方法getObject
? ? ? ? 容器中加載FactoryBean類
public class bookDaoFactoryBean implements FactoryBean<BookDao> {// 得到bean實例:代替原始實例工廠中創建對象的方法@Overridepublic BookDao getObject() throws Exception {System.out.println("FactoryBean...");return new BookDaoImpel();}
}
<!-- FactoryBean-->
<bean id="bookDao2" class="com.kxg.exercise.factory.bookDaoFactoryBean"/>
BookDao bookDao5 = (BookDao) ctx.getBean("bookDao2");
System.out.println(bookDao5);
bean生命周期:
bean生命周期控制:從bean創建后到銷毀前做的一些事情
????????1.類中提供生命周期的控制方法,xml文件中配置生命周期的方法
????????2.類實現InitializingBean、DisposableBean接口
初始化容器:創建對象(內存分配);執行構造方法;執行屬性注入(set);執行bean初始化方法
使用bean:執行業務操作
關閉/銷毀容器:執行bean銷毀方法
bean銷毀時機:容器關閉前觸發bean的銷毀
????????關閉容器的方法:
????????????????1.手動關閉容器:ConfigurableApplicationContext接口close()操作
????????????????2.注冊關閉鉤子,在虛擬機退出前先關閉容器在退出虛擬機:ConfigurableApplicationContext接口registerShutdownHook()
接下來就到了最后一個問題:外部bean怎樣管理?
以數據源對象為例:
1.spring中開啟context命名空間xmlns:context="http://www.springframework.org/schema/context"
?2.在context空間中加載properties文件 ?
???????? system-properties-mode="NEVER":不加載系統屬性 ?????????location="jdbc1.properties,jdbc2.properties:加載多個properties文件? ????????location="classpath:*.properties:加載路徑下的所有properties文件標準格式 ????????location="classpath*:*.properties:從類路徑或jar包中搜索并加載properties文件???
3.使用屬性占位符${}讀取properties文件中的屬性
<context:property-placeholder location="classpath:*.properties"/>
<bean class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/>
</bean>
// 創建數據源對象
DataSource dataSource1 = (DataSource) ctx.getBean("dataSource1");
System.out.println(dataSource1);
此外spring還提供了依賴自動裝配:直接在bean標簽中使用autowire屬性
按類型:autowire="byType"
? ? ? ? 使用類型裝配時,必須保障容器中相同類型的bean唯一
按名稱:autowire="byName"
? ? ? ? 使用名稱自動裝配時,必須保障容器中具有指定名稱的bean
1.自動裝配用于引用類型注入,不適用于簡單類型
2.自動裝配優先級低于setter注入與構造器注入,同時出現時自動裝配失效