天天看點

Spring架構 2了解一下“代理模式”Aspect Oriented Programming-AOP使用Spring實作AOP整合MyBatisMybatis-Spring 學習聲明式事務

Spring架構學習 2

  • 了解一下“代理模式”
    • 代理模式的分類
      • 靜态代理
      • 動态代理
  • Aspect Oriented Programming-AOP
    • 什麼是AOP?
    • Aop在Spring中的作用
  • 使用Spring實作AOP
    • 方式一:使用Spring API實作
    • 方式二:自定義類來實作AOP
    • 方式三:使用注解實作AOP
  • 整合MyBatis
  • Mybatis-Spring 學習
    • 整合方式 1
    • 整合方式 2
  • 聲明式事務
    • Spring中的事務管理

了解一下“代理模式”

為什麼要學習代理模式,因為Spring AOP的底層機制就是動态代理!

代理模式的分類

代理模式就是建立一個代理對象,由代理對象來接管原對象的引用。

  • 靜态代理
  • 動态代理
Spring架構 2了解一下“代理模式”Aspect Oriented Programming-AOP使用Spring實作AOP整合MyBatisMybatis-Spring 學習聲明式事務

上圖通過“租房”的例子講了一下代理模式的核心!

詳細内容參考這篇文章!

靜态代理

靜态代理角色分析

  • 抽象角色 : 一般使用接口或者抽象類來實作。
  • 真實角色 : 被代理的角色。
  • 代理角色 : 代理真實角色 ; 代理真實角色後 , 一般會做一些附屬的操作。
  • 客戶 : 使用代理角色來進行一些操作。

以租房為例,我們體驗一下代碼實作:

定義一個抽象接口:

//租房
public interface Rent {
    public void rent();
}
           

房東類:

//房東
public class Host {
    public void rent(){
        System.out.println("我是房東,有房子出租!");
    }
}
           

中介類:

//房屋中介(代理角色)
public class Proxy implements Rent{
    private Host host;

    public Proxy() {
    }

    public Proxy(Host host) {
        this.host = host;
    }

    //中介出租房屋(這是在幫房東做代理)
    public void rent() {
        host.rent();
        System.out.println("我是中介,我幫房東出租房子!");
        seeHouse();
        hetong();
        fare();
    }

    //中介帶你看房
    public void seeHouse(){
        System.out.println("中介帶你看房字!");  
    //簽租賃合同
    public void hetong(){
        System.out.println("中介帶你簽合同!");
    }
    //收取中介費
    public void fare(){
        System.out.println("中介收取中介費!");
    }
}
           

房客類:

//房客
public class Client {
    public static void main(String[] args) {
        //房東要出租房子
        Host host = new Host();
        //代理角色,找房屋中介,一般會有一些附屬操作
        Proxy proxy = new Proxy(host);
        //不需要直接面對房東,找中介即可
        proxy.rent();
    }
}
           

運作結果:

Spring架構 2了解一下“代理模式”Aspect Oriented Programming-AOP使用Spring實作AOP整合MyBatisMybatis-Spring 學習聲明式事務

案例二:加深了解靜态代理!!!

同學們練習完畢後,我們再來舉一個例子,鞏固大家的學習!

練習步驟:

1、建立一個抽象角色,比如咋們平時做的使用者業務,抽象起來就是增删改查!

public interface UserService {
    void add();
    void delete();
    void update();
    void query();
}
           

2、我們需要一個真實對象來完成這些增删改查操作

//真實對象
public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("增加了一個使用者!");
    }

    public void delete() {
        System.out.println("删除了一個使用者!");
    }

    public void update() {
        System.out.println("修改了一個使用者!");
    }

    public void query() {
        System.out.println("查詢了一個使用者!");
    }
}
           

3、需求來了,現在我們需要增加一個日志功能,怎麼實作!

  • 思路1 :在實作類上增加代碼 【麻煩!】
  • 思路2:使用代理來做,能夠不改變原來的業務情況下,實作此功能就是最好的了!

4、設定一個代理類來處理日志!(代理角色)

public class UserServiceProxy implements UserService{

    private UserServiceImpl userService;

    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }

    public void add() {
        log("add");
        userService.add();
    }

    public void delete() {
        log("delete");
        userService.add();
    }

    public void update() {
        log("update");
        userService.add();
    }

    public void query() {
        log("query");
        userService.add();
    }

    //日志方法
    public void log(String msg){
        System.out.println("[Debug]使用了"+msg+"方法!");
    }
}
           

5、測試通路類:

public class Client {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(userService);

        proxy.add();
        proxy.delete();
        proxy.update();
        proxy.query();
    }
}
           

運作結果:

Spring架構 2了解一下“代理模式”Aspect Oriented Programming-AOP使用Spring實作AOP整合MyBatisMybatis-Spring 學習聲明式事務

OK,到了現在代理模式大家應該都沒有什麼問題了,重點大家需要了解其中的思想;

我們在不改變原來的代碼的情況下,實作了對原有功能的增強,這是AOP中最核心的思想!!!

靜态代理的好處:

  • 可以使得我們的真實角色更加純粹,不再去關注一些公共的事情。
  • 公共的業務由代理來完成,實作了業務的分工 。
  • 公共業務發生擴充時變得更加集中和友善。

缺點 :

  • 類多了 ,多了代理類,工作量變大了 ,開發效率降低 。

我們想要靜态代理的好處,又不想要靜态代理的缺點,是以 , 就有了動态代理 !

動态代理

  • 動态代理的角色和靜态代理的一樣。
  • 動态代理的代理類是動态生成的 . 靜态代理的代理類是我們提前寫好的。
  • 動态代理分為兩類 : 一類是基于接口動态代理 , 一類是基于類的動态代理。
    • 基于接口的動态代理----JDK動态代理
    • 基于類的動态代理–cglib
    • 基于java位元組碼實作,現在用的比較多的是 javasist 來生成動态代理

我們這裡使用JDK的原生代碼來實作,其餘的道理都是一樣的!

JDK的動态代理需要了解兩個類核心 : InvocationHandler 和 Proxy。

InvocationHandler:調用處理程式

InvocationHandler是由代理執行個體的調用處理程式實作的接口。每個代理執行個體都有一個關聯的調用處理程式。 當在 代理執行個體上調用方法時,方法調用将被編碼并分派到其調用處理程式的invoke方法。

object invoke (object proxy,
				方法method, 
				object[] args)
		throws Throwable
           

處理代理執行個體上的方法調用并傳回結果。當在與之關聯的代理執行個體上調用方法時,将在調用處理程式中調用此方法。

參數解析:

  • proxy -調用該方法的代理執行個體。
  • method -所述方法對應于調用代理執行個體上的接口方法的執行個體。方法對象的聲明類将是該方法聲明的接口,它可以是代理類繼承該方法的代理接口的超級接口。
  • args -包含的方法調用傳遞代理執行個體的參數值的對象的陣列,或null如果接口方法沒有參數。原始類型的參數包含在适當的原始包裝器類的執行個體中,例如 java. lang. Integer或java.lang . Boolean。

Proxy:代理類

public class Proxy
extends Object
implements Serializable
           

Proxy提供了建立動态代理類和執行個體的靜态方法,它也是由這些方法建立的所有動态代理類的超類。

為某個接口建立代理Foo:

InvocationHandler handler = new MyInvocationHandler(...) ;
Class<?> proxyClass = Proxy.getProxyClass (Foo.class.getClassLoader(), Foo.class) ;
Foo f = (Foo) proxyClass.getConstructor (InvocationHandler.class).newInstance (handler) ;
           

或更簡單的:

Foo f = (Foo) Proxy.newProxyInstance (Foo.class.getClassLoader() , 
new Class<?>[] {Foo.class },
handler) ; 
           

該類中所有的方法:

Spring架構 2了解一下“代理模式”Aspect Oriented Programming-AOP使用Spring實作AOP整合MyBatisMybatis-Spring 學習聲明式事務

通過代碼來實際體驗:

先建立一個抽象角色:rent

//租房
public interface Rent {
    public void rent();
}
           

再建立一個真實角色:host

//房東
public class Host implements Rent{
    public void rent(){
        System.out.println("我是房東,有房子出租!");
    }
}
           

建立代理角色:ProxyInvocationHandler

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//用這個類來“動态生成”我們的代理類
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Rent rent;

    public void setRent(Rent rent){
        this.rent = rent;
    }

    //生成得到代理類,這個代碼是固定的,隻需要改動“rent”就可以
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                rent.getClass().getInterfaces(),
                this);
    }

    //處理代理執行個體,并傳回結果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //動态代理的本質就是使用反射機制實作!
        seeHouse();
        Object result = method.invoke(rent,args);
        fare();
        return result;
    }

    public void seeHouse(){
        System.out.println("中介帶着看房子!");
    }

    public void fare(){
        System.out.println("中介收取中介費!");
    }
}
           

測試代碼:

public class Client {
    public static void main(String[] args) {
        //真實角色
        Host host = new Host();

        //代理角色,現在沒有
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //通過調用程式處理角色來處理我們要調用的接口對象
        pih.setRent(host);
        //這裡的proxy就是動态生成的
        Rent proxy = (Rent) pih.getProxy();
        proxy.rent();
    }
}
           

運作結果:

Spring架構 2了解一下“代理模式”Aspect Oriented Programming-AOP使用Spring實作AOP整合MyBatisMybatis-Spring 學習聲明式事務

我們來使用動态代理實作代理我們前面寫的UserService!

我們也可以編寫一個通用的動态代理實作的類!所有的代理對象設定為Object即可!把他當做一個工具類使用即可~~

//用這個類來“動态生成”我們的代理類
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Object target;

    public void setTarget(Object target){
        this.target = target;
    }

    //生成得到代理類,這個代碼是固定的,隻需要改動“rent”就可以
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }

    //處理代理執行個體,并傳回結果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //動态代理的本質就是使用反射機制實作!
        log(method.getName());
        Object result = method.invoke(target,args);
        return result;
    }

    public void log(String msg){
        System.out.println("執行了" + msg + "方法!");
    }
}
           

測試代碼:

public class Client {
    public static void main(String[] args) {
        //真實角色
        UserServiceImpl userService = new UserServiceImpl();
        //代理角色
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //設定要代理的對象
        pih.setTarget(userService);
        //動态生成代理類
        UserService proxy = (UserService)pih.getProxy();

        proxy.add();
        proxy.delete();
        proxy.update();
        proxy.query();
    }
}
           

運作結果:

Spring架構 2了解一下“代理模式”Aspect Oriented Programming-AOP使用Spring實作AOP整合MyBatisMybatis-Spring 學習聲明式事務

動态代理的好處:

  • 靜态代理有的它都有,靜态代理沒有的,它也有!
    • 可以使得我們的真實角色更加純粹 . 不再去關注一些公共的事情 .
    • 公共的業務由代理來完成 . 實作了業務的分工 ,
    • 公共業務發生擴充時變得更加集中和友善 .
  • 一個動态代理類代理的是一個接口 , 一般就對應的某一類業務
  • 一個動态代理類可以代理多個類,隻要是實作了同一個接口就行

Aspect Oriented Programming-AOP

什麼是AOP?

AOP(Aspect Oriented Programming)意為:面向切面程式設計,通過預編譯方式和運作期動态代理實作程式功能的統一維護的一種技術。AOP是OOP的延續,是軟體開發中的一個熱點,也是Spring架構中的一個重要内容,是函數式程式設計的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,進而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。

Spring架構 2了解一下“代理模式”Aspect Oriented Programming-AOP使用Spring實作AOP整合MyBatisMybatis-Spring 學習聲明式事務

Aop在Spring中的作用

  • 提供聲明式事務;
  • 允許使用者自定義切面。

以下名詞需要了解下:

  • 橫切關注點 :跨越應用程式多個子產品的方法或功能。即是,與我們業務邏輯無關的,但是我們需要關注的部分,就是橫切關注點。如日志 , 安全 , 緩存 , 事務等等 …
  • 切面(ASPECT):橫切關注點被子產品化的特殊對象。即,它是一個類。
  • 通知(Advice):切面必須要完成的工作。即,它是類中的一個方法。
  • 目标(Target):被通知對象。
  • 代理(Proxy):向目标對象應用通知之後建立的對象。
  • 切入點(PointCut):切面通知 執行的 “地點”的定義。
  • 連接配接點(JointPoint):與切入點比對的執行點。
    Spring架構 2了解一下“代理模式”Aspect Oriented Programming-AOP使用Spring實作AOP整合MyBatisMybatis-Spring 學習聲明式事務
    SpringAOP中,通過Advice定義橫切邏輯,Spring中支援5種類型的Advice:
    Spring架構 2了解一下“代理模式”Aspect Oriented Programming-AOP使用Spring實作AOP整合MyBatisMybatis-Spring 學習聲明式事務
    即 Aop 在 不改變原有代碼的情況下 , 去增加新的功能 。

使用Spring實作AOP

使用之前,需要在配置檔案中導入一個依賴包!!!

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.4</version>
</dependency>
           

方式一:使用Spring API實作

首先編寫我們的業務接口和實作類:

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void select();
}
           
public class UserServiceImpl implements UserService{
    public void add() {
        System.out.println("增加一個使用者!");
    }

    public void delete() {
        System.out.println("删除一個使用者!");
    }

    public void update() {
        System.out.println("更新一個使用者!");
    }

    public void select() {
        System.out.println("查詢一個使用者!");
    }
}
           

然後去寫我們的增強類 , 我們編寫兩個 , 一個前置增強 一個後置增強:

public class BeforeLog implements MethodBeforeAdvice {
    /*
    method:要執行的目标對象的方法
    args:參數
    target:目标對象
     */
    public void before(Method method, Object[] objects, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"
                +method.getName()+"被執行了!");
    }
}
           
public class AfterLog implements AfterReturningAdvice {
    //returnValue:傳回值
    public void afterReturning(Object returnValue, Method method, Object[] objects, Object target) throws Throwable {
        System.out.println("執行了"+method.getName()+"傳回結果為:"+returnValue);
    }
}
           

最後去spring的檔案中注冊 , 并實作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
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注冊bean-->
    <bean id="userService" class="com.kuang.service.UserServiceImpl"/>
    <bean id="afterLog" class="com.kuang.log.AfterLog"/>
    <bean id="beforeLog" class="com.kuang.log.BeforeLog"/>

    <!--方式一:使用原生Spring API接口-->
    <!--配置aop:先在頭部導入aop的限制-->
    <aop:config>
        <!--切入點 expression:表達式 expression:比對要執行的方法-->
        <aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
        <!--執行環繞; advice-ref執行方法 . pointcut-ref切入點-->
        <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>
</beans>
           

測試代碼:

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //動态代理代理的是接口
        UserService userService = (UserService)context.getBean("userService");
        userService.add();
        userService.delete();
        userService.update();
        userService.select();
    }
}
           

運作結果:

Spring架構 2了解一下“代理模式”Aspect Oriented Programming-AOP使用Spring實作AOP整合MyBatisMybatis-Spring 學習聲明式事務

方式二:自定義類來實作AOP

目标業務類不變依舊是userServiceImpl,首先, 寫我們自己的一個切入類:

public class DiyPointCut {
    public void before(){
        System.out.println("==========方法執行==========");
    }

    public void after(){
        System.out.println("==========方法執後==========");
    }
}
           

去Spring中配置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"
       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
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注冊bean-->
    <bean id="userService" class="com.kuang.service.UserServiceImpl"/>
    <bean id="afterLog" class="com.kuang.log.AfterLog"/>
    <bean id="beforeLog" class="com.kuang.log.BeforeLog"/>

    <!--方式二:自定義類-->
    <!--注冊bean-->
    <bean id="diy" class="com.kuang.diy.DiyPointCut"/>
    <!--aop的配置-->
    <aop:config>
        <!--第二種方式:使用AOP的标簽實作-->
        <aop:aspect ref="diy">
            <!--切入點-->
            <aop:pointcut id="point" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
            <!--通知-->
            <aop:before pointcut-ref="point" method="before"/>
            <aop:after pointcut-ref="point" method="after"/>
        </aop:aspect>
    </aop:config>

</beans>
           

測試代碼:

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //動态代理代理的是接口
        UserService userService = (UserService)context.getBean("userService");
        userService.add();
        userService.delete();
        userService.update();
        userService.select();
    }
}
           

運作結果:

Spring架構 2了解一下“代理模式”Aspect Oriented Programming-AOP使用Spring實作AOP整合MyBatisMybatis-Spring 學習聲明式事務

方式三:使用注解實作AOP

首先,編寫一個注解實作的增強類:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect //标注這個類是一個切面
public class AnnotationPointCut {

    @Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("==========方法執行前==========");
    }

    @After("execution(* com.kuang.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("==========方法執行後==========");
    }

    @Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
    //在環繞增強中,我們可以給定一個參數,代表我們要擷取處理切入的點
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("環繞前!");
        System.out.println("簽名:"+jp.getSignature());
        //執行方法
        Object proceed = jp.proceed();
        System.out.println("環繞後!");
        System.out.println(proceed);
    }
}

           

在Spring配置檔案中,注冊bean,并增加支援注解的配置:

<?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
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注冊bean-->
    <bean id="userService" class="com.kuang.service.UserServiceImpl"/>
    <!--方式三-->
    <bean id="annotationPointCut" class="com.kuang.diy.AnnotationPointCut"/>
    <!--開啟注解支援-->
    <aop:aspectj-autoproxy proxy-target-class="true"/>

</beans>
           

測試代碼:

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //動态代理代理的是接口
        UserService userService = (UserService)context.getBean("userService");
        userService.add();
    }
}
           

運作結果:

Spring架構 2了解一下“代理模式”Aspect Oriented Programming-AOP使用Spring實作AOP整合MyBatisMybatis-Spring 學習聲明式事務

整合MyBatis

首先,導入jar包:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-study</artifactId>
        <groupId>com.kuang</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>spring-10-mybatis</artifactId>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <!--Spring操作資料庫需要導入一個spring-jdbc的包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <!--aspectj AOP 織入器-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
        <!--Spring-mybatis 整合包-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>
    </dependencies>
    <!--配置maven靜态資源過濾問題-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
    </build>
</project>
           

編寫配置檔案:

<?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.kuang.pojo"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="1314520sky"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="com/kuang/mapper/UserMapper.xml"/>
    </mappers>

</configuration>
           

測試代碼:

public class MyTest {
    @Test
    public void test() throws IOException {
        String resources = "mybatis-config.xml";
        InputStream in = Resources.getResourceAsStream(resources);
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
        SqlSession sqlSession = sessionFactory.openSession(true);

        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = userMapper.selectUser();
        for (User user : userList) {
            System.out.println(user);
        }

        sqlSession.close();
    }
}
           

運作結果:

Spring架構 2了解一下“代理模式”Aspect Oriented Programming-AOP使用Spring實作AOP整合MyBatisMybatis-Spring 學習聲明式事務

Mybatis-Spring 學習

什麼是 MyBatis-Spring?

MyBatis-Spring 會幫助你将 MyBatis 代碼無縫地整合到 Spring 中。

我們先需要掌握這個前置知識!!!

整合方式 1

主要步驟:

  • 引入Spring配置檔案applicationContext.xml
  • 編寫資料源配置,替換mybatis的資料源
  • 配置sqlSessionFactory,,關聯mybatis
  • 注冊sqlSessionTemplate,關聯sqlSessionFactory
  • 增加接口實作類,私有化sqlSessionTemplate
  • 注冊bean實作
  • 測試代碼

配置檔案:

<?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 的資料源 替換 Mybatis的資料源
    可以使用Spring提供的JDBC:org.springframework.jdbc.datasource
    -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="1314520sky"/>
    </bean>

    <!--salSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--綁定mybatis配置檔案-->
        <property name="configLocation" value="classpath:                mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/kuang/mapper/*.xml"/>
    </bean>

    <!--SqlSessionTemplate:就是我們使用的SqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--隻能使用構造器注入sqlSessionFactory,因為它沒有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

    <bean id="userMapper" class="com.kuang.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>
</beans>
           

接口實作類:

public class UserMapperImpl implements UserMapper {
    //我們的所有操作,原來都是使用的SqlSession來執行
    //現在都是用SqlSessionTemplate來完成
    private SqlSessionTemplate sqlSession;
    public void setSqlSession(SqlSessionTemplate sqlSession){
        this.sqlSession = sqlSession;
    }
    public List<User> selectUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}
           

測試代碼:

public class MyTest {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
        UserMapper userMapper = context.getBean("userMapper",UserMapper.class);
        for (User user : userMapper.selectUser()){
            System.out.println(user);
        }
    }
}
           

整合方式 2

Spring架構 2了解一下“代理模式”Aspect Oriented Programming-AOP使用Spring實作AOP整合MyBatisMybatis-Spring 學習聲明式事務

聲明式事務

事務在項目開發過程非常重要,涉及到資料的一緻性的問題,不容馬虎!事務管理是企業級應用程式開發中必備技術,用來確定資料的完整性和一緻性。

事務就是把一系列的動作當成一個獨立的工作單元,這些動作要麼全部完成,要麼全部不起作用。

事務四個屬性ACID:

  • 原子性(atomicity):事務是原子性操作,由-系列動作組成,事務的原子性確定動作要麼全部完成,要麼完全不起作用。
  • 一緻性 (consistency):一旦所有事務動作完成,事務就要被送出。資料和資源處于一種滿足業務規則的一緻性狀态中。
  • 隔離性(isolation):可能多個事務會同時處理相同的資料,是以每個事務都應該與其他事務隔離開來,防止資料損壞。
  • 持久性(durability):事務一旦完成,無論系統發生什麼錯誤,結果都不會受到影響。通常情況下,事務的結果被寫到持久化存儲器中。

案例1:

建立一個User類:

@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
    private int id;
    private String name;
    private String pwd;
}
           

建立UserMapper接口:

public interface UserMapper {
    public List<User> selectUser();
    public int addUser(User user);
    public int deleteUser(int id);
}
           

配置Mapper檔案,故意把delete寫錯,進行測試:

<?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">
<!--configuration核心配置檔案-->
<mapper namespace="com.kuang.mapper.UserMapper">
    <select id="selectUser" resultType="com.kuang.pojo.User">
        select * from mybatis.user
    </select>

    <select id="addUser" resultType="com.kuang.pojo.User">
        insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd});
    </select>

    <select id="deleteUser" resultType="int">
        deletesssss from mybatis.user where id = #{id}
        <!--這裡的語句是錯誤的,一般來說我們的語句就無法執行成功!-->
    </select>
</mapper>
           

編寫接口的實作類,在實作類中去操作一波~:

public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
    public List<User> selectUser() {

        User user = new User(7,"Seven","7777777");

        UserMapper mapper = getSqlSession().getMapper(UserMapper.class);

        mapper.addUser(user);
        mapper.deleteUser(5);

        return mapper.selectUser();
    }

    public int addUser(User user) {
        return getSqlSession().getMapper(UserMapper.class).addUser(user);
    }

    public int deleteUser(int id) {
        return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
    }
}
           

運作結果:

Spring架構 2了解一下“代理模式”Aspect Oriented Programming-AOP使用Spring實作AOP整合MyBatisMybatis-Spring 學習聲明式事務

我們發現,運作報錯: sq|異常, 這很正常,因為delete寫錯了! 但是,資料庫結果:插入成功!

這就是因為,沒有進行事務的管理,我們想讓他們都成功才成功,有一個失敗, 就都失敗,我們就應該需要進行事務管理!

Spring中的事務管理

以前我們都需要自己手動管理事務,十分麻煩!但是Spring給我們提供了事務管理,我們隻需要配置即可。

Spring在不同的事務管理API之上定義了一個抽象層,使得開發人員不必了解底層的事務管理API就可以使用Spring的事務管理機制。Spring支援程式設計 式事務管理和聲明式的事務管理。

程式設計式事務管理

  • 将事務管理代碼嵌到業務方法中來控制事務的送出和復原
  • 缺點:必須在每個事務操作業務邏輯中包含額外的事務管理代碼

聲明式事務管理

  • 一般情況下比程式設計式事務好用。
  • 将事務管理代碼從業務方法中分離出來,以以明的方式來實作事務管理。
  • 将事務管理作為橫切關注點,通過aop方法子產品化。Spring中通過Spring AOP架構支援

    聲明式事務管理。

步驟:

  • 使用Spring管理事務,注意頭檔案的限制導入:tx
  • 事務管理器,無論使用Spring的哪種事務管理政策,事務管理器都是必須的。(就是Spring的核心事務管理抽象,封裝了一組獨立于技術的方法)
  • 配置好事務管理器後,我們需要去配置事務的通知。Spring的事務傳播特性如下圖所示:
    Spring架構 2了解一下“代理模式”Aspect Oriented Programming-AOP使用Spring實作AOP整合MyBatisMybatis-Spring 學習聲明式事務
  • 配置AOP

測試結果:

Spring架構 2了解一下“代理模式”Aspect Oriented Programming-AOP使用Spring實作AOP整合MyBatisMybatis-Spring 學習聲明式事務

為什麼需要配置事務?

●如果不配置,就需要我們手動送出控制事務;

●事務在項目開發過程非常重要,涉及到資料的一緻性的問題,不容馬虎!