Spring的基本概念和基本使用

Spring的基本概念和基本使用

說明:學習筆記是對B站狂神說的Spring視頻的整理和總結。

Spring概述

Spring相關的網址:
Spring官網
Spring各版本官方下載地址
Spring的GitHub

優點:

  1. Spring是一個開源免費的框架(容器)
  2. Spring是一個輕量級的、非侵入式的框架(非侵入指的是不會對現有代碼產生影響)
  3. 控制反轉(IOC) , 面向切面編程(AOP)
  4. 支持事務的處理 , 對框架整合的支持

總結:Spring是一個輕量級的控制反轉(IoC)和麪向切面(AOP)的容器(框架)。

Spring項目的創建

創建一個Spring項目,最主要的過程是以下幾個步驟。

1、添加依賴
由maven項目來創建Spring項目。首先在maven的配置文件pom.xml中添加主要的依賴:

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<!--webmvc依賴添加進來,其它很多依賴也就添加進來了-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.5.RELEASE</version>
</dependency>


<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<!--jdbc的依賴,後期整合Mybatis的時候會用到-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.5.RELEASE</version>
</dependency>

2、配置文件
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">
    <!--使用Spring來創建對象,在Spring中這些都稱爲bean
    java創建對象:類型 變量名 = new 類型();

    Spring通過bean來創建對象:
    id:變量名
    class:new的對象
    property:相當於給對象中的屬性設置一個值
    -->
	<bean id="..." class="...">
		<!-- collaborators and configuration for this bean go here -->
	</bean>
</beans>

可以理解爲一個類就是一個bean,在Spring中沒有顯式的new關鍵字,而是通過bean在Spring容器中來創建對象。

3、獲取對象
Spring讀取含有bean的配置文件,在容器中實例化各個類,得到ApplicationContext類型的對象,通過此對象來獲得各個實例。

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserService userService = (UserService) context.getBean("userServiceImpl");
        userService.getUser();
    }
}

Java中DAO層、Service層和Controller層的區別:

  • DAO層:數據訪問層,屬於底層的操作,具體對應某個表的增刪改查,也就是說某個DAO一定是和數據庫的某一張表一一對應的。
  • Service層:服務層,是對一個或多個DAO進行的再次封裝,封裝成一個服務,所以這裏也就不會是一個原子操作了,需要事務控制。
  • Controler層:Controler層負責請求轉發,接受頁面過來的參數,傳給Service處理,接到返回值,再傳給頁面。

Spring的配置

Spring的配置文件中主要有以下幾種配置,最主要最重要的是bean的配置。

1、別名

<!--user是某個bean的唯一標識符,user2是這個bean的別名-->
<alias name="user" alias="user2"/>

2、Bean的配置

<!--
id:bean的唯一標識符
class:bean對象所對應的全限定名,即包名+類型
name:也是別名,而且name可以取多個別名
-->
<bean id="user" class="com.xlq.pojo.User" name="user2">
    <constructor-arg name="name" value="xlq"/>
    <constructor-arg name="age" value="25"/>
</bean>

3、import

  • 用於團隊開發使用,將多個人寫的beans.xml合併爲一個總的applicationContext.xml。
  • 使用的時候直接加載總的配置文件即可。

IoC的基本概念

由問題引出IoC

傳統的Dao層和Service層的文件構成:

  1. UserDao–dao層的底層接口
  2. UserDaoImpl–實現類
  3. UserService–業務接口
  4. UserServiceImpl–業務實現類

在業務層中,用戶的需求可能會影響我們原來的代碼,我們需要根據用戶的需求去修改源代碼,如果程序量十分龐大,修改一次的成本代價十分昂貴。
第一版代碼:

public class UserServiceImpl implements UserService {
    // private UserDao userDao = new UserDaoImpl();
    // 如果用戶想從Mysql中獲取數據,有一個問題:這樣的代碼適應不了用戶需求的變更
    private UserDao userDao = new UserDaoMysqlImpl();

    // 在業務層調DAO層
    public void getUser() {
        userDao.getUser();
    }
}

如果使用set方法進入動態注入,將會發生革命性的變化。
第二版代碼:

public class UserServiceImpl implements UserService {

    private UserDao userDao;

    // 利用set方法進入動態注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    // 在業務層調DAO層
    public void getUser() {
        userDao.getUser();
    }
}

控制反轉的意思:

  • 第一版代碼中,userDao是程序員主動創建的,控制權在程序員手上。
  • 第二版代碼中,通過set方法動態注入,程序不再具有主動性,而是變成了被動地接收對象。

IoC的本質

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

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

IoC創建對象的方式

第一類:無參構造。Spring默認使用無參構造創建對象。

第二類:我們需要使用有參構造創建對象。官網提供了三種方式。

  1. 下標賦值
<bean id="user" class="com.xlq.pojo.User">
    <constructor-arg index="0" value="xlq"/>
</bean>
  1. 類型
<!--如果有多個參數,不推薦使用-->  
<bean id="user" class="com.xlq.pojo.User">
    <constructor-arg type="java.lang.String" value="xlq"/>
</bean>
  1. 參數名
<bean id="user" class="com.xlq.pojo.User">
    <constructor-arg name="name" value="xlq"/>
    <constructor-arg name="age" value="25"/>
</bean>

總結:在配置文件加載的時候,容器中管理的對象就已經初始化了!

依賴注入(DI)

1、構造器注入

2、Set方法注入【重點】
依賴注入,即Set注入,含義是什麼?

  • 依賴:bean對象的創建依賴於容器。
  • 注入:bean對象中的所有屬性,由容器來注入!

示例:
類信息爲:

public class Address {
    private String addr;
    public String getAddr() {
        return addr;
    }
    public void setAddr(String addr) {
        this.addr = addr;
    }
    @Override
    public String toString() {
        return "Address{" + "addr='" + addr + '\'' + '}';
    }
}
public class Student {
    private String name;//基本類型
    private Address addr;//引用類型
    private String[] books;
    private Set<String> hobbies;
    private Map<String, String> cards;
    private List<String> games;
    private String wife;// 測試null的注入
    private Properties info;
    // 這裏還必須要有各個屬性的Set方法,太長先省略
}

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="address" class="com.xlq.pojo.Address">
        <property name="addr" value="nanjing"/>
    </bean>

    <bean id="student" class="com.xlq.pojo.Student">
        <!--基本類型的注入-->
        <property name="name" value="xiaoming"/>

        <!--引用類型的注入-->
        <property name="addr" ref="address"/>

        <!--數組類型的注入-->
        <property name="books">
            <array>
                <value>紅樓夢</value>
                <value>西遊記</value>
                <value>水滸傳</value>
                <value>三國演義</value>
            </array>
        </property>

        <!--下面三個是集合類的注入-->
        <property name="hobbies">
            <set>
                <value>唱歌</value>
                <value>跑步</value>
                <value>打羽毛球</value>
            </set>
        </property>

        <property name="cards">
            <map>
                <entry key="身份證" value="123456"/>
                <entry key="銀行卡" value="123456"/>
            </map>
        </property>

        <property name="games">
            <list>
                <value>LOL</value>
                <value>CS</value>
                <value>GTA5</value>
            </list>
        </property>

        <!--空值null的注入-->
        <property name="wife">
            <null/>
        </property>

        <!--Properties的注入-->
        <property name="info">
            <props>
                <prop key="username">root</prop>
                <prop key="password">123456</prop>
            </props>
        </property>
    </bean>
</beans>

3、擴展方法注入–p命名空間和c命名空間
實體類:

public class User {
    private String name;
    private int age;

    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" + "name='" + name + '\'' + ", age=" + age +'}';
    }
}

配置文件beans2.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--p命名空間注入,可以直接注入屬性的值,p表示property-->
    <bean id="user" class="com.xlq.pojo.User" p:age="18" p:name="xxx"/>

    <!--c命名空間的注入,通過構造器注入:construct-args-->
    <bean id="user2" class="com.xlq.pojo.User" c:age="18" c:name="xxx"/>

</beans>

注意點:p命名和c命名空間不能直接使用,需要導入xml約束。

xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"

Bean的作用域

官網中關於Bean的作用域的說明:
在這裏插入圖片描述

簡要介紹:

  1. 單例模式【Spring默認機制】
  2. 原型模式:每次從容器中get的時候,都會產生一個新的對象。
  3. 其餘的request、session、application這些只能在web開發中使用到。

Bean的自動裝配

自動裝配是Spring滿足bean依賴的一種方式。Spring會在上下文中自動尋找,並自動給bean裝配屬性。

Spring中有三種裝配的方式:

  1. 在xml中顯式配置;
  2. 在Java中顯式配置;
  3. 隱式地自動裝配bean【重要】

1、ByName自動裝配

<bean id="dog" class="com.xlq.pojo.Dog"/>
<bean id="cat" class="com.xlq.pojo.Cat"/>

<!--Persong含有兩個Set方法:
public void setDog(Dog dog)
public void setCat(Cat cat)-->
<bean id="person" class="com.xlq.pojo.Person" autowire="byName">
    <property name="name" value="xxx"/>
</bean>

2、ByType自動裝配

<bean class="com.xlq.pojo.Dog"/>
<bean class="com.xlq.pojo.Cat"/>

<bean id="person" class="com.xlq.pojo.Person" autowire="byType">
    <property name="name" value="xxx"/>
</bean>

區別:

  • byname的時候,需要保證自動裝配的bean的id唯一,並且id的名字需要和自動注入的set方法的形參名字一一對應;
  • bytype的時候,需要保證自動裝配的bean的class唯一,並且class的名字需要和自動注入的set方法的形參類型一一對應;

3、註解實現自動裝配
使用註解的步驟:

  1. 導入約束:context約束
  2. 配置註解的支持:context:annotation-config
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
		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
基本使用:直接在屬性上使用即可,也可以在set方法上使用。

使用Autowired我們可以不用編寫set方法了,前提是這個自動裝配的屬性在IoC容器中存在,且符合名字byname。

public class Person {
    private String name;
    // 如果顯式定義了Autowired屬性爲false,說明這個對象可以爲null,否則不允許爲空
    @Autowired(required = false)
    private Dog dog;
    @Autowired
    private Cat cat;
}

如果@Autowired自動裝配的環境比較複雜,自動裝配無法通過一個註解【@Autowired】完成的時候,我們可以使用@Qualifier(value=“xxx”)去配置@Autowired的使用,指定一個唯一的bean對象注入。

public class Person {
    private String name;
    @Autowired
    @Qualifier(value = "dog2")
    private Dog dog;
    @Autowired
    private Cat cat;
}

@Resource

public class Person {
    private String name;
    @Resource(name = "dog2")
    private Dog dog;
    @Resource
    private Cat cat;

@Resource和@Autowired的區別:

  • 都是用來自動裝配的,都可以放在屬性字段上;
  • @Autowired通過bytype的方式實現,而且必須要求這個對象存在。
  • @Resource默認通過byname的方式實現,如果找不到名字,則通過bytype實現,如果兩個都找不到則報錯。

使用註解開發

前置條件

  • 在Spring4之後,要使用註解開發,必須要保證aop包導入了。
  • 使用註解需要導入context約束,增加註解的支持。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
		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不能唯一自動裝配,則需要通過@Qualifier(value="xxx")
  • @Nullable:字段標記了這個註解,說明這個字段可以爲null
  • @Resource:自動裝配通過名字,類型
  • @Component:組件,放在類上,說明這個類被Spring管理了,就是bean。@Component有幾個衍生註解,下面幾個衍生註解的含義與@Component相同。在web開發中,會按照MVC三層架構分層:
    • dao層:@Repository
    • service層:@Service
    • controller層:@Controller
  • @Scope:作用域
@Component
@Scope("singleton")
public class User {
    @Value("xlq")
    public String name;

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

XML和註解使用總結:

  • XML更加萬能,適用於任何場合,維護簡單方便。
  • 註解不是自己的類不能使用,也就是說,註解只是針對使用註解的這個類。維護相對複雜。
  • 最佳實踐:XML用來管理bean,註解只負責注入屬性。

JavaConfig

注:在Springboot中較爲常見。

實體類:

@Component
public class User {
    @Value("xxx")
    public String name;

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

配置類:通過Java類來進行配置,代替了原先配置文件的作用。

// @Configuration表示這是一個配置類,作用相當於是beans.xml
// 同時也會註冊到Spring容器中,本質上是一個@Component
@Configuration
@ComponentScan("com.xlq")  // 掃描指定包下的註解
public class MyConfig {

    // 註冊一個bean,作用相當於是原先配置文件中的<bean>標籤
    // 這個方法的名字,相當於<bean>標籤中的id屬性
    // 這個方法的返回值,相當於<bean>標籤中的class屬性
    @Bean
    public User getUser() {
        return new User();
    }
}

測試類:

public class MyTest {
    @Test
    public void test() {
        ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        User user = context.getBean("getUser", User.class);
        System.out.println(user);
    }
}

代理模式

代理模式:Spring AOP的底層!
代理模式的分類:

  • 靜態代理
  • 動態代理

靜態代理

代理模式示例:
在這裏插入圖片描述

角色介紹:

  • 抽象角色:一般會使用接口或者抽象類來實現。
  • 真實角色:被代理的角色。比如:房東。
  • 代理角色:代理真實角色,代理真實角色後,還會有一些附屬操作,比如:房屋中介,附屬操作是看房子、籤合同等。
  • 客戶端:訪問代理角色的人。比如:租戶。

代碼步驟:

  1. 接口
  2. 真實角色
  3. 代理角色
  4. 客戶端訪問代理角色

代理模式的好處:

  • 可以使真實角色的操作更加純粹,不用去關注一些公共的業務(比如看房、籤合同)
  • 公共業務都交給代理模式,實現了業務的分工。
  • 公共業務發生擴展的時候,方便集中管理。

缺點:一個真實角色就會產生一個代理角色,代碼量會翻倍,開發效率會降低。(從代碼的角度來理解,創建中介對象的時候會傳入一個房東對象,體現出了靜態代理)

動態代理

動態代理中的各個角色和靜態代理是一致的。但是動態代理的代理類是動態生成的,不是我們直接寫好的。

動態代理分爲兩大類:基於接口的動態代理和基於類的動態代理。

  • 基於接口:JDK動態代理
  • 基於類:cglib
  • Java字節碼實現:JAVAsist

動態代理的好處:

  • 一個動態代理類代理的是一個接口,接口對應的是某一類業務,比如:房屋租賃業務。
  • 一個動態代理類可以代理多個類,這些類實現同一個接口即可。

動態代理中兩個重要的類和接口:Proxy(類)和InvocationHandler(接口)

  • InvocationHandler接口裏面只有一個invoke()方法,該方法是proxy代理實例的調用處理程序。每一個proxy代理實例都有一個關聯的調用處理程序;代理實例調用方法時,方法調用被編碼分派到調用處理程序的invoke方法。invoke方法如下:
/**
    * proxy:代理對象
    * method:代理對象調用接口方法所對應的Method實例。注:我們是通過代理對象來調用真實對象中的方法。
    * args:指代理對象調用方法傳遞的參數
    */
public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable;
  • Proxy類就是用來創建一個代理對象的類,它提供了很多方法,但是我們最常用的是newProxyInstance方法。
public static Object newProxyInstance(ClassLoader loader, 
    Class<?>[] interfaces, 
    InvocationHandler h)

三個參數的含義:

  • loader:一個Classloader對象,定義了由哪個Classloader對象對生成的代理類進行加載;
  • interfaces:一個interface對象數組,表示我們將要給我們的代理對象提供一組什麼樣的接口,如果我們提供了這樣一個接口對象數組,那麼也就是聲明瞭代理類實現了這些接口,代理類就可以調用接口中聲明的所有方法。
  • h:一個InvocationHandler對象,表示的是當動態代理對象調用方法的時候會關聯到哪一個InvocationHandler對象上,並最終由其調用。

參考資料:
Java動態代理InvocationHandler和Proxy學習筆記
Java中InvocationHandler接口中第一個參數proxy詳解

AOP

AOP的實現機制:
在這裏插入圖片描述

AOP的實現方式

前提是先要導入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 class Log implements MethodBeforeAdvice {
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被執行了");
    }
}
public class AfterLog implements AfterReturningAdvice {
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("執行了"+method.getName()+"方法,返回結果爲:"+returnValue);
    }
}

配置文件中註冊bean和配置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.xlq.service.UserServiceImpl"/>
    <bean id="log" class="com.xlq.log.Log"/>
    <bean id="afterLog" class="com.xlq.log.AfterLog"/>

    <!--使用AOP方式一:使用原生Spring API接口-->
    <!--配置AOP:需要導入AOP的約束-->
    <aop:config>
        <!--切入點
        id:該切入點的唯一標識符
        expression:按照表達式進行匹配
        第一個 * 表示任意返回值
        com.xlq.service.UserServiceImpl.*表示匹配UserServiceImpl類裏面的任意方法
        (..)表示有無參數均可,有參數的話可以是任意參數-->
        <aop:pointcut id="pointcut" expression="execution(* com.xlq.service.UserServiceImpl.*(..))"/>

        <!--執行環繞增加-->
        <aop:advisor advice-ref="log" 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 = context.getBean("userService", UserService.class);

        userService.delete();
    }
}

方式二:自定義切面來實現AOP
要點:

  • 在配置文件中自定義切面,切面實際上就是一個類,所以切面要和一個類進行關聯。
  • 在切面裏面定義切入點<aop:pointcut>和相應的通知機制<aop:before><aop:after>

自定義類DiyPoint,所在的包是com.xlq.diy

public class DiyPointCut {
    public void before() {
        System.out.println("在切入點之前");
    }

    public void after() {
        System.out.println("在切入點之後");
    }
}

配置文件:

    <!--自定義類-->
    <bean id="diy" class="com.xlq.diy.DiyPointCut"/>

    <aop:config>
        <!--自定義切面,ref爲要引入的類-->
        <aop:aspect ref="diy">
            <!--切入點-->
            <aop:pointcut id="point" expression="execution(* com.xlq.service.UserServiceImpl.*(..))"/>
            <!--通知-->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>

方式三:使用註解來實現。
自定義類AnnotationPointCut.java

@Aspect // 標註這個類是一個切面
public class AnnotationPointCut {
    @Before("execution(* com.xlq.service.UserServiceImpl.*(..))")
    public void before() {
        System.out.println("方法執行之前");
    }

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

    // 在環繞增強中,我們可以給定一個參數,代表我們要獲取處理切入的點
    @Around("execution(* com.xlq.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("環繞前");

        Signature signature = jp.getSignature();
        System.out.println("signature: "+signature);

        Object proceed = jp.proceed();// 執行方法

        System.out.println("環繞後");
    }
}

XML配置:

<bean id="annotationPointCut" class="com.xlq.diy.AnnotationPointCut"/>
<!--開啓註解支持-->
<aop:aspectj-autoproxy/>  

整合MyBatis

參考資料:mybatis-spring官方文檔

一般步驟(推薦)

1、數據源
2、sqlSessionFactory
3、sqlSessionTemplate
4、接口的實現類

<!--用Spring來管理數據源-->
<!--DataSource:使用Spring的數據源來替換Mybatis的配置 c3p0 dpcp druid
使用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/test?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</bean>


<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/xlq/dao/*.xml"/>
</bean>


<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <!--只能使用構造器注入sqlSessionFactory,因爲SqlSessionTemplate沒有set方法-->
    <constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>

UserMapper接口的實現類:

public class UserMapperImpl implements UserMapper {

    // 我們的所有操作,原來使用sqlSession來執行,現在是使用SqlSessionTemplate
    private SqlSessionTemplate sqlSession;

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

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

還需要把實現類注入到Spring中:

<bean id="userMapper" class="com.xlq.dao.UserMapperImpl">
    <property name="sqlSession" ref="sqlSession"/>
</bean>

SqlSessionDaoSupport

獲取SqlSession的另外一種方法。SqlSessionDaoSupport 是一個抽象的支持類,用來爲你提供 SqlSession。調用 getSqlSession() 方法你會得到一個 SqlSessionTemplate,之後可以用於執行 SQL 方法。

public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
    public List<User> getUserList() {
        return getSqlSession().getMapper(UserMapper.class).getUserList();
    }
}

SqlSessionDaoSupport 需要通過屬性設置一個 sqlSessionFactory 或 SqlSessionTemplate。如果兩個屬性都被設置了,那麼 SqlSessionFactory 將被忽略。 配置信息如下:

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

事務

事務概述

  • 把一組業務當成一個業務來做,要麼都成功,要麼都失敗;
  • 事務在項目開發中,十分重要,設計到數據的一致性問題,不能馬虎;
  • 確保完整性和一致性。

事務ACID特性:

  • 原子性
  • 一致性
  • 隔離性:多個業務可能操作同一個資源,防止數據損壞。
  • 持久性:事務一旦提交,無論系統發生什麼問題,結果都不會再被影響,被持久得寫到存儲器中。

事務管理的引入

在之前的Spring整合Mybatis的工程中,用戶Mapper實現類中加入插入用戶和刪除用戶的功能。

public class UserMapperImpl implements UserMapper {

    // 我們的所有操作,原來使用sqlSession來執行,現在是使用SqlSessionTemplate
    private SqlSessionTemplate sqlSession;

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

    public List<User> getUserList() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = new User(6, "lili", "123456");
        mapper.insertUser(user);

        mapper.deleteUser(6);

        return mapper.getUserList();
    }

    public void insertUser(User user) {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.insertUser(user);
    }

    public void deleteUser(int id) {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.deleteUser(6);
    }
}

配置文件:

<?xml version="1.0" encoding="GBK" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace就是綁定一個對應的Mapper接口-->
<mapper namespace="com.xlq.dao.UserMapper">
    <!--select查詢語句-->
    <select id="getUserList" resultType="user">
        select * from test.user
    </select>

    <insert id="insertUser" parameterType="user">
        insert into test.user(id, name, pwd) values (#{id}, #{name}, #{pwd})
    </insert>

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

可以看到,delete語句含有語法錯誤,這是故意寫錯的。在獲取所有用戶的方法getUserList()中有添加一個用戶,和刪除一個用戶的方法,刪除一個用戶出現錯誤,但是添加的用戶依舊被添加了,所以getUserList()方法不是原子性的。

Spring的事務

一個使用 MyBatis-Spring 的其中一個主要原因是它允許 MyBatis 參與到 Spring 的事務管理中。而不是給 MyBatis 創建一個新的專用事務管理器,MyBatis-Spring 藉助了 Spring 中的 DataSourceTransactionManager 來實現事務管理。

要開啓 Spring 的事務處理功能,在 Spring 的配置文件中創建一個 DataSourceTransactionManager 對象,傳入的 DataSource 可以是任何能夠與 Spring 兼容的 JDBC DataSource。包括連接池和通過 JNDI 查找獲得的 DataSource。

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

Spring支持兩種事務:

  • 聲明式事務
  • 編程式事務

聲明式事務就是用AOP來進行橫向切入,在原有基礎上增加事務功能。編程式事務是用try-catch來捕獲異常,從而進行事務的回滾。所以我們肯定都是用聲明式事務,借用AOP來進行橫向切入。

<!--結合AOP實現事務的織入-->
<!--配置事務通知-->
<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="query" read-only="true"/>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

<!--配置事務切入-->
<aop:config>
    <aop:pointcut id="txPointCut" expression="execution(* com.xlq.dao.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章