JavaWeb - Sping & Sping IOC
JavaWeb - Sping & Sping IOC
目錄- JavaWeb - Sping & Sping IOC
- 1 Sping
- 2 Spring IOC
1 Sping
1.1 Sping 概念
Spring是分層的 Java SE/EE應用 full-stack(全棧式) 輕量級開源框架
-
全棧式:
- 對各種主流技術和框架都進進行了整合,同時對三層架構都提供瞭解決方案
-
輕量級:
- 輕量級和重量級的劃分主要依據服務使用的多少,啟動時資源載入的多少及耦合度等等
提供了表現層 SpringMVC
持久層 Spring JDBC Template
業務層事務管理,物件控制等眾多的企業級應用技術
還能整合開源世界眾多著名的第三方框架和類庫
1.1.1 Sping 兩大核心:
-
IOC(Inverse Of Control:控制反轉),把物件的建立權交給sping
-
AOP(Aspect Oriented Programming:面向切面程式設計),在不修改原始碼的情況下對方法進行增強
1.1.2 耦合和解耦的概念
耦合:程式間的依賴關係
解耦:降低程式間的依賴關係,並不是完全消除
例項:
//1. 註冊驅動 存在編譯期依賴,這就是耦合重的體現
//new 關鍵字就會存在編譯期依賴,解耦思路為去掉new改用反射
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
解耦:使用反射機制讓其編譯期不依賴,執行期才依賴,但還存在硬編碼問題
Class.forName("com.mysql.jdbc.Driver");
//最終解耦思路為:配置檔案+反射,自定義IOC用到此思路
1.1.3 Sping 的優點
-
方便解耦,簡化開發
- Spring就是一個容器,可以將所有物件建立和關係維護交給Spring管理
-
AOP程式設計的支援
- Spring提供面向切面程式設計,方便實現程式進行許可權攔截,執行監控等功能
-
宣告式事務的支援
- 通過配置完成事務的管理,無需手動程式設計
-
方便測試,降低JavaEE API的使用
- Spring對Junit4支援,可以使用註解測試
-
方便整合各種優秀框架
- 不排除各種優秀的開源框架,內部提供了對各種優秀框架的直接支援
2 Spring IOC
2.1 IOC的概念
控制反轉(Inverse Of Control)不是什麼技術,而是一種設計思想。它的目的是指導我們設計出更加鬆耦合的程式
- 控制:在java中指的是物件的控制權限(建立、銷燬)
- 反轉:指的是物件控制權由原來 由開發者在類中手動控制 反轉到 由Spring容器控制
2.2 自定義IOC容器
核心思想為將物件的建立通過反射機制來實現進而實現解耦,再通過配置檔案的方式來實現反射建立物件時產生的硬編碼問題
步驟:
- 建立maven專案,匯入需要使用包的座標
<dependencies>
//dom4j 用來解析xml檔案
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
- 編寫userDao介面及實現類,編寫userService介面及實現類並在實現類中建立userDao例項物件並呼叫方法,最後編寫測試類建立userService例項物件並呼叫方法
- 編寫bean.xml 檔案,將需要容器管理類編寫到該檔案中
<beans>
<bean id = "userDao" class = "com.lagou.dao.userDaoImpl"></bean>
<bean id="userService" class="com.lagou.service.userServiceImpl"></bean>
</beans>
- 編寫BeanFactory工廠類,用來完成ioc容器的初始化載入和分配容器中的物件
- 靜態程式碼塊用來載入配置檔案並解析,獲取到配置檔案中類的路徑和名稱,再通過該路徑來建立該類的物件
- 宣告一個Map型別的成員變數用來儲存解類與其對應的物件
- 宣告一個方法用來通過類名獲取物件
public class BeanFactory {
private static Map<String, Object> ioc = new HashMap<>();
static {
try {
//1.將xml檔案讀取,通過呼叫類載入器中的方法
InputStream stream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
//2.解析xml檔案
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(stream);
//3.編寫xpath來指定需要獲取的標籤
String xpath = "//bean";
//4.獲取所有的bean 標籤
List<Element> list = document.selectNodes(xpath);
//將標籤中的元素取出並新建物件再放入集合中
for (Element element : list) {
ioc.put(element.attributeValue("id"),Class.forName(element.attributeValue("class")).newInstance());
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//宣告方法用來獲取物件
public static Object getBean(String id){
return ioc.get(id);
}
}
- 編寫測試類
userService service = (userService) BeanFactory.getBean("userService");
service.save();
2.3 Sping 快速使用
- 建立專案並匯入相關座標
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
- 建立Sping 的核心配置檔案,一般使用applicationContext.xml
// applicationContext.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">
</beans>
- 在核心配置檔案中為Bean物件進行配置
<bean id="" class=""></bean>
- 建立ApplicationContext物件,執行getBean
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
XXX xxx = (XXX) applicationContext.getBean("xxx");
2.4 Spring API
2.4.1 Spring 體系
2.4.2 BeanFactory 介面
BeanFactory是 IOC 容器的核心介面,它定義了IOC的基本功能
- 通過XmlBeanFactory 實現類來載入核心配置檔案
BeanFactory beanFactory =
new XmlBeanFactory(new
ClassPathResource("applicationContext.xml"));
2.4.3 ApplicationContext 介面
代表應用上下文物件,可以獲得spring中IOC容器的Bean物件
常用實現類:
-
ClassPathXmlApplicationContext
- 是從類的根路徑下載入配置檔案 推薦使用這種
-
FileSystemXmlApplicationContext
- 從磁碟路徑上載入配置檔案,配置檔案可以在磁碟的任意位置
-
AnnotationConfigApplicationContext
- 使用註解配置容器物件時,需要使用此類來建立 spring 容器。它用來讀取註解
ApplicationContext app =
new ClassPathXmlApplicationContext("applicationContext.xml");
2.4.4 BeanFactory 和 ApplicationContext 的區別
- BeanFactory 在第一次呼叫 getBean() 方法的時候建立指定物件的例項
//該語句執行並沒有建立例項
new XmlBeanFactory(new ClassPathResource("xxx.xml"));
- ApplicationContext在啟動容器時就載入和建立了所有物件的例項
2.4.5 常用方法 - getBean()
-
Object getBean(String name);
- 根據Bean的id從容器中獲得Bean例項,返回是Object,需要強轉
-
T getBean(Class requiredType);
- 根據型別從容器中匹配Bean例項,當容器中相同型別的Bean有多個時,則此方法會報錯
- 一個介面的每個實現類都需要在核心配置檔案
中配置 - 對於有多個實現類的介面該方法不適用
-
T getBean(String name,Class requiredType);
- 根據Bean的id和型別獲得Bean例項,解決容器中相同型別Bean有多個情況
2.4.6 Bean 標籤配置
2.4.6.1 Bean 標籤的基本配置
基本屬性:
- id:Bean例項在Spring容器中的唯一標識
- class:Bean的全限定名
預設情況下它呼叫的是類中的無參建構函式,如果沒有無參建構函式則不能建立成功
2.4.6.2 Bean 標籤的範圍配置
該屬性指物件的的作用範圍,取值如下
-
singleton
- 不配置時的預設值,代表單例的
-
prototype
- 代表多例的
-
request
- WEB專案中,Spring建立一個Bean的物件,將物件存入到request域中
-
session
- WEB專案中,Spring建立一個Bean的物件,將物件存入到session域中
-
global session
- WEB專案中,應用在Portlet環境,如果沒有Portlet環境那麼globalSession 相當於 session
單例和多例的比較:
當scope的取值為singleton時
- Bean的例項化個數:1個
- Bean的例項化時機:當Spring核心檔案被載入時,例項化配置的Bean例項
- Bean的生命週期:
- 物件建立:當應用載入,建立容器時,物件就被建立了
- 物件執行:只要容器在,物件一直活著
- 物件銷燬:當應用解除安裝,銷燬容器時,物件就被銷燬了
當scope的取值為prototype時
- Bean的例項化個數:多個
- Bean的例項化時機:當呼叫getBean()方法時例項化Bean
- Bean的生命週期:
- 物件建立:當使用物件時,建立新的物件例項
- 物件執行:只要物件在使用中,就一直活著
- 物件銷燬:當物件長時間不用時,被 Java 的垃圾回收器回收了
2.4.6.3 Bean 生命週期的配置
- init-method:指定類中的初始化方法名稱
- destroy-method:指定類中銷燬方法名稱
2.4.6.4 Bean 例項化的三種方式
無參構造方法例項化:
工廠靜態方法例項化:
工廠普通方法例項化:
2.4.7 Bean 依賴注入
依賴注入:DI,Dependency Injection,是 Spring 框架核心 IOC 的具體實現
將不同層之間呼叫的依賴關係交給Spring維護,及通過框架將層物件傳入別的層
2.4.7.1 Bean 依賴注入方式 - 構造方法
(1)在Service 層裡新增另一層介面的例項
- private xxxDao xxxdao;
(2)為Service 層新增有參構造方法,將屬性xxxdao傳入
(3)在實現介面所重寫的方法中適用另一層介面的例項(xxxdao)呼叫方法
(4)配置核心配置檔案
<bean id = "xxxService" class = "service 層裡的實現類">
<constructor-arg index="引數下標從0開始" type="構造方法引數介面型別" ref="構造方法引數bean id"/>
或
<constructor-arg name="構造方法引數的形參名" ref="構造方法引數bean id"/>
<bean/>
2.4.7.2 依賴注入方式 - set方法
(1)在Service 層裡新增另一層介面的例項
- private xxxDao xxxdao;
(2)該方法使用預設的無參構造方法
(3)為該屬性xxxdao 新增set 方法
(4)在實現介面所重寫的方法中使用另一層介面的例項(xxxdao)呼叫方法
(5)配置核心配置檔案
<bean id = "xxxService" class = "service層介面的實現類">
<property name = "set方法set後首字母小寫" ref="屬性對應介面Bean id"/>
<bean/>
set 方法 - 拓展 - P名稱空間注入
在核心配置檔案中檔案頭新增
xmlns:p="http://www.springframework.org/schema/p"
配置Bean
<bean id = "xxxService" class = "service層介面的實現類" p:xxxDao-ref="xxxDao/>
2.4.7.3 Bean 依賴注入的的資料型別
- 普通資料類:基本資料型別及String
-
- 引用資料型別
- 集合資料型別:List,Map,Set,Array,Properties
List:
<property name="">
<list>
<value>基本資料型別的值<value/>
<ref bean = "引用資料類 Bean id"/>
<list/>
<property/>
Set:和List 配置基本一致,區別在於List集合和Set集合本身的區別
<property name="">
<set>
<value>基本資料型別的值<value/>
<ref bean = "引用資料類 Bean id"/>
<set/>
<property/>
Array:和前面基本一致,只是標籤有區別
<property name="">
<array>
<value>基本資料型別的值<value/>
<ref bean = "引用資料類 Bean id"/>
<array/>
<property/>
Map:除了標籤的區別,需要同時設定key 和 value 的值
<property name="">
<map>
<entry key="基本資料型別" value="基本資料型別">
<entry key-ref="引用資料型別Bean id" value-ref="引用資料型別Bean id">
<map/>
<property/>
Properties配置檔案注入:key - value形式,值都是字串
<property name="">
<props>
<prop key="">值</prop>
<props/>
<property/>
2.4.8 配置檔案模組化
意義:Sping 配置內容多且都配置在核心配置檔案造成繁雜,可將部分配置檔案拆解到其他配置檔案中,即配置檔案模組化
拆分原則:
- 按層進行拆分
- 按業務模組進行拆分
拆分配置檔案之後需要載入拆分後的配置檔案,載入方式有兩種:
(1)並列的多個配置檔案
ApplicationContext act =
new
ClassPathXmlApplicationContext("ApplicationContext.xml","xxx.xml","...");
(2)主從配置檔案
- 在核心配置檔案
標籤中新增
<import resource="applicationContext-xxx.xml"/>
注意:
- 同一個xml中不能出現相同名稱的bean,如果出現會報錯
- 多個xml如果出現相同名稱的bean,不會報錯,但是後加載的會覆蓋前載入的bean
2.4.9 Bean 標籤配置,依賴注入和配置檔案模組化小結
<bean>標籤:建立物件並放到spring的IOC容器
id屬性:在容器中Bean例項的唯一標識,不允許重複
class屬性:要例項化的Bean的全限定名
scope屬性:Bean的作用範圍,常用是Singleton(預設)和prototype
<constructor-arg>標籤:屬性注入
name屬性:屬性名稱
value屬性:注入的普通屬性值
ref屬性:注入的物件引用值
<property>標籤:屬性注入
name屬性:屬性名稱
value屬性:注入的普通屬性值
ref屬性:注入的物件引用值
<list>
<set>
<array>
<map>
<props>
<import>標籤:匯入其他的Spring的分檔案
2.5 DbUtils (IOC 實戰)
交給Spring 容器的類:
-
各層介面實現類
-
DataSource
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property> //特別注意該地方的set方法
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
- 使用properties 檔案的方式進行資料庫初始化配置
在核心配置檔案檔案頭中新增:
名稱空間
xmlns:context="http://www.springframework.org/schema/context"
約束路徑
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
標籤內配置:
<context:property-placeholder location="classpath:jdbc.properties" />
- QueryQunner 有參構造初始化(傳遞dataSource)
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
2.6 Spring 註解開發
使用註解開發需要在核心配置檔案中先新增context,再為需要註解掃描的包進行配置
<!--註解的元件掃描-->
<context:component-scan base-package="com.lagou"></context:component-scan>
2.6.1 Spring 註解開發常用註解
2.6.1.1 自定義類相關注解
對應 |
value = "" 相當於 註解中 id = "" |
---|---|
@Component | 使用在類上用於例項化Bean |
@Controller | 使用在Web層類上用於例項化Bean |
@Service | 使用在Service層類上用於例項化Bean |
@Repository | 使用在dao層類上用於例項化Bean |
註解開發依賴注入使用的是反射機制,和構造方法和set方法傳參不同
對於有多個實現類的介面,會先根據介面匹配,再根據引數名匹配實現類或設定@Qualifier 指定
JDK11以後完全移除了javax擴充套件導致不能使用@resource註解,使用需要導包
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
對應 |
前三種對應ref引用資料型別 |
---|---|
@Autowired | 使用在欄位上用於根據型別依賴注入/該方法可用於引數前 |
@Qualifier | 必須結合@Autowired使用,根據名稱依賴注入 |
@Resource | 相當於@Autowired+@Qualifier,name="" 按名稱 |
@Value | 注入普通資料型別 |
配置Bean的作用範圍和生命週期:
對應 |
|
---|---|
@Scope | 標註Bean的作用範圍 |
@PostConstruct | 使用在方法上標註該方法時Bean的初始化方法 |
@PreDestroy | 使用在方法上標註該方法是Bean的銷燬方法 |
2.6.1.2 非自定義類和核心配置檔案相關注解
(1)非自定義類,jar包中的原始碼類需要Spring 管理時
@Bean
- 使用在方法上,標註將該方法的返回值儲存到 Spring 容器中
- value = "" 相當於
不寫預設等於方法名
(2)載入properties檔案的配置
載入properties檔案的配置:<context:property-placeholder location="classpath:jdbc.properties">
@PropertySource
- 用於載入 properties 檔案中的配置
- 傳入: classpath: 檔案位置
(3)元件掃描的配置
元件掃描的配置:<context:component-scan>
@ComponentScan
- 用於指定 Spring 在初始化容器時要掃描的包
(4)引入其他檔案
引入其他檔案:<import>
@Import
- 用於匯入其他配置類
- 傳入Class 類
(5)使用核心配置類來替換核心配置檔案
@Configuration
- 用於指定當前類是一個Spring 配置類,當建立容器時會從該類上載入註解
在使用春註解開發時
new AnnotationConfigApplicationContext(核心配置檔案.class);
2.6.2 Spring 整合Junit
整合Junit的含義是在測試類中無需手動載入核心配置檔案並手動獲取目標類,將其交給Spring 來建立
(1)匯入Spring 整合 Juint 的座標
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
注意:spring5 及以上版本要求 junit 的版本必須是 4.12 及以上
(2)測試類上新增@Runwith註解
@RunWith(SpringJUnit4ClassRunner.class)
(3)載入核心配置檔案或核心配置類
@ContextConfiguration(classes = {SpringConfig.class}) // 載入spring核心配置類
或
@ContextConfiguration(value = {"classpath:applicationContext.xml"}) //載入spring
核心配置檔案
(4)使用@Autowired 注入物件
(5)測試方法中直接呼叫被注入物件
測試時出現異常:
javax.net.ssl.SSLException: closing inbound before receiving peer's close_notify
解決方法:
在url 後加上 &useSSL=false