Spring 5 基礎(chǔ)

一、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 整合缀蹄;
<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)度功能瓦戚;
  • 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茶凳、HibernateiBatis SQL Map嫂拴;
    • 遵從 Spring 的通用事務(wù)和 DAO 異常層次結(jié)構(gòu);
  • 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ù)灶伊,其中包括 JSPVelocity寒跳、Tiles聘萨、iTextPOI

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)方式:

  1. 創(chuàng)建 UserDao 接口:
public interface UserDao {
    public void getUser();
}
  1. 創(chuàng)建 UserDaoImpl 實(shí)現(xiàn)類(lèi) :
public class UserDaoImpl implements UserDao{
    @Override
    public void getUser() {
        System.out.println("獲取用戶數(shù)據(jù)");
    }
}
  1. 創(chuàng)建 UserService 業(yè)務(wù)接口:
public interface UserService {
    public void getUser();
}
  1. 創(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();
    }    
}    
  1. 測(cè)試:
public class MyTest {
    @Test
    public void testUser() {
        UserService service = new UserServiceImpl();
        service.getUser();
    }
}

以前增加需求的實(shí)現(xiàn)方式:

  1. 增加 Userdao 的實(shí)現(xiàn)類(lèi):
public class UserDaoOracleImpl implements UserDao{
    @Override
    public void getUser() {
        System.out.println("Oracle數(shù)據(jù)");
    }
}
  1. 在 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)建方式

  1. 下標(biāo)賦值:
<!--1:下標(biāo)賦值突委!-->
<bean id="user2" class="com.study.spring.pojo.User2">
    <constructor-arg index="0" value="有參測(cè)試"/>
</bean>
  1. 類(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>
  1. 參數(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 拓展方式注入

  • 使用 cp 命名空間,進(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ò))敷硅;
<!--頭文件約束-->
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
}
  • requestsession绞蹦、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:字段可以為 null

    public 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è)試:
    1. 創(chuàng)建真實(shí)角色善涨;
    2. 創(chuàng)建動(dòng)態(tài)代理創(chuàng)建的實(shí)體主到;
    3. 設(shè)置;
    4. 使用躯概;
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&amp;characterEncoding=utf8&amp;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);

整合實(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&amp;characterEncoding=utf8&amp;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ù) 管理乒验;

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)題扇救,不容馬虎刑枝;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市迅腔,隨后出現(xiàn)的幾起案子装畅,更是在濱河造成了極大的恐慌,老刑警劉巖沧烈,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掠兄,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蚂夕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)迅诬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人婿牍,你說(shuō)我怎么就攤上這事侈贷。” “怎么了等脂?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵俏蛮,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我上遥,道長(zhǎng)搏屑,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任粉楚,我火速辦了婚禮辣恋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘解幼。我一直安慰自己抑党,他們只是感情好包警,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布撵摆。 她就那樣靜靜地躺著,像睡著了一般害晦。 火紅的嫁衣襯著肌膚如雪特铝。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,071評(píng)論 1 285
  • 那天壹瘟,我揣著相機(jī)與錄音鲫剿,去河邊找鬼。 笑死稻轨,一個(gè)胖子當(dāng)著我的面吹牛灵莲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播殴俱,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了柜候?” 一聲冷哼從身側(cè)響起否过,我...
    開(kāi)封第一講書(shū)人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎李丰,沒(méi)想到半個(gè)月后苦锨,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年舟舒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拉庶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡秃励,死狀恐怖砍的,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情莺治,我是刑警寧澤廓鞠,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站谣旁,受9級(jí)特大地震影響床佳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜榄审,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一砌们、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧搁进,春花似錦浪感、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至莱革,卻和暖如春峻堰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盅视。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工捐名, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人闹击。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓镶蹋,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親赏半。 傳聞我的和親對(duì)象是個(gè)殘疾皇子贺归,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容