1. 程式人生 > 實用技巧 >Spring 學記 (第三章)

Spring 學記 (第三章)

寫在前面:分享技術,共同進步,有不足請見諒,相關意見可評論告知 ~

程式設計路漫之遠兮,運架構體之帷幄;
勸君專注案前事,亦是杯酒敬蒼生;

目錄

代理模式

前序:AOP的底層機制就是動態代理

代理模式分類

①靜態代理 ②動態代理

靜態代理

(1)抽象角色 : 一般使用介面或者抽象類來實現

(2)真實角色 : 被代理的角色

(3)代理角色 : 代理真實角色 ; 代理真實角色後 , 一般會做一些附屬的操作 .

(4)客戶 : 使用代理角色來進行一些操作 .

平常方式實現:

靜態代理實現:

Rent . java 即抽象角色

//抽象角色:租房
public interface Rent {
   public void rent();
}

Host . java 即真實角色

//真實角色: 房東,房東要出租房子
public class Host implements Rent{
   public void rent() {
       System.out.println("房屋出租");
  }
}

Proxy . java 即代理角色

//代理角色:中介
public class Proxy implements Rent {

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

   //租房
   public void rent(){
       seeHouse();
       host.rent();
       fare();
  }
   //看房
   public void seeHouse(){
       System.out.println("帶房客看房");
  }
   //收中介費
   public void fare(){
       System.out.println("收中介費");
  }
}

Client . java 即客戶

//客戶類,一般客戶都會去找代理!
public class Client {
   public static void main(String[] args) {
       //房東要租房
       Host host = new Host();
       //中介幫助房東
       Proxy proxy = new Proxy(host);

       //你去找中介!
       proxy.rent();
  }
}

實際程式碼效果:

靜態代理的好處與缺點:

(1)可以使得我們的真實角色更加純粹 . 不再去關注一些公共的事情 .

(2)公共的業務由代理來完成 . 實現了業務的分工 ,

(3)公共業務發生擴充套件時變得更加集中和方便 .

缺點 :

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

---> 解決上述問題: 動態代理

代理模式的實際運用

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、增加一個日誌功能,怎麼實現!

原來方法 :在實現類上增加程式碼 【麻煩!】

現在方法:使用代理,具體如下
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.delete();
  }

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

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

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

}

5、測試訪問類:

public class Client {
   public static void main(String[] args) {
       //真實業務
       UserServiceImpl userService = new UserServiceImpl();
       //代理類
       UserServiceProxy proxy = new UserServiceProxy();
       //使用代理類實現日誌功能!
       proxy.setUserService(userService);

       proxy.add();
  }
}

總結:在不破壞原有程式碼的基礎上,利用”織入“的模式實現程式碼的動態配置

AOP:縱向開發,橫向開發(圖示)

動態代理

與靜態代理的異同


動態代理的角色和靜態代理一樣
(1)抽象角色 : 一般使用介面或者抽象類來實現

(2)真實角色 : 被代理的角色

(3)代理角色 : 代理真實角色 ; 代理真實角色後 , 一般會做一些附屬的操作 .

(4)客戶 : 使用代理角色來進行一些操作 .


(1)動態代理的代理類是動態生成的 . 靜態代理的代理類是我們提前寫好的

(2)動態代理分為兩類 : 一類是基於介面動態代理(JDK動態代理) , 一類是基於的動態代理(cglib)

JDK動態代理

其他需瞭解:javasist 動態代理 (基於位元組碼)

https://baike.baidu.com/item/JAVAssist/6039181

JDK的動態代理需要了解兩個類

核心 : InvocationHandler 和 Proxy , 參考JDK幫助文件

【InvocationHandler:呼叫處理程式】

Object invoke(Object proxy, 方法 method, Object[] args);

上述方法引數分析:
proxy - 呼叫該方法的代理例項
method -所述方法對應於呼叫代理例項上的介面方法的例項。方法物件的宣告類將是該方法宣告的介面,它可以是代理類繼承該方法的代理介面的超級介面。
args -包含的方法呼叫傳遞代理例項的引數值的物件的陣列,或null如果介面方法沒有引數。原始型別的引數包含在適當的原始包裝器類的例項中,例如java.lang.Integer或java.lang.Boolean 。

Proxy 代理方式

//生成代理類
public Object getProxy(){
   return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                                 rent.getClass().getInterfaces(),this);
}

程式碼實現

Rent . java 即抽象角色

//抽象角色:租房
public interface Rent {
   public void rent();
}

Host . java 即真實角色

//真實角色: 房東,房東要出租房子
public class Host implements Rent{
   public void rent() {
       System.out.println("房屋出租");
  }
}

ProxyInvocationHandler. java 即代理角色

public class ProxyInvocationHandler implements InvocationHandler {
   private Rent rent;

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

   //生成代理類,重點是第二個引數,獲取要代理的抽象角色!之前都是一個角色,現在可以代理一類角色
   public Object getProxy(){
       return Proxy.newProxyInstance(this.getClass().getClassLoader(),
               rent.getClass().getInterfaces(),this);
  }
     // proxy : 代理類 method : 代理類的呼叫處理程式的方法物件.
   // 處理代理例項上的方法呼叫並返回結果
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       seeHouse();
       //核心:本質利用反射實現!
       Object result = method.invoke(rent, args);
       fare();
       return result;
  }

Client . java

//租客
public class Client {

   public static void main(String[] args) {
       //真實角色
       Host host = new Host();
       //代理例項的呼叫處理程式
       ProxyInvocationHandler pih = new ProxyInvocationHandler();
       pih.setRent(host); //將真實角色放置進去!
       Rent proxy = (Rent)pih.getProxy(); //動態生成對應的代理類!
       proxy.rent();
  }

}

總結:一個動態代理 , 一般代理某一類業務 , 一個動態代理可以代理多個類,代理的是介面!、

程式碼優化
使用動態代理實現代理後面的UserService!

編寫一個通用的動態代理實現的類!所有的代理物件設定為Object即可!

public class ProxyInvocationHandler implements InvocationHandler {
   private Object target;

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

   //生成代理類
   public Object getProxy(){
       return Proxy.newProxyInstance(this.getClass().getClassLoader(),
               target.getClass().getInterfaces(),this);
  }

   // proxy : 代理類
   // method : 代理類的呼叫處理程式的方法物件.
   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 methodName){
       System.out.println("執行了"+methodName+"方法");
  }

}

測試!

public class Test {
   public static void main(String[] args) {
       //真實物件
       UserServiceImpl userService = new UserServiceImpl();
       //代理物件的呼叫處理程式
       ProxyInvocationHandler pih = new ProxyInvocationHandler();
       pih.setTarget(userService); //設定要代理的物件
       UserService proxy = (UserService)pih.getProxy(); //動態生成代理類!
       proxy.delete();
  }
}

實體類與介面:

實際體現:

動態代理優點

(1)可以使得我們的真實角色更加純粹 . 不再去關注一些公共的事情 .

(2)公共的業務由代理來完成 . 實現了業務的分工 ,

(3)公共業務發生擴充套件時變得更加集中和方便 .

(4)一個動態代理 , 一般代理某一類業務

(5)一個動態代理可以代理多個類,代理的是介面!

AOP技術

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

AOP 在Spring中的作用

提供宣告式事務;允許使用者自定義切面

Before
After
AfterRunning
AfterThorwing
Around

重要名詞

橫切關注點:跨越應用程式多個模組的方法或功能。即是,與我們業務邏輯無關的,但是我們需要關注的部分,就是橫切關注點。如日誌 , 安全 , 快取 , 事務等等 ....

切面(ASPECT):橫切關注點 被模組化 的特殊物件。即,它是一個類。

通知(Advice):切面必須要完成的工作。即,它是類中的一個方法。

目標(Target):被通知物件。

代理(Proxy):向目標物件應用通知之後建立的物件。

切入點(PointCut):切面通知 執行的 “地點”的定義。

連線點(JointPoint):與切入點匹配的執行點。

SpringAOP中,通過Advice定義橫切邏輯,Spring中支援5種類型的Advice:

使用Spring實現AOP

法一:通過 Spring API 實現

業務介面

public interface UserService {

   public void add();

   public void delete();

   public void update();

   public void search();

}

實現類

public class UserServiceImpl implements UserService{

   @Override
   public void add() {
       System.out.println("增加使用者");
  }

   @Override
   public void delete() {
       System.out.println("刪除使用者");
  }

   @Override
   public void update() {
       System.out.println("更新使用者");
  }

   @Override
   public void search() {
       System.out.println("查詢使用者");
  }
}

編寫兩個增強類 , , 一個前置增強 一個後置增強

public class Log implements MethodBeforeAdvice {

   //method : 要執行的目標物件的方法
   //objects : 被呼叫的方法的引數
   //Object : 目標物件
   @Override
   public void before(Method method, Object[] objects, Object o) throws Throwable {
       System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被執行了");
  }
}
public class AfterLog implements AfterReturningAdvice {
   //returnValue 返回值
   //method被呼叫的方法
   //args 被呼叫的方法的物件的引數
   //target 被呼叫的目標物件
   @Override
   public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
       System.out.println("執行了" + target.getClass().getName()
       +"的"+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.lzh.service.UserServiceImpl"/>
    <bean id="log" class="com.lzh.log.Log"/>
    <bean id="afterLog" class="com.lzh.log.AfterLog"/>

    <!--aop的配置-->
    <aop:config>
        <!--切入點 expression:表示式匹配要執行的方法-->
        <aop:pointcut id="pointcut" expression="execution(* com.lzh.service.UserServiceImpl.*(..))"/>
        <!--執行環繞; advice-ref執行方法 . pointcut-ref切入點-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>

</beans>

測試類

public class MyTest {
   @Test
   public void test(){
       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
       UserService userService = (UserService) context.getBean("userService");
       userService.search();
  }
}

法二、通過自定義類來實現Aop

目標業務類不變為userServiceImpl。java

第一步 : 寫我們自己的一個切入類

public class DiyPointcut {

   public void before(){
       System.out.println("---------方法執行前---------");
  }
   public void after(){
       System.out.println("---------方法執行後---------");
  }
   
}

去spring中配置

    <!--第二種方式自定義實現-->
    <!--註冊bean-->
    <bean id="diy" class="com.lzh.config.DiyPointcut"/>

    <!--aop的配置-->
    <aop:config>
        <!--第二種方式:使用AOP的標籤實現-->
        <aop:aspect ref="diy">
            <aop:pointcut id="diyPonitcut" expression="execution(* com.lzh.service.UserServiceImpl.*(..))"/>
            <aop:before pointcut-ref="diyPonitcut" method="before"/>
            <aop:after pointcut-ref="diyPonitcut" method="after"/>
        </aop:aspect>
    </aop:config>
</beans>

測試:

public class MyTest {
   @Test
   public void test(){
       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
       UserService userService = (UserService) context.getBean("userService");
       userService.add();
  }
}

法三、使用註解實現

1、第一步:編寫一個註解實現的增強類

package com.kuang.config;

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());


       //執行目標方法proceed
       Object proceed = jp.proceed();
       System.out.println("環繞後");
       System.out.println(proceed);
  }
}

第二步:在Spring配置檔案中,註冊bean,並增加支援註解的配置

  <!--第三種方式:註解實現-->
    <bean id="annotationPointcut" class="com.lzh.config.AnnotationPointCut"/>
<!--    開啟註解支援  JDK(預設 proxy-target-class="false" cglib( proxy-target-class="true"))-->
    <aop:aspectj-autoproxy proxy-target-class="false"/>

注意:這裡沒有執行方法(輸出結果沒有顯示前兩個方法)

注意:在配置檔案中還有註冊bean

  <!--註冊bean-->
    <bean id="userService" class="com.lzh.service.UserServiceImpl"/>
    <bean id="log" class="com.lzh.log.Log"/>
    <bean id="afterLog" class="com.lzh.log.AfterLog"/>

後續:新增執行方法

詳解
通過aop名稱空間的<aop:aspectj-autoproxy />宣告自動為spring容器中那些配置@aspectJ切面的bean建立代理,織入切面。spring 在內部依舊採用AnnotationAwareAspectJAutoProxyCreator進行自動代理的建立工作,但具體實現的細節已經被<aop:aspectj-autoproxy />隱藏起來了

<aop:aspectj-autoproxy />有一個proxy-target-class屬性,預設為false,表示使用jdk動態代理織入增強,當配為<aop:aspectj-autoproxy poxy-target-class="true"/>時,表示使用CGLib動態代理技術織入增強。不過即使proxy-target-class設定為false,如果目標類沒有宣告介面,則spring將自動使用CGLib動態代理。

總述:Spring的Aop就是將公共的業務 (日誌 , 安全等) 和領域業務結合起來 , 當執行領域業務時 , 將會把公共業務加進來 . 實現公共業務的重複利用 . 領域業務更純粹 , 是我們專注領域業務 , 其本質還是動態代理 .

整合Mybatis 技術

一、匯入相關jar包

junit

<dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.12</version>
</dependency>

mybatis

<dependency>
   <groupId>org.mybatis</groupId>
   <artifactId>mybatis</artifactId>
   <version>3.5.2</version>
</dependency>

mysql-connector-java

<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>5.1.47</version>
</dependency>

spring相關

<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>5.1.10.RELEASE</version>
</dependency>
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-jdbc</artifactId>
   <version>5.1.10.RELEASE</version>
</dependency>

aspectJ AOP 織入器

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

mybatis-spring整合包 【重點】

<dependency>
   <groupId>org.mybatis</groupId>
   <artifactId>mybatis-spring</artifactId>
   <version>2.0.2</version>
</dependency>

配置Maven靜態資源過濾問題

<build>
   <resources>
       <resource>
           <directory>src/main/java</directory>
           <includes>
               <include>**/*.properties</include>
               <include>**/*.xml</include>
           </includes>
           <filtering>true</filtering>
       </resource>
   </resources>
</build>

lombok

   <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.2</version>
        <scope>provided</scope>

    </dependency>

二、編寫Mybatis 具體內容

1、編寫實體類
2、編寫核心配置檔案
3、編寫介面
4、編寫Mapper.xml
5、測試T'lo'm'bo'k
S

Mybatis-Spring 技術

作用:MyBatis-Spring 將 MyBatis 程式碼無縫地整合到 Spring 中。

mybatis-spring包中的一些重要類;

http://www.mybatis.org/spring/zh/index.html

MyBatis-Spring 需要以下版本:

Mybatis-Spring Mybatis Spring Spring Batch Java
2.0 3.5+ 5.0+ 4.0+ Java 8+
1.3 3.4+ 3.2.2+ 2.1+ Java 6+

使用 Maven 作為構建工具,僅需要在 pom.xml 匯入以下程式碼:

<dependency>
   <groupId>org.mybatis</groupId>
   <artifactId>mybatis-spring</artifactId>
   <version>2.0.2</version>
</dependency>

將Mybatis整合至Spring前提:
一個 SqlSessionFactory 和至少一個數據對映器類。

在 MyBatis-Spring 中,可使用SqlSessionFactoryBean來建立 SqlSessionFactory。要配置這個工廠 bean程式碼如下:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
 <property name="dataSource" ref="dataSource" />
</bean>

注意異同比較:

(1)SqlSessionFactory需要一個 DataSource(資料來源)。
(2)MyBatis 中,是通過 SqlSessionFactoryBuilder 來建立 SqlSessionFactory 的。而在 MyBatis-Spring 中,則使用 SqlSessionFactoryBean 來建立。
(3)在 MyBatis 中,使用 SqlSessionFactory 來建立 SqlSession。一旦獲得一個 session 之後,可以使用它來執行映射了的語句,提交或回滾連線,最後,當不再需要它的時候,可以關閉 session。
(4)SqlSessionFactory唯一必要的屬性:用於 JDBC 的 DataSource。這可以是任意的 DataSource 物件,它的配置方法和其它 Spring 資料庫連線是一樣的。
(5)屬性 configLocation,它用來指定 MyBatis 的 XML 配置檔案路徑。它在需要修改 MyBatis 的基礎配置非常有用。通常,基礎配置指的是 < settings> 或 < typeAliases>元素。

注意:配置檔案並不需要是一個完整的 MyBatis 配置。確切地說,任何環境配置(<environments>),資料來源(<DataSource>)和 MyBatis 的事務管理器(<transactionManager>)都會被忽略。SqlSessionFactoryBean 會建立它自有的 MyBatis 環境配置(Environment),並按要求設定自定義環境的值。

SqlSessionTemplate 是 MyBatis-Spring 的核心。作為 SqlSession 的一個實現,這意味著可以使用它無縫代替你程式碼中已經在使用的 SqlSession。

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
 <constructor-arg index="0" ref="sqlSessionFactory" />
</bean>

bean 已經直接注入到的 DAO bean 中了。 bean 中新增一個 SqlSession 屬性,如下:

public class UserDaoImpl implements UserDao {

 private SqlSession sqlSession;

 public void setSqlSession(SqlSession sqlSession) {
   this.sqlSession = sqlSession;
}

 public User getUser(String userId) {
   return sqlSession.getMapper...;
}
}

注入 SqlSessionTemplate:

整合實現一

1、引入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
       http://www.springframework.org/schema/beans/spring-beans.xsd">

2、配置資料來源替換mybaits的資料來源

<!--配置資料來源:資料來源有非常多,可以使用第三方的,也可使使用Spring的-->
<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=utf8"/>
   <property name="username" value="root"/>
   <property name="password" value="123456"/>
</bean>

3、配置SqlSessionFactory,關聯MyBatis

<!--配置SqlSessionFactory-->
<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/dao/*.xml"/>
</bean>

4、註冊sqlSessionTemplate,關聯sqlSessionFactory;

<!--註冊sqlSessionTemplate , 關聯sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
   <!--利用構造器注入-->
   <constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>

5、增加Dao介面的實現類;私有化sqlSessionTemplate

public class UserDaoImpl implements UserMapper {

   //sqlSession不用我們自己建立了,Spring來管理
   private SqlSessionTemplate sqlSession;

   public void setSqlSession(SqlSessionTemplate sqlSession) {
       this.sqlSession = sqlSession;
  }

   public List<User> selectUser() {
       UserMapper mapper = sqlSession.getMapper(UserMapper.class);
       return mapper.selectUser();
  }
   
}

6、註冊bean實現

<bean id="userDao" class="com.kuang.dao.UserDaoImpl">
   <property name="sqlSession" ref="sqlSession"/>
</bean>

7、測試

   @Test
   public void test2(){
       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
       UserMapper mapper = (UserMapper) context.getBean("userDao");
       List<User> user = mapper.selectUser();
       System.out.println(user);
  }

現在我們的Mybatis配置檔案的狀態,被Spring整合

<?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>
   <typeAliases>
       <package name="com.kuang.pojo"/>
   </typeAliases>
</configuration>

整合實現二

支援mybatis-spring1.2.3以上版本

dao繼承Support類 , 直接利用 getSqlSession() 獲得 , 然後直接注入SqlSessionFactory . 比起方式1 , 不需要管理SqlSessionTemplate , 而且對事務的支援更加友好 . 可跟蹤原始碼檢視

1、修改上面寫的UserDaoImpl

public class UserDaoImpl extends SqlSessionDaoSupport implements UserMapper {
   public List<User> selectUser() {
       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
       return mapper.selectUser();
  }
}

2、修改bean的配置

<bean id="userDao" class="com.kuang.dao.UserDaoImpl">
   <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
3、測試

@Test

public void test2(){
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
   UserMapper mapper = (UserMapper) context.getBean("userDao");
   List<User> user = mapper.selectUser();
   System.out.println(user);
}

總結 : 整合到spring以後可以完全不要mybatis的配置檔案

Spring與事務

概述:事務管理是企業級應用程式開發中必備技術,用來確保資料的完整性和一致性。事務就是把一系列的動作當成一個獨立的工作單元,這些動作要麼全部完成,要麼全部不起作用。

事務四個屬性ACID

原子性(atomicity)事務是原子性操作,由一系列動作組成,事務的原子性確保動作要麼全部完成,要麼完全不起作用

一致性(consistency)一旦所有事務動作完成,事務就要被提交。資料和資源處於一種滿足業務規則的一致性狀態中

隔離性(isolation)可能多個事務會同時處理相同的資料,因此每個事務都應該與其他事務隔離開來,防止資料損壞

永續性(durability)事務一旦完成,無論系統發生什麼錯誤,結果都不會受到影響。通常情況下,事務的結果被寫到持久化儲存器中

在之前的案例中,我們給userDao介面新增兩個方法,刪除和增加使用者;

//新增一個使用者
int addUser(User user);

//根據id刪除使用者
int deleteUser(int id);
mapper檔案,我們故意把 deletes 寫錯,測試!

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

<delete id="deleteUser" parameterType="int">
deletes from user where id = #{id}
</delete>

編寫介面的實現類

public class UserDaoImpl extends SqlSessionDaoSupport implements UserMapper {

   //增加一些操作
   public List<User> selectUser() {
       User user = new User(4,"小明","123456");
       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
       mapper.addUser(user);
       mapper.deleteUser(4);
       return mapper.selectUser();
  }

   //新增
   public int addUser(User user) {
       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
       return mapper.addUser(user);
  }
   //刪除
   public int deleteUser(int id) {
       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
       return mapper.deleteUser(id);
  }

}

測試類

@Test
public void test2(){
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
   UserMapper mapper = (UserMapper) context.getBean("userDao");
   List<User> user = mapper.selectUser();
   System.out.println(user);
}

配置圖示:


介面圖示:

Spring中的事務管理

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

程式設計式事務管理

將事務管理程式碼嵌到業務方法中來控制事務的提交回滾

缺點:必須在每個事務操作業務邏輯中包含額外的事務管理程式碼

宣告式事務管理(常用)

將事務管理程式碼從業務方法中分離出來,以宣告的方式來實現事務管理。

將事務管理作為橫切關注點,通過aop方法模組化。Spring中通過Spring AOP框架支援宣告式事務管理。

使用Spring管理事務,注意標頭檔案的約束匯入 : tx

xmlns:tx="http://www.springframework.org/schema/tx"

http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">

事務管理器

無論使用Spring的哪種事務管理策略(程式設計式或者宣告式)事務管理器都是必須的。

就是 Spring的核心事務管理抽象,管理封裝了一組獨立於技術的方法。

JDBC事務

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <property name="dataSource" ref="dataSource" />
</bean>

配置好事務管理器後我們需要去配置事務的通知

<!--配置事務通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
   <tx:attributes>
       <!--配置哪些方法使用什麼樣的事務,配置事務的傳播特性-->
       <tx:method name="add" propagation="REQUIRED"/>
       <tx:method name="delete" propagation="REQUIRED"/>
       <tx:method name="update" propagation="REQUIRED"/>
       <tx:method name="search*" propagation="REQUIRED"/>
       <tx:method name="get" read-only="true"/>
       <tx:method name="*" propagation="REQUIRED"/>
   </tx:attributes>
</tx:advice>

spring事務傳播特性:

事務傳播行為就是多個事務方法相互呼叫時,事務如何在這些方法間傳播。spring支援7種事務傳播行為:

propagation_requierd:如果當前沒有事務,就新建一個事務,如果已存在一個事務中,加入到這個事務中,這是最常見的選擇。(預設的事務傳播行為

propagation_supports:支援當前事務,如果沒有當前事務,就以非事務方法執行。

propagation_mandatory:使用當前事務,如果沒有當前事務,就丟擲異常。

propagation_required_new:新建事務,如果當前存在事務,把當前事務掛起。

propagation_not_supported:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。

propagation_never:以非事務方式執行操作,如果當前事務存在則丟擲異常。

propagation_nested:如果當前存在事務,則在巢狀事務內執行。如果當前沒有事務,則執行與propagation_required類似的操作

配置AOP

spring-dao.xml匯入aop的標頭檔案!

<!--配置aop織入事務-->
<aop:config>
   <aop:pointcut id="txPointcut" expression="execution(* com.lzh.dao.*.*(..))"/>
   <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

進行測試

刪掉剛才插入的資料,再次測試!

@Test
public void test2(){
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
   UserMapper mapper = (UserMapper) context.getBean("userDao");
   List<User> user = mapper.selectUser();
   System.out.println(user);
}

常見異常與錯誤總結

①未有輸出結果


原因:①在測試類中關鍵語句缺失

②在代理類中核心方法缺失

②未找到方法呼叫Resource資源...報錯

未找到方法呼叫Resource.getResourceAsStream(資源)的候選人

解法:在其中新增一個class

③已宣告但未找到模組

Error:(3, 20) java: 程式包 jdk.internal.loader 不可見
(程式包 jdk.internal.loader 已在模組 java.base 中宣告, 但該模組未將它匯出到未命名模組)

④transaction-manager報紅


解決如下:忘記匯入相應檔案

基於實戰中學習,學習快樂中成長
.
時間會回答成長,成長會回答夢想