【急速入門】Spring 極速入門

高清思惟導圖已同步Git:https://github.com/SoWhat1412/xmindfile,關注公衆號sowhat1412獲取海量資源html

在這裏插入圖片描述

一、Spring概述及IOC理論推導

Spring簡介

Spring : 給軟件行業帶來了春天java

2002年,Rod Jahnson首次推出了Spring框架雛形interface21框架。2004年3月24日,Spring框架以interface21框架爲基礎,通過從新設計,發佈了1.0正式版。很難想象Rod Johnson的學歷 , 他是悉尼大學的博士,然而他的專業不是計算機,而是音樂學。mysql

Spring理念 : 使現有技術更加實用 . 自己就是一個大雜燴 , 整合現有的框架技術git

官網 : http://spring.io/程序員

官方下載地址 : https://repo.spring.io/libs-release-local/org/springframework/spring/github

GitHub : https://github.com/spring-projectsweb

優勢

  • 一、Spring是一個開源免費的框架 , 容器 .spring

  • 二、Spring是一個輕量級的框架 , 非侵入式的 .sql

  • 三、控制反轉 IoC , 面向切面 Aop數據庫

  • 四、對事物的支持 , 對框架的支持

  • 五、…

一句話歸納:Spring是一個輕量級、非***式的控制反轉(IoC)和麪向切面(AOP)的容器(框架)

組成

在這裏插入圖片描述
Spring 框架是一個分層架構,由 7 個定義良好的模塊組成。Spring 模塊構建在覈心容器之上,核心容器定義了建立、配置和管理 bean 的方式 。
在這裏插入圖片描述
組成 Spring 框架的每一個模塊(或組件)均可以單獨存在,或者與其餘一個或多個模塊聯合實現。每一個模塊的功能以下:

  • 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要組件是 BeanFactory,它是工廠模式的實現。BeanFactory 使用控制反轉(IOC) 模式將應用程序的配置和依賴性規範與實際的應用程序代碼分開。

  • Spring 上下文:Spring 上下文是一個配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企業服務,例如 JNDI、EJB、電子郵件、國際化、校驗和調度功能。

  • Spring AOP:經過配置管理特性,Spring AOP 模塊直接將面向切面的編程功能 , 集成到了 Spring 框架中。因此,能夠很容易地使 Spring 框架管理任何支持 AOP的對象。Spring AOP 模塊爲基於 Spring 的應用程序中的對象提供了事務管理服務。經過使用 Spring AOP,不用依賴組件,就能夠將聲明性事務管理集成到應用程序中。

  • Spring DAO:JDBC DAO 抽象層提供了有意義的異常層次結構,可用該結構來管理異常處理和不一樣數據庫供應商拋出的錯誤消息。異常層次結構簡化了錯誤處理,而且極大地下降了須要編寫的異常代碼數量(例如打開和關閉鏈接)。Spring DAO 的面向 JDBC 的異常聽從通用的 DAO 異常層次結構。

  • Spring ORM:Spring 框架插入了若干個 ORM 框架,從而提供了 ORM 的對象關係工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。全部這些都聽從 Spring 的通用事務和 DAO 異常層次結構。

  • Spring Web 模塊:Web 上下文模塊創建在應用程序上下文模塊之上,爲基於 Web 的應用程序提供了上下文。因此,Spring 框架支持與 Jakarta Struts 的集成。Web 模塊還簡化了處理多部分請求以及將請求參數綁定到域對象的工做。

  • Spring MVC 框架:MVC 框架是一個全功能的構建 Web 應用程序的 MVC 實現。經過策略接口,MVC 框架變成爲高度可配置的,MVC 容納了大量視圖技術,其中包括 JSP、Velocity、Tiles、iText 和 POI。

拓展

Spring Boot與Spring Cloud

  • Spring Boot 是 Spring 的一套快速配置腳手架,能夠基於Spring Boot 快速開發單個微服務;
  • Spring Boot專一於快速、方便集成的單個微服務個體,Spring Cloud關注全局的服務治理框架;
  • Spring Boot使用了約束優於配置的理念,不少集成方案已經幫你選擇好了,能不配置就不配置 , Spring Cloud很大的一部分是基於Spring Boot來實現,Spring Boot能夠離開Spring Cloud獨立使用開發項目,可是Spring Cloud離不開Spring Boot,屬於依賴的關係。
  • Spring Cloud是基於Spring Boot實現的;
  • SpringBoot在SpringClound中起到了承上啓下的做用,若是你要學習SpringCloud必需要學習SpringBoot。

IOC理論推導

IOC基礎:新建一個空白的maven項目

分析實現

咱們先用咱們原來的方式寫一段代碼 .
一、先寫一個UserDao接口

public interface UserDao {
   public void getUser();
}

二、再去寫Dao的實現類

public class UserDaoImpl implements UserDao {
   @Override
   public void getUser() {
       System.out.println("獲取用戶數據");
  }
}

三、而後去寫UserService的接口

public interface UserService {
   public void getUser();
}

四、最後寫Service的實現類

public class UserServiceImpl implements UserService {
   private UserDao userDao = new UserDaoImpl();
   @Override
   public void getUser() {
       userDao.getUser();
  }
}

五、測試一下

@Test
public void test(){
   UserService service = new UserServiceImpl();
   service.getUser();
}

這是咱們原來的方式 , 開始你們也都是這麼去寫的對吧 . 那咱們如今修改一下 ,把Userdao的實現類增長一個 .

public class UserDaoMySqlImpl implements UserDao {
   @Override
   public void getUser() {
       System.out.println("MySql獲取用戶數據");
  }
}

緊接着咱們要去使用MySql的話 , 咱們就須要去service實現類裏面修改對應的實現

public class UserServiceImpl implements UserService {
   private UserDao userDao = new UserDaoMySqlImpl(); // 此處被修改了
   @Override
   public void getUser() {
       userDao.getUser();
  }
}

在假設, 咱們再增長一個Userdao的實現類 .

public class UserDaoOracleImpl implements UserDao {
   @Override
   public void getUser() {
       System.out.println("Oracle獲取用戶數據");
  }
}

那麼咱們要使用Oracle , 又須要去service實現類裏面修改對應的實現 . 假設咱們的這種需求很是大 , 這種方式就根本不適用了, 甚至反人類對吧 , 每次變更 , 都須要修改大量代碼 . 這種設計的耦合性過高了, 牽一髮而動全身 .徹底違背了 設計模式中的開閉原則。

那咱們如何去解決呢 ? 咱們能夠在須要用到他的地方 , 不去實現它 , 而是留出一個接口 , 利用set , 咱們去代碼裏修改下 .

public class UserServiceImpl implements UserService {
   private UserDao userDao;
// 利用set實現
   public void setUserDao(UserDao userDao) {
       this.userDao = userDao;
  }
   @Override
   public void getUser() {
       userDao.getUser();
  }
}

如今去咱們的測試類裏 , 進行測試 ;

@Test
public void test(){
   UserServiceImpl service = new UserServiceImpl();
   service.setUserDao( new UserDaoMySqlImpl() );
   service.getUser();
   //那咱們如今又想用Oracle去實現呢
   service.setUserDao( new UserDaoOracleImpl() );
   service.getUser();
}

你們發現了區別沒有 ? 可能不少人說沒啥區別 . 可是它們已經發生了根本性的變化 , 不少地方都不同了 . 仔細去思考一下 , 之前全部東西都是由程序在Service層固定死去進行控制建立 , 而如今是由咱們自行控制建立對象 , 把主動權交給了調用者 . 程序不用去管怎麼建立,怎麼實現了 . 它只負責提供一個接口 .

這種思想 , 從本質上解決了問題 , 咱們程序員再也不去管理對象的建立了 , 更多的去關注業務的實現 . 耦合性大大下降 . 這也就是IOC的原型 !

在這裏插入圖片描述

IOC本質

控制反轉IoC(Inversion of Control),是一種設計思想,DI(依賴注入)是實現IoC的一種方法,也有人認爲DI只是IoC的另外一種說法。沒有IoC的程序中 , 咱們使用面向對象編程 , 對象的建立與對象間的依賴關係徹底硬編碼在程序中,對象的建立由程序本身控制,控制反轉後將對象的建立轉移給第三方,我的認爲所謂控制反轉就是:得到依賴對象的方式反轉了。

  • IoC是Spring框架的核心內容,使用多種方式完美的實現了IoC,可使用XML配置,也可使用註解,新版本的Spring也能夠零配置實現IoC。

  • Spring容器在初始化時先讀取配置文件,根據配置文件或元數據建立與組織對象存入容器中,程序使用時再從Ioc容器中取出須要的對象。

  • 採用XML方式配置Bean的時候,Bean的定義信息是和實現分離的,而採用註解的方式能夠把二者合爲一體,Bean的定義信息直接以註解的形式定義在實現類中,從而達到了零配置的目的。

  • 控制反轉是一種經過描述(XML或註解)並經過第三方去生產或獲取特定對象的方式。在Spring中實現控制反轉的是IoC容器,其實現方法是依賴注入(Dependency Injection,DI)

  • 明白IOC的思想,是理解Spring的核心技巧

  • 經過反射跟XML解析手動實現IOC

二、快速上手Spring

HelloSpring

導入Jar包
注 : spring 須要導入commons-logging進行日誌記錄 . 咱們利用maven , 他會自動下載對應的依賴項 .

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

編寫代碼

一、編寫一個Hello實體類

public class Hello {
   private String name;

   public String getName() {
       return name;
  }
   public void setName(String name) {
       this.name = name;
  }

   public void show(){
       System.out.println("Hello,"+ name );
  }
}

二、編寫咱們的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">

   <!--bean就是java對象 , 由Spring建立和管理-->
   <bean id="hello" class="com.sowhat.pojo.Hello">
       <property name="name" value="Spring"/>
   </bean>
</beans>

三、咱們能夠去進行測試了 .

@Test
public void test(){
   //解析beans.xml文件 , 生成管理相應的Bean對象
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
   //getBean : 參數即爲spring配置文件中bean的id .
   Hello hello = (Hello) context.getBean("hello");
   hello.show();
}

思考

  • Hello 對象是誰建立的 ? 由Spring建立的
  • Hello 對象的屬性是怎麼設置的 ? hello 對象的屬性是由Spring容器設置的
  • 這個過程就叫控制反轉 :
  • 控制 : 誰來控制對象的建立 , 傳統應用程序的對象是由程序自己控制建立的 , 使用Spring後 , 對象是由Spring來建立的
  • 反轉 : 程序自己不建立對象 , 而變成被動的接收對象 .
  • 依賴注入 : 就是利用set方法來進行注入的.
  • IOC是一種編程思想,由主動的編程變成被動的接收
    能夠經過ClassPathXmlApplicationContext去瀏覽一下底層源碼

在這裏插入圖片描述
在這裏插入圖片描述
一個XML文件的解析就能夠上延8層,可見Spring容器爲了實現IOC進行了全面性的考慮。

修改案例一

咱們在案例一中, 新增一個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">

    <bean id="MysqlImpl" class="com.sowhat.dao.impl.UserDaoMySqlImpl"/>
    <bean id="OracleImpl" class="com.sowhat.dao.impl.UserDaoOracleImpl"/>

    <bean id="ServiceImpl" class="com.sowhat.server.impl.UserServiceImpl">
        <!--注意: 這裏的name並非屬性 , 而是set方法後面的那部分 , 首字母小寫-->
        <!--引用另一個bean , 不是用value 而是用 ref-->
        <property name="userDao" ref="MysqlImpl"/>
    </bean>
</beans>

測試!

@Test
public void test2(){
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
   UserServiceImpl serviceImpl = (UserServiceImpl) context.getBean("ServiceImpl");
   serviceImpl.getUser();
}

OK , 到了如今 , 咱們完全不用再程序中去改動了 , 要實現不一樣的操做 , 只須要在xml配置文件中進行修改 , 所謂的IoC,一句話搞定 : 對象由Spring 來建立 , 管理 , 裝配 !

IOC建立對象方式

經過無參構造方法來建立

一、User.java

public class User {

   private String name;

   public User() {
       System.out.println("user無參構造方法");
  }

   public void setName(String name) {
       this.name = name;
  }

   public void show(){
       System.out.println("name="+ name );
  }
}

二、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">

    <!--bean就是java對象 , 由Spring建立和管理-->
    <bean id="hello" class="com.sowhat.pojo.Hello">
        <property name="name" value="Spring"/>
    </bean>

    <bean id="user" class = "com.sowhat.pojo.User">
        <property name="name" value="sowhat"/>
    </bean>
</beans>

三、測試類

@Test
public void test(){
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
   //在執行getBean的時候, user已經建立好了 , 經過無參構造
   User user = (User) context.getBean("user");
   //調用對象的方法 .
   user.show();
}

結果能夠發現,在調用show方法以前,User對象已經經過無參構造初始化了!
在這裏插入圖片描述

經過有參構造方法來建立

一、UserT . java

public class UserT {

   private String name;

   public UserT(String name) {
       this.name = name;
  }

   public void setName(String name) {
       this.name = name;
  }

   public void show(){
       System.out.println("name="+ name );
  }
}

二、beans.xml 有三種方式編寫

<!-- 第一種根據index參數下標設置 -->
    <bean id="userT1" class="com.sowhat.pojo.UserT">
        <!-- index指構造方法 , 下標從0開始 -->
        <constructor-arg index="0" value="sowhat1"/>
    </bean>
    <!-- 第二種根據參數名字設置 -->
    <bean id="userT2" class="com.sowhat.pojo.UserT">
        <!-- name指參數名 -->
        <constructor-arg name="name" value="sowhat2"/>
    </bean>
    <!-- 第三種根據參數類型設置 -->
    <bean id="userT3" class="com.sowhat.pojo.UserT">
        <constructor-arg type="java.lang.String" value="sowhat3"/>
    </bean>

三、測試

@Test
public void testT(){
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
   UserT user = (UserT) context.getBean("userT");
   user.show();
}

結論:在配置文件加載的時候。其中管理的對象都已經初始化了!

Spring配置

別名:alias 設置別名 , 爲bean設置別名 , 能夠設置多個別名

<!--設置別名:在獲取Bean的時候可使用別名獲取-->
<alias name="userT" alias="userNew"/>

Bean的配置
bean就是java對象,由Spring建立和管理-

<!--
   id 是bean的標識符,要惟一,若是沒有配置id,name就是默認標識符
   若是配置id,又配置了name,那麼name是別名
   name能夠設置多個別名,能夠用逗號,分號,空格隔開
   若是不配置id和name,能夠根據applicationContext.getBean(.class)獲取對象;
   class是bean的全限定名=包名+類名
-->
<bean id="hello" name="hello2 h2,h3;h4" class="com.sowhat.pojo.Hello">
   <property name="name" value="Spring"/>
</bean>

import

團隊的合做經過import來實現 .

<import resource="{path}/beans.xml"/>
三、依賴注入(DI)

Dependency Injection

概念

  • 依賴注入(Dependency Injection,DI)。

  • 依賴 : 指Bean對象的建立依賴於容器 . Bean對象的依賴資源 .

  • 注入 : 指Bean對象所依賴的資源 , 由容器來設置和裝配 .

構造器注入

第二章已經講解過了

Set 注入 (重點)

要求被注入的屬性 , 必須有set方法 , set方法的方法名由set + 屬性首字母大寫 , 若是屬性是boolean類型 , 沒有set方法 , 是 is ,主要有常量、Bean注入、數組注入、List注入、Map注入、set注入、Null注入、Properties注入等。
測試pojo類 :
Address.java

public class Address {
 
     private String address;
 
     public String getAddress() {
         return address;
    }
 
     public void setAddress(String address) {
         this.address = address;
    }
 }

Student.java

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
    }
 }

一、常量注入

<bean id="student" class="com.sowhat.pojo.Student">
     <property name="name" value="小明"/>
 </bean>

測試:

@Test
	 public void test01(){
	     ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
	     Student student = (Student) context.getBean("student");
	     System.out.println(student.getName());
	 }

二、Bean注入
注意點:這裏的值是一個引用,ref

<bean id="addr" class="com.sowhat.pojo.Address">
     <property name="address" value="重慶"/>
 </bean>
 
 <bean id="student" class="com.sowhat.pojo.Student">
     <property name="name" value="小明"/>
     <property name="address" ref="addr"/>
 </bean>

三、數組注入

<bean id="student" class="com.sowhat.pojo.Student">
     <property name="name" value="小明"/>
     <property name="address" ref="addr"/>
     <property name="books">
         <array>
             <value>西遊記</value>
             <value>紅樓夢</value>
             <value>水滸傳</value>
         </array>
     </property>
 </bean>

四、List注入

<property name="hobbys">
     <list>
         <value>聽歌</value>
         <value>看電影</value>
         <value>登山</value>
     </list>
 </property>

五、Map注入

<property name="card">
     <map>
         <entry key="中國郵政" value="456456456465456"/>
         <entry key="建設" value="1456682255511"/>
     </map>
 </property>

六、set注入

<property name="games">
     <set>
         <value>LOL</value>
         <value>BOB</value>
         <value>COC</value>
     </set>
 </property>

七、Null注入

<property name="wife"><null/></property>

八、Properties注入

<property name="info">
     <props>
         <prop key="學號">20190604</prop>
         <prop key="性別">男</prop>
         <prop key="姓名">小明</prop>
     </props>
 </property>

p命名和c命名注入

public class User2 {
     private String name;
     private int age;
 
     public void setName(String name) {
         this.name = name;
    }
 
     public void setAge(int age) {
         this.age = age;
    }
 
     @Override
     public String toString() {
         return "User2{" +
                 "name='" + name + '\'' +
                 ", age=" + age +
                 '}';
    }
 }
一、P命名空間注入 : 須要在頭文件中加入約束文件

導入約束 :

xmlns:p="http://www.springframework.org/schema/p
 
 <!--P(屬性: properties)命名空間 , 屬性依然要設置set方法,調用系統自帶無參構造函數而後調用set方法-->
 <bean id="user2" class="com.sowhat.pojo.User2" p:name="sowhat" p:age="18"/>

測試:

@org.junit.Test
	public void test2(){
		ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
		//在執行getBean的時候, user已經建立好了 , 經過無參構造
		User2 user2 = (User2) context.getBean("user2");
		//調用對象的方法 .
		System.out.println(user2);
	}
二、c 命名空間注入 : 須要在頭文件中加入約束文件

導入約束 :

xmlns:c="http://www.springframework.org/schema/c
 <!--C(構造: Constructor)命名空間 , 屬性依然要設置set方法-->
 <bean id="user3" class="com.sowhat.pojo.User2" c:name="sowhat" c:age="18"/>

c 就是所謂的構造器注入!調用有參構造函數。

Bean的做用域

在Spring中,那些組成應用程序的主體及由Spring IoC容器所管理的對象,被稱之爲bean。簡單地講,bean就是由IoC容器初始化、裝配及管理的對象 。

類別 說明
singleton 在Spring IOC容器中僅存在一個Bean實例,能夠認爲是單例模式,是spring默認模式
prototype 每次從容器中調用Bean時都會返回一個新的實例,每次getBean()都至關於執行了new xxxBean()
request 每次HTTP請求都會建立一個新的Bean,該做用域僅適用於WebApplicationContext環境
session 同一個HTTP Session共享一個Bean,不一樣Session使用不一樣Bean,僅使用於WebApplicationContext環境

幾種做用域中,request、session做用域僅在基於web的應用中使用(沒必要關心你所採用的是什麼web應用框架),只能用在基於web的Spring ApplicationContext環境。

Singleton

當一個bean的做用域爲Singleton,那麼Spring IoC容器中只會存在一個共享的bean實例,而且全部對bean的請求,只要id與該bean定義相匹配,則只會返回bean的同一實例。Singleton是單例類型,就是在建立起容器時就同時自動建立了一個bean的對象,無論你是否使用,他都存在了,每次獲取到的對象都是同一個對象。注意,Singleton做用域是Spring中的缺省做用域。要在XML中將bean定義成singleton,能夠這樣配置:

<bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton">

測試:

@Test
 public void test03(){
     ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
     User user = (User) context.getBean("user");
     User user2 = (User) context.getBean("user");
     System.out.println(user==user2);
 }
Prototype

當一個bean的做用域爲Prototype,表示一個bean定義對應多個對象實例。Prototype做用域的bean會致使在每次對該bean請求(將其注入到另外一個bean中,或者以程序的方式調用容器的getBean()方法)時都會建立一個新的bean實例。Prototype是原型類型,它在咱們建立容器的時候並無實例化,而是當咱們獲取bean的時候纔會去建立一個對象,並且咱們每次獲取到的對象都不是同一個對象。根據經驗,對有狀態的bean應該使用prototype做用域,而對無狀態的bean則應該使用singleton做用域。在XML中將bean定義成prototype,能夠這樣配置:

<bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>  
  或者
 <bean id="account" class="com.foo.DefaultAccount" singleton="false"/>
Request

當一個bean的做用域爲Request,表示在一次HTTP請求中,一個bean定義對應一個實例;即每一個HTTP請求都會有各自的bean實例,它們依據某個bean定義建立而成。該做用域僅在基於web的Spring ApplicationContext情形下有效。考慮下面bean定義:

<bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>

針對每次HTTP請求,Spring容器會根據loginAction bean的定義建立一個全新的LoginAction bean實例,且該loginAction bean實例僅在當前HTTP request內有效,所以能夠根據須要放心的更改所建實例的內部狀態,而其餘請求中根據loginAction bean定義建立的實例,將不會看到這些特定於某個請求的狀態變化。當處理請求結束,request做用域的bean實例將被銷燬。

Session

當一個bean的做用域爲Session,表示在一個HTTP Session中,一個bean定義對應一個實例。該做用域僅在基於web的Spring ApplicationContext情形下有效。考慮下面bean定義:

<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

針對某個HTTP Session,Spring容器會根據userPreferences bean定義建立一個全新的userPreferences bean實例,且該userPreferences bean僅在當前HTTP Session內有效。與request做用域同樣,能夠根據須要放心的更改所建立實例的內部狀態,而別的HTTP Session中根據userPreferences建立的實例,將不會看到這些特定於某個HTTP Session的狀態變化。當HTTP Session最終被廢棄的時候,在該HTTP Session做用域內的bean也會被廢棄掉。

四、自動裝配

自動裝配說明

  • 自動裝配是使用spring知足bean依賴的一種方法
  • spring會在應用上下文中爲某個bean尋找其依賴的bean。

Spring中bean有三種裝配機制,分別是:

  • 在xml中顯式配置;
  • 在java中顯式配置;
  • 隱式的bean發現機制和自動裝配。

這裏咱們主要講第三種:自動化的裝配bean,Spring的自動裝配須要從兩個角度來實現,或者說是兩個操做:

  • 組件掃描(component scanning):spring會自動發現應用上下文中所建立的bean;
  • 自動裝配(autowiring):spring自動知足bean之間的依賴,也就是咱們說的IoC/DI

組件掃描和自動裝配組合發揮巨大威力,使得顯示的配置下降到最少。推薦不使用自動裝配xml配置 , 而使用註解

環境搭建

一、新建兩個實體類,Cat Dog 都有一個叫的方法

public class Cat {
   public void shout() {
       System.out.println("miao~");
  }
}
public class Dog {
   public void shout() {
       System.out.println("wang~");
  }
}

二、新建一個用戶類 User

public class User {
   private Cat cat;
   private Dog dog;
   private String str;
   // get set 方法
}

三、編寫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="dog" class="com.sowhat.pojo.Dog"/>
    <bean id="cat" class="com.sowhat.pojo.Cat"/>

    <bean id="user" class="com.sowhat.pojo.User">
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
        <property name="str" value="sowhat"/>
    </bean>
</beans>

四、測試

public class MyTest {
   @Test
   public void testMethodAutowire() {
       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
       User user = (User) context.getBean("user");
       user.getCat().shout();
       user.getDog().shout();
  }
}

結果正常輸出,環境OK

byName

autowire byName (按名稱自動裝配),因爲在手動配置xml過程當中,經常發生字母缺漏和大小寫等錯誤,而沒法對其進行檢查,使得開發效率下降。採用自動裝配將避免這些錯誤,而且使配置簡單化。

測試:
一、修改bean配置,增長一個屬性 autowire=「byName」

<bean id="user" class="com.sowhat.pojo.User" autowire="byName">
   <property name="str" value="sowhat"/>
   <!-- 注意此處沒有 ref cat dog 自動查找配置 -->
</bean>

二、再次測試,結果依舊成功輸出!

@Test
	public void testMethodAutowire() {
		ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
		User user = (User) context.getBean("user");
		user.getCat().shout();
		user.getDog().shout();
	}

三、咱們將 cat 的bean id修改成 catXXX

四、再次測試, 執行時報空指針java.lang.NullPointerException。由於按byName規則找不對應set方法,真正的setCat就沒執行,對象就沒有初始化,因此調用時就會報空指針錯誤。

小結:

當一個bean節點帶有 autowire=byName的屬性時。

  • 將查找其類中全部的set方法名,例如setCat,得到將set去掉而且首字母小寫的字符串,即cat。

  • 去spring容器中尋找是否有此字符串名稱id的對象。

  • 若是有,就取出注入;若是沒有,就報空指針異常。

byType

autowire byType (按類型自動裝配),使用autowire byType首先須要保證:同一類型的對象,在spring容器中惟一。若是不惟一,會報不惟一的異常。NoUniqueBeanDefinitionException

測試:

一、將user的bean配置修改一下 : autowire=「byType」

<bean id="user" class="com.sowhat.pojo.User" autowire="byType">
        <property name="str" value="sowhat"/>
    </bean>

二、測試,正常輸出

@Test
	public void testMethodAutowire() {
		ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
		User user = (User) context.getBean("user");
		user.getCat().shout();
		user.getDog().shout();
	}

三、再註冊一個cat 的bean對象!

<bean id="dog1" class="com.sowhat.pojo.Dog"/>
    <bean id="cat" class="com.sowhat.pojo.Cat"/>
    <bean id="cat2" class="com.sowhat.pojo.Cat"/>
    <bean id="user" class="com.sowhat.pojo.User" autowire="byType">
        <property name="str" value="sowhat"/>
    </bean>

在這裏插入圖片描述
五、刪掉cat2,將cat的bean名稱改掉!測試!由於是按類型裝配,因此並不會報異常,也不影響最後的結果。甚至將id屬性去掉,也不影響結果。

<bean id="dog1" class="com.sowhat.pojo.Dog"/>
    <bean class="com.sowhat.pojo.Cat"/>
    <bean id="user" class="com.sowhat.pojo.User" autowire="byType">
        <property name="str" value="sowhat"/>
    </bean>

這就是按照類型自動裝配!

使用註解

jdk1.5開始支持註解,spring2.5開始全面支持註解。簡單的一些自動裝備建議使用註解更加便捷。

準備工做:利用註解的方式注入屬性。

<?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
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">
    
    <context:annotation-config/>
    <!-- 開啓屬性註解支持!-->

</beans>
@Autowired

@Autowired是按類型自動轉配的,不支持id匹配。須要導入 spring-aop的包!

一、將User類中的set方法去掉,使用@Autowired註解

public class User {
   @Autowired
   private Cat cat;
   @Autowired
   private Dog dog;
   private String str;

   public Cat getCat() {
       return cat;
  }
   public Dog getDog() {
       return dog;
  }
   public String getStr() {
       return str;
  }
}

二、此時配置文件內容

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

    <context:annotation-config/>

    <bean id="dog" class="com.sowhat.pojo.Dog"/>
    <bean id="cat" class="com.sowhat.pojo.Cat"/>
    <bean id="user" class="com.sowhat.pojo.User"/>

</beans>

三、測試,成功輸出結果!

@Test
	public void testMethodAutowire() {
		ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
		User user = (User) context.getBean("user");
		user.getCat().shout();
		user.getDog().shout();
	}

PS:@Autowired(required=false) 說明:false,對象能夠爲null;true,對象必須存對象,不能爲null。默認爲true。

@Qualifier
  • @Autowired是根據類型自動裝配的,加上@Qualifier則能夠根據byName的方式自動裝配
  • @Qualifier不能單獨使用。

一、配置文件修改內容,保證類型存在對象。且名字不爲類的默認名字!

<bean id="dog1" class="com.sowhat.pojo.Dog"/>
<bean id="dog2" class="com.sowhat.pojo.Dog"/>
<bean id="cat1" class="com.sowhat.pojo.Cat"/>
<bean id="cat2" class="com.sowhat.pojo.Cat"/>

二、沒有加Qualifier測試,直接報錯
三、在屬性上添加Qualifier註解

@Autowired
@Qualifier(value = "cat2")
private Cat cat;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;

測試,成功輸出!

@Resource

@Resource若有指定的name屬性,先按該屬性進行byName方式查找裝配;

  • 其次再進行默認的byName方式進行裝配;
  • 若是以上都不成功,則按byType的方式自動裝配。
  • 都不成功,則報異常。
  • 前面的幾個都是spring中的註解,Resource是javax.annotation.Resource
  • 用的很少,仍是 @Autowired 多。

實體類:

public class User {
   //若是容許對象爲null,設置required = false,默認爲true
   @Resource(name = "cat2")
   private Cat cat;
   @Resource
   private Dog dog;
   @Value("123321") // 等價於 xml中的property 設置
   private String str;
}

beans.xml

<bean id="dog" class="com.sowhat.pojo.Dog"/>
<bean id="cat1" class="com.sowhat.pojo.Cat"/>
<bean id="cat2" class="com.sowhat.pojo.Cat"/>
<bean id="user" class="com.sowhat.pojo.User"/>

測試:結果OK

配置文件2:beans.xml , 刪掉cat2

<bean id="dog" class="com.sowhat.pojo.Dog"/>
<bean id="cat1" class="com.sowhat.pojo.Cat"/>

實體類上只保留註解

@Resource
private Cat cat;
@Resource
private Dog dog;

結果:OK

結論:先進行byName查找,失敗;再進行byType查找,成功。

小結

@Autowired與@Resource 異同

  • 一、@Autowired與@Resource均可以用來裝配bean。均可以寫在字段上,或寫在setter方法上。
  • 二、@Autowired默認按類型裝配(屬於spring規範),默認狀況下必需要求依賴對象必須存在,若是要容許null 值,能夠設置它的required屬性爲false,如:@Autowired(required=false) ,若是咱們想使用名稱裝配能夠結合@Qualifier註解進行使用
  • 三、@Resource(屬於J2EE範圍),默認按照名稱進行裝配,名稱能夠經過name屬性進行指定。若是沒有指定name屬性,當註解寫在字段上時,默認取字段名進行按照名稱查找,若是註解寫在setter方法上默認取屬性名進行裝配。當找不到與名稱匹配的bean時才按照類型進行裝配。可是須要注意的是,若是name屬性一旦指定,就只會按照名稱進行裝配。

它們的做用相同都是用註解方式注入對象,但執行順序不一樣。@Autowired先byType,@Resource先byName

五、使用註解開發

在spring4以後,想要使用註解形式,必須得要引入aop的包

在這裏插入圖片描述

註解開發配置

一、咱們以前都是使用 bean 的標籤進行bean注入(在xml文件中將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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 開啓自動裝載 -->
    <context:annotation-config/>

    <!--指定註解掃描包-->
    <context:component-scan base-package="com.sowhat.pojo"/>
</beans>

二、在指定包下編寫類,增長註解

@Component()
public class Cat {
   public void shout() {
       System.out.println("miao~");
  }

	@Override
	public String toString()
	{
		return "Cat{}";
	}
}
@Component()
public class Dog {
   public void shout() {
       System.out.println("wang~");
  }

	@Override
	public String toString()
	{
		return "Dog{}";
	}
}
@Component()
public class User
{
	@Autowired
	private Cat cat;
	@Autowired
	private Dog dog;
	@Value("123321")
	private String str;

	public Cat getCat()
	{
		return cat;
	}

	public Dog getDog()
	{
		return dog;
	}

	public void setStr(String str)
	{
		this.str = str;
	}

	@Override
	public String toString()
	{
		return "User{" +
				"cat=" + cat +
				", dog=" + dog +
				", str='" + str + '\'' +
				'}';
	}
}

三、測試

public class test
{
	@Test
	public void testMethodAutowire() {
		ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
		User user = (User) context.getBean("user");
		user.getCat().shout();
		user.getDog().shout();
		System.out.println(user.toString());
	}
}

結果:
在這裏插入圖片描述

屬性注入

使用註解注入屬性

一、能夠不用提供set方法,直接在直接名上添加@value(「值」)

@Component("user")
// 至關於配置文件中 <bean id="user" class="當前註解的類"/>
public class User {
   @Value("sowhat")
   // 至關於配置文件中 <property name="name" value="sowhat"/>
   public String name;
}

二、若是提供了set方法,在set方法上添加@value(「值」);

@Component("user")
public class User {
   public String name;
   @Value("sowhat")
   public void setName(String name) {
       this.name = name;
  }
}

衍生註解

咱們這些註解,就是替代了在配置文件當中配置步驟而已!更加的方便快捷!

@Component三個衍生註解

爲了更好的進行分層,Spring可使用其它三個註解,功能同樣,目前使用哪個功能都同樣。

  • @Controller:web層
  • @Service:service層
  • @Repository:dao層

寫上這些註解,就至關於將這個類交給Spring管理裝配了!

自動裝配註解

在Bean的自動裝配已經講過了 主要是byType or byName,能夠回顧!

做用域

@scope

singleton:默認的,Spring會採用單例模式建立這個對象。關閉工廠 ,全部的對象都會銷燬。
prototype:多例模式。關閉工廠 ,全部的對象不會銷燬。內部的垃圾回收機制會回收

@Controller("user")
@Scope("prototype")
public class User {
   @Value("sowhat")
   public String name;
}

小結

XML與註解比較

  • XML能夠適用任何場景 ,結構清晰,維護方便。
  • 註解不是本身提供的類使用不了,開發簡單方便

xml與註解整合開發 :推薦最佳實踐

  • xml管理Bean
  • 註解完成屬性注入
  • 使用過程當中, 能夠不用掃描,掃描是爲了類上的註解

自動裝載配置:

<context:annotation-config/>

做用:

  • 進行註解驅動註冊,從而使註解生效
  • 用於激活那些已經在spring容器裏註冊過的bean上面的註解,也就是顯示的向Spring註冊
  • 若是不掃描包,就須要手動配置bean
  • 若是不加註解驅動,則注入的值爲null!

基於Java類進行配置

JavaConfig 原來是 Spring 的一個子項目,它經過 Java 類的方式提供 Bean 的定義信息,在 Spring4 的版本, JavaConfig 已正式成爲 Spring4 的核心功能 。徹底不用xml配置,全權交給Java來作。

測試:

一、編寫一個實體類,Dog

public class Dog {
   public String name = "dog";
}

二、新建一個config配置包,編寫一個MyConfig配置類

@Configuration  //表明這是一個配置類
public class MyConfig {
   @Bean //經過方法註冊一個bean,這裏的返回值就Bean的類型,方法名就是bean的id!
   public Dog dog(){
       return new Dog();
  }
}

三、測試

@Test
public void test2(){
   ApplicationContext applicationContext =
           new AnnotationConfigApplicationContext(MyConfig.class);
   Dog dog = (Dog) applicationContext.getBean("dog");
   System.out.println(dog.name);
}

四、成功輸出結果!

導入其餘配置如何作呢?

一、咱們再編寫一個配置類!

@Configuration  //表明這是一個配置類
public class MyConfig2 {
}

二、在以前的配置類中咱們來選擇導入這個配置類

@Configuration
@Import(MyConfig2.class)  //導入合併其餘配置類,相似於配置文件中的 inculde 標籤
public class MyConfig {
   @Bean
   public Dog dog(){
       return new Dog();
  }
}

關於這種Java類的配置方式,咱們在以後的SpringBootSpringCloud中還會大量看到,咱們須要知道這些註解的做用便可!底層來來回回也是常見到若干註解。

@Configuation總結

  • @Configuation等價於<Beans> </Beans>
  • @Bean等價於<Bean> </Bean>
  • @ComponentScan等價於<context:component-scan base-package="com.sowhat.pojo"/>
六、靜態/動態代理模式

爲何要學習代理模式,由於AOP的底層機制就是動態代理!

代理模式:

  • 靜態代理
  • 動態代理

學習aop以前 , 咱們要先了解一下代理模式!

靜態代理

靜態代理角色分析

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

代碼實現

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

分析:在這個過程當中,你直接接觸的就是中介,就如同現實生活中的樣子,你看不到房東,可是你依舊租到了房東的房子經過代理,這就是所謂的代理模式,程序源自於生活。

靜態代理的好處:

  • 可使得咱們的真實角色更加純粹 . 再也不去關注一些公共的事情 .
  • 公共的業務由代理來完成 . 實現了業務的分工 ,
  • 公共業務發生擴展時變得更加集中和方便 .

缺點 :

  • 類多了 , 多了代理類 , 工做量變大了 . 開發效率下降 .

咱們想要靜態代理的好處,又不想要靜態代理的缺點,因此 , 就有了動態代理 !

靜態代理再理解

同窗們練習完畢後,咱們再來舉一個例子,鞏固你們的學習!

練習步驟:

一、建立一個抽象角色,好比咋們平時作的用戶業務,抽象起來就是增刪改查!

//抽象角色:增刪改查業務
public interface UserService {
   void add();
   void delete();
   void update();
   void query();
}

二、咱們須要一個真實對象來完成這些增刪改查操做

//真實對象,完成增刪改查操做的人
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("查詢了一個用戶");
  }
}

三、需求來了,如今咱們須要增長一個日誌功能,怎麼實現!

思路1 :在實現類上增長代碼,違背開閉原則。。

思路2:使用代理來作,可以不改變原來的業務狀況下,實現此功能就是最好的了!

四、設置一個代理類來處理日誌!代理角色

//代理角色,在這裏面增長日誌的實現
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+"方法");
  }
}

五、測試訪問類:

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

OK,到了如今代理模式你們應該都沒有什麼問題了,重點你們須要理解其中的思想;

咱們在不改變原來的代碼的狀況下,實現了對原有功能的加強,這是AOP中最核心的思想

動態代理

  • 動態代理的角色和靜態代理的同樣 .

  • 動態代理的代理類是動態生成的, 靜態代理的代理類是咱們提早寫好的。

  • 動態代理分爲兩類 : 一類是基於接口動態代理 , 一類是基於類的動態代理

    • 基於接口的動態代理 ===> JDK動態代理

    • 基於類的動態代理 ===> cglib

    • 如今用的比較多的是 javasist 來生成動態代理 . 百度一下javasist

    • 咱們這裏使用JDK的原生代碼來實現,其他的道理都是同樣的!、

*.java 文件最終要編譯成*.class文件而後通過JVM加載後執行,問題是*.class文件來源能夠有多個,好比常規的*.java文件,網絡傳輸*.class文件,內存中生成的*.class文件。動態代理用的就是內存中生成的class文件。細節部分可參考 AOP手動實現

動態代理必備兩類

核心 : InvocationHandler和Proxy,百度便可看到各類教程,跟八股文同樣的固定格式與套路。

接口 InvocationHandler,需實現invoke函數。

Object invoke(Object proxy, 方法 method, Object[] args);
//參數
//proxy: 調用該方法的代理實例
//method: -所述方法對應於調用代理實例上的接口方法的實例。方法對象的聲明類將是該方法聲明的接口,它能夠是代理類繼承該方法的代理接口的超級接口。
//args: -包含的方法調用傳遞代理實例的參數值的對象的陣列,或null若是接口方法沒有參數。原始類型的參數包含在適當的原始包裝器類的實例中,例如java.lang.Integer或java.lang.Boolean 。

Proxy代理類

//生成代理類
public Object getProxy(){
   return Proxy.newProxyInstance(loader,interfaces,h);
}
loader:類加載器來定義代理類
interfaces:代理類實現的接口列表
h:調度方法調用的處理函數

動態代理實戰

代碼實現 抽象角色和真實角色和以前的同樣!

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;
  }

   //看房
   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();
       //代理實例的調用處理程序
       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();
  }
}

測試,增刪改查,查看結果!

動態代理的好處

靜態代理有的它都有,靜態代理沒有的,它也有!

  • 可使得咱們的真實角色更加純粹 . 再也不去關注一些公共的事情 。
  • 公共的業務由代理來完成 . 實現了業務的分工 。
  • 公共業務發生擴展時變得更加集中和方便 。
  • 一個動態代理 , 通常代理某一類業務。
  • 一個動態代理能夠代理多個類,代理的是接口!
七、AOP就這麼簡單

什麼是AOP

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

在這裏插入圖片描述

Aop在Spring中的做用

提供聲明式事務;容許用戶自定義切面,SpringAOP中,經過Advice定義橫切邏輯,Spring中支持5種類型的Advice,五種通知

【重點】使用AOP織入,須要導入一個依賴包!

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->·
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.4</version>
</dependency>
第一種方式 實現指定接口實現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.sowhat.demo4.UserServiceImpl"/>
    <bean id="log" class="com.sowhat.demo4.Log"/>
    <bean id="afterLog" class="com.sowhat.demo4.AfterLog"/>

    <!--aop的配置-->
    <aop:config>
        <!--切入點 expression:表達式匹配要執行的方法-->
        <aop:pointcut id="pointcut" expression="execution(* com.sowhat.demo4.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的重要性 : 很重要 . 必定要理解其中的思路 , 主要是思想的理解這一塊 .

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

第二種方式 自定義類來實現Aop

目標業務類不變依舊是userServiceImpl

第一步 : 寫咱們本身的一個切入類

public class DiyPointcut {

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

去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"
       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="diy" class="com.sowhat.demo4.DiyPointcut"/>

    <!--aop的配置-->
    <aop:config>
        <!--第二種方式:使用AOP的標籤實現-->
        <aop:aspect ref="diy">
            <aop:pointcut id="diyPonitcut" expression="execution(* com.sowhat.demo4.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();
  }
}
第三種方式 使用註解實現

第一步:編寫一個註解實現的加強類

package com.sowhat.demo4;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

@Aspect
public class AnnotationPointcut {

	@Pointcut("execution(* com.sowhat.demo4.UserServiceImpl.*(..))")
	public void pointCut() {}


   @Before("pointCut()")
   public void before(){
       System.out.println("---------方法執行前---------");
  }

   @After("pointCut()")
   public void after(){
       System.out.println("---------方法執行後---------");
  }

   @Around("pointCut()")
   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);
  }
}

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 id="userService" class="com.sowhat.demo4.UserServiceImpl"/>
    <bean id="annotationPointcut" class="com.sowhat.demo4.AnnotationPointcut"/>
    <aop:aspectj-autoproxy proxy-target-class="false"/>

</beans>

aop:aspectj-autoproxy:說明

經過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動態代理

End

公共部分:

  1. xml中導入切面依賴。
  2. 引入切面模糊表達式依賴pom。

1、繼承接口實現AOP

  1. 前置依賴後置依賴的繼承接口實現類。
  2. xml中配置切面依賴。

2、自定義類實現AOP

  1. 自定義切面類跟函數
  2. xml中配置切面依賴

3、基於註解實現AOP

  1. 自定義註解切面類
  2. xml中配置切面自動autoproxy

五種通知的執行順序

在這裏插入圖片描述

若是存在多個切面,多切面執行時,採用了責任鏈設計模式。切面的配置順序決定了切面的執行順序,多個切面執行的過程,相似於方法調用的過程,在環繞通知的proceed()執行時,去執行下一個切面或若是沒有下一個切面執行目標方法,從而達成了以下的執行過程:
在這裏插入圖片描述
若是目標方法拋出異常:在這裏插入圖片描述
不一樣通知功能:

通知類型 功能
環繞通知 控制事務 權限控制
後置通知 記錄日誌(方法已經成功調用)
異常通知 異常處理 控制事務
最終通知 記錄日誌(方法已經調用,但不必定成功)
8、Spring整合MyBatis

什麼是 MyBatis-Spring?

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

知識基礎

在開始使用 MyBatis-Spring 以前,你須要先熟悉 Spring 和 MyBatis 這兩個框架和有關它們的術語。這很重要

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>

要和 Spring 一塊兒使用 MyBatis,須要在 Spring 應用上下文中定義至少兩樣東西:一個 SqlSessionFactory 和至少一個數據映射器類。

MyBatis-Spring 中,可以使用SqlSessionFactoryBean來建立 SqlSessionFactory。要配置這個工廠 bean,只須要把下面代碼放在 Spring 的 XML 配置文件中:

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

注意:SqlSessionFactory須要一個 DataSource(數據源)。這能夠是任意的 DataSource,只須要和配置其它 Spring 數據庫鏈接同樣配置它就能夠了。

在基礎的 MyBatis 用法中,是經過 SqlSessionFactoryBuilder 來建立 SqlSessionFactory 的。而在 MyBatis-Spring 中,則使用 SqlSessionFactoryBean 來建立。

在 MyBatis 中,你可使用 SqlSessionFactory 來建立 SqlSession。一旦你得到一個 session 以後,你可使用它來執行映射了的語句,提交或回滾鏈接,最後,當再也不須要它的時候,你能夠關閉 session。

SqlSessionFactory有一個惟一的必要屬性:用於 JDBC 的 DataSource。這能夠是任意的 DataSource 對象,它的配置方法和其它 Spring 數據庫鏈接是同樣的。

一個經常使用的屬性是 configLocation,它用來指定 MyBatis 的 XML 配置文件路徑。它在須要修改 MyBatis 的基礎配置很是有用。一般,基礎配置指的是 < settings> 或 < typeAliases>元素。

須要注意的是,這個配置文件並不須要是一個完整的 MyBatis 配置。確切地說,任何環境配置(),數據源()和 MyBatis 的事務管理器()都會被忽略。SqlSessionFactoryBean 會建立它自有的 MyBatis 環境配置(Environment),並按要求設置自定義環境的值。

SqlSessionTemplate 是 MyBatis-Spring 的核心。做爲 SqlSession 的一個實現,這意味着可使用它無縫代替你代碼中已經在使用的 SqlSession

模板能夠參與到 Spring 的事務管理中,而且因爲其是線程安全的,能夠供多個映射器類使用,你應該老是用 SqlSessionTemplate 來替換 MyBatis 默認的 DefaultSqlSession 實現。在同一應用程序中的不一樣類之間混雜使用可能會引發數據一致性的問題。

可使用 SqlSessionFactory 做爲構造方法的參數來建立 SqlSessionTemplate 對象。

一、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">

    <import resource="spring-dao.xml"/>
<!--    <import resource="spring-mvc.xml"/>-->

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

    <bean id="userMapper2" class="com.sowhat.mapper.UserMapperImpl2">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

</beans>

二、spring-dao.xml配置,全權接管mybatis配置幾乎

<?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 c3po dbcp druid spring 來管理數據庫鏈接  -->
    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
    </bean>

    <!--sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 綁定MyBatis 配置文件 -->
        <property name="dataSource" ref="datasource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/sowhat/mapper/*.xml"/>
         <!-- <property name="typeAliases" value="com.sowhat.pojo.User"/>-->
    </bean>

    <!-- SqlSessionTemplate 就是咱們使用的sqlSession-->
    <!-- 若是用了方式二 SqlSessionDaoSupport 就不須要sqlSession了 -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!-- 只能經過構造器注入 沒有set方法 -->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
    <!-- 至此 Spring 接管了 MyBatis的建立工做 通常狀況下此處就不用動了-->
</beans>

三、mybatis-config.xml配置

<?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>

    <properties resource="db.properties">
        <property name="password" value="123"/>
        <!--  此處的優先級小於外面 db.properties 設定參數的優先級   -->
    </properties>
    <typeAliases>
        <package name="com.sowhat.pojo"/>
    </typeAliases>
</configuration>

四、db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8
username=root
password=root

五、pom.xml

<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.10.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.10</version>
        </dependency>
    </dependencies>

    <!-- 解決文件過濾問題 -->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

六、pojo.xml

import lombok.Data;
@Data
public class User {
    private int id;
    private String name;
    private  String pwd;
}

七、接口跟Mapper

public interface UserMapper {
    public List<User> selectUsers();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sowhat.mapper.UserMapper">

    <select id="selectUsers" resultType="User">
         select * from user
    </select>
</mapper>

八、自動實現類

public class UserMapperImpl implements UserMapper {
    // 咱們是因此操做都使用SqlSessionTemplate了
    private SqlSessionTemplate sqlSessionTemplate;

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

    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSessionTemplate = sqlSessionTemplate;
    }
}

九、測試:

@Test
    public void test(){
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapper userMapper = classPathXmlApplicationContext.getBean("userMapper", UserMapper.class);
        for (User selectUser : userMapper.selectUsers()) {
            System.out.println(selectUser);
        }
    }

PS :SqlSessionDaoSupport
mybatis-spring1.2.3版以上的纔有這個 .dao繼承Support類 , 直接利用 getSqlSession() 得到 , 而後直接注入SqlSessionFactory . 比起方式1 , 不須要管理SqlSessionTemplate , 並且對事務的支持更加友好 . 可跟蹤源碼查看。
具體代碼查看地址:spring整合mybatis

總結

Spring整合MyBatis的核心其實就是xml配置地獄。

  1. applicationContext.xml是全局配置文件,裏面能夠註冊若干Bean。import若干配置文件
  2. spring-dao.xml就是spring整合mybatis的配置文件,通常狀況下不多動。
  3. mybatis-config.xml 是mybatis的配置文件,spring-dao.xml能夠引入該配置。
  4. db.properties是 mybatis-config.xml 配置須要的信息。
  5. 總體感受就是配置 配置 再配置。
  6. MyBatis整合到spring之後能夠徹底不要mybatis的配置文件,除了這些方式能夠實現整合以外,咱們還可使用註解來實現,這個等SpringBoot教程時候還會測試整合!
九、聲明式事務

回顧事務

  • 事務在項目開發過程很是重要,涉及到數據的一致性的問題,不容馬虎!

  • 事務管理是企業級應用程序開發中必備技術,用來確保數據的完整性和一致性。

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

事務四個屬性ACID

  1. 原子性(atomicity)
    事務是原子性操做,由一系列動做組成,事務的原子性確保動做要麼所有完成,要麼徹底不起做用

  2. 一致性(consistency)
    一旦全部事務動做完成,事務就要被提交。數據和資源處於一種知足業務規則的一致性狀態中

  3. 隔離性(isolation)
    可能多個事務會同時處理相同的數據,所以每一個事務都應該與其餘事務隔離開來,防止數據損壞

  4. 持久性(durability)
    事務一旦完成,不管系統發生什麼錯誤,結果都不會受到影響。一般狀況下,事務的結果被寫到持久化存儲器中

Spring中的事務管理

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

  • 編程式事務管理

    • 將事務管理代碼嵌到業務方法中來控制事務的提交和回滾
    • 缺點:必須在每一個事務操做業務邏輯中包含額外的事務管理代碼
  • 聲明式事務管理

    • 通常狀況下比編程式事務好用。
    • 將事務管理代碼從業務方法中分離出來,以聲明的方式來實現事務管理。
    • 將事務管理做爲橫切關注點,經過aop方法模塊化。Spring中經過Spring AOP框架支持聲明式事務管理。
      給出一個生命式事務的demo
<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <!-- DataSource 使用Spring的數據源替換Mybatis c3po dbcp druid spring 來管理數據庫鏈接  -->
    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="url"
                  value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
    </bean>

    <!--sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 綁定MyBatis 配置文件 -->
        <property name="dataSource" ref="datasource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/sowhat/mapper/*.xml"/>
        <!-- <property name="typeAliases" value="com.sowhat.pojo.User"/>-->
    </bean>

    <!-- SqlSessionTemplate 就是咱們使用的sqlSession-->
    <!-- 若是用了方式二 SqlSessionDaoSupport 就不須要sqlSession了 -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!-- 只能經過構造器注入 沒有set方法 -->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

    <!-- 配置聲明式事務-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="datasource"/>
    </bean>


    <!-- 結合AOP 實現事務 -->
    <!--配置事務通知 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--給那些方法配置事務,配置事務的傳播特性  propagation -->
            <tx:method name="add" 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>

    <!--配置事務切入 -->
    <aop:config>
        <aop:pointcut id="txpointCut" expression="execution(* com.sowhat.mapper.UserMapperImpl.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txpointCut"/>
    </aop:config>
    <!-- 至此 Spring 接管了 MyBatis的建立工做 通常狀況下此處就不用動了-->
</beans>

spring事務傳播特性:

事務傳播行爲就是多個事務方法相互調用時,事務如何在這些方法間傳播。spring支持7種事務傳播行爲:

  • propagation_requierd:若是當前沒有事務,就新建一個事務,若是已存在一個事務中,加入到這個事務中,這是最多見的選擇。
  • propagation_supports:支持當前事務,若是沒有當前事務,就以非事務方法執行。
  • propagation_mandatory:使用當前事務,若是沒有當前事務,就拋出異常。
  • propagation_required_new:新建事務,若是當前存在事務,把當前事務掛起。
  • propagation_not_supported:以非事務方式執行操做,若是當前存在事務,就把當前事務掛起。
  • propagation_never:以非事務方式執行操做,若是當前事務存在則拋出異常。
  • propagation_nested:若是當前存在事務,則在嵌套事務內執行。若是當前沒有事務,則執行與propagation_required相似的操做

Spring 默認的事務傳播行爲是 PROPAGATION_REQUIRED,它適合於絕大多數的狀況。

假設 ServiveX#methodX() 都工做在事務環境下(即都被 Spring 事務加強了),假設程序中存在以下的調用鏈:Service1#method1()->Service2#method2()->Service3#method3(),那麼這 3 個服務類的 3 個方法經過 Spring 的事務傳播機制都工做在同一個事務中。

就比如,咱們剛纔的幾個方法存在調用,因此會被放在一組事務當中!若是Spring中不配置,就須要咱們手動提交控制事務;事務在項目開發過程很是重要,涉及到數據的一致性的問題,不容馬虎!

十、循環依賴

什麼是循環依賴,如何解決循環依賴,三層緩存做用

參考

spring整合mybatis
帥丙
依賴通俗說

相關文章
相關標籤/搜索