• 一文讲明 Spring 的使用 【全网超详细教程】


    文章底部有个人公众号:热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享? 踩过的坑没必要让别人在再踩,自己复盘也能加深记忆。利己利人、所谓双赢。

    前言

    目录结构
    Spring 的相关代码 都公开在了 GitHub上的代码仓库:GitHub源代码地址

    在这里插入图片描述

    目录

    1、搭建一个简单的项目

    项目代码在Github上的位置:
    源码地址

    1.1 导入依赖包
    依赖包下载地址:Spring所需要的核心jar包

    在这里插入图片描述

    将项目导入jar包
    在这里插入图片描述

    1.2 添加一个实体

    package com.zyz.SpringDemo1.entity;
    
    /**
     * @author zyz
     * @version 1.0
     * @data 2023/11/8 13:58
     * @Description: 用户实体
     */
    public class Person {
        private Integer id;
        private String name;
        private String age;
    
        public Person() {
        }
    
        public Person(Integer id, String name, String age) {
            this.id = id;
            this.name = name;
            this.age = age;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAge() {
            return age;
        }
    
        public void setAge(String age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", age='" + age + '\'' +
                    '}';
        }
    
        //定义一个方法
        public void say() {
            System.out.println("我是Person中的方法");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    1.3 添加配置文件
    创建配置文件:application.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">
    
        <!-- 配置Person对象创建-->
        <bean id="person" class="com.zyz.SpringDemo1.entity.Person"></bean>
    
    </beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    1.4 测试单元

    @Test
    public void TestDemo(){
    //1、加载spring配置文件
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(“application.xml”);

    //2、获取配置创建的对象
    Person person = context.getBean("person", Person.class);
    person.setId(1);
    person.setName("张三");
    person.setAge("18");
    System.out.println(person);
    person.say();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    }

    1.5 测试效果
    在这里插入图片描述

    2、IOC

    2.1 ioc概念

    (1)控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理
    (2)使用 IOC 目的:为了耦合度降低
    (3)做入门案例就是 IOC 实现:例子

    IoC (Inversion of Control) : 控制反转, 是一个理论,概念,思想。把对象的创建,赋值,管理工作都交给代码之外的容器实现, 也就是对象的创建是有其它外部资源完成,这样做实现了与解耦合。
    正转:对象的创建、赋值等操作交由程序员手动完成,即使用类似new Xxx(Xxx Xxx)、Xxx.setXxx()语句完成对象的创建与赋值,缺点是一旦程序功能发生改变,涉及到的类就要修改代理,耦合度高,不便于维护和管理。
    反转:对象的创建、赋值等操作交由代码之外的容器实现,有容器代替程序员完成对象的创建、赋值;且当程序功能发生变化时,只需要修改容器的配置文件即可。

    2.2 Di 依赖注入

    2.3.1 依赖注入是什么?

    DI(Dependency Injection) : 依赖注入, 只需要在程序中提供要使用的对象名称就可以, 至于对象如何在容器中创建, 赋值,查找都由容器内部实现。
    DI是ioc技术的实现方式(即容器如何创建对象这一问题的实现方式)

    2.3.2 入门案例 及 环境搭建参考

    入门案例:使用ioc容器创建对象,调用对象的方法

    环境搭建 具体过程 参考 目录一
    1、创建maven项目,目前都是javase项目,推荐使用骨架,选择quickstart
    2、加入maven依赖:分别是spring依赖、junit依赖
    3、创建类(接口和它的实现类)
    4、创建spring需要使用的配置文件
    5、测试

    项目的大致结构
    在这里插入图片描述

    创建类

    public interface PersonService {
         void doWork();
    }
    
    
    public class PersonServiceImpl implements PersonService {
    
        @Override
        public void doWork(){
            System.out.println("认真摸鱼,跳槽、涨工资!!!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    配置文件

    <?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(告诉spring要创建某个类的对象)
            1、id:自定义名称,唯一值,spring通过该id的属性值找到对象
            2class:要创建类的全限定类名
            3、下述的声明语句在spring底层类似与执行了以下代码:
                PersonServiceImpl personService = new PersonServiceImpl();
            4、对象的保存:
                spring将对象保存到内部的map中,map.put(id值,对象)
                map.put("personService",new PersonServiceImpl())
            5、一个bean标签声明一个java对象
            6、spring容器根据bean标签创建对象,尽管存在class属性相同的bean标签,只要是id值不同,
               spring容器就会创建该class的对象
        -->
        <bean id="personService" class="com.zyz.SpringDemo1.service.impl.PersonServiceImpl"></bean>
        <bean id="personService2" class="com.zyz.SpringDemo1.service.impl.PersonServiceImpl"></bean>
    
        <!--
           spring容器也可以创建非自定义类的对象,例如java.lang.String类的对象,只要指定了
           class属性,spring容器就可以创建该类的对象
       -->
        <bean id="myString" class="java.lang.String"/>
    
    </beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    测试单元

      /**
         * 使用spring容器创建对象
         */
        @Test
        public void TestDemo1(){
            //1、指定spring配置文件的名称
            String config = "personBean.xml";
            //2、创建表示spring容器的对象 ApplicationContext
            //ClassPathXmlApplicationContext:表示从类路径中加载spring配置文件
            ApplicationContext ac = new ClassPathXmlApplicationContext(config);
            //3、从容器中获取对象
            PersonService personService = (PersonService)ac.getBean("personService");
            //4、调用方法
            personService.doWork();
        }
    
    
        //获取spring容器中java对象的信息
        @Test
        public void TestDemo2() {
            String config = "personBean.xml";
            ApplicationContext ac = new ClassPathXmlApplicationContext(config);
            //获取spring容器中对象的个数
            int beansCount = ac.getBeanDefinitionCount();
            System.out.println("spring容器中的对象个数="+beansCount);
            //获取spring容器中对象的名称(即bean标签的id值)
            String[] beansNames = ac.getBeanDefinitionNames();
            for (String beanName : beansNames) {
                System.out.println(beanName);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    输出效果

    在这里插入图片描述
    在这里插入图片描述

    2.3.3 案例小结

    spring配置文件中一个bean标签就代表一个对象,该对象有bean标签的id值唯一标识,从spring拿对象是使用getBean(“bean标签的id值”)
    spring默认是使用类的无参构造来创建对象的

    2.3.4 简单类型属性赋值(set注入)

    在入门案例的总结我们说过了spring容器默认是使用无参构造构造来实例化对象的,那么对象的属性必定为初始值,例如int类型为0,boolean类型为false等,那么当我们想使用相关属性进行操作时必然要手动使用set方法给属性赋值,那么有没有办法让容器帮我们完成对象属性的赋值呢?让我们直接就能够从容器中拿到有属性值的对象?答案是肯定的,下面就通过代码演示简单类型的属性赋值。

    set注入要求
    JavaBean必须要有set方法,因为ioc容器是使用javabean的set方法进行属性赋值的
    spring容器调用的是setXxx()方法,而不管对象是否具有Xxx属性(即对象没有的属性只要有set方法也可以实现注入),Xxx不区分大小写

    实体类

    package com.zyz.SpringDemo1.entity;
    
    /**
     * @author zyz
     * @version 1.0
     * @data 2023/11/9 8:42
     * @Description: 学生类
     */
    public class Student {
        private String name;
        private Integer age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    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">
    
        <!--声明Student对象-->
        <bean id="student" class="com.zyz.SpringDemo1.entity.Student">
            <!--
               1、简单类型使用property和value标签给对象属性赋值
               2、简单类型:8个基本类型+String
               3、当spring容器加载到这一行时会在创建完对象的同时使用对象的set方法给属性赋值,底层
                  调用的是对象的set方法
               4、spring容器调用的是setXxx()方法,而不管对象是否具有Xxx属性,Xxx不区分大小写
           -->
            <property name="name" value="张三"></property>
            <property name="age" value="18"></property>
    
            <!--测试对象没有属性的set方法   这部分代码会爆红 报错-->
            <property name="graName" value="s1"/> 
        </bean>
    
    </beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    爆红信息如下
    在这里插入图片描述

    测试单元

    /**
     * 注入的方式 set方式 注入属性测试
     */
    @Test
    public void TestDemo3() {
        String config = "StudentBean.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
    
        Student student = (Student) ac.getBean("student");
        System.out.println(student);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    输出如下
    在这里插入图片描述

    2.3.4.1 正常注入

    Spring 配置文件

     <!--声明Student对象-->
        <bean id="student" class="com.zyz.SpringDemo1.entity.Student">
            <!--
               1、简单类型使用property和value标签给对象属性赋值
               2、简单类型:8个基本类型+String
               3、当spring容器加载到这一行时会在创建完对象的同时使用对象的set方法给属性赋值,底层
                  调用的是对象的set方法
               4、spring容器调用的是setXxx()方法,而不管对象是否具有Xxx属性,Xxx不区分大小写
           -->
            <property name="name" value="张三"></property>
            <property name="age" value="18"></property>
        </bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    测试单元

    @Test
    public void TestDemo3() {
        String config = "StudentBean.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        Student student = (Student) ac.getBean("student");
        System.out.println(student); // 输出结果:Student{name='张三', age=18}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    2.3.4.2 属性注入空值

    Spring配置文件

    <!--声明Student2对象  测试属性值是 null-->
    <bean id="student2" class="com.zyz.SpringDemo1.entity.Student">
        <!--
            两种写法赋予null 值
                第一种 value赋值 写成 null
                第二种 不写value ,直接使用 <null/>
         -->
        <property name="name" value="null"></property>
        <property name="age">
            <null/>
        </property>
    </bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    测试单元

    @Test
    public void TestDemo4() {
        String config = "StudentBean.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        Student student = (Student) ac.getBean("student2");
        System.out.println(student); // 输出结果:Student{name='null', age=null}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    2.3.4.3 注入 特殊符号

    Spring配置文件

    <!--声明Student3对象  测试属性值是 特殊符号-->
    <bean id="student3" class="com.zyz.SpringDemo1.entity.Student">
        <!--
            属性值包含特殊符号
                1、把<> 进行转义 &lt;&gt;
                2、把带特殊符号内容写到 CDATA 比如:<![CDATA[<<王五>>]]>
         -->
        <property name="name">
            <value><![CDATA[<<王五>>]]></value>
        </property>
        <property name="age">
            <value>23</value>
        </property>
    </bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    单元测试

    @Test
    public void TestDemo5() {
        String config = "StudentBean.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        Student student = (Student) ac.getBean("student3");
        System.out.println(student); // 输出结果:Student{name='<<王五>>', age=23}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    错误演示
    在这里插入图片描述

    2.3.4.4 属性集合类注入的方式

    实体类

    package com.zyz.SpringDemo1.entity;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    /**
     * @author zyz
     * @version 1.0
     * @data 2023/11/9 10:01
     * @Description: 大学生实体类
     */
    public class UniversityStudent {
        private String name;
    
        /**
         * 1、数组类型属性 课程
         */
        private String [] courses;
    
        /**
         * 2、list集合类型属性 水果
         */
        private List<String> fruitList;
    
        /**
         * 3、map集合类型属性 体育运动
         */
        private Map<String,String> sportMaps;
    
        /**
         * 4、set集合类型属性 爱好
         */
        private Set<String> hobbySets;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String[] getCourses() {
            return courses;
        }
    
        public void setCourses(String[] courses) {
            this.courses = courses;
        }
    
        public List<String> getFruitList() {
            return fruitList;
        }
    
        public void setFruitList(List<String> fruitList) {
            this.fruitList = fruitList;
        }
    
        public Map<String, String> getSportMaps() {
            return sportMaps;
        }
    
        public void setSportMaps(Map<String, String> sportMaps) {
            this.sportMaps = sportMaps;
        }
    
        public Set<String> getHobbySets() {
            return hobbySets;
        }
    
        public void setHobbySets(Set<String> hobbySets) {
            this.hobbySets = hobbySets;
        }
    
        @Override
        public String toString() {
            return "UniversityStudent{" +
                    "name='" + name + '\'' +
                    ", courses=" + Arrays.toString(courses) +
                    ", fruitList=" + fruitList +
                    ", sportMaps=" + sportMaps +
                    ", hobbySets=" + hobbySets +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    Spring 配置文件

     <!-- 配置 UniversityStudent 对象创建-->
        <bean id="universityStudent" class="com.zyz.SpringDemo1.entity.UniversityStudent">
            <property name="name" value="张三"></property>
    
            <!--1、数组类型属性注入-->
            <property name="courses">
                <array>
                    <value>C语言</value>
                    <value>Go语言</value>
                    <value>Java语言</value>
                </array>
            </property>
    
            <!--2、list类型属性注入  水果-->
            <property name="fruitList">
                <list>
                    <value>🍎苹果</value>
                    <value>🍑桃子</value>
                    <value>🍇葡萄</value>
                </list>
            </property>
    
            <!--3、map类型属性注入 体育运动 -->
            <property name="sportMaps">
                <map>
                    <entry key="basketball" value="篮球"/>
                    <entry key="football" value="足球"/>
                    <entry key="badminton" value="羽毛球"/>
                </map>
            </property>
    
            <!--4、set类型属性注入 爱好-->
            <property name="hobbySets">
                <set>
                    <value>读书</value>
                    <value>敲代码</value>
                    <value>听音乐</value>
                </set>
            </property>
    
        </bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    测试单元

      @Test
        public void TestDemo6() {
            String config = "UniversityStudentBean.xml";
            ApplicationContext ac = new ClassPathXmlApplicationContext(config);
            UniversityStudent universityStudent = (UniversityStudent) ac.getBean("universityStudent");
            System.out.println(universityStudent);
            /**
             * UniversityStudent{name='张三',
             *                   courses=[C语言, Go语言, Java语言],
             *                   fruitList=[🍎苹果, 🍑桃子, 🍇葡萄],
             *                   sportMaps={basketball=篮球, football=足球, badminton=羽毛球},
             *                   hobbySets=[读书, 敲代码, 听音乐]
             *                  }
             */
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2.3.5 非简单类型属性的赋值(set注入)

    上文中的set注入使用property标签的name和value属性给对象属性赋值,但是value知识给简单类型属性赋值,对于非简单类型我们是使用property标签的name和ref属性给对象属性赋值。我们现在给Student类增加一个属性address,该属性是一个引用类型,那当ioc容器创建Student对象时如何给address属性赋值呢?

    2.3.5.1 注入外部对象

    创建一个外部对象 Address ,然后在 Student类中,引入这个外部属性
    Address 代码如下

    package com.zyz.SpringDemo1.entity;
    
    /**
     * @author zyz
     * @version 1.0
     * @data 2023/11/9 11:13
     * @Description:  学生家庭地址
     */
    public class Address {
        private String province;
        private String city;
    
        public String getProvince() {
            return province;
        }
    
        public void setProvince(String province) {
            this.province = province;
        }
    
        public String getCity() {
            return city;
        }
    
        public void setCity(String city) {
            this.city = city;
        }
    
        @Override
        public String toString() {
            return "Address{" +
                    "province='" + province + '\'' +
                    ", city='" + city + '\'' +
                    '}';
        }
    }
    
    Student 中添加 外部属性
    private Address address; // 引用类型属性
    
        public void setAddress(Address address) {
            this.address = address;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    Spring 配置文件

    <!--声明Student对象-->
    <bean id="student" class="com.zyz.SpringDemo1.entity.Student">
        <property name="name" value="张三"></property>
        <property name="age" value="18"></property>
        <!--注入 Address 对象
                   name 属性:类里面属性名称
                   ref 属性:创建 Address 对象 bean 标签 id 值
         -->
        <property name="address" ref="address"></property>
    </bean>
    
    <!--声明Address对象-->
    <bean id="address" class="com.zyz.SpringDemo1.entity.Address">
        <property name="province" value="河南"></property>
        <property name="city" value="郑州"></property>
    </bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    测试单元

    @Test
    public void TestDemo7() {
        String config = "StudentAndAddress.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        Student student = (Student) ac.getBean("student");
        System.out.println(student);
        // 输出结果:Student{name='张三', age=18, address=Address{province='河南', city='郑州'}}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    上文执行流程分析:当ioc容器创建id为student的对象时,会进行set注入,当执行到最后一个propert标签时发现使用了ref属性,则ioc容器知道了name为address的属性是非简单类型,它就会暂时跳过address属性的赋值以及Student对象的创建,转而去配置文件的下文去找bean标签id值等于ref属性值的对象,现将该对象创建,再将该对象赋值给之前的address属性并将Student对象创建。

    2.3.5.2 注入外部对象

    基本上和外部对象的使用过程相似,不同点是 在Spring 配置中,是否使用了ref 。

    Spring配置文件

      <!--内部 Bean 的形式-->
        <bean id="student2" class="com.zyz.SpringDemo1.entity.Student">
            <property name="name" value="李四"></property>
            <property name="age" value="23"></property>
            <!--注入 Address 对象
                       name 属性:类里面属性名称
                       直接在内部创建一个对象
             -->
            <property name="address">
                <bean id="address2" class="com.zyz.SpringDemo1.entity.Address">
                    <property name="province" value="广东"></property>
                    <property name="city" value="广州"></property>
                </bean>
            </property>
        </bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    测试单元

     @Test
        public void TestDemo8() {
            String config = "StudentAndAddress.xml";
            ApplicationContext ac = new ClassPathXmlApplicationContext(config);
            Student student = (Student) ac.getBean("student2");
            System.out.println(student);
            //输出结果:Student{name='李四', age=23, address=Address{province='广东', city='广州'}}
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.3.6 构造注入

    顾名思义,构造注入是使用javabean的构造方法进行属性的赋值的。与set注入一样,构造注入要求javabean必须提供构造方法,且必须是有参构造(如果是无参构造还怎么给属性赋值,对吧),构造注入使用较少,了解就可以了,我们一般使用set注入。看看代码吧,将Student类的set方法注释,加入构造方法,别的地方不用改变,只需要改变spring配置文件即可(这里就可以看出ioc容器与程序的解耦合的好处了)。

    实体类 School 使用构造方法

    public class School {
        private String name;
        private Integer hisotry;
        private Address address;
    
        public School(String name, Integer hisotry, Address address) {
            this.name = name;
            this.hisotry = hisotry;
            this.address = address;
        }
    
        @Override
        public String toString() {
            return "School{" +
                    "name='" + name + '\'' +
                    ", address='" + address + '\'' +
                    ", hisotry=" + hisotry +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    Spring 配置类
    还可以使用 ref 引入 外部bean

    
    
    
        
        
        
    
    
    
        
        
        
    
    
    
    
        
        
        
    
    
    
    
        
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    单元测试

      @Test
        public void TestDemo9() {
            String config = "SchoolBean.xml";
            ApplicationContext ac = new ClassPathXmlApplicationContext(config);
            School school = (School) ac.getBean("school");
            System.out.println(school);
            //构造参数按照顺序 输出结果:School{name='国防科技大学', address='Address{province='湖南', city='长沙'}', hisotry=70}
    
            School school2 = (School) ac.getBean("school2");
            System.out.println(school2);
            //构造参数不按照顺序 输出结果:School{name='国防科技大学', address='Address{province='湖南', city='长沙'}', hisotry=70}
    
            School school3 = (School) ac.getBean("school3");
            System.out.println(school3);
            //构造参数按照下标 输出结果:School{name='国防科技大学', address='Address{province='湖南', city='长沙'}', hisotry=70}
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2.3.7 非简单类型 自动注入

    对于非简单类型,我们在上面是使用ref属性指向一个非简单类型的对象来完成赋值的,那么当ioc容器每次给一个对象的非简单类型属性赋值时,就要在bean标签内部写一行ref这样的代码,这样会造成重复代码的大量堆积,可以使用引用类型的自动注入。
    有两种方式的引用类型自动注入
    byName形式的引用类型自动注入:
    通过java对象引用类型的属性名与spring容器中bean标签对象的id值一样且数据类型是一致的,这样能够实现引用类型的自动注入

    byType形式的引用类型自动注入
    通过java对象引用类型属性的数据类型和spring容器中 bean标签的class属性值是同源关系;

    常见的同源关系:
    1)java引用类型属性数据类型和bean标签的class属性值数据类型一样
    2)java引用类型属性数据类型和bean标签的class属性值数据类型是父子关系
    3)java引用类型属性数据类型和bean标签的class属性值数据类型是接口和实现类关系

    注意:在一个配置文件中,符合条件的同源关系只能有一个

    2.3.7.1 通过byName 的形式

    实体类 这里的 address 要和 bean对象的值一样。byName 就是通过外部属性的name ,去配置文件中找对应的bean对象。

    public class Student {
        private String name;
        private Integer age;
    
        private Address address; // 引用类型属性
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Spring配置文件

       <!--
            创建一个Student对象
                1. 使用byName 自动装配  不写外部bean的情况下 对应的属性值为空
                    测试结果:Student{name='张三', age=18, address=null}
                2.在写外部bean的情况下 外部bean 的 id  和类里边外部对象属性 一致.
                    测试结果:Student{name='张三', age=18, address=Address{province='河南', city='郑州'}}
                3.写外部bean的情况下,但是 id 的值 和 类里外部对象属性值 不一样情况
                    测试结果:Student{name='张三', age=18, address=null}
        -->
        <bean id="student" class="com.zyz.SpringDemo1.entity.Student" autowire="byName">
            <property name="name" value="张三"></property>
            <property name="age" value="18"></property>
        </bean>
    
        <!--外部bean 对象  id 要和 Student 类里边 对应的 Address 对象名一致.-->
        <bean id="address" class="com.zyz.SpringDemo1.entity.Address">
            <property name="province" value="河南"></property>
            <property name="city" value="郑州"></property>
        </bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    单元测试

    /**
     * 注入的方式 set方式 注入属性测试
     *  属性的值赋予 自动装配   byType  bean对象设置成 autowire="byName"  配置文件中寻找和实体类中对应类对象一样的 值
     */
    @Test
    public void TestDemo10() {
        String config = "AutomaticAssemblyBean.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        Student student = (Student) ac.getBean("student");
        System.out.println(student);
        //输出结果:Student{name='张三', age=18, address=Address{province='河南', city='郑州'}}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    匹配详解: 当ioc容器在创建Student对象时,发现使用了autowire属性且属性值为byName,ioc容器就会去Student类中去拿 引用类型的属性名 与和spring配置文件中的 bean标签的id值进行比对,若发现有一致的且数据类型一致,则将该对象赋值给引用类型属性。

    2.3.7.2 通过byType 的形式

    Spring配置文件 这里一定要确保 配置文件中只有一个对象。多个对象会报错



    
    
        
        
    
    
    
    
    
        
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    如果存在多个bean 对象 报错如下

    单元测试

    /**
     * 注入的方式 set方式 注入属性测试
     *  属性的值赋予 自动装配  byType  bean对象设置成 autowire="byType" 会在配置文件中,寻找该类型的对象
     */
    @Test
    public void TestDemo11() {
        String config = "AutomaticAssemblyBean.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        Student student2 = (Student) ac.getBean("student2");
        System.out.println(student2);
        //输出结果: Student{name='李四', age=24, address=Address{province='河南', city='郑州'}}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.3.8 基于注解的 DI 实现

    除了使用配置文件实现 ioc 创建对象的功能外,使用spring提供的注解也可以实现di。下面来介绍注解方式的di实现,下面是spring提供的di实现的常用注解。

    @Component:该注解的功能是使用spring容器创建对象
    1)、在要创建对象的类的声明上方加入该注解,该注解有一个属性value,value为spring创建的该类对象的id值
    2)、开发中使用将value省略,直接使用双引号将值键入即可
    3)、该注解使用类的无参构造创建对象
    @Repository 创建dao类对象,访问数据库的对象
    @Service 创建service类对象,业务层对象
    @Controller 创建控制器对象,用于分发用户的请求和显示处理结果

    下面通过代码来看看@Component注解是怎么实现di的。

    @Component(value = "student")
    public class Student {
        ...
    }
    
    • 1
    • 2
    • 3
    • 4

    //在注解里面 value 属性值可以省略不写
    //默认值是类名称,首字母小写

    //UserService -- userService
    @Component(value = "employee")//
    public class Employee {
        private String ename;
        private String gender;
    }
    
    
    
    @Component("student")
    public class Student {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    该语句就等价为在spring配置文件中进行了以下声明

    但是怎么让配置文件知道哪些类是使用注解进行创建对象的呢?需要在配置文件中声明组件扫描器

    当spring读取配置文件时,读取到组件扫描器声明语句时,就会去base-package指定的包和其子包下去递归的寻找有注解修饰的类,并根据注解的功能去执行相应的动作

    2.3.8.1 简单类型的注解di实现

    简单类型的注入使用@Value注解实现,哪些简单类型要设置属性值,直接在简单类型属性声明语句的上面加入注解@Value即可,并在@Value的括号内键入属性值,注意不论简单类型属性的数据类型,均由双引号将属性值括起来。例如之前的Student类使用注解注入如下。

    @Component("student")
    public class Student {
        @Value("张三")
        private String name;
        @Value("23")
        private int age;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意别忘了该类要加注解@Component注解,因为要创建该类对象。

    2.3.8.2 引用类型的注解di实现

    引用类型的注入使用@Autowired注解完成。
    @Autowired
    @Autowired是spring提供的属性赋值,用于给引用类型赋值,有byName和byType两种方式,默认使用byType方式自动注入
    若是要强制至于byName方式,要在@Autowired注解下面加入 @Qualifier(value = “bean的id”)注解,若程序在给引用类型注入时在xml文件中找不到 该id的bean标签或者手找不到该id的@Component注解,则报错;若不想让程序在赋值失败时报错,可以在@Autowired注解的required属性值置为false
    还是拿Student类的school属性的赋值来举例。
    学生类

    @Component("student")
    public class Student {
        /*引用类型注入(byType方式)
        @Autowired
        private School school;*/
    
        //引用类型赋值(byName方式)
        @Autowired(required = false)
        @Qualifier(value = "mySchool")
        private School school;
    }
    
    School类
    @Component("mySchool")
    public class School {
    
        //注入值
        @Value("西南大学")
        private String schoolAddress;
        @Value("新疆")
        private String homeAddress;
    
        @Override
        public String toString() {
            return "School{" +
                    "schoolAddress='" + schoolAddress + '\'' +
                    ", homeAddress='" + homeAddress + '\'' +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    重点提示:
    在注解里面 value 属性值可以省略不写 默认值是类名称,首字母小写
    例如:UserService – userService
    在这里插入图片描述

    测试单元 中 取对象的时候 如果取值 和命名不一致 则会报如下错误:
    在这里插入图片描述

    2.3.8.3 案例代码

    参考GitHub 上的

    @Qualifier(“userDaoImplTwo”) @Repository(“userDaoImplTwo”)
    当一个类型 有多个对象的时候。这个时候通过类型自动装配。需要 让 @Autowired 和 @Qualifier 配合使用。同时需要指定对象的名称

    自动装配,更具byType ,同时一个类型 有多个对象的时候。需要配合 @Repository

    @Repository("userDaoImplTwo") //起个名称
    public class UserDaoImplTwo implements UserDao{
        @Override
        public void showTime() {
            System.out.println("UserDaoImplTwo showTime ......");
    
        }
    }
    
    
    @Service
    public class UserService {
        @Autowired  //根据类型自动装配
        @Qualifier("userDaoImplTwo")  //根据起的名称
        private UserDao userDao;
    
        public void say(){
            userDao.showTime();
            System.out.println("UserService say .......");
        }
    
    }
    
        @Test
        public void testDemo13(){
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("AnnotationDevelopment.xml");
            UserService userService1 = context.getBean("userService", UserService.class);
            userService1.say();
            //输出结果:UserDaoImplTwo showTime ......
            //        UserService say .......
    
        }
    
    或者简化成这样写:  使用@Resource 
        // 这里的userDaoImplTwo是指bean的ID
        @Resource(name = "userDaoImplTwo")   
        private UserDao userDao;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    3、AOP

    3.1 动态代理

    3.1.1 jdk动态代理

    使用jdk中的Proxy,Method,InvocaitonHanderl创建代理对象。 jdk动态代理要求目标类必须实现接口

    /**
     * @author zyz
     * @version 1.0
     * @data 2023/11/16 20:41
     * @Description: 动态代理 Dao
     */
    public interface DynamicProxiesDao {
         int add(int a,int b);
    
         void update(int a);
    }
    
    
    public class DynamicProxiesDaoImpl implements DynamicProxiesDao{
        @Override
        public int add(int a, int b) {
            System.out.println("执行了add方法");
            return a+b;
        }
    
        @Override
        public void update(int a) {
            System.out.println(a);
        }
    
    }
    
    public class JdkProxy {
        public static void main(String[] args) {
            //创建接口实现类代理对象
            Class[] interfaces = {DynamicProxiesDao.class};
    //        Proxy.newProxyInstance(JdkProxy.class.getClassLoader(), interfaces,
    //                new InvocationHandler() {
    //                    @Override
    //                    public Object invoke(Object proxy, Method method, Object[] args)
    //                            throws
    //                            Throwable {
    //                        return null;
    //                    }
    //                });
            DynamicProxiesDaoImpl dynamicProxiesDao = new DynamicProxiesDaoImpl();
            DynamicProxiesDao dao = (DynamicProxiesDao) Proxy.newProxyInstance(JdkProxy.class.getClassLoader(), interfaces, new DynamicProxiesDaoProxy(dynamicProxiesDao));
            int rs = dao.add(4, 5);
            System.out.println("result:" + rs);
            //测试结果
            // 方法执行之前:add,传递的参数是:[4, 5]
            // 执行了add方法
            // 方法执行之后:com.zyz.SpringDemo1.dao.DynamicProxiesDaoImpl@63947c6b
            // result:9
    
        }
    }
    
    /**
     * 代理对象代码
     */
    class DynamicProxiesDaoProxy implements InvocationHandler {
        /**
         * 1 把创建的是谁的代理对象,把谁传递过来
         *  有参数构造传递
         */
        private Object obj;
    
        public DynamicProxiesDaoProxy(Object obj) {
            this.obj = obj;
        }
    
    
        /**
         *  增强的逻辑
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //方法之前
            System.out.println("方法执行之前:" + method.getName() + ",传递的参数是:" + Arrays.toString(args));
            //被增强的方法执行
            Object rs = method.invoke(obj, args);
            //方法之后
            System.out.println("方法执行之后:" + obj);
            return rs;
        }
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    3.1.2 cglib动态代理

    第三方的工具库,创建代理对象,原理是继承。 通过继承目标类,创建子类。子类就是代理对象。 要求目标类不能是final的,方法也不能是final的
    
    • 1

    3.1.3 动态代理的好处

    在目标类源代码不改变的情况下,增加功能。
    减少代码的重复
    专注业务逻辑代码
    解耦合,让你的业务功能和日志,事务非业务功能分离。

    3.2 什么是AOP

    面向切面编程, 基于动态代理的,可以使用jdk,cglib两种代理方式。Aop就是动态代理的规范化, 把动态代理的实现步骤,方式都定义好了, 让开发人员用一种统一的方式,使用动态代理实现

    3.2.1 AOP常用术语

    Aspect: 切面,给你的目标类增加的功能,就是切面。 像日志,事务都是切面。切面的特点: 一般都是非业务方法,独立使用的。
    JoinPoint:连接点 ,连接业务方法和切面的位置。需要给哪个方法增加切面,这个方法就是连接点。
    Pointcut : 切入点 ,指多个连接点方法的集合。
    目标对象: 给哪个类的方法增加功能, 这个类就是目标对象。
    Advice:通知,通知表示切面功能执行的时间。

    3.2.2 切面的构成

    切面就是要给别的方法进行增强的方法,一个切面有以下三个要素。
    切面的功能代码,切面干什么
    切面的执行位置,使用Pointcut表示切面执行的位置
    切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。

    3.3 使用aspectJ框架实现AOP

    3.3.1 aspectJ简介

    aspectJ是一个开源的专门做aop的框架。spring框架中集成了aspectj框架,通过spring就能使用aspectj的功能。aspectJ框架实现aop有两种方式:
    
    • 1

    使用xml的配置文件 : 配置全局事务
    使用注解,我们在项目中要做aop功能,一般都使用注解, aspectj有5个注解。
    再使用aspectJ做aop之前要先加入aspectJ依赖。

    
      org.springframework
      spring-aspects
      5.2.5.RELEASE
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.3.2 @Before前置通知

    前置通知注解修饰的切面在连接点方法之前执行。下面通过一段代码体验一下。

    声明接口IService
    public interface IService {
        void doSome(String name, int age);
    }
    
    声明实现类ServiceImpl
    @Component(value = "service")
    public class ServiceImpl implements IService {
        @Override
        public void doSome(String name, int age) {
            System.out.println("===doSome()===");
        }
    }
    
    声明切面
    @Component
    @Aspect
    public class MyAspectJ {
    
        /**
         * 定义功能增强方法(方法就是切面)
         * 1、方法的必须为public
         * 2、方法无返回值
         * 3、方法名称自定义
         * 4、方法可以有参数,也可以没有参数
         * 5、方法的定义上方加入注解,表示切入点的执行时机
    
        @Before(value = "execution(public void com.mms.ba01.ServiceImpl.doSome(String,int))")
        public void beforeLog() {
            System.out.println("前置通知->系统当前时间:" + new Date());
        }
        */
    
        /*
            前置通知,带方法参数的切面
            切面方法有参数时要求参数是JoinPoint类型,参数名自定义,该参数就代表了连接点方法,即doSome方法
            使用该参数可以获取切入点表达式、切入点方法签名、目标对象等
         */
        @Before(value = "execution(* *..ServiceImpl.doSome(..))")
        public void beforeLog(JoinPoint jp) {
            System.out.println("连接点方法的方法签名="+jp.getSignature());
            System.out.println("连接点方法的方法名="+jp.getSignature().getName());
            //获取连接点方法参数
            Object[] args = jp.getArgs();
            for (Object arg : args) {
                System.out.println("arg="+arg);
            }
        }
    }
    
    测试
    public class MyTest {
        //aop前置通知
        @Test
        public void test01() {
            String config = "ba01/applicationContext.xml";
            ApplicationContext ac = new ClassPathXmlApplicationContext(config);
            //获取目标对象,此时的service就是spring生成的代理对象
            //注意返回值类型是接口类型,不能是实现类接口,否则报错
            IService service = (IService) ac.getBean("service");
            //使用代理对象执行方法
            service.doSome("张三",23);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    3.3.3 @AfterReturning后置通知

    在IService接口中新增方法:
    Student doStudent(Student student);
    
    在ServiceImpl实现doStudent方法
    @Override
        public Student doStudent(Student student) {
            return student;
        }
    
    切面类代码
    @Component
    @Aspect
    public class MyAspectJ {
        /**
         * @AfterReturning: 后置通知,在连接点方法执行之后执行后置通知方法
         * 方法定义格式:
         * 1、公共方法
         * 2、没有返回值
         * 3、方法名称自定义
         * 4、与前置通知一样,可以有JoinPoint类型参数,该参数表示连接点方法对象;还可以有一个
         *    Object类型参数,用于接收连接点方法的执行结果,注意该参数的参数名必须与切入点表达式
         *    的returning属性的属性值一致,表示将returning属性值赋给Object对象
         */
        /*@AfterReturning(value = "execution(* *..ServiceImpl.doOther(..))", returning = "obj")
            public void afterTransaction(JoinPoint jp, Object obj) {
            System.out.println("doOther方法的返回参数="+obj);
            System.out.println("事务已提交...");
            经过验证:在后置通知切面内不能改变连接点方法的返回值
        }*/
    
        @AfterReturning(value = "execution(* *..ServiceImpl.doStudent(..))", returning = "obj")
        public void afterTransaction(JoinPoint jp, Object obj) {
            System.out.println(obj);
            Student student = new Student();
            student.setName("李四");
            student.setAge(24);
            obj = student;
            System.out.println("===查看是否改变了连接点方法的返回值==="+obj);
            /*
                经过验证:在后置通知切面内不能改变连接点方法的返回值
             */
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    3.3.4 @Around环绕通知(功能最强的通知)

    环绕通知是功能最强的通知,它的本质就是jdk动态代理,他可以在连接点方法之前和之后都可以执行,最厉害的是他可以改变连接点方法的执行结果(返回结果)。还是拿上面的doStudent(Student student)方法来说明,经过验证前置通知和后置通知都不能改变doStudent(Student student)方法的返回值。下面看一下环绕通知是如何做的。

    切面类
    @Aspect
    public class MyAspectJ {
    
        /*
            环绕通知:@Around(切入点表达式)
            1、环绕通知是最重要的一个通知,他表示在连接点方法的前或者后都可以执行,它的本质就是jdk动态代理的invoke
               方法的method参数
            2、定义格式
                a、public
                b、必须有返回值,类型为Object
    
         */
        @Around(value = "pointCut()")
    
        /*
            再次回忆一下jdk动态代理的invoke方法的定义
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            是不是感觉与下面的方法定义眼熟啊,没错,环绕通知切面的定义实质上就是jdk动态代理
         */
        public Object around(ProceedingJoinPoint pj) throws Throwable {
            System.out.println("环绕通知在连接点方法之前执行了...");
            Object result = null;
            result = pj.proceed();
            Student student = new Student();
            student.setName("李四");
            student.setAge(24);
            //改变连接点方法返回值
            result = student;
            System.out.println("事务已提交...");
            return result;
        }
    
        /*
            使用pointcut管理切面表达式
            1、在一个切面类中,若多个切面的切面表达式均为同一个,每次都要写重复的代码,此时就可以使用pointcut来
              管理切面表达式了
            2、定义格式:
                公共public
                无返回值
                无参数
         */
        @Pointcut(value = "execution(* *.doStudent(..))")
        public void pointCut() {
            //空方法体
        }
    }3
    
    3.3.5 完全注解开发
    添加一个配置类  。在类中 完成 开启包扫描 及 aop 注解 
    @Configuration
    @ComponentScan(basePackages = {"com.zyz"})
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    public class ConfigAop {
    }
    
    
    测试单元
        // 完全注解开发 添加配置类 。开启包扫描 及 aop 注解
        @Test
        public void test04(){
            //加载配置类
            ApplicationContext context = new AnnotationConfigApplicationContext(ConfigAop.class);
            IService service = context.getBean("service",IService.class);
            service.doSome("张三",18);
            // 测试结果:
            // 连接点方法的方法签名=void com.zyz.SpringDemo1.service.impl.ServiceImpl.doSome(String,int)
            // 连接点方法的方法名=doSome
            // arg=张三
            // arg=18
            // ===doSome()===
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    4、整合mybatis

    4.1 梳理整合思路

    所谓的spring整合mybatis就是把两者集成在一起,就像使用一个框架一样。

    mybatis框架开发步骤
    定义mapper接口,定义方法
    定义mapper.xml映射文件
    创建mybatis核心配置文件
    创建SqlSession对象,使用该对象生成mapper接口的代理对象执行方法

    spring整合mybatis的核心就是把mybatis开发用到的对象交由spring容器ioc来创建,这样就做到了整合的目的。
    在开发中,我们一般不使用mybatis自带的数据源,而是使用别的数据源,比如c3p0,dbcp等,本人使用的是阿里的druid数据源。

    4.2 整合实现

    4.2.1 环境搭建

    pom中导入相关依赖

    <dependencies>
        <!--单元测试-->
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.11</version>
          <scope>test</scope>
        </dependency>
        <!--spring核心ioc-->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.2.5.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-jdbc</artifactId>
          <version>5.2.5.RELEASE</version>
        </dependency>
        <!--mybatis依赖-->
        <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis</artifactId>
          <version>3.5.1</version>
        </dependency>
        <!--mybatis和spring集成的依赖-->
        <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis-spring</artifactId>
          <version>1.3.1</version>
        </dependency>
        <!--mysql驱动-->
        <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>5.1.9</version>
        </dependency>
        <!--阿里公司的数据库连接池-->
        <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>druid</artifactId>
          <version>1.1.12</version>
        </dependency>
      </dependencies>
    
      <build>
        <!--目的是把src/main/java目录中的xml文件包含到输出结果中。输出到classes目录中-->
        <resources>
          <resource>
            <directory>src/main/java</directory><!--所在的目录-->
            <includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
              <include>**/*.properties
              **/*.xml</include>
            </includes>
            <filtering>false</filtering>
          </resource>
        </resources>
        <!--指定jdk的版本-->
        <plugins>
          <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
              <source>1.8</source>
              <target>1.8</target>
            </configuration>
          </plugin>
        </plugins>
      </build>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    重点是注意resources标签的配置,很多人都是在这里出错导致程序运行报错找不到mapper.xml文件

    4.2.2 案例分析

    数据库脚本

    CREATE TABLE `student`  (
      `stuno` int(11) NOT NULL COMMENT '学号',
      `stuname` varchar(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '姓名',
      `cardid` int(11) NULL DEFAULT NULL,
      `classid` int(11) NULL DEFAULT NULL COMMENT '班级',
      PRIMARY KEY (`stuno`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    本案例从student表中查询学生和新增学生功能。

    public class Student {
        private int stuNo;
        private String stuName;
        private int cardID;
        private int classID;
        public Student() {
        }
        public Student(int stuNo, String stuName, int cardID, int classID) {
            this.stuNo = stuNo;
            this.stuName = stuName;
            this.cardID = cardID;
            this.classID = classID;
        }
        public int getStuNo() {
            return stuNo;
        }
        public void setStuNo(int stuNo) {
            this.stuNo = stuNo;
        }
        public String getStuName() {
            return stuName;
        }
        public void setStuName(String stuName) {
            this.stuName = stuName;
        }
        public int getCardID() {
            return cardID;
        }
        public void setCardID(int cardID) {
            this.cardID = cardID;
        }
        public int getClassID() {
            return classID;
        }
        public void setClassID(int classID) {
            this.classID = classID;
        }
        @Override
        public String toString() {
            return "Student{" +
                    "stuNo=" + stuNo +
                    ", stuName='" + stuName + '\'' +
                    ", cardID=" + cardID +
                    ", classID=" + classID +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    mapper接口

    public interface StudentMapper {
        /**
         * @description: 查询全部
         * @author: zhengyuzhu
         * @date: 2023/11/16 22:34
         * @return: java.util.List
         **/
        List<Student> queryAll();
    
        /**
         * @description: 新增学生
         * @author: zhengyuzhu
         * @date: 2023/11/16 22:34
         * @param: student
         **/
        void addStudent(Student student);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    mapper.xml映射文件

    <?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.zyz.SpringDemo2.mapper.StudentMapper">
    
        <!--查询全部-->
        <select id="queryAll" resultType="Student">
            select * from student
        </select>
    
        <!--新增学生-->
        <insert id="addStudent" parameterType="Student">
            insert into student (stuno,stuname,cardid,classid)
            values (#{stuNo},#{stuName},#{cardID},#{classID})
        </insert>
    </mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    service接口

    public interface IStudentService {
        List<Student> queryAll();
        void addStudent(Student student);
    }
    
    • 1
    • 2
    • 3
    • 4

    service实现类

    public class StudentServiceImpl implements IStudentService {
        //mapper属性
        private StudentMapper mapper;
        //set注入给mapper对象赋值
        public void setMapper(StudentMapper mapper) {
            this.mapper = mapper;
        }
    
        @Override
        public List<Student> queryAll() {
            return mapper.queryAll();
        }
    
        @Override
        public void addStudent(Student student) {
            mapper.addStudent(student);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    mybatis核心配置文件 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>
        <typeAliases>
            <!--
                批量设置别名,会自动的将该包下的所有类定义了别名,别名就是其自身且不区分大小
            -->
            <package name="com.zyz.SpringDemo2.entity" />
        </typeAliases>
        <!--加载映射配置文件-->
        <mappers>
            <mapper resource="com/zyz/SpringDemo2/mapper/StudentMapper.xml"></mapper>
        </mappers>
    </configuration>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里由于数据源对象我们是交由spring容器托管了,因此mybatsi核心配置文件中就没有environments标签了。

    数据库配置相关 db.properties

    jdbc.driverClassName=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/spring_test?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&useSSL=false
    jdbc.username=root
    jdbc.password=root
    
    • 1
    • 2
    • 3
    • 4

    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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--加载数据库配置文件-->
        <context:property-placeholder location="classpath:db.properties"/>
        <!--声明数据源-->
        <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
            <!--set注入给数据库信息赋值,不需要指定驱动类,spring根据url自动识别
            <property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=true&serverTimezone=UTC"/>
            <property name="username" value="root"/>
            <property name="password" value="333"/>-->
    
            <!--使用db配置文件读取数据库信息,格式类似el表达式-->
            <property name="driverClassName" value="${jdbc.driverClassName}"></property>
            <property name="url" value="${jdbc.url}"></property>
            <property name="username" value="${jdbc.username}"></property>
            <property name="password" value="${jdbc.password}"></property>
        </bean>
    
        <!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory的-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!--set注入赋值-->
            <!--set注入,把数据库连接池付给了dataSource属性-->
            <property name="dataSource" ref="myDataSource" />
            <!--mybatis主配置文件的位置
               configLocation属性是Resource类型,读取配置文件
               它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置
            -->
            <property name="configLocation" value="classpath:mybatis-config.xml" />
        </bean>
    
        <!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
            MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象。
        -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <!--指定SqlSessionFactory对象的id-->
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
            <!--指定包名, 包名是dao接口所在的包名。
                MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行
                一次getMapper()方法,得到每个接口的dao对象。
                创建好的dao对象放入到spring的容器中的。 dao对象的默认名称是 接口名首字母小写
            -->
            <property name="basePackage" value="com.zyz.SpringDemo2.mapper"/>
        </bean>
    
        <!--声明service-->
        <bean id="studentServiceImpl" class="com.zyz.SpringDemo2.service.impl.StudentServiceImpl">
            <property name="mapper" ref="studentMapper"/>
        </bean>
    </beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    测试

     //执行查询全部,不使用service
        @Test
        public void test02() {
            String config = "SpringApplication.xml";
            ApplicationContext ac = new ClassPathXmlApplicationContext(config);
            //获取mapper的代理对象
            StudentMapper mapper = (StudentMapper) ac.getBean("studentMapper");
            List<Student> students = mapper.queryAll();
            for (Student student : students) {
                System.out.println("student--->"+student);
            }
        }
    
        //执行增加学生,使用service
        @Test
        public void test03() {
            String config = "SpringApplication.xml";
            ApplicationContext ac = new ClassPathXmlApplicationContext(config);
            //获取service对象
            IStudentService service = (IStudentService) ac.getBean("studentServiceImpl");
            Student student = new Student();
            student.setStuName("呵呵");
            student.setStuNo(1111);
            student.setCardID(1115);
            student.setClassID(1);
            service.addStudent(student);
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    测试结果
    在这里插入图片描述

    5、处理事务

    5.1 什么是事务

    事务是指一组sql语句的集合, 集合中有多条sql语句可能是insert , update ,select ,delete, 我们希望这些多个sql语句都能成功,或者都失败, 这些sql语句的执行是一致的,作为一个整体执行。关于事务最经典的例子就是转账了。
    
    • 1

    5.2 使用spring内部注解处理事务

    5.2.1 环境搭建

    加入事务相关依赖

    <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-tx</artifactId>
          <version>5.2.5.RELEASE</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    案例:数据库有两张表,一张sale表,该表是商品订单信息;一张goods表,该表是商品库存信息。service类有一个业务方法buy,该方法指定要购买商品的id和数量,dao有三个方法分别是像sale表增加一条购买记录、更新goods表的库存信息、在goods表查询指定id商品是否存在。
    下面是两张表的记录信息

    sale表(初始默认添加了一些数据),id字段自增长

    在这里插入图片描述

    goods表
    在这里插入图片描述

    业务流程大致,用户向buy方法传递两个参数,分别是goods表的id字段和购买数量nums。buy方法体中的第一个方法是向sale表增加一条记录,类似与buy(1002,20),那么sale表应该多出来一条记录。

    buy方法的定义

    public void buy(Integer goodsId, Integer nums) {
            //sale表新增购买记录
            //更新库存
            //先查询该编号的商品是否存在
            Goods goods = goodsMapper.queryGoods(goodsId);
            //进行判断
            if (goods == null) {
                //说明商品不存在
            } else if (goods.getAmount() < nums) {
                //说明库存不足
            }
            //能够进行到这里说明商品信息是合法的,可以更新库存
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    首先根据用户传入的参数id和nums向sale表新增一条购买记录。即有商品卖出去了,那么我们理应更新一下库存对吧,不然怎么知道还有多少货,万一别人要买100件商品而你只有50件肯定会导致别人购买失败对吧。那么问题就就来了,当我们执行完向sale表新增记录后,就该更新库存了,我们必须要知道刚卖出去的商品是谁对吧,这个通过用户传入的id和商品表goods的id字段一一对应,所以先去数据库查询用户传入的id是否存在goods表中,若不存在应该将事务回滚,即前面向sale表增加的记录是不应该存在的;同理,若用户传入的id商品在goods表中,但是用户要求的数量大于该商品当前库存,事务也应该回滚;只有当用户传入的id商品和数量都满足条件时我们才应该更新库存并且提交事务。

    实体类 Goods 和 Sale

    package com.zyz.SpringDemo3.entity;
    
    /**
     * @BelongsProject: SpringDemo1
     * @BelongsPackage: com.zyz.SpringDemo3.entity
     * @Author: zhengyuzhu
     * @CreateTime: 2023-11-17  16:40
     * @Description: 商品实体类
     * @Version: 1.0
     */
    public class Goods {
        private Integer id;
        private String name;
        private Integer amount;
        private Float price;
        public Goods() {
        }
        public Goods(Integer id, String name, Integer amount, Float price) {
            this.id = id;
            this.name = name;
            this.amount = amount;
            this.price = price;
        }
        public Integer getId() {
            return id;
        }
        public void setId(Integer id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Integer getAmount() {
            return amount;
        }
        public void setAmount(Integer amount) {
            this.amount = amount;
        }
        public Float getPrice() {
            return price;
        }
        public void setPrice(Float price) {
            this.price = price;
        }
        @Override
        public String toString() {
            return "Goods{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", amount=" + amount +
                    ", price=" + price +
                    '}';
        }
    }
    
    package com.zyz.SpringDemo3.entity;
    
    /**
     * @BelongsProject: SpringDemo1
     * @BelongsPackage: com.zyz.SpringDemo3.entity
     * @Author: zhengyuzhu
     * @CreateTime: 2023-11-17  16:41
     * @Description: 销售实体类
     * @Version: 1.0
     */
    public class Sale {
        private Integer id;
        private Integer gid;
        private Integer nums;
        public Sale() {
        }
        public Sale(Integer id, Integer gid, Integer nums) {
            this.id = id;
            this.gid = gid;
            this.nums = nums;
        }
        public Integer getId() {
            return id;
        }
        public void setId(Integer id) {
            this.id = id;
        }
        public Integer getGid() {
            return gid;
        }
        public void setGid(Integer gid) {
            this.gid = gid;
        }
        public Integer getNums() {
            return nums;
        }
        public void setNums(Integer nums) {
            this.nums = nums;
        }
        @Override
        public String toString() {
            return "Sale{" +
                    "id=" + id +
                    ", gid=" + gid +
                    ", nums=" + nums +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106

    mapper接口 GoodsMapper 和 SaleMapper

    package com.zyz.SpringDemo3.mapper;
    
    import com.zyz.SpringDemo3.entity.Goods;
    
    /**
     * @BelongsProject: SpringDemo1
     * @BelongsPackage: com.zyz.SpringDemo3.mapper
     * @Author: zhengyuzhu
     * @CreateTime: 2023-11-17  16:43
     * @Description: TODO
     * @Version: 1.0
     */
    public interface GoodsMapper {
        /**
         * 更新库存
         * goods表示本次用户购买的商品信息
         * @param goods
         */
        void updateGoods(Goods goods);
    
        /**
         * @description:查询商品信息
         * @author: zhengyuzhu
         * @date: 2023/11/17 16:44
         * @param: id
         * @return: com.zyz.SpringDemo3.entity.Goods
         **/
        Goods queryGoods(Integer id);
    }
    
    
    
    package com.zyz.SpringDemo3.mapper;
    
    import com.zyz.SpringDemo3.entity.Sale;
    
    /**
     * @BelongsProject: SpringDemo1
     * @BelongsPackage: com.zyz.SpringDemo3.mapper
     * @Author: zhengyuzhu
     * @CreateTime: 2023-11-17  16:43
     * @Description: TODO
     * @Version: 1.0
     */
    public interface SaleMapper {
        //增加销售记录
        void addSale(Sale sale);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    mapper.xml映射文件 GoodsMapper.xml 和 SaleMapper.xml

    <?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.zyz.SpringDemo3.mapper.GoodsMapper">
        <!--更新商品信息-->
        <update id="updateGoods" parameterType="Goods">
            update goods set amount = amount - #{amount} where id = #{id}
        </update>
        <!--查询商品信息-->
        <select id="queryGoods" resultType="Goods">
            select * from goods where id = #{id}
        </select>
    </mapper>
    
    
    <?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.zyz.SpringDemo3.mapper.SaleMapper">
        <!--增加销售记录-->
        <insert id="addSale" parameterType="Sale">
            insert into sale (gid,nums) values (#{gid},#{nums})
        </insert>
    </mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    service接口

    package com.zyz.SpringDemo3.service;
    
    import com.zyz.SpringDemo3.entity.Goods;
    
    /**
     * @author zyz
     * @version 1.0
     * @data 2023/11/17 16:48
     * @Description:
     */
    public interface IBuyGoodsService {
    
        /**
         * @description: 根据 ID 查询商品
         * @author: zhengyuzhu
         * @date: 2023/11/17 23:09
         * @param: id
         * @return: com.zyz.SpringDemo3.entity.Goods
         **/
         Goods queryGoodsById(Integer id);
        /**
         * @description:购买商品的方法,goodsId是购买商品的编号,nums是购买商品的数量
         * @author: zhengyuzhu
         * @date: 2023/11/17 16:48
         * @param: goodsId
         * @param: nums
         **/
        void buy(Integer goodsId,Integer nums);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    service实现类

    package com.zyz.SpringDemo3.service.impl;
    
    import com.zyz.SpringDemo3.entity.Goods;
    import com.zyz.SpringDemo3.entity.Sale;
    import com.zyz.SpringDemo3.mapper.GoodsMapper;
    import com.zyz.SpringDemo3.mapper.SaleMapper;
    import com.zyz.SpringDemo3.service.IBuyGoodsService;
    import org.springframework.transaction.annotation.Transactional;
    
    /**
     * @BelongsProject: SpringDemo1
     * @BelongsPackage: com.zyz.SpringDemo3.service.impl
     * @Author: zhengyuzhu
     * @CreateTime: 2023-11-17  16:49
     * @Description: TODO
     * @Version: 1.0
     */
    public class BuyGoodsServiceImpl implements IBuyGoodsService {
    
        private SaleMapper saleMapper;
        private GoodsMapper goodsMapper;
    
        //set注入赋值
        public void setSaleMapper(SaleMapper saleMapper) {
            this.saleMapper = saleMapper;
        }
    
        public void setGoodsMapper(GoodsMapper goodsMapper) {
            this.goodsMapper = goodsMapper;
        }
    
        /**
         * @description: 更具ID 查询商品
         * @author: zhengyuzhu
         * @date: 2023/11/17 23:08
         * @param: id
         * @return: com.zyz.SpringDemo3.entity.Goods
         **/
        @Override
        public Goods queryGoodsById(Integer id) {
            Goods goods = null;
            goods = goodsMapper.queryGoods(id);
            return goods;
        }
    
    
        /**
         * 购买商品方法
         * goodsId表示要购买的商品编号,nums表示购买的数量
         *
         * @param goodsId
         * @param nums
         */
    
        /*
            1、@Transactional定义在需要加入事务处理的方法上面
            2、rollbackFor
                1)当方法发生的异常属于rollbackFor,事务就会回滚
                2)当方法发生的异常不属于rollbackFor,spring就会判断发生的异常是不是运行时异常,
                   若是运行时异常,事务就会回滚
    
        @Transactional(
                propagation = Propagation.REQUIRED,
                isolation = Isolation.DEFAULT,
                readOnly = false,
                rollbackFor = {
                        NullPointerException.class,
                        NotEnoughException.class
                }
    
        )*/
    
        //开发中推荐使用默认值,直接使用@Transactional注解
        @Transactional
        @Override
        public void buy(Integer goodsId, Integer nums) {
            System.out.println("buy开始...");
    
            /*
                记录销售的信息,向sale表添加记录,由于sale表id是自增长的,所以不需要设置id属性
                向sale表添加一条购买记录
             */
            Sale sale = new Sale();
            sale.setGid(goodsId);
            sale.setNums(nums);
            saleMapper.addSale(sale);
    
            //更新库存
            //先查询该编号的商品是否存在
            Goods goods = goodsMapper.queryGoods(goodsId);
            //进行判断
            if (goods == null) {
                //说明商品不存在
                throw new NullPointerException("编号为:" + goodsId + "商品不存在...");
            } else if (goods.getAmount() < nums) {
                //说明库存不足
                throw new NotEnoughException("编号为:"+goodsId+"商品库存不足...");
            }
    
            //能够进行到这里说明商品信息是合法的,可以更新库存
            Goods good = new Goods();
            good.setId(goodsId);
            good.setAmount(nums);
            goodsMapper.updateGoods(good);
    
            System.out.println("buy结束...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108

    异常处理类 NotEnoughException

    package com.zyz.SpringDemo3.service.impl;
    
    /**
     * @BelongsProject: SpringDemo1
     * @BelongsPackage: com.zyz.SpringDemo3.service.impl
     * @Author: zhengyuzhu
     * @CreateTime: 2023-11-18  15:57
     * @Description: 定义异常类
     * @Version: 1.0
     */
    public class NotEnoughException extends RuntimeException {
        public NotEnoughException() {
    
        }
        public NotEnoughException(String msg) {
            super(msg);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    spring配置文件也和整合部分一致,只需要在根标签beans加入以下部分即可

    <!--声明使用spring的事务处理-->
        <!--1、声明事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!--连接的数据库,指定数据源-->
            <property name="dataSource" ref="myDataSource"/>
        </bean>
        <!--2、开启事务注解驱动,告诉sprint使用注解管理事务,创建代理对象-->
        <tx:annotation-driven transaction-manager="transactionManager"/>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    mybatis核心配置文件 TransactionMybatis-config.xml 数据库配置文件同整合 mybatis 这里不在给出。

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <typeAliases>
            <!--
                批量设置别名,会自动的将该包下的所有类定义了别名,别名就是其自身且不区分大小
            -->
            <package name="com.zyz.SpringDemo3.entity" />
        </typeAliases>
        <!--加载映射配置文件-->
        <mappers>
            <mapper resource="com/zyz/SpringDemo3/mapper/GoodsMapper.xml"></mapper>
            <mapper resource="com/zyz/SpringDemo3/mapper/SaleMapper.xml"></mapper>
        </mappers>
    
    
    </configuration>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    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: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">
    
    
    
    
            <!--加载数据库配置文件-->
        <context:property-placeholder location="classpath:db.properties"/>
        <!--声明数据源-->
        <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
            <!--set注入给数据库信息赋值,不需要指定驱动类,spring根据url自动识别
            <property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=true&serverTimezone=UTC"/>
            <property name="username" value="root"/>
            <property name="password" value="333"/>-->
    
            <!--使用db配置文件读取数据库信息,格式类似el表达式-->
            <property name="driverClassName" value="${jdbc.driverClassName}"></property>
            <property name="url" value="${jdbc.url}"></property>
            <property name="username" value="${jdbc.username}"></property>
            <property name="password" value="${jdbc.password}"></property>
        </bean>
    
        <!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory的-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!--set注入赋值-->
            <!--set注入,把数据库连接池付给了dataSource属性-->
            <property name="dataSource" ref="myDataSource" />
            <!--mybatis主配置文件的位置
               configLocation属性是Resource类型,读取配置文件
               它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置
            -->
            <property name="configLocation" value="classpath:TransactionMybatis-config.xml" />
        </bean>
    
        <!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
            MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象。
        -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <!--指定SqlSessionFactory对象的id-->
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
            <!--指定包名, 包名是dao接口所在的包名。
                MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行
                一次getMapper()方法,得到每个接口的dao对象。
                创建好的dao对象放入到spring的容器中的。 dao对象的默认名称是 接口名首字母小写
            -->
            <property name="basePackage" value="com.zyz.SpringDemo3.mapper"/>
        </bean>
    
        <!--声明service-->
        <bean id="buyGoodsService" class="com.zyz.SpringDemo3.service.impl.BuyGoodsServiceImpl">
            <!--dao对象的默认名称是 接口名首字母小写-->
            <property name="saleMapper" ref="saleMapper"/>
            <property name="goodsMapper" ref="goodsMapper"/>
        </bean>
    
        <!--声明使用spring的事务处理-->
        <!--1、声明事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!--连接的数据库,指定数据源-->
            <property name="dataSource" ref="myDataSource"/>
        </bean>
        <!--2、开启事务注解驱动,告诉sprint使用注解管理事务,创建代理对象-->
        <tx:annotation-driven transaction-manager="transactionManager"/>
    
    </beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    测试类

     //不使用事务测试 和使用事务  取决于是否添加  @Transactional  使用默认值就可以了
        @Test
        public void test01() {
            String config = "TransactionApplication.xml";
            ApplicationContext ac = new ClassPathXmlApplicationContext(config);
            //获取service对象
            IBuyGoodsService service = (IBuyGoodsService) ac.getBean("buyGoodsService");
    
            //商品编号不存在情况
            //java.lang.NullPointerException: 编号为:1003商品不存在...
            // service.buy(1006,50);
            //商品库存不足情况
            //com.mms.exception.NotEnoughException: 编号为:1002商品库存不足...
            service.buy(1002,100);
            //正常情况
            // service.buy(1002,2);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    测试结果如下:

    原始数据
    商品表
    在这里插入图片描述

    订单表
    在这里插入图片描述

    购买不存在的商品 或者购买商品的数量超过库存,未开启事务的情况下:sql语句都将执行

       //不使用事务测试
        @Test
        public void test01() {
            String config = "TransactionApplication.xml";
            ApplicationContext ac = new ClassPathXmlApplicationContext(config);
            //获取service对象
            IBuyGoodsService service = (IBuyGoodsService) ac.getBean("buyGoodsService");
    
            //商品编号不存在情况
            //java.lang.NullPointerException: 编号为:1006商品不存在...
            service.buy(1006,50);
             //商品库存不足情况
            //NotEnoughException: 编号为:1002商品库存不足...
            service.buy(1002,100);
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    控制台报错信息如下:
    在这里插入图片描述
    在这里插入图片描述

    查看数据库表情况

    在这里插入图片描述

    购买商品数量不足的情况下,也会添加一条记录

    在这里插入图片描述

    开启事务注解
    在这里插入图片描述

    5.3 使用aspectj框架处理事务(配置文件)

    aspectJ处理事务是基于配置文件形式的,别的地方都和上面一样,只需要更改service实现类的buy方法和spring核心配置文件即可
    1、取消buy方法上的事务注解,因为我们现在是使用aspectJ,基于配置文件
    2、将spring注解形式事务开发的spring配置文件中加入的声明事务管理器的部分换成下面的配置

    <!--
           使用aspectj方式的事务(声明式事务)
           1、aspectj事务适合大型项目,因为其声明在配置文件中,大大与程序解耦合
        -->
        <!--1、声明事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="myDataSource"/>
        </bean>
        <!--
            2、声明业务方法的事务属性(隔离级别、传播行为、超时时间)
                id:自定义
                transaction-manager:事务故管理器对象的id值
        -->
        <tx:advice id="myAdvice" transaction-manager="transactionManager">
            <!--tx:attributes配置事务属性-->
            <tx:attributes>
                <!--
                    tx:method:给具体的方法配置事务属性,method可以有多个,由于给多个方法配置事务
                    name:方法名,不带包名和类名,方法可以使用通配符
                    propagation:传播行为,枚举类
                    isolation:隔离级别
                    rollback-for:你指定的异常全限定类名,发生异常一定回滚
                -->
                <tx:method name="buy"
                           propagation="REQUIRED"
                           isolation="DEFAULT"
                           rollback-for="java.lang.NullPointerException,com.zyz.SpringDemo3.service.impl.NotEnoughException"/>
            </tx:attributes>
        </tx:advice>
        
        <aop:config>
            <!--
            3、配置切入点表达式
            为什么需要配置切入点表达式?
                因为步骤2中的方法不确定是哪一个类、哪一个包的方法,所以需要指定
            -->
            <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
            <!--
                配置增强器,用于关联advice和pointcut
                advice-ref:通知,上面的tx:advice的id值
                pointcut-ref:切入点表达式的id值
            -->
            <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt"/>
        </aop:config>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    完整配置文件

    <?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: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.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
                                http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
        <!--加载数据库配置文件-->
        <context:property-placeholder location="classpath:db.properties"/>
        <!--声明数据源-->
        <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
            <!--set注入给数据库信息赋值,不需要指定驱动类,spring根据url自动识别
            <property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=true&serverTimezone=UTC"/>
            <property name="username" value="root"/>
            <property name="password" value="333"/>-->
    
            <!--使用db配置文件读取数据库信息,格式类似el表达式-->
            <property name="driverClassName" value="${jdbc.driverClassName}"></property>
            <property name="url" value="${jdbc.url}"></property>
            <property name="username" value="${jdbc.username}"></property>
            <property name="password" value="${jdbc.password}"></property>
        </bean>
    
        <!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory的-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!--set注入赋值-->
            <!--set注入,把数据库连接池付给了dataSource属性-->
            <property name="dataSource" ref="myDataSource" />
            <!--mybatis主配置文件的位置
               configLocation属性是Resource类型,读取配置文件
               它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置
            -->
            <property name="configLocation" value="classpath:TransactionMybatis-config.xml" />
        </bean>
    
        <!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
            MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象。
        -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <!--指定SqlSessionFactory对象的id-->
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
            <!--指定包名, 包名是dao接口所在的包名。
                MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行
                一次getMapper()方法,得到每个接口的dao对象。
                创建好的dao对象放入到spring的容器中的。 dao对象的默认名称是 接口名首字母小写
            -->
            <property name="basePackage" value="com.zyz.SpringDemo3.mapper"/>
        </bean>
    
        <!--声明service-->
        <bean id="buyGoodsService" class="com.zyz.SpringDemo3.service.impl.BuyGoodsServiceImpl">
            <!--dao对象的默认名称是 接口名首字母小写-->
            <property name="saleMapper" ref="saleMapper"/>
            <property name="goodsMapper" ref="goodsMapper"/>
        </bean>
    
        <!--
           使用aspectj方式的事务(声明式事务)
           1、aspectj事务适合大型项目,因为其声明在配置文件中,大大与程序解耦合
        -->
        <!--1、声明事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="myDataSource"/>
        </bean>
        <!--
            2、声明业务方法的事务属性(隔离级别、传播行为、超时时间)
                id:自定义
                transaction-manager:事务故管理器对象的id值
        -->
        <tx:advice id="myAdvice" transaction-manager="transactionManager">
            <!--tx:attributes配置事务属性-->
            <tx:attributes>
                <!--
                    tx:method:给具体的方法配置事务属性,method可以有多个,由于给多个方法配置事务
                    name:方法名,不带包名和类名,方法可以使用通配符
                    propagation:传播行为,枚举类
                    isolation:隔离级别
                    rollback-for:你指定的异常全限定类名,发生异常一定回滚
                -->
                <tx:method name="buy"
                           propagation="REQUIRED"
                           isolation="DEFAULT"
                           rollback-for="java.lang.NullPointerException,com.zyz.SpringDemo3.service.impl.NotEnoughException"/>
            </tx:attributes>
        </tx:advice>
    
        <aop:config>
            <!--
            3、配置切入点表达式
            为什么需要配置切入点表达式?
                因为步骤2中的方法不确定是哪一个类、哪一个包的方法,所以需要指定
            -->
            <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
            <!--
                配置增强器,用于关联advice和pointcut
                advice-ref:通知,上面的tx:advice的id值
                pointcut-ref:切入点表达式的id值
            -->
            <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt"/>
        </aop:config>
    </beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
  • 相关阅读:
    卡尔曼滤波:The Scaler Kalman Filter常量卡尔曼滤波器
    2023前端大厂高频面试题之JavaScript篇(5)
    3. Error Handle和获取硬件信息
    在一张 24 GB 的消费级显卡上用 RLHF 微调 20B LLMs
    后视镜为什么要检测反射率
    Linux收发包工具
    java中Collections.addAll方法具有什么功能呢?
    功率放大器的设计要点
    【JavaEE初阶】多线程 _ 基础篇 _ 单例模式
    java数组
  • 原文地址:https://blog.csdn.net/weixin_43304253/article/details/134485329