1. 程式人生 > 實用技巧 >Spring個人筆記

Spring個人筆記

課程流程

  1. Spring概念
  2. IOC容器
  3. AOP
  4. JDBCTemplate
  5. 事務管理
  6. Spring5新特性

Spring框架概述

  1. Spring是輕量級的,開源的JaveEE框架

  2. Spring可以解決企業應用開發的複雜性

  3. Spring有兩個核心部分:AOC和AOP

    • IOC:控制反轉,把建立物件過程交給Spring進行管理
    • AOP:面向切面,不修改原始碼的情況下進行功能增強
  4. Spring特點

    1. 方便解耦,簡化開發
    2. AOP程式設計的支援
    3. 方便程式的測試
    4. 方便整合整合其它框架
    5. 方便進行事務的操作
    6. 降低API開發難度

入門案例

Spring5模組

  1. 匯入jar包,使用maven

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    
  2. 建立普通類,在這個類建立普通方法

    package com.yang;
    
    public class User {
        public void add(){
            System.out.println("add...");
        }
    }
    
  3. 建立spring配置檔案,在配置檔案中配置建立的物件

    • spring配置檔案使用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">
      <!--配置User類物件建立-->
          <bean id="user" class="com.yang.User"/>
      </beans>
      
  4. 進行測試程式碼編寫

    
    public class TestSpring5 {
        @Test
        public void testAdd() {
            //載入spring的配置檔案
            ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
            //傳值為xml中的id值
            User user = applicationContext.getBean("user", User.class);
            user.add();
            //獲取配置建立的物件
        }
    }
    

IOC容器(控制反轉)

1. IOC底層原理

  1. xml解析
  2. 工廠模式
  3. 反射

概念和原理

什麼是IOC

  • 控制反轉,把物件的建立和物件之間的呼叫過程,交給Spring進行管理
  • 使用IOC目的:為了耦合度降低
  • 做入門的案例就是IOC實現

工廠模式

目的:降低耦合度到最低限度

IOC過程,進一步降低耦合度

  1. xml配置檔案,配置建立的物件

    <bean id="dao" class="com.yang.UserDao"/>
    
  2. 第二步 有service和dao類,建立工廠類

    class UserFactory{
    	public static UserDao getDao{
    		String classValue = class屬性值;//1.xml解析得到 
            //2.通過反射得到物件
            Class clazz = Class.forName(classValue);
            return (UserDao)clazz.newInstance();
    	}
    }
    

2. IOC介面(BeanFactory)

  1. IOC思想基於IOC容器完成,IOC容器底層就是物件工廠
  2. Spring提供IOC容器實現的兩種方式:(兩個介面)
    • BeanFactory:IOC容器基本實現,是Spring內部使用的介面,不提供給開發人員使用,載入配置檔案的時候不會建立物件,獲取或使用時才去建立物件。
    • ApplicationContext: BeanFactory介面的子介面,提供更強大的功能。在載入配置檔案的時候就會把配置物件進行建立。
  3. ApplicationContext介面有實現類

3. IOC操作,Bean管理(基於xml)

什麼是Bean管理?包括建立物件與注入屬性

  1. Spring建立物件
  2. Spring注入屬性

3.1 基於xml方式建立物件

<bean id="user" class="com.yang.User"/>
  1. 在spring配置檔案中,使用bean標籤,標籤中新增上對應屬性,就可以實現物件建立
  2. 在bean標籤中有很多屬性,介紹常用屬性
    • id屬性:唯一標識
    • class屬性:類的全路徑
  3. 建立物件的時候預設執行無參的構造方法

3.2 基於xml方式注入屬性

DI:依賴注入,就是注入屬性,DI是IOC的一種實現方式

第一種注入方式,使用set注入

  • 實體類建立

    package com.yang;
    
    public class Book {
        private String bname;
        private String author;
    
        //set方法注入
        public void setBname(String bname, String author) {
            this.bname = bname;
            this.author = author;
        }
    
        public Book() {
    
        }
    
        public void setBname(String bname) {
            this.bname = bname;
        }
    
        public void setAuthor(String author) {
            this.author = author;
        }
    
        //有參構造注入
        public Book(String bname) {
            this.bname = bname;
        }
    
    }
    
  • bean檔案配置

    <!--    set方法注入屬性-->
    <bean id="book" class="com.yang.Book">
        <!--
        使用property完成屬性注入
        name屬性代表欄位名稱
        value為注入的值
        -->
        <property name="bname" value="天龍八部"/>
        <property name="author" value="楊劍"/>
    </bean>
    

第二種注入方式,使用有參構造注入

  • 建立類,定義屬性,建立構造方法

    public class Orders {
        private String name;
        private String address;
    
        public Orders(String name, String address) {
            this.name = name;
            this.address = address;
        }
    }
    
  • 在spring配置檔案中配置

    <bean id="orders" class="com.yang.Orders">
        <constructor-arg name="name" value="電腦"/>
        <constructor-arg name="address" value="中國"/>
    </bean>
    

第三種注入方式:p名稱空間注入(瞭解即可)

  1. 使用p名稱空間注入可以簡化基於xml的配置方式

  2. 在配置檔案中新增p名稱空間

    xmlns:p="http://www.springframework.org/schema/p"
    
  3. 進行屬性注入,在bean標籤裡進行操作

    <bean id="book" class="com.yang.Book" p:bname="名字" p:author="作者"/>
    

IOC操作Bean管理(XML注入其他型別屬性)

  1. 字面量
  2. 注入屬性-外部bean
  3. 注入屬性-內部bean
  4. 級聯賦值

字面量

  1. 空值

    <property name="author">
        <null/>
    </property>
    
  2. 屬性值包含特殊符號

    • 轉義:><

    • CDATE

      <property name="author">
          <value>
              <![CDATA[
                  <<南京>>
              ]]>
          </value>
      </property>
      

注入屬性-外部bean

  1. 建立兩個類service和dao類

  2. 在service呼叫dao裡面的方法

  3. 在Spring配置檔案中進行配置

    <!--    service和dao物件的建立-->
    <bean id="userService" class="com.yang.service.UserService">
        <!--
        注入userDao物件
        name:屬性值:類裡面的屬性名稱
        -->
    
        <property name="userDao" ref="userDaoImpl"/>
    </bean>
    <bean id="userDaoImpl" class="com.yang.dao.UserDaoImpl"/>
    

注入屬性-內部bean和級聯賦值

  1. 一對多關係:部門和員工

    一個部門有多個員工,一個員工屬於某一個部門

  2. 在實體類中體現一對多關係

    員工表示所屬部門,用物件表示

    //部門類
    public class Department {
        private String dName;
    
        public void setdName(String dName) {
            this.dName = dName;
        }
    }
    
    //員工類
    public class Emp {
        private String ename;
        private String gender;
        //員工屬於某一個部門,使用物件形式表示
        private Department department;
        public void setEname(String ename) {
            this.ename = ename;
        }
    
        public void setGender(String gender) {
            this.gender = gender;
        }
    }
    
    
  3. 在spring配置檔案中進行相關配置

        <!--內部bean-->
        <bean id="emp" class="com.yang.bean.Emp">
            <!--        先設定兩個普通屬性-->
            <property name="ename" value="ya"/>
            <property name="gender" value="男"/>
    <!--        物件型別屬性-->
            <property name="department">
                <bean class="com.yang.bean.Department">
                    <property name="dName" value="公司"/>
                </bean>
            </property>
        </bean>
    

    注入屬性:級聯賦值

  4. 第一種方式

    <bean id="emp" class="com.yang.bean.Emp">
        <!--        先設定兩個普通屬性-->
        <property name="ename" value="ya"/>
        <property name="gender" value="男"/>
        <!--        物件型別屬性-->
<!--        級聯賦值-->
        <property name="department" ref="deparment"/>
    </bean>
    <bean id="deparment" class="com.yang.bean.Department">
        <property name="dName" value="財務部"/>
    </bean>
  1. 第二種方式,需要生成deparment的get方法
<bean id="emp" class="com.yang.bean.Emp">
        <!--        先設定兩個普通屬性-->
        <property name="ename" value="ya"/>
        <property name="gender" value="男"/>
        <!--        物件型別屬性-->
<!--        級聯賦值-->
        <property name="department" ref="department"/>
        <property name="department.dName" value="cai"/>
    </bean>
    <bean id="department" class="com.yang.bean.Department">
        <property name="dName" value="財務部"/>
    </bean>

xml注入集合屬性

  1. 注入陣列型別屬性
  2. 注入List集合
  3. 注入Map集合型別的屬性
  • 建立類,定義陣列, list,map,set集合屬性,生成對應的set方法

  • 在spring配置檔案中配置

    <bean id="student" class="com.yang.collectionType.Student">
        <!--        陣列型別-->
        <property name="courses">
            <array>
                <value>java</value>
                <value>資料庫</value>
            </array>
        </property>
        <!--        list型別-->
        <property name="list">
            <list>
                <value>張三</value>
                <value>小三</value>
            </list>
        </property>
        <!--        Map型別-->
        <property name="maps">
            <map>
                <entry key="Java" value="java"/>
                <entry key="PHP" value="php"/>
            </map>
        </property>
        <!--        set-->
        <property name="set">
            <set>
                <value>123</value>
                <value>mysql</value>
            </set>
        </property>
    </bean>
    
  • 在集合裡設定物件型別值

    <property name="courseList">
        <list>
            <ref bean="course1"/>
            <ref bean="course2"/>
        </list>
    </property>
    
    <bean id="course1" class="com.yang.collectionType.Course">
        <property name="cname" value="Spring5框架"/>
    </bean>
    <bean id="course2" class="com.yang.collectionType.Course">
        <property name="cname" value="MyBatis框架"/>
    </bean>
    
  • 把集合注入部分提取出來

    1. 在Spring配置檔案中引入名稱空間 util

      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:util="http://www.springframework.org/schema/util"
             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
                      http://www.springframework.org/schema/util
                      http://www.springframework.org/schema/util/spring-util.xsd
      ">
      
    2. 使用util標籤提取

      <util:list id="bookList">
          <value>三國</value>
          <value>水滸</value>
      </util:list>
      <bean id="book" class="com.yang.collectionType.Book">
          <property name="list" ref="bookList"/>
      </bean>
      

IOC操作Bean管理(FactoryBean)

  1. Spring有兩種型別的Bean,一種普通Bean,另一種:工廠Bean(FactoryBean)

  2. 普通Bean:在配置檔案中定義的Bean型別就是你的返回型別

  3. 工廠Bean:在配置檔案中定義的Bean型別可以和返回型別不一樣

    第一步:讓這個類作為工廠Bean,實現介面FactoryBean

    <bean id="myBean" class="com.yang.factorybean.MyBean">
    
    </bean>
    

    第二步:實現接口裡的方法,在實現的方法中定義返回的bean型別

    public class MyBean implements FactoryBean<Course> {
        //定義返回Bean
        public Course getObject() throws Exception {
            Course course = new Course();
            course.setCname("abc");
            return course;
        }
    
        public Class<?> getObjectType() {
            return null;
        }
    
        public boolean isSingleton() {
            return false;
        }
    }
    

    IOC操作Bean管理(Bean作用域)

  4. 在Spring裡面,設定建立bean例項是單例項還是多例項

  5. 在Spring裡面,預設情況下,bean是單例項物件

  1. 如何設定單例項還是多例項

    • spring配置檔案中bean標籤裡有屬性(scope)用於設定單例項還是多例項

    • scope屬性值

      第一個值 預設值,singleton,表示單例項物件

      第二個值 prototype,多例項物件

scope和prototype的區別

第一:singleton:單例項

​ prototype:多例項

第二:設定scope為singleton的時候,載入配置檔案的時候就會建立一個單例項物件

​ 設定scope值是prototype的時候,不是載入配置檔案時建立物件,在呼叫getBean方法的時候去建立多例項物件

還有requestsession,一般不用,瞭解即可

生命週期: 從物件建立到物件銷燬的的過程

bean生命週期

  1. 通過構造器建立bean例項(無參構造)
  2. 為bean的屬性設定值和對其他bean的引用(呼叫set方法)
  3. 呼叫bean的初始化的方法(需要進行配置)
  4. bean可以使用了(物件獲取到了)
  5. 當容器在關閉的時候,呼叫bean的銷燬方法(需要進行配置銷燬的方法)

演示bean的生命週期

package com.yang.bean;

public class Orders {
    private String oname;

    public void setOname(String oname) {
        this.oname = oname;
        System.out.println("第二部,呼叫set方法設定屬性值");
    }
    public Orders(){
        System.out.println("第一步,無參構造建立bean例項");
    }

    //建立執行的初始化方法
    public void initMethod(){
        System.out.println("第三步,執行初始化的方法");
    }
    //建立銷燬的方法
    public void destroyMethod(){
        System.out.println("第五步,執行銷燬的方法");
    }
}
public void test2(){
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
    Orders orders = context.getBean("orders", Orders.class);
    System.out.println("第四部,獲取建立bean例項的物件");
    System.out.println(orders);
    //手動銷燬bean例項
    context.close();
}

bean的後置處理器,bean生命週期變為七步

bean生命週期

  1. 通過構造器建立bean例項(無參構造)
  2. 為bean的屬性設定值和對其他bean的引用(呼叫set方法)
  3. 把bean例項傳給bean後置處理器的方法postProcessBeforeInitialization
  4. 呼叫bean的初始化的方法(需要進行配置)
  5. 把bean例項傳給bean後置處理器的方法postProcessAfterInitialization
  6. bean可以使用了(物件獲取到了)
  7. 當容器在關閉的時候,呼叫bean的銷燬方法(需要進行配置銷燬的方法)

新增後置處理器效果

  1. 建立類,實現介面BeanPostProcessor,建立後置處理器

    package com.yang.bean;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    
    public class MyBeanPost implements BeanPostProcessor {
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("在初始化之前執行的方法");
            return bean;
        }
    
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("在初始化之後執行的方法");
            return bean;
        }
    }
    
  2. 在bean中配置,自動為所有bean加上

    <bean id="myBeanPost" class="com.yang.bean.MyBeanPost"/>
    

xml自動裝配

什麼是自動裝配?

根據指定裝配規則(屬性名稱或屬性型別),Spring自動將匹配的屬性值進行注入

演示自動裝配過程

  1. 根據屬性名稱進行自動注入 byName

    <bean id="emp" class="com.yang.autowire.Emp" autowire="byName">
        <!--        <property name="dept" ref="dept"/>-->
    </bean>
    <bean id="dept" class="com.yang.autowire.Dept"/>
    
  2. 根據屬性型別進行自動注入 byType

    <bean id="emp" class="com.yang.autowire.Emp" autowire="byType">
        <!--        <property name="dept" ref="dept"/>-->
    </bean>
    <bean id="dept" class="com.yang.autowire.Dept"/>
    

外部屬性檔案

  1. 直接配置資料庫資訊,配置druid連線池

    • 配置druid連線池

    • 引入druid連線池依賴

      <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
          <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
          <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true"/>
          <property name="username" value="root"/>
          <property name="password" value="123456"/>
      </bean>
      
  2. 引入外部屬性檔案配置資料庫連線池

    • 建立外部屬性檔案,properties格式檔案,寫資料庫資訊

      prop.driverClass=com.mysql.jdbc.Driver
      prop.url=jdbc:mysql://localhost:3306/mybatis?useSSL=true
      prop.userName=root
      prop.password=123456
      
    • 把外部properties屬性檔案引入到Spring配置檔案中

      引入context名稱空間

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

      在spring配置檔案中使用標籤引入外部屬性檔案

      寫入

      <context:property-placeholder location="classpath:jdbc.properties"/>
      <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
          <property name="driverClassName" value="${prop.driverClass}"/>
          <property name="url" value="${prop.url}"/>
          <property name="username" value="${prop.userName}"/>
          <property name="password" value="${prop.password}"/>
      </bean>
      

4. IOC操作,Bean管理(基於註解)

  1. 什麼是註解
  2. 使用註解的目的:簡化xml操作

spring針對Bean管理中建立物件提供註解

  1. @Component

  2. @Service

  3. @Controller

  4. @Respository

    上面的四個註解功能是一樣的,都可以用來建立bean例項

基於註解方式實現物件的建立

  1. 引入AOP依賴

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    
    
  2. 開啟元件掃描

    <!--
    開啟元件掃描
    1.如果掃描多個包,多個包用逗號隔開
    2.掃描上層目錄
    -->
    <context:component-scan base-package="com.yang"/>
    
  3. 建立類,在類上面新增物件註解

    //在註解裡的value屬性值可以省略不寫
    //預設值是類的名稱,把首字母小寫
    @Service(value = "userService") //<bean id="userService" class=""/>
    public class UserService {
        public void add() {
            System.out.println("service add..............");
        }
    }
    

開啟元件掃描細節配置

<!--    示例1
use-default-filters="false"  表示現在不使用預設的filter,而是使用自己配置的filter
context:include-filter:設定要掃描哪些內容
-->
<context:component-scan base-package="com.yang" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>

</context:component-scan>
<!--    事例2:
        context:exclude-filter  設定哪些內容不進行掃描
-->
<context:component-scan base-package="com.yang">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>

註解方式屬性注入

  1. @Autowired:根據屬性型別進行自動裝配
  2. @Qualifier:根據屬性名稱進行注入
  3. @Resource:可以根據型別注入,也可以根據名稱注入
  4. @Value:注入普通型別屬性

@Autowired

  1. 把service和dao物件建立,在service和dao類新增建立物件的註解

  2. 在service注入dao,在service類新增dao型別的屬性,在屬性上面使用註解

    //在註解裡的value屬性值可以省略不寫
    //預設值是類的名稱,把首字母小寫
    @Service(value = "userService") //<bean id="userService" class=""/>
    public class UserService {
        //定義dao型別屬性
        //不需要新增set方法
        //新增屬性註解
        @Autowired //根據型別進行注入
        private UserDao userDao;
        public void add() {
            System.out.println("service add..............");
            userDao.add();
        }
    }
    
    @Repository
    public class UserDaoImpl implements UserDao{
        public void add() {
            System.out.println("dao add....");
        }
    }
    

@Qualifier:根據屬性名稱進行注入

  1. 這個@Qualifier註解的使用要和上面的@Autowired一起使用

    @Repository(value = "userDaoImpl1")
    public class UserDaoImpl implements UserDao{
        public void add() {
            System.out.println("dao add....");
        }
    }
    
    @Autowired //根據型別進行注入
    @Qualifier(value = "userDaoImpl1") //根據名稱及逆行注入
    

@Resource:可以根據型別注入,也可以根據名稱注入,包:javax.annotation.Resource

@Service(value = "userService") //<bean id="userService" class=""/>
public class UserService {
    //@Resource //根據型別進行注入
    @Resource(name = "userDaoImpl1")//根據名稱進行注入
    private UserDao userDao;
    public void add() {
        System.out.println("service add..............");
        userDao.add();
    }
}

@Value:注入普通屬性

@Value(value = "yj")
private String name;

完全註解開發

  1. 建立配置類,替代xml配置檔案

    @Configuration  //作為配置類 ,替代xml配置檔案
    @ComponentScan(basePackages = {"com.yang"})
    public class SpringConfig {
    }
    
  2. 測試類

    @Test
    public void test2(){
        //載入配置類
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }
    

AOP(面向切面程式設計)

概念:面向切面程式設計(面向方面程式設計)利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。

通俗描述:不通過修改原始碼方式,在主幹功能裡新增新功能

底層原理

  1. AOP底層使用動態代理方式
    • 有兩種情況的動態代理
    • 有介面情況,使用JDK動態代理
    • 沒有介面情況,使用CGLIB動態代理

有介面情況,使用JDK動態代理

建立UserDao介面實現類代理物件,增強類的方法

沒有介面情況,使用CGLIB動態代理

建立當前類子類的代理物件

AOP(JDK動態代理)

  1. 使用JDK動態代理,使用Proxy類裡面的方法建立代理物件
  2. 呼叫newProxyInstance方法來實現,有三個引數
    • 類載入器
    • 增強方法所在的類,類實現的介面,支援多個介面
    • 實現這個介面InvocationHandler,建立代理物件,寫增強的方法

  1. 編寫JDK動態代理程式碼

    • 建立介面,定義方法

      public interface UserDao {
      
          int add(int a,int b);
          
          String update(String id);
      }
      
    • 建立介面實現類,實現方法

      public class UserDaoImpl implements UserDao{
          public int add(int a, int b) {
              return a+b;
          }
      
          public String update(String id) {
              return id; 
          }
      }
      
    • 使用Proxy建立介面的代理物件

      public class JDKProxy {
          public static void main(String[] args) {
              //建立介面實現類的代理物件
              UserDao userDao = new UserDaoImpl();
              Class[] interfaces = {UserDao.class};
              UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
              int res = dao.add(1, 2);
              System.out.println("result = "+res);
          }
      }
      
      //建立代理物件程式碼
      class UserDaoProxy implements InvocationHandler {
          //把建立的代理物件的本體傳遞過來
          //有慘構造進行傳遞
          private Object obj;
      
          public UserDaoProxy(Object obj) {
              this.obj = obj;
          }
      
          //增強的邏輯
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              //方法之前
              System.out.println("方法前執行 : " + method.getName() + "傳遞的引數:" + Arrays.toString(args));
              //被增強的方法執行
              Object res = method.invoke(obj, args);
              //方法之後
              System.out.println("方法後執行 : " + obj);
              return res;
          }
      }
      

AOP術語

  1. 連線點,類裡面哪些方法可以被增強,這些方法稱為連線點
  2. 切入點,實際被增強的方法,稱為切入點
  3. 通知(增強),
    • 實際增強的邏輯部分稱為通知(增強)
    • 通知有多種型別,前置通知,後置通知,環繞通知,異常通知最終通知(finally)
  4. 切面:把通知應用到切入點的過程

AOP操作(準備)

  1. Spring框架中,一般基於AspectJ實現AOP操作

    AspectJ:不是spring組成部分,獨立AOP框架,一半把AspectJ與Spring框架一起使用。進行AOP相關操作

  2. 基於AspectJ實現AOP操作

    • 基於xml配置檔案
    • 基於註解方式(使用)
  3. 在專案工程中引入AOP相關依賴

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    
  4. 切入點表示式

    • 切入點表示式的作用:知道對哪個類裡面的哪個方法進行增強
    • 語法結構execution([許可權修飾符][返回型別][全類名][方法名稱]([引數列表])

    舉例1:對com.yang.dao.Book類裡面的add進行增強

    execution(* com.yang.dao.Book.add(..))

    舉例2:對com.yang.dao.Book類裡面的所有方法進行增強

    execution(* com.yang.dao.Book.*(..))

    舉例3:對com.yang.dao包裡面的所有類裡面的所有包進行增強

    execution(* com.yang.dao.*.*(..))

AOP操作(AspectJ註解)

  1. 建立類,在類裡面定義方法,

    public class User {
        public void add(){
            System.out.println("add,,,,");
        }
    }
    
  2. 建立增強類(編寫增強的邏輯)

    在增強類裡面建立方法,讓不同方法代表不同通知型別

    //增強的類
    public class UserProxy {
        //前置通知
        public void before(){
            System.out.println("before........");
        }
    }
    
  3. 進行通知的配置

    • 在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:context="http://www.springframework.org/schema/context"
             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/context
             http://www.springframework.org/schema/context/spring-context.xsd
             http://www.springframework.org/schema/aop
             http://www.springframework.org/schema/aop/spring-aop.xsd">
          <!--開啟註解掃描-->
          <context:component-scan base-package="com.yang.aopanno"/>
      </beans>
      
    • 使用註解建立Userh和UserProxy物件

      package com.yang.aopanno;
      
      import org.springframework.stereotype.Component;
      
      //被增強類
      @Component
      public class User {
          public void add(){
              System.out.println("add,,,,");
          }
      }
      
      package com.yang.aopanno;
      
      import org.springframework.stereotype.Component;
      
      //增強的類
      @Aspect
      @Component
      public class UserProxy {
          //前置通知
          public void before(){
              System.out.println("before........");
          }
      }
      
    • 在增強類上面添加註解@Aspect

    • 在Spring配置檔案中,開啟生成代理物件

      <aop:aspectj-autoproxy/>
      
    • 配置不同型別的通知

      在增強類的裡面,在作為通知方法上面新增通知型別註解,使用切入點表示式

package com.yang.aopanno;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

//增強的類
@Component
@Aspect //生成代理物件
public class UserProxy {
    //前置通知
    //@Before註解表示作為前置通知
    @Before(value = "execution(* com.yang.aopanno.User.add(..))")
    public void before() {
        System.out.println("before........");
    }
    //最終通知
    @After(value = "execution(* com.yang.aopanno.User.add(..))")
    public void after() {
        System.out.println("after.........");
    }
    //後置通知(返回通知),有異常不執行
    @AfterReturning(value = "execution(* com.yang.aopanno.User.add(..))")
    public void afterReturning() {
        System.out.println("afterReturning....");
    }

    @AfterThrowing(value = "execution(* com.yang.aopanno.User.add(..))")
    public void afterThrowing() {
        System.out.println("afterThrowing....");
    }
    @Around(value = "execution(* com.yang.aopanno.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("環繞之前。。。");
        proceedingJoinPoint.proceed();
        System.out.println("環繞之後。。。");
    }
}

公共切入點提取

//相同切入點抽取
@Pointcut(value = "execution(* com.yang.aopanno.User.add(..))")
public void pointDemo(){

}
@Before(value = "pointDemo()")
public void before() {
    System.out.println("before........");
}

有多個增強類對同一個方法進行增強,設定增強類優先順序

  1. 在增強類上面添加註解@Order(數字型別值),數字型別值越小,優先順序越高

    @Component//物件建立
    @Aspect//代理物件建立
    @Order(1)
    public class PersonProxy {
        @Before(value = "execution(* com.yang.aopanno.User.add(..))")
        public void before(){
            System.out.println("Person before......");
        }
    }
    
  2. 
    @ComponentScan(basePackages = {"com.yang"})
    @Configuration
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    public class MyConfig {
    
    }
    

AOP操作(AspectJ配置檔案)

  1. 建立兩個類,增強類和被增強類,建立方法

  2. 在spring配置檔案中建立兩個類物件

    <!--    建立物件-->
    <bean id="book" class="com.yang.aopxml.Book"/>
    
    <bean id="bookProxy" class="com.yang.aopxml.BookProxy"/>
    
  3. 在spring配置檔案中配置切入點

        <!--    建立物件-->
        <bean id="book" class="com.yang.aopxml.Book"/>
    
        <bean id="bookProxy" class="com.yang.aopxml.BookProxy"/>
    <!--    配置aop增強-->
        <aop:config>
    <!--        配置切入點-->
            <aop:pointcut id="p" expression="execution(* com.yang.aopxml.Book.buy(..))"/>
    <!--        配置切面-->
            <aop:aspect ref="bookProxy">
    <!--            增強在具體的方法上-->
                <aop:before method="before" pointcut-ref="p"/>
            </aop:aspect>
        </aop:config>
    

JDBCTemplate

概念和準備

Spring框架對JDBC進行封裝,使用JDBCTemplate方便實現對資料庫操作

  1. 引入依賴

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>org.springframework.orm</artifactId>
        <version>3.1.2.RELEASE</version>
    </dependency>
    
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.21</version>
    </dependency>
    
  2. 在Spring配置檔案配置連線池

        <context:property-placeholder location="classpath:database.properties"/>
    <!--    資料庫連線池-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
            <property name="url" value="${prop.url}"/>
            <property name="username" value="${prop.userName}"/>
            <property name="password" value="${prop.password}"/>
            <property name="driverClassName" value="${prop.driverClass}"/>
        </bean>
    
  3. 配置JdbcTemplate物件,注入dataSource

    <!--    建立JDBCTemplate物件-->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!--注入dataSource-->
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
  4. 建立service類與dao類,在dao注入jdbctemplate物件

    配置檔案中開啟元件掃描

    <context:component-scan base-package="com.yang"/>
    

    Dao層

    @Repository
    public class BookDaoImpl implements BookDao{
        //注入JDBCTemplate
        @Autowired
        private JdbcTemplate jdbcTemplate;
    }
    

    Service層

    @Service
    public class BookService {
        //注入Dao
        @Autowired
        private BookDao bookDao;
    }
    

JdbcTemplate操作資料庫(新增)

  1. 建立實體類

    package com.yang.entity;
    
    public class User {
        private String id;
        private String name;
        private String pwd;
    
        @Override
        public String toString() {
            return "User{" +
                    "id='" + id + '\'' +
                    ", name='" + name + '\'' +
                    ", pwd='" + pwd + '\'' +
                    '}';
        }
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getPwd() {
            return pwd;
        }
    
        public void setPwd(String pwd) {
            this.pwd = pwd;
        }
    }
    
  2. 編寫service和dao

    • 在dao進行資料庫新增操作

      兩個引數:1. sql語句

      ​ 2.可變長引數

      @Repository
      public class BookDaoImpl implements BookDao{
          //注入JDBCTemplate
          @Autowired
          private JdbcTemplate jdbcTemplate;
      
          public void add(Book book) {
      //        1.建立sql語句
              String sql = "insert into mybatis.book values(?,?,?)";
      //        2.呼叫方法實現
              Object[] args = {book.getId(), book.getName(), book.getPwd()};
              int update = jdbcTemplate.update(sql,args);
              System.out.println(update);
          }
      }
      

修改與刪除操作

public void updateBook(Book book) {
    String sql = "update mybatis.book set name=?,pwd=? where id=?";
    Object[] args = {book.getName(),book.getPwd(),book.getId()};
    int update = jdbcTemplate.update(sql,args);
    System.out.println(update);
}

public void deleteBook(Integer id) {
    String sql = "delete from mybatis.book where id = ?";
    Object[] args = {id};
    int delete = jdbcTemplate.update(sql,args);
    System.out.println(delete);
}

查詢返回某個值

  1. 查詢表裡有多少條記錄,返回某個值

  2. 使用JDBCTemplate實現查詢返回某個值

    兩個引數:sql語句,返回型別的Class

    public int findCount() {
        String sql = "select count(*) from mybatis.book";
        int count = jdbcTemplate.queryForObject(sql,Integer.class);
        return count;
    }
    

查詢返回物件

第一個引數:sql語句

第二個引數:RowMapper,是介面,返回不同型別的資料,使用這個接口裡面的實現類可以完成資料封裝

第三個引數:可變長引數

查詢返回集合

  1. 查詢圖書列表分頁
  2. 呼叫JDBCTemplate方法實現查詢返回集合

批量操作

  1. JdbcTemplate實現批量新增的操作

    有兩個引數

    第一個引數:sql語句

    第二個引數:List集合,新增多條記錄資料

    public void batchAddBook(List<Object[]> batchArgs) {
        String sql = "insert into mybatis.book values (?,?,?)";
        int[] res = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(Arrays.toString(res));
    }
    

實現批量修改操作

注意:在傳值的時候要按照問號的順序傳

@Override
public void batchUpdate(List<Object[]> batchArgs) {
    String sql = "update mybatis.book set name=?,pwd=? where id=?";
    int[] res = jdbcTemplate.batchUpdate(sql, batchArgs);
    System.out.println(Arrays.toString(res));
}

實現批量刪除操作

@Override
public void batchDelete(List<Object[]> batchArgs) {
    String sql = "delete from mybatis.book where id = ?";
    int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
    System.out.println(Arrays.toString(ints));
}

事務操作

概念

什麼是事務

  1. 事務是資料庫操作最基本單元,邏輯上一組操作,要麼都成功,如果有一個失敗,所有操作都失敗
  2. 典型場景:銀行轉賬

事務四個特性(ACID):

  1. 原子性:不可分割
  2. 一致性:總量不變
  3. 隔離性:兩個人同時操作,不會對彼此產生影響
  4. 永續性:提交後表中資料發生真正變化

搭建環境

  1. 建立資料庫表,新增記錄

  2. 建立service,搭建dao,完成物件的建立和注入。

    • service注入dao
    • dao注入jdbctemplate,在JDBCTemplate注入dataSource
    • 在dao建立兩個方法,多錢和少錢,在service建立轉賬方法
    • 問題,在有異常時一半的方法執行

事務操作過程

        try {
            userDao.reduceMoney();
//        模擬異常
            int i = 1 / 0;
            userDao.addMoney();
            //沒有發生異常,事務提交
        } catch (Exception e) {
            //出現了異常,事務回滾
            e.printStackTrace();
        }

Spring事務管理介紹

  1. 事務一般新增在javaee三層結構裡的Service層
  2. 在Spring進行事務管理操作有兩種方式
    • 程式設計式事務管理
    • 宣告式事務管理(使用)基於註解方式,基於xml配置檔案方式

宣告式事務管理

Spring進行宣告式事務管理,底層使用AOP

Spring事務管理API

​ 1. 提供了一個介面,代表事務管理器,這個介面針對不同的框架提供了不同的實現類

註解方式實現宣告式事務管理

  1. 在Spring配置檔案中配置事務管理器

    <!--    配置事務管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
  2. 在spring配置檔案中開啟事務註解

    • 在spring配置檔案中引入名稱空間tx

      <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"
             xmlns:tx="http://www.springframework.org/schema/tx"
             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
             http://www.springframework.org/schema/tx
             http://www.springframework.org/schema/tx/spring-tx.xsd">
      
    • 開啟事務

      <!--    開啟事務註解-->
      <tx:annotation-driven transaction-manager="transactionManager"/>
      
  3. 在Service類上面(或者service類方法上面)新增事務註解

    • @Transactional 加在類和方法上均可

    • 如果新增在類上面,代表類裡面的所有方法都添加了事務

    • 如果放在方法上面,只是為這個方法新增事務

      @Service(value = "userServiceImpl")
      @Transactional //加在類和方法上均可
      public class UserServiceImpl implements UserService {
          @Autowired
          @Qualifier("userDaoImpl")
          private UserDao userDao;
      
          public void accountMoney() {
              userDao.reduceMoney();
              int i = 1 / 0;
              userDao.addMoney();
      
          }
      }
      

宣告式事務管理引數配置

  1. 在service類上面添加註解@Transactional,在這個註解裡面可以配置事務相關引數

  2. propagation:事務傳播行為

    多事務方法之間執行呼叫,這個過程中事務是如何進行管理的

    事務方法:對資料庫表進行變化的操作

    七種傳播行為

  3. isolation:事務的隔離級別

    事務有特性:隔離性,多事務操作之間不會產生影響。不考慮隔離性會產生很多問題

    有三個讀的問題,髒讀,不可重複讀,虛(幻)讀

    髒讀:一個未提交事務讀取到另一個未提交事務的資料

    不可重複讀:一個未提交事務讀取到一個提交事務修改資料

    虛讀:一個未提交事務讀取到另一提交事務新增資料

    通過設定事務隔離級別,就能解決三個讀的問題

    @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ) 
    
  4. timeout超時時間。

    事務需要在一定時間內進行提交,如果不提交,就會回滾

    預設值是-1,設定時間以秒(s)為單位

  5. readonly:是否只讀

    讀:查詢操作,寫:新增修改刪除

    readOnly預設為false

    設定為true後,只能查詢

  6. rollbackfor,回滾

    設定出現哪些異常進行事務回滾

  7. norollbackfor:不回滾

    設定出現哪些異常不進行回滾

XML宣告式事務管理

  1. 在Spring配置檔案中進行配置

    第一步:配置事務管理器

    第二步:配置通知

    第三步:配置切入點和切面

    <!--    配置事務管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--    配置通知-->
    <tx:advice id="txadvice">
        <!--        配置事務相關引數-->
        <tx:attributes>
            <!--            指定在哪種規則的方法上新增事務-->
            <tx:method name="accountMoney" propagation="REQUIRED"/>
            <!--            <tx:method name="account*"/>-->
        </tx:attributes>
    </tx:advice>
    
    <!--    配置切入點和切面-->
    <aop:config>
        <!--        配置切入點-->
        <aop:pointcut id="pt" expression="execution(* com.yang.service.UserServiceImpl.*(..))"/>
        <!--        配置切面-->
        <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
    </aop:config>
    

完全註解開發

  1. 建立配置類,使用配置類替代xml配置檔案

    @Configuration
    @ComponentScan(basePackages = "com.yang")
    @EnableTransactionManagement //開啟事務
    public class TxConfig {
        //建立資料庫連線池
        @Bean
        public DruidDataSource dataSource() {
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis?useSSL=true&serverTimezone=Asia/Shanghai&CharacterEncoding=utf-8");
            dataSource.setUsername("root");
            dataSource.setPassword("123456");
            return dataSource;
        }
    
        //建立jdbcTemplate模板物件
        @Bean
        public JdbcTemplate jdbcTemplate(DataSource dataSource) {
            //到ioc容器中根據型別找到dataSource
            JdbcTemplate jdbcTemplate = new JdbcTemplate();
            jdbcTemplate.setDataSource(dataSource);
            return jdbcTemplate;
        }
    
        //建立事務管理器
        @Bean
        public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) {
            DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
            dataSourceTransactionManager.setDataSource(dataSource);
            return dataSourceTransactionManager;
        }
    }
    

Spring5框架新功能

  1. 整個Spring5框架基於java8,執行時相容jdk9,許多不建議使用的類和方法在程式碼庫中刪除
  2. spring5框架自帶了通用的日誌封裝
  3. Spring5已經移除了Log4jConfigListener,官方建議使用Log4j2

Spring5框架整合Log4j2

  1. 引入依賴

    <dependencies>
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-api</artifactId>
                <version>2.12.1</version>
            </dependency>
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-core</artifactId>
                <version>2.12.1</version>
            </dependency>
        </dependencies>
    
  2. 建立log4j2.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration>
        <!--<Configuration status="WARN" monitorInterval="30"> -->
        <Appenders>
            <!--*********************控制檯日誌***********************-->
            <console name="Console" target="SYSTEM_OUT">
                <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
            </console>
        </Appenders>
    
        <Loggers>
            <!-- 根日誌設定 -->
            <root level="info">
                <appender-ref ref="Console"/>
            </root>
        </Loggers>
    
    </Configuration>
    

Spring5框架核心容器支援@Nullable註解

@Nullable可以使用在方法上,屬性上面,引數上面

  1. 用在方法上,表示方法返回值可以為空
  2. 註解用在方法引數裡面,表示引數值可以為空
  3. 用在屬性上,屬性值可以為空
  4. Spring5核心容器裡面支援函式式風格

支援整合JUnit5

第一步 引入Spring相關針對測試依賴

SpringWebFlux