一、Spring 概述
1.1 簡(jiǎn)介
-
Spring:春天 --> 給軟件行業(yè)帶來(lái)了春天;
2002,首次推出了 Spring 框架的雛形
interface21
框架榨乎;Spring 框架,即以 interface21 框架為基礎(chǔ)瘫筐,經(jīng)過(guò)重新設(shè)計(jì)蜜暑,并不斷豐富內(nèi)涵,于 2004 年 3 月 24 日策肝,發(fā)布了 1.0 正式版肛捍;
Rod Johnson:Spring Framework 創(chuàng)始人;
Spring 理念:使現(xiàn)有的技術(shù)更加容易使用之众,本身是一個(gè)大雜燴拙毫,整合了現(xiàn)有的技術(shù)框架。
SSH:Struct2 + Spring + Hibernate
SSM:SpringMVC + Spring + Mybatis
官網(wǎng):鏈接
下載地址:鏈接
Github:鏈接
-
Maven 依賴:
- 導(dǎo)入
webmvc
會(huì)自動(dòng)導(dǎo)入相關(guān)依賴棺禾; -
jdbc
用于和 Mybatis 整合缀蹄;
- 導(dǎo)入
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.17</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.17</version>
</dependency>
1.2 優(yōu)點(diǎn)
- Spring 是一個(gè)開(kāi)源的免費(fèi)的框架(容器);
- Spring 是一個(gè)輕量級(jí)的、非入侵式的框架缺前;
- 控制反轉(zhuǎn)(IOC)蛀醉、面向切面編程(AOP);
- 支持事務(wù)的處理诡延,對(duì)框架整合的支持滞欠;
- ==總結(jié)一句話:Spring 就是,輕量級(jí)的控制反轉(zhuǎn)(IOC)和面向切面(AOP)編程的框架==
1.3 組成
Spring 框架是一個(gè)分層架構(gòu)肆良,由 7 個(gè)定義良好的模塊組成;
-
Spring 模塊逸绎,構(gòu)建在核心容器之上惹恃,核心容器定義了創(chuàng)建、配置和管理 bean 的方式棺牧;
組成 Spring 框架的每個(gè)模塊(或組件)都可以單獨(dú)存在巫糙,或者與其他一個(gè)或多個(gè)模塊聯(lián)合實(shí)現(xiàn);
模塊簡(jiǎn)介:
-
核心容器:
- 提供 Spring 框架的基本功能颊乘;
- 主要組件是
BeanFactory
参淹,它是工廠模式的實(shí)現(xiàn),使用控制反轉(zhuǎn)(IOC)模式乏悄,將應(yīng)用程序的配置浙值,和依賴性規(guī)范與實(shí)際的應(yīng)用程序代碼分開(kāi);
-
Spring 上下文:
- 是一個(gè)配置文件檩小,向 Spring 框架提供上下文信息开呐,包括企業(yè)服務(wù),例如:
JNDI
规求、EJB
筐付、電子郵件、國(guó)際化阻肿、校驗(yàn)和調(diào)度功能瓦戚;
- 是一個(gè)配置文件檩小,向 Spring 框架提供上下文信息开呐,包括企業(yè)服務(wù),例如:
-
Spring AOP:
- 面向切面的編程功能,可管理任何支持 AOP 的對(duì)象丛塌;
- 基于 Spring 的應(yīng)用程序中的對(duì)象较解,提供了事務(wù)管理服務(wù);
- 不用依賴組件姨伤,就可以將聲明性事務(wù)管理哨坪,集成到應(yīng)用程序中;
-
Spring DAO:
-
JDBC DAO
抽象層乍楚,提供了有意義的異常層次結(jié)構(gòu)当编,可用該結(jié)構(gòu)來(lái)管理異常處理和不同數(shù)據(jù)庫(kù)供應(yīng)商拋出的錯(cuò)誤消息; - 異常層次結(jié)構(gòu)徒溪,簡(jiǎn)化了錯(cuò)誤處理忿偷,降低了需要編寫(xiě)的異常代碼數(shù)量(例如打開(kāi)和關(guān)閉連接)金顿;
-
Spring DAO
的面向 JDBC 的異常,遵從通用的 DAO 異常層次結(jié)構(gòu)鲤桥;
-
-
Spring ORM:
- Spring 框架插入了若干個(gè) ORM 框架揍拆,從而提供了 ORM 的對(duì)象關(guān)系工具,其中包括
JDO
茶凳、Hibernate
和iBatis SQL Map
嫂拴; - 遵從 Spring 的通用事務(wù)和 DAO 異常層次結(jié)構(gòu);
- Spring 框架插入了若干個(gè) ORM 框架揍拆,從而提供了 ORM 的對(duì)象關(guān)系工具,其中包括
-
Spring Web 模塊:
- 建立在應(yīng)用程序上下文模塊之上贮喧,為基于 Web 的應(yīng)用程序提供了上下文筒狠;
- Spring 框架支持與
Jakarta Struts
的集成; - 簡(jiǎn)化了處理多部分請(qǐng)求箱沦,以及將請(qǐng)求參數(shù)綁定到域?qū)ο蟮墓ぷ鳎?/li>
-
Spring MVC 框架:
- 全功能的構(gòu)建 Web 應(yīng)用程序的 MVC 實(shí)現(xiàn)辩恼;
- 通過(guò)策略接口,MVC 框架變成為高度可配置的谓形;
- MVC 容納了大量視圖技術(shù)灶伊,其中包括
JSP
、Velocity
寒跳、Tiles
聘萨、iText
和POI
;
1.4 拓展
-
Spring Boot:
- 快速開(kāi)發(fā)的腳手架冯袍;
- 基于 Spring Boot匈挖,可以快速的開(kāi)發(fā)單個(gè)微服務(wù);
- 約定大于配置康愤;
-
Spring Cloud:
- SpringCloud 是基于 SpringBoot 實(shí)現(xiàn)的儡循;
學(xué)習(xí) SpringBoot 的前提,需要完全掌握 Spring 以及SpringMVC征冷,承上啟下的作用择膝;
弊端:發(fā)展了太久之后,違背了原來(lái)的理念检激,配置十分繁瑣肴捉;
二、IOC 基礎(chǔ)
- 新建空白的 maven 項(xiàng)目叔收;
2.1 IOC 理論推導(dǎo)
以前代碼的實(shí)現(xiàn)方式:
- 創(chuàng)建 UserDao 接口:
public interface UserDao {
public void getUser();
}
- 創(chuàng)建 UserDaoImpl 實(shí)現(xiàn)類(lèi) :
public class UserDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("獲取用戶數(shù)據(jù)");
}
}
- 創(chuàng)建 UserService 業(yè)務(wù)接口:
public interface UserService {
public void getUser();
}
- 創(chuàng)建 UserServiceImpl 業(yè)務(wù)實(shí)現(xiàn)類(lèi):
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
- 測(cè)試:
public class MyTest {
@Test
public void testUser() {
UserService service = new UserServiceImpl();
service.getUser();
}
}
以前增加需求的實(shí)現(xiàn)方式:
- 增加 Userdao 的實(shí)現(xiàn)類(lèi):
public class UserDaoOracleImpl implements UserDao{
@Override
public void getUser() {
System.out.println("Oracle數(shù)據(jù)");
}
}
- 在 UserServiceImpl 實(shí)現(xiàn)類(lèi)里齿穗,修改對(duì)應(yīng)的實(shí)現(xiàn):
public class UserServiceImpl implements UserService {
// 修改UserDao的對(duì)應(yīng)實(shí)現(xiàn)類(lèi)
private UserDao userDao = new UserDaoOracleImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
- 發(fā)現(xiàn)問(wèn)題:用戶的需求變化,會(huì)影響內(nèi)部的實(shí)現(xiàn)代碼饺律,需要根據(jù)用戶的需求去修改源代碼窃页;
解決方案:
- 使用 Set 接口,實(shí)現(xiàn)不同的需求:
public class UserServiceImpl implements UserService {
private UserDao userDao;
// 利用set進(jìn)行動(dòng)態(tài)實(shí)現(xiàn)值的注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
- 測(cè)試:
public class MyTest {
@Test
public void testUser() {
UserServiceImpl service = new UserServiceImpl();
service.setUserDao(new UserDaoImpl());
service.getUser();
// 用Oracle去實(shí)現(xiàn)
service.setUserDao(new UserDaoOracleImpl());
service.getUser();
}
}
小結(jié):
-
實(shí)現(xiàn)方式對(duì)比:
之前,程序是主動(dòng)創(chuàng)建對(duì)象脖卖,控制權(quán)在程序員乒省;
使用了 set 注入后,程序不再具有主動(dòng)性畦木,而是變成了被動(dòng)的接收對(duì)象袖扛;
這種思想蛆封,從本質(zhì)上解決了問(wèn)題,程序員不用再去管理對(duì)象的創(chuàng)建械姻,只專注于業(yè)務(wù)的實(shí)現(xiàn),使系統(tǒng)的耦合性大大降低机断,這就是 IOC 的原型楷拳;
2.2 IOC 本質(zhì)
控制反轉(zhuǎn) IOC(Inversion of Control),是一種設(shè)計(jì)思想吏奸,DI(依賴注入)是實(shí)現(xiàn) IOC 的一種方法(也有人認(rèn)為 DI 只是 IOC 的另一種說(shuō)法)欢揖;
沒(méi)有 IOC 的程序中,使用面向?qū)ο缶幊谭芪担瑢?duì)象的創(chuàng)建與對(duì)象間的依賴關(guān)系她混,完全硬編碼在程序中,對(duì)象的創(chuàng)建由程序自己控制泊碑,控制反轉(zhuǎn)后,將對(duì)象的創(chuàng)建轉(zhuǎn)移給第三方;
-
所謂控制反轉(zhuǎn)搔涝,就是獲得依賴對(duì)象的方式反轉(zhuǎn)了窍霞;
-
IOC 是 Spring 框架的核心內(nèi)容冷溃,實(shí)現(xiàn)方式:
- 使用 XML 配置凿歼;
- 使用注解虐拓;
- 新版本的 Spring 可以零配置實(shí)現(xiàn) IOC态兴;
-
Spring 容器在初始化時(shí)敢订,先讀取配置文件矾柜,根據(jù)配置文件或元數(shù)據(jù)創(chuàng)建與組織對(duì)象存入容器中缆瓣,程序使用時(shí)渡冻,再?gòu)?IOC 容器中取出需要的對(duì)象超歌;
采用 XML 方式配置 Bean 時(shí),Bean 的定義信息是和實(shí)現(xiàn)分離的,而采用注解的方式,可以把兩者合為一體,Bean 的定義信息直接以注解的形式定義在實(shí)現(xiàn)類(lèi)中,從而達(dá)到了零配置的目的;
控制反轉(zhuǎn),是一種通過(guò)描述(XML 或注解)并通過(guò)第三方,去生產(chǎn)或獲取特定對(duì)象的方式;
在 Spring 中,實(shí)現(xiàn)控制反轉(zhuǎn)的是 IOC 容器人乓,其實(shí)現(xiàn)方法是金抡,依賴注入(Dependency Injection,DI)巫击;
三什黑、Hello Spring
3.1 搭建環(huán)境
- 導(dǎo)入相關(guān)依賴,spring 需要導(dǎo)入commons-logging 進(jìn)行日志記錄橘蜜,maven 會(huì)自動(dòng)下載對(duì)應(yīng)的依賴項(xiàng);
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.17</version>
</dependency>
3.2 編碼代碼
- 創(chuàng)建實(shí)體類(lèi):
Hello.java
public class Hello {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public void show() {
System.out.println("Hello " + str);
}
}
- 創(chuàng)建 spring 配置文件说订,
beans.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--bean就是java對(duì)象 , 由Spring創(chuàng)建和管理-->
<!--id:對(duì)象名 class:類(lèi)-->
<bean id="hello" class="com.study.spring.pojo.Hello">
<!--name:屬性 value:值-->
<property name="str" value="Spring"/>
</bean>
</beans>
- 測(cè)試:
public class MyTest {
@Test
public void helloTest() {
// 獲取beans.xml:拿到Spring管理對(duì)象的容器
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// genBean:參數(shù)就是Spring配置文件中bean的id(對(duì)象名)
Hello hello = (Hello) context.getBean("hello");
hello.show();
}
}
3.3 思考
- Hello 對(duì)象是誰(shuí)創(chuàng)建埂伦?
- 由 Spring 創(chuàng)建类早;
- Hello 對(duì)象的屬性是怎么設(shè)置的逆日?
- 由 Spring 容器設(shè)置的晓折;
這個(gè)過(guò)程就叫做 控制反轉(zhuǎn):
-
控制:控制對(duì)象的創(chuàng)建胃珍;
- 傳統(tǒng)應(yīng)用程序填抬,對(duì)象是由程序本身控制創(chuàng)建读拆;
- 使用 Spring 后辟灰,對(duì)象是由 Spring 來(lái)創(chuàng)建凰萨;
反轉(zhuǎn):程序本身不創(chuàng)建對(duì)象冶忱,而變成被動(dòng)的接收對(duì)象眶拉;
依賴注入:利用 set 方法來(lái)進(jìn)行注入耀里;
IOC 是一種編程思想房官,由主動(dòng)的編程,變成被動(dòng)的接收;
可以通過(guò)
new ClassPathXmlApplicationContext
查看底層源碼怀浆;
3.4 修改之前代碼
-
IDEA 快捷創(chuàng)建
beans.xml
文件,自動(dòng)導(dǎo)入 spring 配置信息: -
配置上下文:按提示操作
bean 對(duì)象添加:
<bean id="userDaoImpl" class="com.study.spring.dao.UserDaoImpl"/>
<bean id="oracleImpl" class="com.study.spring.dao.UserDaoOracleImpl"/>
<bean id="service" class="com.study.spring.service.UserServiceImpl">
<!--
注意:
name不是屬性,而是set方法后面的那部分(首字母小寫(xiě))
ref:引用Spring容器中已經(jīng)創(chuàng)建好的對(duì)象
value:具體的值顽聂,基本數(shù)據(jù)類(lèi)型
-->
<property name="userDao" ref="oracleImpl"/>
</bean>
- 測(cè)試:
@Test
public void testSpring() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService service = (UserService) context.getBean("service");
service.getUser();
}
小結(jié):
- 要實(shí)現(xiàn)不同的操作爸黄,不用在程序中去改動(dòng)梆奈,只需要在 xml 配置文件中進(jìn)行修改;
- 所謂的 IOC鲁驶,就是對(duì)象由 Spring 來(lái)創(chuàng)建鉴裹、管理、裝配;
四馍忽、IOC 創(chuàng)建對(duì)象方式
4.1 通過(guò)無(wú)參構(gòu)造(默認(rèn))
- 創(chuàng)建實(shí)體類(lèi):無(wú)參構(gòu)造
public class User {
private String name;
public User() {
System.out.println("無(wú)參構(gòu)造");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show() {
System.out.println("name=" + name);
}
}
- 創(chuàng)建配置文件:
beans.xml
<bean id="user" class="com.study.spring.pojo.User">
<property name="name" value="測(cè)試"/>
</bean>
- 測(cè)試:
@Test
public void testUser() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 在執(zhí)行g(shù)etBean的時(shí)候,user已經(jīng)通過(guò)無(wú)參構(gòu)造創(chuàng)建好了
User user = (User) context.getBean("user");
// 調(diào)用對(duì)象的方法
user.show();
}
-
運(yùn)行結(jié)果:
在調(diào)用 show 方法之前褒傅,User 對(duì)象扎运,已經(jīng)通過(guò)無(wú)參構(gòu)造初始化了遣蚀;
4.2 通過(guò)有參構(gòu)造
- 創(chuàng)建實(shí)體類(lèi):有參構(gòu)造
public class User2 {
private String name;
public User2(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show() {
System.out.println("name=" + name);
}
}
有參構(gòu)造
beans.xml
的三種創(chuàng)建方式
- 下標(biāo)賦值:
<!--1:下標(biāo)賦值突委!-->
<bean id="user2" class="com.study.spring.pojo.User2">
<constructor-arg index="0" value="有參測(cè)試"/>
</bean>
- 類(lèi)型賦值:不建議使用望蜡,重復(fù)類(lèi)型難以分辨
<!--2:類(lèi)型賦值界逛,不建議使用,重復(fù)類(lèi)型難以分辨-->
<bean id="user2" class="com.study.spring.pojo.User2">
<constructor-arg type="java.lang.String" value="有參測(cè)試2"/>
</bean>
- 參數(shù)名賦值:
<!--3:直接通過(guò)參數(shù)名來(lái)設(shè)置-->
<bean id="user2" class="com.study.spring.pojo.User2">
<constructor-arg name="name" value="有參測(cè)試3"/>
</bean>
- 測(cè)試:
@Test
public void testUser2() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User2 user2 = (User2) context.getBean("user2");
user2.show();
}
小結(jié):
- 在配置文件加載時(shí)洁奈,容器中管理的對(duì)象,就已經(jīng)初始化了;
五伟阔、Spring 配置
5.1 別名
- alias:設(shè)置別名;
<!--設(shè)置別名:在獲取Bean的時(shí)候可以使用別名獲取-->
<alias name="user" alias="userNew"/>
5.2 Bean 的配置
- bean 就是 java 對(duì)象推姻,由 Spring 創(chuàng)建和管理:
<!--
id:bean的唯一標(biāo)識(shí)符,相當(dāng)于對(duì)象名;
如果沒(méi)有配置id,name就是默認(rèn)標(biāo)識(shí)符,配置id后,name為別名;
name可設(shè)多個(gè)別名,可以用逗號(hào),分號(hào),空格隔開(kāi);
如果不配置id和name,可以根據(jù)applicationContext.getBean(.class)獲取對(duì)象;
class:bean對(duì)象所對(duì)應(yīng)的全限定名:包名 + 類(lèi)名;
-->
<bean id="hello" name="hello2 h2,h3;h4" class="com.study.spring.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
5.2 import
- 一般用于團(tuán)隊(duì)開(kāi)發(fā)眉厨,可以將多個(gè)配置文件锌奴,導(dǎo)入合并為一個(gè);
-
applicationContext.xml
(總配置文件):- 使用時(shí)憾股,直接使用總的配置就可以鹿蜀;
<?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">
<!--applicationContext.xml導(dǎo)入其它配置文件,合并成總配置文件-->
<import resource="beans.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
</beans>
六服球、依賴注入(DI)
6.1 構(gòu)造器注入
- 查看上文茴恰;
6.2 set 注入(重點(diǎn))
- 依賴注入(Dependency Injection,DI):set 方法注入
- 依賴:指 Bean 對(duì)象的創(chuàng)建,依賴于容器斩熊;
- 注入:Bean 對(duì)象中的所有屬性往枣,由容器來(lái)注入;
搭建環(huán)境
-
要求被注入的屬性粉渠,必須有 set 方法:
- 方法名由 set + 屬性首字母大寫(xiě)分冈;
- 屬性是 boolean 類(lèi)型,沒(méi)有set方法霸株,是 is雕沉;
創(chuàng)建實(shí)體類(lèi):復(fù)雜類(lèi)型(引用類(lèi))
public class Address {
private String address;
// get、set去件、toString
}
- 真實(shí)測(cè)試對(duì)象:
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String, String> card;
private Set<String> games;
private String wife;
private Properties info;
// get蘑秽、set、toString
}
- 配置文件:
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">
<bean id="student" class="com.study.spring.pojo.Student">
<!--1. 普通值注入箫攀,value-->
<property name="name" value="學(xué)生1"/>
</bean>
</beans>
- 測(cè)試:
@Test
public void studentTest() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.getName());
}
- 完善注入信息:
<!--bean類(lèi)型肠牲,引用類(lèi)-->
<bean id="address" class="com.study.spring.pojo.Address"/>
<bean id="student" class="com.study.spring.pojo.Student">
<!--1. 普通值注入,value-->
<property name="name" value="學(xué)生1"/>
<!--2. Bean注入,ref-->
<property name="address" ref="address"/>
<!--3. 數(shù)組注入,array-->
<property name="books">
<array>
<value>西游記</value>
<value>紅樓夢(mèng)</value>
<value>水滸傳</value>
</array>
</property>
<!--4. list-->
<property name="hobbys">
<list>
<value>爬山</value>
<value>閱讀</value>
<value>聽(tīng)歌</value>
</list>
</property>
<!--5. Map-->
<property name="card">
<map>
<entry key="建行" value="217842215439"/>
<entry key="工行" value="54358942439"/>
</map>
</property>
<!--6. Set-->
<property name="games">
<set>
<value>LOL</value>
<value>BOB</value>
<value>COC</value>
</set>
</property>
<!--7. null-->
<property name="wife">
<null/>
</property>
<!--8. Properties-->
<property name="info">
<props>
<prop key="driver">com.mysql.cj.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/數(shù)據(jù)庫(kù)名?</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
- 測(cè)試:
@Test
public void studentTest() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.toString());
/*
Student{
name='學(xué)生1',
address=Address{address='null'},
books=[西游記, 紅樓夢(mèng), 水滸傳],
hobbys=[爬山, 閱讀, 聽(tīng)歌],
card={建行=217842215439, 工行=54358942439},
games=[LOL, BOB, COC],
wife='null',
info={
password=123456,
driver=com.mysql.cj.jdbc.Driver,
url=jdbc:mysql://localhost:3306/數(shù)據(jù)庫(kù)名?,
username=root
}
}
*/
}
}
6.3 拓展方式注入
使用
c
和p
命名空間,進(jìn)行注入靴跛;-
官方解釋:
創(chuàng)建實(shí)例類(lèi):
public class User {
private String name;
private int age;
// // get缀雳、set、toString
}
- 創(chuàng)建配置文件:
beans.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"
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.study.spring.pojo.User" p:age="20" p:name="p 測(cè)試"/>
<!--c命名空間注入肥印,通過(guò)構(gòu)造器注入:construct-args-->
<bean id="user2" class="com.study.spring.pojo.User" c:age="18" c:name="c 測(cè)試"/>
</beans>
- 測(cè)試:
@Test
public void testUser() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
User user2 = (User) context.getBean("user2");
System.out.println(user);
System.out.println(user2);
}
- 注意點(diǎn):
- p 和 c 命名空間识椰,不能直接使用,需要導(dǎo)入
xml
頭文件約束深碱; - c 命名空間腹鹉,通過(guò)構(gòu)造器注入,必須定義無(wú)參構(gòu)造(否則報(bào)錯(cuò))敷硅;
- p 和 c 命名空間识椰,不能直接使用,需要導(dǎo)入
<!--頭文件約束-->
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
6.4 Bean 的作用域
-
六種模式:
單例模式(Spring 默認(rèn)機(jī)制):singleton
-
get 到的都是同一個(gè)對(duì)象:
配置:
<bean id="user" class="com.study.spring.pojo.User" scope="singleton"/>
- 測(cè)試:
@Test
public void testUser2() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
System.out.println(user==user2); // true
}
原型模式:prototype
-
每次從容器中 get 時(shí)功咒,都產(chǎn)生一個(gè)新的對(duì)象:
配置:
<bean id="user" class="com.study.spring.pojo.User" scope="prototype"/>
- 測(cè)試:
@Test
public void testUser2() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
System.out.println(user==user2); // false
}
-
request
、session
绞蹦、application
只能在 web 開(kāi)發(fā)中使用力奋;
七、Bean 的自動(dòng)裝配
- 自動(dòng)裝配是 Spring 滿足 bean 依賴的一種方式幽七;
- Spring 會(huì)在上下文中自動(dòng)尋找景殷,并自動(dòng)給 bean 裝配屬性;
- Spring 中的三種裝配方式:
- 在 xml 中顯式配置澡屡;
- 在 Java 中顯式配置猿挚;
- 隱式的自動(dòng)裝配 bean(重點(diǎn));
7.1 搭建測(cè)試環(huán)境
創(chuàng)建項(xiàng)目:
一個(gè)人有兩個(gè)寵物驶鹉;
實(shí)體類(lèi):Cat
public class Cat {
public void shout() {
System.out.println("miao~");
}
}
- 實(shí)體類(lèi):Dog
public class Dog {
public void shout() {
System.out.println("wang~");
}
}
- 實(shí)體類(lèi):People
public class People {
private Cat cat;
private Dog dog;
private String name;
// get绩蜻、set、toString
}
- 創(chuàng)建 Spring 配置文件:
<?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="cat" class="com.study.spring.pojo.Cat"/>
<bean id="dog" class="com.study.spring.pojo.Dog"/>
<bean id="people" class="com.study.spring.pojo.People">
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
<property name="name" value="測(cè)試"/>
</bean>
</beans>
- 測(cè)試:
@Test
public void myTest() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
People people = (People) context.getBean("people");
people.getCat().shout();
people.getDog().shout();
}
-
查看運(yùn)行結(jié)果:
7.2 ByName 自動(dòng)裝配
- 修改 bean 配置梁厉,增加屬性
autowire="byName"
:
<!--byName:會(huì)自動(dòng)在容器上下文中,查找和自己對(duì)象set方法后面的值對(duì)應(yīng)的bean的id-->
<bean id="people" class="com.study.spring.pojo.People" autowire="byName">
<property name="name" value="測(cè)試"/>
</bean>
- 當(dāng) bean 節(jié)點(diǎn)有
autowire="byName"
屬性時(shí):- 查找此類(lèi)中踏兜,所有的 set 方法名(去掉 set 后词顾,首字母小寫(xiě) );
- 去 spring 容器中碱妆,尋找對(duì)應(yīng) set 方法名的 id 對(duì)象肉盹;
- 如果有,就取出注入疹尾,如果沒(méi)有上忍,就報(bào)空指針異常;
7.3 ByType 自動(dòng)裝配
- 修改 bean 配置纳本,
autowire="byType"
:- 被引用的 bean 不再需要 id;
- 類(lèi)型:必須保證全局唯一窍蓝,否則報(bào)錯(cuò);
<bean class="com.study.spring.pojo.Cat"/>
<bean class="com.study.spring.pojo.Dog"/>
<!--byType:會(huì)自動(dòng)在容器上下文中查找繁成,和自己對(duì)象屬性類(lèi)型相同的bean吓笙!必須保證類(lèi)型全局唯一!-->
<bean id="people" class="com.study.spring.pojo.People" autowire="byType">
<property name="name" value="測(cè)試"/>
</bean>
小結(jié):
-
byName:
- 必須保證所有 bean 的 id 唯一巾腕;
- bean 需要和自動(dòng)注入屬性的 set 方法值一致面睛;
-
byType:
- 必須保證所有 bean 的 class 唯一絮蒿;
- bean 需要和自動(dòng)注入屬性的類(lèi)型一致;
7.4 使用注解實(shí)現(xiàn)自動(dòng)裝配
jdk1.5 支持注解叁鉴,Spring2.5 開(kāi)始支持注解土涝;
-
使用注解實(shí)現(xiàn)自動(dòng)裝配,需重新配置 xml 頭部信息:
- 導(dǎo)入 context 約束幌墓;
- 開(kāi)啟屬性注解支持:
<context:annotation-config/>
但壮;
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--開(kāi)啟屬性注解支持-->
<context:annotation-config/>
</beans>
@Autowired
-
@Autowired
是按類(lèi)型自動(dòng)轉(zhuǎn)配的,不支持 id 匹配克锣; - 需要導(dǎo)入
spring-aop
的包茵肃,或依賴; - 使用方式:
- 直接在屬性上使用即可袭祟;
- 也可以在 set 方式上使用验残;
- 注:使用 @Autowired 可以不用寫(xiě) Set 方法,前提:
- 自動(dòng)裝配的屬性在 IOC(Spring)容器中存在巾乳;
- 且符合名字 byName您没;
- 修改:在類(lèi)中去掉 Set 方法,使用
@Autowired
注解胆绊;
public class People {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String name;
// get氨鹏、toString
}
- 修改配置文件:
<!--開(kāi)啟屬性注解支持-->
<context:annotation-config/>
<bean id="cat" class="com.study.spring.pojo.Cat"/>
<bean id="dog" class="com.study.spring.pojo.Dog"/>
<bean id="people" class="com.study.spring.pojo.People"/>
拓展
-
@Nullable
:字段可以為 nullpublic People(@Nullable String name) { this.name = name; }
-
@Autowired(required=false)
:- 默認(rèn):true(必須存在對(duì)象,不能為 null)
// Autowired 源碼 public @interface Autowired { boolean required() default true; }
-
false
:對(duì)象可以為 null压状;
// 如果允許對(duì)象為 null仆抵,設(shè)置required = false,默認(rèn)為true @Autowired(required = false) private Cat cat;
@Qualifier
@Autowired
是根據(jù)類(lèi)型自動(dòng)裝配,加上@Qualifier
就可以根據(jù) byName 的方式自動(dòng)裝配种冬;@Qualifier 不能單獨(dú)使用镣丑;
-
測(cè)試:
- 修改配置文件內(nèi)容,類(lèi)型不變娱两,名字不為類(lèi)的默認(rèn)名字:
<bean id="cat1" class="com.study.spring.pojo.Cat"/>
<bean id="dog1" class="com.study.spring.pojo.Dog"/>
<bean id="people" class="com.study.spring.pojo.People"/>
- 在屬性上添加 @Qualifier 注解:
@Autowired
@Qualifier(value = "cat1")
private Cat cat;
@Autowired
@Qualifier(value = "dog1")
private Dog dog;
小結(jié):
- 沒(méi)有加
@Qualifier
測(cè)試莺匠,會(huì)直接報(bào)錯(cuò); - 如果
@Autowired
自動(dòng)裝配的環(huán)境比較復(fù)雜十兢,自動(dòng)裝配無(wú)法通過(guò)@Autowired
一個(gè)注解完成時(shí)趣竣,可以使用@Qualifier(value=“xxx”)
去配置@Autowired
的使用,指定一個(gè)唯一的 bean 對(duì)象注入旱物;
@Resource:Java 注解
-
JDK11 以上遥缕,包被移除了,需要導(dǎo)入包或依賴:
<dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency>
-
修改實(shí)體類(lèi)注解:
@Resource(name = "cat11") private Cat cat; @Resource private Dog dog; private String name;
-
修改配置文件:
<bean id="cat11" class="com.study.spring.pojo.Cat"/> <bean id="dog11" class="com.study.spring.pojo.Dog"/> <bean id="people" class="com.study.spring.pojo.People"/>
小結(jié):
-
@Resource 和 @Autowired 的區(qū)別:
都是用來(lái) 自動(dòng)裝配 的宵呛,都可以放在屬性字段上通砍;
@Autowired
通過(guò) byType 的方式實(shí)現(xiàn),而且必須要求這個(gè)對(duì)象存在;【常用】@Resource
默認(rèn)通過(guò) byName 的方式實(shí)現(xiàn)封孙,如果找不到名字迹冤,則通過(guò) byType 實(shí)現(xiàn),如果兩個(gè)都找不到的情況下虎忌,就報(bào)錯(cuò)泡徙;【常用】執(zhí)行順序不同:
@Autowired
通過(guò) byType 的方式實(shí)現(xiàn),@Resource
默認(rèn)通過(guò) byName 的方式實(shí)現(xiàn)膜蠢;
注意:byType 的類(lèi)型堪藐,必須全局唯一;
八挑围、使用注解開(kāi)發(fā)
環(huán)境配置:
-
spring 4 之后礁竞,想要使用注解,必須要引入 aop 的包杉辙;
配置文件中模捂,要引入 context 約束,增加注解的支持:
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
8.1 Bean 的實(shí)現(xiàn)
- 之前是使用 bean 的標(biāo)簽蜘矢,進(jìn)行 bean 注入狂男,但實(shí)際開(kāi)發(fā)中,一般會(huì)使用注解品腹;
- 配置掃描哪些包下的注解:
<!--指定注解掃描包-->
<context:component-scan base-package="com.study.spring"/>
- 在 dao 包下創(chuàng)建類(lèi)岖食,增加注解:
/*
@Component:組件
相當(dāng)于配置文件中 <bean id="user" class="當(dāng)前注解的類(lèi)"/>
可以指定對(duì)象名:@Component("對(duì)象名"),默認(rèn):類(lèi)名首字母小寫(xiě)
*/
@Component
public class User {
public String name;
}
8.2 屬性注入
- 不需要 set 方法舞吭,屬性名上直接添加:
@value("值")
:
@Component
public class User {
// 相當(dāng)于配置文件中 <property name="name" value="測(cè)試"/>
@Value("測(cè)試")
public String name;
}
- 如果提供了 set 方法泡垃,在 set 方法上添加
@value("值")
:
@Value("測(cè)試")
public void setName(String name) {
this.name = name;
}
- 測(cè)試:
@Test
public void testUser() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
System.out.println(user.name);
}
8.3 衍生注解
-
@Component
的衍生注解:web 開(kāi)發(fā)中,按 mvc 三層架構(gòu)分層羡鸥;-
@Repository
:dao 層蔑穴; -
@Service
:service 層; -
@Controller
:web 層兄春;
-
四個(gè)注解功一樣:將類(lèi)注冊(cè)到 Spring 中澎剥,裝配 Bean锡溯;
8.4 自動(dòng)裝配注解
-
@Autowired
:- 自動(dòng)裝配赶舆,通過(guò)類(lèi)型,名字祭饭;
- 如果
@Autowired
不能唯一自動(dòng)裝配上屬性芜茵,則需要通過(guò)@Qualifier(value="xxx")
;
-
@Nullable
:字段標(biāo)記了這個(gè)注解倡蝙,說(shuō)明這個(gè)字段可以為 null九串; -
@Resource
(Java 注解):自動(dòng)裝配,通過(guò)名字,類(lèi)型猪钮;
8.5 作用域
- @Scope:
- singleton(默認(rèn)):?jiǎn)卫J狡飞剑瑒?chuàng)建這個(gè)對(duì)象,關(guān)閉工廠烤低,所有的對(duì)象都會(huì)銷(xiāo)毀肘交;
- prototype:原型模式,關(guān)閉工廠扑馁,所有的對(duì)象不會(huì)銷(xiāo)毀涯呻,內(nèi)部的垃圾回收機(jī)制會(huì)回收;
@Component
@Scope("prototype")
public class User {
public String name;
}
小結(jié):
- xml 與注解:
- xml 更加萬(wàn)能腻要,適用于任何場(chǎng)合复罐,維護(hù)簡(jiǎn)單方便;
- 注解雄家,不是自己的類(lèi)無(wú)法使用效诅,維護(hù)相對(duì)復(fù)雜;
- xml 與注解整合開(kāi)發(fā)咳短,推薦最佳實(shí)踐方式:
- xml 用來(lái)管理 bean填帽;
- 注解,只負(fù)責(zé)完成屬性的注入咙好;
- 在使用的過(guò)程中篡腌,需要注意:
- 讓注解生效,必須開(kāi)啟注解的支持勾效;
<!--指定注解掃描包-->
<context:component-scan base-package="com.study.spring"/>
<context:annotation-config/>
九嘹悼、使用 Java 的方式配置 Spring
- JavaConfig 原來(lái)是 Spring 的一個(gè)子項(xiàng)目;
- 它通過(guò) Java 類(lèi)的方式层宫,提供 Bean 的定義信息杨伙;
- 在 Spring4 的版本,JavaConfig 已正式成為 Spring4 的核心功能萌腿;
搭建環(huán)境
- 創(chuàng)建項(xiàng)目限匣;
- 創(chuàng)建實(shí)體類(lèi):
// @Component:將這個(gè)類(lèi),標(biāo)注為Spring的一個(gè)組件,放到容器中
@Component
public class User {
private String name;
public String getName() {
return name;
}
@Value("測(cè)試")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
- 新建 config 配置包毁菱,創(chuàng)建 MyConfig 配置類(lèi):
/*
@Configuration:
代表這是一個(gè)配置類(lèi)米死,等同于beans.xml
本身就是一個(gè)組件(@Component),
也會(huì)被Spring容器托管,注冊(cè)到容器中
*/
@Configuration
// 可以設(shè)置掃描包
@ComponentScan("com.study.spring.config")
// 可以引入其它配置類(lèi)
@Import(MyConfig2.class)
public class MyConfig {
/*
注冊(cè)一個(gè)bean贮庞,相當(dāng)于bean標(biāo)簽
方法名:相當(dāng)于bean標(biāo)簽中的id屬性
方法的返回值:相當(dāng)于bean標(biāo)簽中的class屬性
*/
@Bean
public User getUser() {
// 返回要注入到bean的對(duì)象
return new User();
}
}
- 測(cè)試:
@Test
public void testUser() {
// 使用了配置類(lèi)方式,只能通過(guò)AnnotationConfig上下文來(lái)獲取容器峦筒,通過(guò)配置類(lèi)的class對(duì)象加載
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
// getBean的參數(shù)為,@Bean對(duì)應(yīng)的方法名窗慎,非類(lèi)名首字母小寫(xiě)N锱纭卤材!
User user = (User) context.getBean("getUser");
System.out.println(user.getName());
}
- 純 Java 的配置方式勤庐,SpringBoot 中比較常見(jiàn)催束;
十痒谴、代理模式
-
為什么要學(xué)習(xí)代理模式榕订?
- Spring AOP 的底層就是代理模式隘竭;
-
代理模式的分類(lèi):
- 靜態(tài)代理可训;
- 動(dòng)態(tài)代理绘梦;
10.1 靜態(tài)代理
- 靜態(tài)代理角色分析:
- 抽象角色:一般使用接口或者抽象類(lèi)來(lái)實(shí)現(xiàn)嘀掸;
- 真實(shí)角色:被代理的角色材蹬;
- 代理角色:代理真實(shí)角色实幕,代理后 , 會(huì)增加附屬操作;
- 客戶:訪問(wèn)代理對(duì)象的人堤器;
代碼實(shí)現(xiàn)
- 抽象角色:租房
// 租房
public interface Rent {
public void rent();
}
- 真實(shí)角色:房東
// 房東:實(shí)現(xiàn)租房接口
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房東出租房屋...");
}
}
- 代理角色:代理人
// 代理:代理房東昆庇、實(shí)現(xiàn)租房接口、并增加附屬操作
public class Proxy implements Rent {
private Host host;
public Proxy() {
}
// 有參構(gòu)造方式闸溃,注入真實(shí)對(duì)象
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
seeHouse();
// 房東租房
host.rent();
hetong();
fare();
}
// 看房
public void seeHouse() {
System.out.println("中介帶看房");
}
// 簽合同
public void hetong() {
System.out.println("簽訂合同");
}
// 收中介費(fèi)
public void fare() {
System.out.println("收中介費(fèi)");
}
}
- 客戶:客戶端訪問(wèn)代理角色
// 客戶
public class Client {
public static void main(String[] args) {
// 房東要租房子
Host host = new Host();
// 代理:中介代理房東出租房子整吆,但是,代理會(huì)增加附屬操作
Proxy proxy = new Proxy(host);
// 客戶不用面對(duì)房東辉川,直接找中介即可
proxy.rent();
}
}
- 代理模式的好處:
- 可以使真實(shí)角色的操作更加純粹表蝙,不用去關(guān)注一些公共的業(yè)務(wù);
- 公共業(yè)務(wù)交給代理角色乓旗,實(shí)現(xiàn)了業(yè)務(wù)的分工府蛇;
- 公共業(yè)務(wù)發(fā)生擴(kuò)展時(shí),方便集中管理屿愚;
- 缺點(diǎn):
- 一個(gè)真實(shí)角色汇跨,就會(huì)產(chǎn)生一個(gè)代理角色,代碼量會(huì)翻倍妆距,開(kāi)發(fā)效率會(huì)變低穷遂;
10.2 加深理解
靜態(tài)代理實(shí)現(xiàn) CRUD
- 抽象角色:用戶業(yè)務(wù)(增、刪娱据、改蚪黑、查)
// 抽象角色:增刪改查業(yè)務(wù)
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
- 真實(shí)對(duì)象:完成增、刪中剩、改忌穿、查操作
// 真實(shí)對(duì)象:完成增刪改查操作
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("增加一個(gè)用戶");
}
@Override
public void delete() {
System.out.println("刪除一個(gè)用戶");
}
@Override
public void update() {
System.out.println("修改一個(gè)用戶");
}
@Override
public void query() {
System.out.println("查詢用戶");
}
}
- 代理角色:通過(guò)代理類(lèi),增加日志功能
// 代理角色:增加日志的實(shí)現(xiàn)
public class UserServiceProxy implements UserService {
public UserServiceImpl userService;
// set方法咽安,注入真實(shí)對(duì)象(注入方式:有參構(gòu)造伴网、set方法)
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
@Override
public void add() {
log("add");
userService.add();
}
@Override
public void delete() {
log("delete");
userService.delete();
}
@Override
public void update() {
log("update");
userService.update();
}
@Override
public void query() {
log("query");
userService.query();
}
// 日志方法
public void log(String msg) {
System.out.println("[Debug] 使用了" + msg + " 方法");
}
}
- 測(cè)試:
@Test
public void testUser() {
// 真實(shí)業(yè)務(wù)
UserServiceImpl userService = new UserServiceImpl();
// 代理類(lèi)
UserServiceProxy proxy = new UserServiceProxy();
// 使用代理類(lèi)蓬推,增加了日志功能的實(shí)現(xiàn)
proxy.setUserService(userService);
proxy.add();
proxy.delete();
proxy.update();
proxy.query();
}
-
運(yùn)行結(jié)果:
-
AOP 核心思想:
- 不改變?cè)写a妆棒,通過(guò)代理,實(shí)現(xiàn)對(duì)原有功能的增強(qiáng);
10.3 動(dòng)態(tài)代理
動(dòng)態(tài)代理和靜態(tài)代理角色一樣糕珊;
動(dòng)態(tài)代理的代理類(lèi)动分,是動(dòng)態(tài)生成的,不是直接寫(xiě)好的红选;
-
動(dòng)態(tài)代理分為兩大類(lèi):
- 基于接口的動(dòng)態(tài)代理:JDK 動(dòng)態(tài)代理澜公;
- 基于類(lèi)的動(dòng)態(tài)代理:
cglib
; - Java 字節(jié)碼實(shí)現(xiàn):
javasisit
喇肋;
-
JDK 動(dòng)態(tài)代理坟乾,需要了解兩個(gè)類(lèi):
Proxy:代理;
InvocationHandler:調(diào)用處理程序蝶防;
InvocationHandler
- 用來(lái)生成代理的類(lèi)甚侣,不需要去為每個(gè)代理,都單獨(dú)生成一個(gè)類(lèi)间学;
/*
處理代理實(shí)例,并返回結(jié)果
參數(shù):
proxy:代理類(lèi)殷费,代理的真實(shí)代理對(duì)象;
method:要調(diào)用某個(gè)對(duì)象真實(shí)的方法的Method對(duì)象低葫;
args:代理對(duì)象方法傳遞的參數(shù)详羡;
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target, args);
return result;
}
Proxy
- Proxy 類(lèi):用來(lái)創(chuàng)建一個(gè)代理對(duì)象的類(lèi);
- 常用方法:
newProxyInstance()
嘿悬;
代碼實(shí)現(xiàn)
抽象角色实柠、真實(shí)角色,參照 10.2
創(chuàng)建自動(dòng)生成代理的類(lèi):
// 用這個(gè)類(lèi),自動(dòng)生成代理類(lèi)
public class ProxyInvocationHandler implements InvocationHandler {
// 被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
// 生成得到代理類(lèi)(固定代碼)
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
@Override
// 處理代理實(shí)例,并返回結(jié)果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 動(dòng)態(tài)代理的本質(zhì),就是使用反射機(jī)制實(shí)現(xiàn)
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
// 日志方法
public void log(String msg) {
System.out.println("[Debug] 使用了" + msg + " 方法");
}
}
- 測(cè)試:
- 創(chuàng)建真實(shí)角色善涨;
- 創(chuàng)建動(dòng)態(tài)代理創(chuàng)建的實(shí)體主到;
- 設(shè)置;
- 使用躯概;
public class Client {
public static void main(String[] args) {
// 真實(shí)角色
UserServiceImpl userService = new UserServiceImpl();
// 代理角色,不存在,需要?jiǎng)討B(tài)創(chuàng)建
ProxyInvocationHandler pih = new ProxyInvocationHandler();
// 設(shè)置要代理的對(duì)象
pih.setTarget(userService);
// 動(dòng)態(tài)生成代理類(lèi)
UserService proxy = (UserService) pih.getProxy();
proxy.add();
proxy.delete();
proxy.update();
proxy.query();
}
}
- 如果需要生成多個(gè)代理對(duì)象登钥,只需要去創(chuàng)建不同的 userService 和其方法,無(wú)需再對(duì)每個(gè)代理進(jìn)行設(shè)置娶靡;
動(dòng)態(tài)代理的好處
- 可以使真實(shí)角色的操作更加純粹牧牢,不用去關(guān)注一些公共的業(yè)務(wù);
- 公共業(yè)務(wù)交給代理角色姿锭,實(shí)現(xiàn)了業(yè)務(wù)的分工塔鳍;
- 公共業(yè)務(wù)發(fā)生擴(kuò)展時(shí),方便集中管理呻此;
- 一個(gè)動(dòng)態(tài)代理類(lèi)轮纫,代理的是一個(gè)接口,一般就是對(duì)應(yīng)的一類(lèi)業(yè)務(wù)焚鲜;
- 一個(gè)動(dòng)態(tài)代理類(lèi)掌唾,可以代理多個(gè)類(lèi)放前,需要實(shí)現(xiàn)同一個(gè)接口;
十一糯彬、AOP
11.1 什么是 AOP
-
AOP(Aspect Oriented Programming):面向切面編程
- 通過(guò)預(yù)編譯方式凭语,和運(yùn)行期動(dòng)態(tài)代理,實(shí)現(xiàn)程序功能統(tǒng)一維護(hù)的一種技術(shù)撩扒;
- AOP 是 OOP 的延續(xù)似扔,是軟件開(kāi)發(fā)中的一個(gè)熱點(diǎn),也是Spring 框架中的一個(gè)重要內(nèi)容搓谆,是函數(shù)式編程的一種衍生泛型炒辉;
- 利用 AOP,可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離泉手,從而使得業(yè)務(wù)邏輯辆脸,各部分之間的耦合度降低,提高程序的可重用性螃诅,同時(shí)提高開(kāi)發(fā)的效率啡氢;
11.2 Aop 在 Spring 中的作用
提供聲明式事務(wù),允許用戶自定義切面术裸;
-
橫切關(guān)注點(diǎn):
- 跨越應(yīng)用程序多個(gè)模塊的方法或功能倘是;
- 與業(yè)務(wù)邏輯無(wú)關(guān),但是需要關(guān)注的部分袭艺,就是橫切關(guān)注點(diǎn)搀崭,如日志、安全猾编、緩存瘤睹、事務(wù),等等答倡;
切面(ASPECT):橫切關(guān)注點(diǎn)轰传,被模塊化的特殊對(duì)象,是一個(gè)類(lèi)瘪撇;
通知(Advice):切面必須要完成的工作获茬,是類(lèi)中的一個(gè)方法;
目標(biāo)(Target):被通知對(duì)象倔既;
代理(Proxy):向目標(biāo)對(duì)象應(yīng)用通知之后恕曲,創(chuàng)建的對(duì)象;
切入點(diǎn)(PointCut):切面通知渤涌,執(zhí)行的 地點(diǎn) 的定義佩谣;
-
連接點(diǎn)(JointPoint):與切入點(diǎn)匹配的執(zhí)行點(diǎn);
-
Spring AOP 中实蓬,通過(guò) Advice 定義橫切邏輯茸俭,Spring 中支持 5 種類(lèi)型的 Advice:
即 Aop 在不改變?cè)写a的情況下吊履,去增加新的功能;
11.3 使用 Spring 實(shí)現(xiàn) Aop
- 使用 AOP 織入瓣履,需要導(dǎo)入
aspectjweaver
依賴:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.8</version>
</dependency>
第一種方式:通過(guò) Spring API 實(shí)現(xiàn)
- 創(chuàng)建接口:
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
- 創(chuàng)建實(shí)現(xiàn)類(lèi):
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加一個(gè)用戶");
}
@Override
public void delete() {
System.out.println("刪除一個(gè)用戶");
}
@Override
public void update() {
System.out.println("修改一個(gè)用戶");
}
@Override
public void select() {
System.out.println("查詢用戶");
}
}
- 前置增強(qiáng)
MethodBeforeAdvice
:AOP 增加的業(yè)務(wù)(前置日志)
public class Log implements MethodBeforeAdvice {
/*
method:要執(zhí)行的目標(biāo)對(duì)象的方法
args:參數(shù)
target:目標(biāo)對(duì)象
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName() + "的" + method.getName() + "被執(zhí)行了");
}
}
- 后置增強(qiáng)
AfterReturningAdvice
:AOP 增加的業(yè)務(wù)(后置日志)
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
// returnValue:返回值
System.out.println("執(zhí)行了" + method.getName() + "方法,返回值:" + returnValue);
}
}
- 在 spring 的配置文件中练俐,注冊(cè) bean袖迎,并實(shí)現(xiàn) aop 切入:
- 頭文件,需導(dǎo)入 Aop 約束腺晾;
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注冊(cè)bean-->
<bean id="userService" class="com.study.spring.service.UserServiceImpl"/>
<bean id="log" class="com.study.spring.log.Log"/>
<bean id="afterLog" class="com.study.spring.log.AfterLog"/>
<!--方式一:使用原生Spring API接口-->
<!--配置AOP,需要導(dǎo)入Aop的約束-->
<aop:config>
<!--切入點(diǎn):expression:表達(dá)式燕锥,execution(要執(zhí)行的位置:修飾符 返回值 類(lèi)名 方法名 參數(shù) )-->
<aop:pointcut id="pointcut" expression="execution(* com.study.spring.service.UserServiceImpl.*(..))"/>
<!--advisor:環(huán)繞增加 advice-ref:對(duì)應(yīng)增加方法的bean pointcut-ref:切入點(diǎn)-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
- 測(cè)試:
@Test
public void testUser() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 增加 UserService.class 后,不再需要強(qiáng)制轉(zhuǎn)換
// 注意點(diǎn):動(dòng)態(tài)代理悯蝉,代理的是接口归形,不是實(shí)現(xiàn)類(lèi)
UserService userService = context.getBean("userService", UserService.class);
userService.add();
userService.delete();
userService.update();
userService.select();
}
- 注意點(diǎn):動(dòng)態(tài)代理,代理的是接口鼻由,不是實(shí)現(xiàn)類(lèi)暇榴;
第二種方式:自定義類(lèi)(切面),實(shí)現(xiàn) Aop
切面定義:自定義一個(gè)需要被插入的類(lèi)蕉世;
自定義類(lèi)(切面):
// 自定義切面(需要插入的類(lèi))
public class DiyPointCut {
public void before() {
System.out.println("=========方法執(zhí)行前=========");
}
public void after() {
System.out.println("=========方法執(zhí)行后=========");
}
}
- 配置 xml 文件:
<!--方式二:自定義類(lèi)(切面)蔼紧,實(shí)現(xiàn) Aop-->
<!--注冊(cè)bena-->
<bean id="diy" class="com.study.spring.diy.DiyPointCut"/>
<aop:config>
<!--自定義切面,ref:要引用的類(lèi)-->
<aop:aspect ref="diy">
<!--切入點(diǎn):-->
<aop:pointcut id="point" expression="execution(* com.study.spring.service.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
- 測(cè)試:其它代碼不變
切入點(diǎn)位置表達(dá)式
- 格式:
execution(* com.service ..*.*(..))
- 解釋:
符號(hào) | 含義 |
---|---|
execution() | 執(zhí)行,表達(dá)式的主體 |
第一個(gè) * | 表示返回值的類(lèi)型任意 |
com.service | AOP 所切入的服務(wù)的包名狠轻,業(yè)務(wù)部分 |
包名后面的 .. | 表示當(dāng)前包及子包 |
第二個(gè) * | 表示類(lèi)名奸例,即所有類(lèi) |
.*(..) | 表示任何方法名,括號(hào)表示參數(shù)向楼,兩個(gè)點(diǎn)表示任何參數(shù)類(lèi)型 |
- 第二種方式查吊,實(shí)現(xiàn)更加簡(jiǎn)單,但是實(shí)現(xiàn)的功能少了湖蜕,比如:不能使用反射逻卖,獲取執(zhí)行方法的名稱;
第三種方式:使用注解實(shí)現(xiàn)
- 創(chuàng)建用注解實(shí)現(xiàn)的增強(qiáng)類(lèi):
// @Aspect:標(biāo)注這個(gè)類(lèi)是一個(gè)切面
@Aspect
public class AnnotationPointCut {
@Before("execution(* com.study.spring.service.UserServiceImpl.*(..))")
public void before() {
System.out.println("=========方法執(zhí)行前=========");
}
@After("execution(* com.study.spring.service.UserServiceImpl.*(..))")
public void after() {
System.out.println("=========方法執(zhí)行后=========");
}
// 環(huán)繞增強(qiáng):可以給定義一個(gè)參數(shù)昭抒,代表要獲取處理切入的點(diǎn)
@Around("execution(* com.study.spring.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("環(huán)繞前");
// getSignature():方法簽名箭阶,查看目標(biāo)方法名
System.out.println(jp.getSignature());
// 執(zhí)行目標(biāo)方法
Object proceed = jp.proceed();
System.out.println(proceed);
System.out.println("環(huán)繞后");
}
}
- 配置 xml 文件,注冊(cè) bean戈鲁,并增加支持注解的配置:
<!--方式三:使用注解實(shí)現(xiàn)-->
<bean id="annotationPointCut" class="com.study.spring.diy.AnnotationPointCut"/>
<!--
開(kāi)啟注解支持:兩種方式都可以實(shí)現(xiàn)
JDK(默認(rèn):proxy-target-class="false")
cglib:(proxy-target-class="true")
-->
<aop:aspectj-autoproxy/>
十二仇参、整合 Mybatis
12.1 搭建環(huán)境
導(dǎo)入相關(guān)依賴
- junit:
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
- spring 相關(guān):
<!--spring相關(guān)-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.17</version>
</dependency>
- Java 注解:(JDK11 以上需要)
<!--Java 注解:JDK11 以上需要-->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
- mybatis:
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
- mybatis-spring 整合包 (重點(diǎn)):
<!--mybatis-spring 重點(diǎn)-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
- mysql:
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
- spring-jdbc:
<!--spring-jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.17</version>
</dependency>
- AOP 織入:
<!--AOP 織入-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.8</version>
</dependency>
- Maven 靜態(tài)資源過(guò)濾:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
12.2 MyBatis 項(xiàng)目回顧
- 創(chuàng)建實(shí)體類(lèi):
public class User {
private int id;
private String name;
private String pwd;
// get、set婆殿、toString
}
- 編寫(xiě)核心配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration核心配置文件-->
<configuration>
<typeAliases>
<package name="com.study.mybatis.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--注冊(cè) Mapper.xml-->
<mappers>
<mapper class="com.study.mybatis.mapper.UserMapper"/>
</mappers>
</configuration>
- 創(chuàng)建接口類(lèi):
public interface UserMapper {
public List<User> selectUser();
}
- 創(chuàng)建
Mapper.xml
:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.study.mybatis.mapper.UserMapper">
<select id="selectUser" resultType="user">
select * from user;
</select>
</mapper>
- 測(cè)試:
@Test
public void testUser() throws IOException {
String resource = "mybatis-config.xml";
InputStream in = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.selectUser();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
-
運(yùn)行結(jié)果:
12.3 MyBatis-Spring
MyBatis-Spring:將 MyBatis 代碼诈乒,無(wú)縫整合到 Spring 中;
-
MyBatis-Spring 對(duì)應(yīng)版本:
MyBatis-Spring 相關(guān)依賴:
<!--mybatis-spring 重點(diǎn)-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
- 要和 Spring 一起使用 MyBatis婆芦,需要在 Spring 應(yīng)用上下文中定義:
- 一個(gè)
SqlSessionFactory
怕磨;-
SqlSessionFactory
需要一個(gè)DataSource
(數(shù)據(jù)源)喂饥;
-
- 至少一個(gè)數(shù)據(jù)映射器類(lèi);
- 一個(gè)
整合實(shí)現(xiàn)方式一:SqlSessionTemplate
- 創(chuàng)建 Mybatis 配置文件:
mybatis-config.xml
<!--Spring配合MyBatis肠鲫,一般在MyBatis設(shè)置別名和set员帮,其它在spring中設(shè)置-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<package name="com.study.mybatis.pojo"/>
</typeAliases>
- 創(chuàng)建配置文件:
spring-dao.xml
- 替換 mybaits 的數(shù)據(jù)源;
- 配置 SqlSessionFactory导饲,關(guān)聯(lián) MyBatis捞高;
- 注冊(cè) sqlSessionTemplate,關(guān)聯(lián) sqlSessionFactory渣锦;
<?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">
<!--DataSource:使用Spring的數(shù)據(jù)源替換Mybatis的配置,如:c3p0 dbcp druid
這里使用Spring-Jdbc:DriverManagerDataSource
-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--SqlSessionFactory(固定格式)-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--數(shù)據(jù)源-->
<property name="dataSource" ref="dataSource"/>
<!--綁定Mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/study/mybatis/mapper/*.xml"/>
</bean>
<!--SqlSessionTemplate:SQLSession(固定格式)-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--只能使用構(gòu)造器注入sqlSessionFactory,因?yàn)闆](méi)有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
- 增加 UserMapper 接口的實(shí)現(xiàn)類(lèi)硝岗,私有化 sqlSessionTemplate:
public class UserMapperImpl implements UserMapper {
// 以前使用SqlSession操作,現(xiàn)在都使用SqlSessionTemplate
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
// 在接口實(shí)現(xiàn)類(lèi)中袋毙,實(shí)現(xiàn)以前測(cè)試類(lèi)的方法型檀,并將方法值返回(使用時(shí)在spring中直接調(diào)用方法即可)
@Override
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
- 創(chuàng)建總配置文件:
applicationContext.xml
- 引入
spring-dao.xml
; - 注冊(cè)實(shí)現(xiàn)類(lèi):
UserMapperImpl
听盖;
- 引入
<?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">
<import resource="spring-dao.xml"/>
<bean id="userMapper" class="com.study.mybatis.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
- 測(cè)試:
@Test
public void testSpringMybatis() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
整合實(shí)現(xiàn)方式二:繼承 SqlSessionDaoSupport
- mybatis-spring1.2.3 版以上的才有胀溺;
- dao 層通過(guò)繼承 Support 類(lèi),直接利用 getSqlSession() 獲得皆看,然后直接注入 SqlSessionFactory月幌;
- 與方式一相比,不需要管理 SqlSessionTemplate悬蔽,而且對(duì)事務(wù)的支持更加友好扯躺,可跟蹤源碼查看;
代碼實(shí)現(xiàn):
- 創(chuàng)建 UserMapper 接口實(shí)現(xiàn)類(lèi):
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> selectUser() {
// getSqlSession()直接獲取蝎困,不需要?jiǎng)?chuàng)建
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
}
- 注冊(cè) bean:
<!--SqlSessionDaoSupport實(shí)現(xiàn)录语,只需要sqlSessionFactory-->
<bean id="userMapper2" class="com.study.mybatis.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
- 測(cè)試:
@Test
public void testSpringMybatis2() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper2", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
十三、聲明式事務(wù)
13.1 回顧事務(wù)
事務(wù):把一系列的動(dòng)作禾乘,當(dāng)成一個(gè)獨(dú)立的工作單元澎埠,這些動(dòng)作要么全部完成,要么全部不起作用始藕;
-
事務(wù)四個(gè)屬性:ACID
- 原子性(atomicity):事務(wù)是原子性操作蒲稳,由一系列動(dòng)作組成,事務(wù)的原子性伍派,確保動(dòng)作要么全部完成江耀,要么完全不起作用;
- 一致性(consistency):一旦所有事務(wù)動(dòng)作完成诉植,事務(wù)就要被提交祥国,數(shù)據(jù)和資源處于一種,滿足業(yè)務(wù)規(guī)則的一致性狀態(tài)中;
- 隔離性(isolation):可能多個(gè)事務(wù)舌稀,會(huì)同時(shí)處理相同的數(shù)據(jù)啊犬,因此每個(gè)事務(wù),都應(yīng)該與其他事務(wù)隔離開(kāi)來(lái)壁查,防止數(shù)據(jù)損壞觉至;
- 持久性(durability):事務(wù)一旦完成,無(wú)論系統(tǒng)發(fā)生什么錯(cuò)誤睡腿,結(jié)果都不會(huì)受到影響语御,通常情況下,事務(wù)的結(jié)果嫉到,被寫(xiě)到持久化存儲(chǔ)器中沃暗;
未開(kāi)啟事務(wù)測(cè)試:
- 復(fù)制上例中的代碼到新項(xiàng)目中月洛;
- 在接口 UserMapper 中新增兩個(gè)方法何恶,增加和刪除用戶:
// 增加用戶
public int addUser(User user);
// 刪除用戶
public int deleteUser(int id);
- 修改
Mapper.xml
文件,把 deletes 寫(xiě)錯(cuò)(模擬程序出錯(cuò))
<insert id="addUser" parameterType="User">
insert into user (id, name, pwd)
values (#{id}, #{name}, #{pwd});
</insert>
<delete id="deleteUser" parameterType="int">
<!--deletes 寫(xiě)錯(cuò)嚼黔,模擬程序出錯(cuò)-->
deletes from user where id = #{id};
</delete>
- 在接口實(shí)現(xiàn)類(lèi)细层,添加增加、刪除對(duì)應(yīng)的方法:
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> selectUser() {
User user = new User(4, "小王", "123");
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
mapper.addUser(user);
mapper.deleteUser(1);
return mapper.selectUser();
}
@Override
public int addUser(User user) {
return getSqlSession().getMapper(UserMapper.class).addUser(user);
}
@Override
public int deleteUser(int id) {
return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
}
}
- 測(cè)試:
@Test
public void testSpringMybatis() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
-
運(yùn)行結(jié)果:
-
報(bào)錯(cuò):sql 異常唬涧,delete 寫(xiě)錯(cuò)了疫赎;
-
結(jié)果:插入成功;
-
小結(jié):
- 沒(méi)有進(jìn)行事務(wù)的管理碎节,程序部分出現(xiàn)錯(cuò)誤捧搞,依舊可以提交;
- 如果想讓一組程序狮荔,都成功時(shí)才成功胎撇,有一個(gè)失敗,就都失敗殖氏,就需要事務(wù)晚树;
13.2 Spring 中的事務(wù)管理
-
需要用到的依賴:
-
aspectjweaver
:<!--AOP 織入--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.8</version> </dependency>
-
spring-jdbc
:<!--spring-jdbc--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.17</version> </dependency>
-
Spring 在不同的事務(wù)管理 API 之上定義了一個(gè)抽象層,使得開(kāi)發(fā)人員雅采,不必了解底層的事務(wù)管理 API爵憎,就可以使用 Spring 的事務(wù)管理機(jī)制;
-
Spring 支持:
- 編程式事務(wù)管理:
- 代碼嵌到業(yè)務(wù)方法中婚瓜,來(lái)控制事務(wù)的提交和回滾宝鼓;
- 缺點(diǎn):必須在每個(gè)事務(wù)操作業(yè)務(wù)邏輯中,包含額外的事務(wù)管理代碼巴刻;
- 聲明式事務(wù)管理:AOP
- 代碼與業(yè)務(wù)方法分離席函,以聲明的方式,來(lái)實(shí)現(xiàn)冈涧;
- 將事務(wù)管理茂附,作為橫切關(guān)注點(diǎn)正蛙,通過(guò) AOP 方法模塊化;
- Spring 中通過(guò) Spring AOP 框架营曼,支持聲明式事務(wù) 管理乒验;
- 編程式事務(wù)管理:
spring 七種事務(wù)類(lèi)型
事務(wù)類(lèi)型 | 說(shuō)明 |
---|---|
REQUIRED | (默認(rèn))支持當(dāng)前事務(wù),無(wú)事務(wù)蒂阱,另起新事物 |
SUPPORTS | 支持當(dāng)前事務(wù)锻全,無(wú)事務(wù),以非事務(wù)執(zhí)行 |
MANDATORY | 以事務(wù)方式執(zhí)行录煤,無(wú)事務(wù)鳄厌,拋異常 |
REQUIRES_NEW | 新建事務(wù),若有舊事務(wù)妈踊,掛起 |
NOT_SUPPORTED | 不支持事務(wù)了嚎,如有事務(wù),掛起 |
NEVER | 以非事務(wù)執(zhí)行廊营,有事務(wù)歪泳,拋異常 |
NESTED | 內(nèi)切事務(wù) |
配置 spring-dao.xml 文件
- 使用 Spring 管理事務(wù),注意頭文件的約束導(dǎo)入:tx
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
- 配置聲明式事務(wù):JDBC 事務(wù)
<!--配置聲明式事務(wù)-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
- 配置事務(wù)的通知:
<!--結(jié)合AOP實(shí)現(xiàn)事務(wù)的織入-->
<!--配置事務(wù)通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--給哪些方法配置事務(wù)-->
<!--配置事務(wù)的傳播特性,propagation:傳播-->
<tx:attributes>
<tx:method name="insert" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="query" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
- 配置事務(wù)切入:AOP(注意導(dǎo)入頭文件)
<!--配置事務(wù)切入-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.study.mybatis.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
-
測(cè)試:
運(yùn)行出現(xiàn)異常(deletes 寫(xiě)錯(cuò))露筒;
查看數(shù)據(jù)庫(kù)呐伞,未插入成功;
-
更改刪除語(yǔ)句后慎式,運(yùn)行正常伶氢;
小結(jié):
- 為什么需要配置事務(wù):
- 如果不配置事務(wù),可能存在數(shù)據(jù)提交不一致的情況瘪吏;
- 如果不在 Spring 中去配置聲明式事務(wù)癣防,就需要在代碼中,手動(dòng)配置事務(wù)肪虎;
- 事務(wù)在項(xiàng)目的開(kāi)發(fā)中十分重要劣砍,涉及到數(shù)據(jù)的一致性,和完整性問(wèn)題扇救,不容馬虎刑枝;