乐趣区

关于spring:Spring框架学习记录

Spring 介绍

Spring 框架是什么?

Spring 是于 2003 年衰亡的一个轻量级的 Java 开发框架,它是为了解决企业应用开发的复杂性而创立的。Spring 的外围是管制反转(IoC)和面向切面编程(AOP)。Spring 是能够在 Java SE/EE 中应用的轻量级开源框架。

Spring 的次要作用就是为代码“解耦”,升高代码间的耦合度。就是让对象和对象(模块和模块)之间关系不是应用代码关联,而是通过配置来阐明。即在 Spring 中阐明对象(模 块)的关系。

Spring 依据代码的性能特点,应用 Ioc 升高业务对象之间耦合度。IoC 使得主业务在互相调用过程中,不必再本人保护关系了,即不必再本人创立要应用的对象了。而是由 Spring 容器对立治理,主动“注入”, 注入即赋值。而 AOP 使得零碎级服务失去了最大复用,且不必再由程序员手工将零碎级服务“混淆”到主业务逻辑中了,而是由 Spring 容器对立实现“织入”。

Spring 的长处

Spring 是一个框架,是一个半成品的软件。有 20 个模块组成。它是一个容器治理对象,容器是装货色的,Spring 容器不装文本,数字。装的是对象。Spring 是存储对象的容器。

轻量

Spring 框架应用的 jar 都比拟小,个别在 1M 以下或者几百 kb。Spring 外围性能的所需 的 jar 总共在 3M 左右。Spring 框架运行占用的资源少,运行效率高。不依赖其余 jar

针对接口编程,解耦合

Spring 提供了 Ioc 管制反转,由容器治理对象,对象的依赖关系。原来在程序代码中的 对象创立形式,当初由容器实现。对象之间的依赖解耦合。

AOP 编程的反对

通过 Spring 提供的 AOP 性能,不便进行面向切面的编程,许多不容易用传统 OOP 实现 的性能能够通过 AOP 轻松应酬 在 Spring 中,开发人员能够从繁冗的事务管理代码中解脱进去,通过申明式形式灵便地 进行事务的治理,进步开发效率和品质。

不便集成各种优良的框架

Spring 不排挤各种优良的开源框架,相同 Spring 能够升高各种框架的应用难度,Spring 提供了对各种优良框架(如 Struts,Hibernate、MyBatis)等的间接反对。简化框架的应用。Spring 像插线板一样,其余框架是插头,能够容易的组合到一起。须要应用哪个框架,就把这个插头放入插线板。不须要能够轻易的移除。

Spring 体系结构

Spring 由 20 多个模块组成,它们能够分为数据拜访 / 集成(Data Access/Integration)、Web、面向切面编程(AOP, Aspects)、提供 JVM 的代理(Instrumentation)、音讯发送(Messaging)、外围容器(Core Container)和测试(Test)。

为什么应用 Spring

目标就是缩小对代码的改变,也能实现不同的性能。实现解耦合。

IoC——管制反转

Java 中创建对象有哪些形式

  1. 构造方法
  2. 反射
  3. 序列化
  4. 克隆
  5. IOC
  6. 动静代理

IoC 概述

管制反转(IoC,Inversion of Control),是一个概念,是一种思维。指将传统上由程序代码间接操控的对象调用权交给容器,通过容器来实现对象的拆卸和治理。管制反转就是对对象控制权的转移,从程序代码自身反转到了内部容器。通过容器实现对象的创立,属性赋值,依赖的治理。把对象的创立,赋值,管理工作都交给代码之外的容器实现,也就是对象的创立是有其它内部资源实现。

Spring 框架应用依赖注入(DI)实现 IoC。

Spring 容器是一个超级大工厂,负责创立、治理所有的 Java 对象,这些 Java 对象被称 为 Bean。Spring 容器治理着容器中 Bean 之间的依赖关系,Spring 应用“依赖注入”的形式 来治理 Bean 之间的依赖关系。应用 IoC 实现对象之间的解耦和。

  • 管制:创建对象,对象的属性赋值,对象之间的关系治理
  • 反转:把原来的开发人员治理,创建对象的权限转移给代码之外的容器实现。由容器代替开发人员治理对象。创建对象,

        给属性赋值。
  • 正转:由开发人员在代码中,应用 new 构造方法创建对象,开发人员被动治理对象
  • 容器:是一个服务器软件,一个框架(spring)

依赖与依赖注入

依赖

classA 类中含有 classB 的实例,在 classA 中调用 classB 的办法实现性能,即 classA 对 classB 有依赖。

依赖注入

DI(Dependency Injection),程序代码不做定位查问,这些工作由容器自行实现。依赖注入 DI 是指程序运行过程中,若须要调用另一个对象帮助时,毋庸在代码中创立 被调用者,而是依赖于内部容器,由内部容器创立后传递给程序。Spring 的依赖注入对调用者与被调用者简直没有任何要求,齐全反对对象之间依赖关系 的治理。

依赖注入,只须要在程序中提供要应用的对象名称就能够,至于对象如何在容器中创立,赋值,查找都由容器外部实现。

DI 是 ioc 的技术实现。

spring 是应用的 di 实现了 ioc 的性能,spring 底层创建对象,应用的是反射机制。

spring 是一个容器,治理对象,给属性赋值,底层是反射创建对象。

第一个 Spring 程序

开发环境筹备

  1. jdk 版本 1.8
  2. idea 版本 2020.2.4
  3. Maven 版本 3.6.1

创立工程项目

  1. 创立一个 maven-archetype-quickstart 模版的 Maven 工程

  1. 配置 pom.xml 文件(我创立的子项目)

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.3.8</version>
        </dependency>
  2. 定义 SomeService 接口

    package xyz.rtx3090.service;
    
    public interface SomeService {void doSome();
    }
  3. 编写接口的实现类 SomeServiceImpl

    package xyz.rtx3090.service;
    
    public class SomeServiceImpl implements SomeService{public SomeServiceImpl() {super();
            System.out.println("SomeServiceImpl 实现类的无参构造方法");
        }
        @Override
        public void doSome() {System.out.println("业务办法 doSome()");
        }
    }
  4. 在 src/main/resources/ 目录下创立一个名为 applicationContext.xml 的配置文件,并在配置文件中编写 bean

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!-- 注册 bean 对象
            id:自定义对象的名称,通过 id 在代码中应用对象
            class:类的全限定名称,不能是接口 -->
        <bean id="someService" class="xyz.rtx3090.service.SomeServiceImpl"/>
    </beans>
  5. 定义测试类,并进行 Spring 框架应用的测试

    package xyz.rtx3090;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import xyz.rtx3090.service.SomeServiceImpl;
    
    public class MyTest {
        @Test
        public void test01() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            SomeServiceImpl someService = context.getBean("someService", SomeServiceImpl.class);
            someService.doSome();}
    }

    测试后果:

    SomeServiceImpl 实现类的无参构造方法
    业务办法 doSome()

到这里咱们这一个 Spring 程序曾经创立并运行胜利了,咱们能够仅仅通过 xml 配置文件让 IoC 容器来帮忙咱们创立和配置 Java 对象。

应用 Spring 创立非自定义类对象

  1. 咱们在 第一个 Spring 程序 我的项目代码的根底上批改 applicationContext.xml 配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!-- 应用 Spring 创立非自定义类对象 -->
        <bean id="myDate" class="java.util.Date"/>
    </beans>
  2. 在测试类中进行测试

    package xyz.rtx3090;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import java.util.Date;
    
    public class MyTest {
    
        @Test
        public void test02() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            Date myDate = context.getBean("myDate", Date.class);
            System.out.println(myDate);
        }
    }

    测试后果:

    SomeServiceImpl 实现类的无参构造方法
    Thu Jun 24 15:11:04 CST 2021

容器接口和实现类

ApplicationContext 容器接口

ApplicationContext 用于加载 Spring 的配置文件,在程序中充当“容器”的角色。其实现类有两个。

咱们次要应用 ClassPathXmlApplicationContext 这个实现类,来加载在我的项目门路中的配置文件。

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
应用 Spring 容器创立 Java 对象

基于 XML 的依赖注入(DI)

依赖注入概述

bean 实例在调用无参结构器创建对象后,就要对 bean 对象的属性进行初始化。初始化是由容器主动实现的,称为注入。

依赖注入形式分类

依据注入形式的不同,罕用的有两类:set 办法注入 构造方法注入

set 办法注入

set 注入也叫设值注入,是指通过 setter 办法传入被调用者的实例。这种注入形式简略、直观,因此在 Spring 的依赖注入中大量应用。

    <bean id="student" class="xyz.rtx3090.pojo.Student">
        <property name="id" value="01"/><!--setId(01)-->
        <property name="name" value="Jason"/><!--setName("Jason")-->
        <property name="school" ref="school"/><!--setSchool(school)-->
    </bean>
  1. 创立一个 Maven 我的项目,构造目录如图所示

  2. 编写 pom.xml 配置文件,导入相干 Jar 包

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.3.8</version>
        </dependency>
  3. 编写 School 类(只写 setter 办法和重写 toString 办法)

    package xyz.rtx3090.pojo;
    
    public class School {
        private String name;
        private String address;
    
        //setter
            //toString
    }
  4. 编写 Student 类(只写 setter 办法和重写 toString 办法)

    package xyz.rtx3090.pojo;
    
    public class Student {
        private int id;
        private String name;
        private School school;
    
        //setter
            //toString
    }
  5. 编写 applicationContext.xml 配置文件,并进行 set 办法依赖注入

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="school" class="xyz.rtx3090.pojo.School">
            <property name="name" value="清华大学"/><!--setName("清华大学")-->
            <property name="address" value="北京"/><!--setAddress("北京")-->
        </bean>
    
        <bean id="student" class="xyz.rtx3090.pojo.Student">
            <property name="id" value="01"/><!--setId(01)-->
            <property name="name" value="Jason"/><!--setName("Jason")-->
            <property name="school" ref="school"/><!--setSchool(school)-->
        </bean>
    </beans>
    • 简略属性赋值采纳 value
    • 援用属性赋值采纳 ref
  6. 编写测试类进行测试

    package xyz.rtx3090.pojo;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class MyTest {
        @Test
        public void test01() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            Student student = context.getBean("student", Student.class);
            System.out.println(student);
        }
    }

    测试后果:

    Student{id=1, name=’Jason’, school=School{name=’ 清华大学 ’, address=’ 北京 ’}}

构造方法注入

结构注入是指,在结构调用者实例的同时,实现被调用者的实例化。即应用结构器设 置依赖关系。

    <bean id="dog" class="xyz.rtx3090.pojo.Dog">
        <constructor-arg name="id" value="1011"/>
        <constructor-arg name="name" value="小胖"/>
        <constructor-arg name="healthy" value="true"/>
        <constructor-arg name="animal" ref="animal"/>
    </bean>
  1. 创立一个 Maven 我的项目,构造目录如图所示

  2. 编写 pom.xml 配置文件,导入相干 Jar 包

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.3.8</version>
        </dependency>
  3. 编写 Animal 类(只写有参构造方法和重写 toString 办法)

    package xyz.rtx3090.pojo;
    
    public class Animal {
        private String type;
        private String place;
    
        //constructor(有参)
        //toString
    }
  4. 编写 Cat 类(只写有参构造方法和重写 toString 办法)

    package xyz.rtx3090.pojo;
    
    public class Cat {
        private int id;
        private String name;
        private Boolean healthy;
        private Animal animal;
    
        //constructor(有参)
        //toString
    }
  5. 编写 Dog 类(只写有参构造方法和重写 toString 办法)

    package xyz.rtx3090.pojo;
    
    public class Dog {
        private int id;
        private String name;
        private Boolean healthy;
        private Animal animal;
    
        //constructor
        public Dog(int id, String name, Boolean healthy, Animal animal) {
            this.id = id;
            this.name = name;
            this.healthy = healthy;
            this.animal = animal;
        }
    
        @Override
        public String toString() {
            return "Dog{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", healthy=" + healthy +
                    ", animal=" + animal +
                    '}';
        }
    }
  6. 编写 applicationContext.xml 配置文件,并进行构造方法依赖注入

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="animal" class="xyz.rtx3090.pojo.Animal">
            <constructor-arg name="type" value="pets"/>
            <constructor-arg name="place" value="黄石公园"/>
        </bean>
    
        <bean id="cat" class="xyz.rtx3090.pojo.Cat">
            <constructor-arg name="id" value="1010"/>
            <constructor-arg name="name" value="元宝儿"/>
            <constructor-arg name="healthy" value="true"/>
            <constructor-arg name="animal" ref="animal"/>
        </bean>
    
        <bean id="dog" class="xyz.rtx3090.pojo.Dog">
            <constructor-arg name="id" value="1011"/>
            <constructor-arg name="name" value="小胖"/>
            <constructor-arg name="healthy" value="true"/>
            <constructor-arg name="animal" ref="animal"/>
        </bean>
    </beans>
  7. 编写测试类进行测试

    package xyz.rtx3090.pojo;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class MyTest {
        @Test
        public void test01() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            Cat cat = context.getBean("cat", Cat.class);
            Dog dog = context.getBean("dog", Dog.class);
            System.out.println(cat);
            System.out.println(dog);
        }
    }

    测试后果:

    Cat{id=1010, name=’ 元宝儿 ’, healthy=true, animal=Animal{type=’pets’, place=’ 黄石公园 ’}}
    Dog{id=1011, name=’ 小胖 ’, healthy=true, animal=Animal{type=’pets’, place=’ 黄石公园 ’}}

案例:应用结构注入创立一个零碎类 File 对象
  1. 编写 applicationContext.xml 配置文件,并进行构造方法依赖注入

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!-- 通过结构注入,创立一个 jdk 自带的类对象,门路为 /Users/bernardo/Public/ 星辰大海.mp3-->
        <bean id="file" class="java.io.File">
            <constructor-arg name="parent" value="/Users/bernardo/Public"/>
            <constructor-arg name="child" value="星辰大海.mp3"/>
        </bean>
    </beans>
  2. 在测试类中进行测试

    package xyz.rtx3090.pojo;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import java.io.File;
    import java.io.IOException;
    
    public class MyTest {
        @Test
        public void test02() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            File file = context.getBean("file", File.class);
            boolean newFile = false;
            try {newFile= file.createNewFile();
            } catch (IOException e) {e.printStackTrace();
            }
            if (newFile) {System.out.println("创立 mp3 文件胜利!");
            } else {System.out.println("创立 mp3 文件失败!");
            }
        }
    }

    测试后果:

    创立 mp3 文件胜利!

属性主动注入

留神主动注入只能是对于援用类型,根本属性只能手动赋值,不能主动注入

对于援用类型属性的注入,也可不在配置文件中显示的注入。能够通过为标签 设置 autowire 属性值,为援用类型属性进行隐式主动注入(默认是不主动注入援用类型属性)。依据主动注入判断规范的不同,能够分为两种:

  • byName——依据名称主动注入
  • byType——依据类型主动注入
byName 形式主动注入

当配置文件中被调用者 bean 的 id 值与代码中调用者 bean 类的属性名雷同时,可应用 byName 形式,让容器主动将被调用者 bean 注入给调用者 bean。容器是通过调用者的 bean 类的属性名与配置文件的被调用者 bean 的 id 进行比拟而实现主动注入的。

  1. 编写 Teacher 类(只写 setter 办法和重写 toString 办法)

    package xyz.rtx3090.pojo;
    
    public class Teacher {
        private int id;
        private String name;
    
        //setter
            //toString
    }
  2. 编写 Student 类(只写 setter 办法和重写 toString 办法)

    package xyz.rtx3090.pojo;
    
    public class Student {
        private int id;
        private String name;
        private Teacher teacher;
    
        //setter
            //toString
    }
  3. 编写 applicationContext.xml 配置文件,采纳 byName 形式实现援用属性的主动注入

        <bean id="teacher" class="xyz.rtx3090.pojo.Teacher">
            <property name="id" value="01"/>
            <property name="name" value="Jason"/>
        </bean>
    
        <bean id="student" class="xyz.rtx3090.pojo.Student" autowire="byName">
            <property name="id" value="01"/>
            <property name="name" value="Bernardo"/>
            <!-- 下面 bean 的 id 属性与 student 类的属性名统一,所以能够采纳 byName 的形式主动注入 -->
        </bean>
byType 形式主动注入

应用 byType 形式主动注入,要求:配置文件中被调用者 bean 的 class 属性指定的类,要与代码中调用者 bean 类的某援用类型属性类型同源。即要么雷同,要么有 is-a 关系(子 类,或是实现类)。但这样的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配哪一个了。

  1. 编写 Cat 类(只写 setter 办法和重写 toString 办法)

        package xyz.rtx3090.pojo;
    
        public class Cat {
            private String name;
            private String color;
            private Boolean healthy;
    
            //setter
                    //toString
        }
  2. 编写 Person 类(只写 setter 办法和重写 toString 办法)

    package xyz.rtx3090.pojo;
    
    public class Person {
        private int id;
        private String name;
        private Cat cat;
    
        //setter
            //toString
    }
  3. 编写 applicationContext.xml 配置文件,采纳 byType 形式实现援用属性的主动注入

        <bean id="cat" class="xyz.rtx3090.pojo.Cat">
            <property name="name" value="元宝儿"/>
            <property name="color" value="yellow"/>
            <property name="healthy" value="true"/>
        </bean>
    
        <bean id="person" class="xyz.rtx3090.pojo.Person" autowire="byType">
            <property name="id" value="01"/>
            <property name="name" value="Jason"/>
            <!-- 下面 bean 的 class 属性类型与 person 类的某援用属性类型同源,所以能够采纳 byType 的形式主动注入 -->
        </bean>

为利用指定多个 Spring 配置文件

在理论利用里,随着利用规模的减少,零碎中 Bean 数量也大量减少,导致配置文件变 得十分宏大、臃肿。为了防止这种状况的产生,进步配置文件的可读性与可维护性,能够将 Spring 配置文件分解成多个配置文件。

多个配置文件中有一个总文件,总配置文件将各其它子文件通过引入。在 Java 代码中只须要应用总配置文件对容器进行初始化即可。

  1. 创立第一个 spring 配置文件xyz/rtx3090/pojo/spring-cat.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="cat" class="xyz.rtx3090.pojo.Cat">
            <property name="name" value="元宝儿"/>
            <property name="color" value="yellow"/>
            <property name="healthy" value="true"/>
        </bean>
    </beans>
  2. 创立第二个 spring 配置文件xyz/rtx3090/pojo/spring-person.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="person" class="xyz.rtx3090.pojo.Person" autowire="byType">
            <property name="id" value="01"/>
            <property name="name" value="Jason"/>
            <!-- 下面 bean 的 class 属性类型与 person 类的某援用属性类型同源,所以能够采纳 byType 的形式主动注入 -->
        </bean>
    </beans>
  3. 创立第三个 spring 配置文件total.xml,用于整合之前的两个配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <import resource="classpath:xyz/rtx3090/pojo/spring-cat.xml"/>
        <import resource="classpath:xyz/rtx3090/pojo/spring-person.xml"/>
    </beans>
  4. 三个文件的目录构造如图所示

基于注解的 DI

组件扫描

对于 DI 应用注解,将不再须要在 Spring 配置文件中申明 bean 实例。Spring 中应用注解,须要在原有 Spring 运行环境根底上再做一些扭转。须要在 Spring 配置文件中配置组件扫描器,用于在指定的根本包中扫描注解。

扫描单个包
<context:component-scan base-package="xyz.rtx3090.pojo"/>
扫描多个包
  1. 应用多个 context:component-scan 指定不同的包门路

        <context:component-scan base-package="xyz.rtx3090.pojo"/>
        <context:component-scan base-package="xyz.rtx3090.mapper"/>
  2. 指定 base-package 的值应用分隔符【分隔符能够应用逗号,分号;还能够应用空格,但不倡议应用空格】

    <context:component-scan base-package="xyz.rtx3090.pojo;xyz.rtx3090.mapper"/>
  3. 指定 base-package 到父包名

    <context:component-scan base-package="xyz.rtx3090"/>

    但不倡议应用顶级的父包,扫描的门路比拟多,导致容器启动工夫变慢。指定到指标包和合 适的。也就是注解所在包全门路。

定义 Bean 的注解 @Component

须要在类上应用注解 @Component,该注解的 value 属性用于指定该 bean 的 id 值。作用相等于 applicationContext.xml 配置文件中的<bean id=""class=""/

@Component(value = "student")
public class Student{
  private int id;
  private String name;
  private School school;
}

@Component(value = “student”)还能够有上面两种写法:

  1. @Component(“student”)——省略 value
  2. @Component——省略赋值命名,这样 value 的值默认为小写类名

另外,Spring 还提供了 3 个创建对象的注解:

  1. @Repository——用于对 DAO 实现类进行注解
  2. @Service——用于对 Service 实现类进行注解
  3. @Controller——用于对 Controller 实现类进行注解

这三个注解与 @Component 都能够创建对象,但这三个注解还有其余的含意,@Service 创立业务层对象,业务层对象能够退出事务性能,@Controller 注解创立的对象能够作为处 理器接管用户的申请。@Repository,@Service,@Controller 是对 @Component 注解的细化,标注不同层的对 象。即长久层对象,业务层对象,管制层对象。

简略类型属性注入 @Value

须要在属性上应用注解 @Value,该注解的 value 属性用于指定要注入的值。应用该注解实现属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加 到 setter 上。

@Component(value = "school")
public class School {@Value("清华大学")
    private String name;
    @Value("北京")
    private String address;
  
}

byType 援用类型属性主动注入 @Autowired

须要在援用属性上应用注解 @Autowired,该注解默认应用按类型主动拆卸 Bean 的形式。应用该注解实现属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加 到 setter 上。

@Component(value = "student")
public class Student {@Value("10")
    private int id;
    @Value("Bernardo")
    private String name;
    @Autowired
    private School school;

}

byName 主动注入 @Autowired 与 @Qualifier

如果须要指定 name 主动注入,就须要在援用属性上联结应用注解 @Autowired 与 @Qualifier。@Qualifier 的 value 属性用 于指定要匹配的 Bean 的 id 值。类中无需 set 办法,也可加到 set 办法上(@Qualifier 不能独自应用)。

@Component(value = "student")
public class Student {@Value("10")
    private int id;
    @Value("Bernardo")
    private String name;
    @Autowired
    @Qualifier("school")
    private School school;

}

@Autowired 还有一个属性 required,默认值为 true,示意当匹配失败后,会终止程序运 行。若将其值设置为 false,则匹配失败,将被疏忽,未匹配的属性值为 null。

@Component(value = "student")
public class Student {@Value("10")
    private int id;
    @Value("Bernardo")
    private String name;
    @Autowired(required = false)
    @Qualifier("school")
    private School school;

}

JDK 注解 @Resource 主动注入

Spring 提供了对 jdk 中 @Resource 注解的反对。@Resource 注解既能够按名称匹配 Bean,也能够按类型匹配 Bean。默认是按名称注入。应用该注解,要求 JDK 必须是 6 及以上版本。@Resource 可在属性上,也可在 set 办法上。

  • 首先依照默认的与属性名雷同的名称匹配。
  • 默认名称匹配失败,则依照类型进行匹配
  • 当然咱们也能够采纳 name 属性来指定匹配的名称
  1. 默认按名称注入,其次依照类型注入

    @Resource 注解若不带任何参数,采纳默认按属性名的形式注入,如果按默认属性名注入失败,则会依照类型进行 Bean 的匹配注入。

    @Component(value = "student")
    public class Student {@Value("10")
        private int id;
        @Value("Bernardo")
        private String name;
        @Resource
        private School school;
    
    }
  2. 设置按指定名称注入

    @Resource 注解指定其 name 属性,则 name 的值即为依照名称进行匹配的 Bean 的 id。

    @Component(value = "student")
    public class Student {@Value("10")
        private int id;
        @Value("Bernardo")
        private String name;
        @Resource(name = "school")
        private School school;
    
    }

小结

  1. @Autowired 与 @Resource 都能够用来拆卸 bean。都能够写在字段上,或写在 setter 办法上。
  2. @Autowired 默认按类型拆卸(属于 spring 标准),其次依照名字拆卸,但无奈指定名字。默认状况下必须要求依赖对象必须存在,如果要容许 null 值,能够设置它的 required 属性为 false,如:@Autowired(required=false),如果咱们想应用名称拆卸能够联合 @Qualifier 注解进行应用
  3. @Resource(属于 J2EE 标准),默认依照名称进行拆卸,名称能够通过 name 属性进行指定。如果没有指定 name 属性,当注解写在字段上时,默认取字段名进行依照名称查找,如果注解写在 setter 办法上默认取属性名进行拆卸。当找不到与名称匹配的 bean 时才依照类型进行拆卸。然而须要留神的是,如果 name 属性一旦指定,就只会依照名称进行拆卸。
  4. 它们的作用雷同都是用注解形式注入对象,但执行程序不同。@Autowired 先 byType,@Resource 先 byName。

案例演示

  1. 创立 Maven 我的项目,工程构造如图所示

  2. 配置 pom.xml 文件

        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.3.8</version>
        </dependency>
  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"
           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:component-scan base-package="xyz.rtx3090.pojo"/>
    </beans>
  4. 编写 School 类,采纳注解给进行属性的依赖注入

    package xyz.rtx3090.pojo;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    @Component(value = "school")
    public class School {@Value("清华大学")
        private String name;
        @Value("北京")
        private String address;
    
        //setter
            //toString
    }
  5. 编写 Student 类,采纳注解给进行属性的依赖注入

    package xyz.rtx3090.pojo;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    @Component(value = "student")
    public class Student {@Value("10")
        private int id;
        @Value("Bernardo")
        private String name;
        @Autowired// 依据类型进行主动注入
        private School school;
    
        //setter
            //toString
    }
  6. 在测试类中进行测试

        @Test
        public void test01() {ApplicationContext context = new ClassPathXmlApplicationContext("xyz/rtx3090/pojo/applicationContext.xml");
            Student student = context.getBean("student", Student.class);
            System.out.println(student);
        }

    创立调用对象胜利,并胜利注入依赖!测试后果:

    Student{id=10, name=’Bernardo’, school=School{name=’ 清华大学 ’, address=’ 北京 ’}}

xml 与注解比照

xml 长处与毛病

长处
  • 不便
  • 直观
  • 高效(代码少,没有配置文件的书写那么简单)
毛病

以硬编码的形式写入到 Java 代码中,批改是须要从新编译代码的

注解长处与毛病

长处
  • 配置和代码是拆散的
  • 在 xml 中做批改,无需编译代码,只需重启服务器即可将新的配置加载
毛病

编写麻烦,效率低,大型项目过于简单

AOP 面向面编程

不应用 AOP 的开发方式

  1. 创立 Maven 我的项目,工程目录如图所示

  2. 配置 pom.xml 文件

        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.3.8</version>
        </dependency>
  3. 定义接口 UserService

    package xyz.rtx3090.service;
    
    public interface UserService {void doSome();
        void doOther();}
  4. 实现接口类 UserServiceImpl

    package xyz.rtx3090.service;
    
    import org.springframework.stereotype.Service;
    
    @Service("userService")
    public class UserServiceImpl implements UserService{
        @Override
        public void doSome() {printLog();
            System.out.println("UserServiceImpl 所实现的 doSome 办法");
            addTrans();}
    
        @Override
        public void doOther() {printLog();
            System.out.println("UserServiceImpl 所实现的 doOther 办法");
            addTrans();}
    
        public void printLog() {System.out.println("打印日志, 非业务性能");
        }
    
        public void addTrans() {System.out.println("退出事务,非业务性能");
        }
    }
  5. 编写 applicationContext.xml 配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           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:component-scan base-package="xyz.rtx3090.service"/>
    </beans>
  6. 在测试类中进行测试

    package xyz.rtx3090.service;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class MyTest {
        @Test
        public void test01() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserServiceImpl userService = context.getBean("userService", UserServiceImpl.class);
            userService.doSome();
            userService.doOther();}
    }

    测试后果:

    打印日志, 非业务性能
    UserServiceImpl 所实现的 doSome 办法
    退出事务,非业务性能
    打印日志, 非业务性能
    UserServiceImpl 所实现的 doOther 办法
    退出事务,非业务性能

咱们能够发现真正与业务相干的办法只有 doSome() 办法和 doOther() 办法,而 printLog() 办法和 addTrans() 办法都是与业务无关的办法。这就照成了业务代码与非业务代码的混搅。穿插业务与主业务深度耦合在一起。当穿插业务逻辑 较多时,在主业务代码中会呈现大量的穿插业务逻辑代码调用语句,大大影响了主业务逻辑 的可读性,升高了代码的可维护性,同时也减少了开发难度。所以,能够采纳动静代理形式。在不批改主业务逻辑的前提下,扩大和加强其性能。

采纳动静代理的开发方式

动静代理实现形式

  • jdk 动静代理,应用 jdk 中的 Proxy,Method,InvocaitonHanderl 创立代理对象。jdk 动静代理要求指标类必须实现接口
  • cglib 动静代理:第三方的工具库,创立代理对象,原理是继承。通过继承指标类,创立子类。子类就是代理对象。要求指标类不能是 final 的,办法也不能是 final 的

动静代理作用

  • 在指标类源代码不扭转的状况下,减少性能。
  • 缩小代码的反复
  • 专一业务逻辑代码
  • 解耦合,让你的业务性能和日志,事务非业务性能拆散。

代码实现

  1. 创立 Maven 我的项目,工程目录如图所示

  2. 配置 pom.xml 文件

        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.3.8</version>
        </dependency>
  3. 定义接口 UserService

    package xyz.rtx3090.service;
    
    public interface UserService {void doSome();
        void doOther();}
  4. 编写接口实现类 UserServiceImpl

    package xyz.rtx3090.service;
    
    import org.springframework.stereotype.Service;
    
    @Service("userService")
    public class UserServiceImpl implements UserService{
        @Override
        public void doSome() {System.out.println("UserServiceImpl 实现类的 doSome 办法");
        }
    
        @Override
        public void doOther() {System.out.println("UserServiceImpl 实现类的 doOther 办法");
        }
    }
  5. 编写非业务办法工具类 ServiceUtils

    package xyz.rtx3090.service;
    
    public class ServiceUtils {public static void printLog() {System.out.println("非业务办法,打印日志");
        }
        public static void addTrans() {System.out.println("非业务办法,增加事务");
        }
    }
  6. 编写动静代理类 MyInvocationHandler

    package xyz.rtx3090.service;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class MyInvocationHandler implements InvocationHandler {
        private Object target;
    
        public MyInvocationHandler() {super();
        }
    
        public MyInvocationHandler(Object target) {super();
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object obj = null;
            // 在办法之前输入日志
            ServiceUtils.printLog();
            // 执行指标办法,执行 target 对象的办法
            obj = method.invoke(target, args);
            // 在办法之后,执行增加事务办法
            ServiceUtils.addTrans();
            return obj;
        }
    }
  7. 编写配置文件 applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           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:component-scan base-package="xyz.rtx3090.service"/>
    </beans>
  8. 编写测试类进行测试

    package xyz.rtx3090.service;
    
    import org.junit.Test;
    
    import java.lang.reflect.Proxy;
    
    public class MyTest {
        @Test
        public void test01() {
            // 创立代理对象
            UserService userService = new UserServiceImpl();
            MyInvocationHandler myInvocationHandler = new MyInvocationHandler(userService);
            UserService proxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), myInvocationHandler);
            // 通过带出对象执行业务办法,实现日志,事务的加强
            proxy.doSome();
            proxy.doOther();}
    }

    测试后果:

    非业务办法,打印日志
    UserServiceImpl 实现类的 doSome 办法
    非业务办法,增加事务
    非业务办法,打印日志
    UserServiceImpl 实现类的 doOther 办法
    非业务办法,增加事务

采纳动静代理的形式进行开发,能够无效的防止业务代码与非业务的混搅。然而因为实现的思维不只一种,所以不对立,对于我的项目交换不利。所以咱们能够通过 aop 的形式来帮忙咱们更好的动静代理的开发业务。

AOP 概述

AOP 面向切面编程,基于动静代理的,能够应用 jdk,cglib 两种代理形式。Aop 就是动静代理的规范化,把动静代理的实现步骤,形式都定义好了,让开发人员用一种对立的形式,应用动静代理。

AOP(Aspect Orient Programming),面向切面编程。面向切面编程是从动静角度思考程序运行过程。AOP 底层,就是采纳动静代理模式实现的。采纳了两种代理:JDK 的动静代理,与 CGLIB 的动静代理。

AOP 可通过运行期动静代理实现程序性能的对立保护的一种技术。AOP 是 Spring 框架中的一个重要内容。利用 AOP 能够对业务逻辑的各个局部进行隔离,从而使得业务逻辑各局部之间的耦合度升高,进步程序的可重用性,同时进步了开发的效率。

面向切面编程,就是将穿插业务逻辑封装成切面,利用 AOP 容器的性能将切面织入到 主业务逻辑中。所谓穿插业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、事务、日志、缓存等。若不应用 AOP,则会呈现代码纠缠,即穿插业务逻辑与主业务逻辑混合在一起。这样,会使主业务逻辑变的混淆不清。例如,转账,在真正转账业务逻辑前后,须要权限管制、日志记录、加载事务、完结事务等穿插业务逻辑,而这些业务逻辑与主业务逻辑间并无间接关系。但,它们的代码量所占比重能达到总代码量的一半甚至还多。它们的存在,不仅产生了大量的“冗余”代码,还大 大烦扰了主业务逻辑 — 转账。

  • Aspect:切面,给你的指标类减少的性能,就是切面。像下面用的日志,事务都是切面。切面的特点:个别都是非业务办法,独立应用的。
  • Orient:面向,对着
  • Programming : 编程

AOP 益处

  1. 缩小反复
  2. 专一业务

留神:面向切面编程只是面向对象编程的一种补充。

AOP 术语

切面(Aspect)

切面 泛指穿插业务逻辑。上例中的事务处理、日志解决就能够了解为切面。罕用的切面 是告诉(Advice)。理论就是对主业务逻辑的一种加强。

连接点(JoinPoint)

连接点 指能够被切面织入的具体方法。通常业务接口中的办法均为连接点。

切入点(Pointcut)

切入点 指申明的一个或多个连接点的汇合。通过切入点指定一组办法。被标记为 final 的办法是不能作为连接点与切入点的。因为最终的是不能被批改的,不能被加强的。

指标对象(Target)

指标对象 指将要被加强的对象。即蕴含主业务逻辑的类的对象。上例中的 StudentServiceImpl 的对象若被加强,则该类称为指标类,该类对象称为指标对象。当然,不被加强,也就无所谓指标不指标了。

告诉(Advice)

告诉 示意切面的执行工夫,Advice 也叫加强。上例中的 MyInvocationHandler 就能够理 解为是一种告诉。换个角度来说,告诉定义了加强代码切入到指标代码的工夫点,是指标方 法执行之前执行,还是之后执行等。告诉类型不同,切入工夫不同。切入点定义切入的地位,告诉定义切入的工夫。

AOP 的实现——AspectJ

AspectJ 概述

对于 AOP 这种编程思维,很多框架都进行了实现。Spring 就是其中之一,能够实现面向 切面编程。然而,AspectJ 也实现了 AOP 的性能,且其实现形式更为简捷,应用更为不便,而且还反对注解式开发。所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了本人的框 架中。在 Spring 中应用 AOP 开发时,个别应用 AspectJ 的实现形式

AspectJ 是一个优良面向切面的框架,它扩大了 Java 语言,提供了弱小的切面实现

AspectJ 的告诉类型

AspectJ 中罕用的告诉有五种类型

  1. 前置告诉
  2. 后置告诉
  3. 盘绕告诉
  4. 异样告诉
  5. 最终告诉

AspectJ 的切入点表达式

AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
  • modifiers-pattern——拜访权限类型
  • ret-type-pattern——返回值类型
  • declaring-type-pattern——包名类名
  • name-pattern(param-pattern)——办法名(参数类型及个数)
  • throws-pattern——抛出异样类型
  • ? ——示意可选的局部

总结来说 AspectJ 切入点表达式为:

execution(拜访权限 办法返回值 办法申明(参数) 异样类型)

拜访权限、异样类型都可省略

AspectJ 表达式还是应用到一些符号,如下:

符号 意义
* 0 至多个任意字符
.. 用在包名后,示意以后包及其子包门路
用在办法参数中,示意任意多个参数
+ 用在类名后,示意以后类及其子类
用在接口后,示意以后接口及其实现类

举例

  1. execution(public * *(..))

    切入点为:任意公共办法

  2. execution(* set*(..))

    切入点为:任意以“set”结尾的办法

  3. execution(* xyz.rtx3090.service.*.*(..))

    切入点为:xyz.rtx3090.service 包内任意类的任意办法

  4. execution(* xyz.rtx3090.service..*.*(..))

    切入点为:xyz.rtx3090.service 包或子包内任意类的任意办法

  5. execution(* *..service.*.*(..))

    切入点为:所有包下的 service 包的任意类的任意办法

  6. * xyz.rtx3090.service.IAccountService+.*(..)

    切入点为:IAccountService 若为接口,则示意为该接口中、及其实现类中的任意办法;IAccountService 若为类,则示意为该类中、及其子类中的任意办法

  7. execution(* joke(String, int))

    切入点为:所有名为 joke 的办法,且第一参数为 String 类型,第二个参数为 int 类型。【如果办法中的参数类型是 java.lang 包下的类,能够间接应用类名,否则必须应用 全限定类名,如 joke(java.util.List, int)】

  8. execution(* joke(String,*))

    切入点为:所有名为 joke 的办法,且第一个参数为 String 类型,第二个参数任意类型。

    如:joke(String s1,String s2)和 joke(String s1,double d2)都是,但 joke(String s1,double d2,String s3)不是。

  9. execution(* joke(String,..))

    切入点为:所有名为 joke 的办法,且第一个参数类型为 String,前面能够有任意个参数且类型不限。

    如:joke(String s1)、joke(String s1,String s2)和 joke(String s1,double d2,String s3) 都是。

  10. execution(* joke(Object))

    切入点为:所有名为 joke 的办法,只领有一个参数,且参数类型为 Object 类型。

    如:joke(Object ob) 是,但 joke(String s)与 joke(User u)均不是。

  11. exectuion(* joke(Object+))

    切入点为:所有名为 joke 的办法,只领有一个参数,且参数类型为 Object 或 Object 子类。

    如:不仅 joke(Object ob)是,joke(String s)和 joke(User u)也是。

AspectJ 根本实现步骤演示

  1. 创立 Maven 我的项目,工程构造如图所示:

  2. 配置 pom.xml

        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.3.8</version>
        </dependency>
    
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aspects</artifactId>
          <version>5.3.7</version>
        </dependency>
  3. 定义业务接口 UserService

    package xyz.rtx3090.service;
    
    public interface UserService {void doSome();
    }
  4. 实现业务接口类 UserServiceImpl

    package xyz.rtx3090.service;
    
    import org.springframework.stereotype.Service;
    
    @Service("userService")
    public class UserServiceImpl implements UserService{
        @Override
        public void doSome() {System.out.println("执行了业务办法 doSome()");
        }
    }
  5. 定义切面类 MyAspect

    package xyz.rtx3090.service;
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;
    
    @Component("myAspect")
    @Aspect
    public class MyAspect {
        /**
         * @Before: 前置告诉
         *      value——切入点表达式,示意切面执行的地位
         */
        @Before(value = "execution(* xyz.rtx3090.service.*.*(..))")
        public void myBefore() {
            // 切面代码的性能,例如日志输入,事务处理
            System.out.println("前置告诉:在指标办法之前执行");
        }
    }
  6. 编写配置文件 applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!-- 扫描包中的注解申明 -->
        <context:component-scan base-package="xyz.rtx3090.service"/>
    
        <!-- 申明主动代理生成器,创立代理 -->
        <aop:aspectj-autoproxy/>
    
        <!-- 注册 UserServiceImpl 类到 SpringIOC 容器中 -->
        <bean id="userService" class="xyz.rtx3090.service.UserServiceImpl"/>
    </beans>
  7. 在测试类中进行测试

    package xyz.rtx3090.service;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class MyTest {
        @Test
        public void test01(UserService userService1) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService1 = (UserService) context.getBean("userService");
            // 查看生成的代理类,运行时类型为什么
            String name = userService1.getClass().getName();
            System.out.println("代理类运行时类型为:" + name);
            // 通过代理执行业务办法,实现性能加强
            userService1.doSome();}
    }

    退出切面胜利,测试后果:

    代理类运行时类型为:com.sun.proxy.$Proxy14
    前置告诉:在指标办法之前执行
    执行了业务办法 doSome()

@Before——前置告诉

后面咱们曾经演示过 AspectJ 具体的代码实现步骤了,所有上面我只会展现要害的 Aspect 类的定义

阐明
  • @Before——前置告诉
  • 作用:在指标办法执行之前,来执行咱们须要执行的代码
  • 属性:

    • value——切入点表达式,示意切面执行的地位
  • 办法参数:

    • JoinPoint——前置告诉能够蕴含一个 JoinPoint 类型的参数,形参名可自定义,作用是可用来获取切入点表达式、办法签名、指标对象等(不只 @Before 前置告诉用这个办法参数,其余告诉也都有,可写可不写)
  • 办法返回值:void 无
代码演示
  1. Aspect 类的定义编写

    package xyz.rtx3090.service;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;
    
    @Component("myAspect")
    @Aspect
    public class MyAspect {
        /**
         * @Before: 前置告诉
         *      value——切入点表达式,示意切面执行的地位
         */
        @Before(value = "execution(* xyz.rtx3090.service.*.*(..))")
        public void myBefore(JoinPoint joinPoint) {
            //JoinPoint 可能获取到办法的定义、参数等音讯
            System.out.println("连接点的表达式定义:" + joinPoint.getSignature());
            System.out.println("连接点办法的参数个数:" + joinPoint.getArgs().length);
            // 切面代码的性能,例如日志输入,事务处理
            System.out.println("前置告诉:在指标办法之前执行");
        }
    }
  2. 在测试类中进行测试

        @Test
        public void testBefore() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService = context.getBean("userService", UserService.class);
            userService.doSome();}

    测试胜利,测试后果:

    连接点的表达式定义:void xyz.rtx3090.service.UserService.doSome()
    前置告诉:在指标办法之前执行
    执行了业务办法 doSome()

@AfterReturning——后置告诉

阐明
  • @AfterReturning——后置告诉
  • 作用:在指标办法执行之后,来执行咱们须要执行的代码
  • 属性:

    • value——切入点表达式,示意切面执行的地位
    • returning——承受指标办法返回值【属性名要与上面的办法形参 result 名统一】
  • 办法参数:

    • JoinPoint——后置告诉能够蕴含一个 JoinPoint 类型的参数,形参名可自定义,作用是可用来获取切入点表达式、办法签名、指标对象等(不只 @AfterReturning 后置告诉用这个办法参数,其余告诉也都有,可写可不写)
    • result——用于承受指标办法返回值,形参名可自定义但类型倡议为 Object,因为指标办法的返回值可能是任何类型【形参名要与下面的属性名 returning 名统一】
  • 办法返回值:void 无
代码演示
  1. Aspect 类的定义编写

    package xyz.rtx3090.service;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.stereotype.Component;
    
    @Component("myAspect")
    @Aspect
    public class MyAspect {@AfterReturning(value = "execution(* doOther(String, int))", returning = "result")
        public void myAfterReturning(JoinPoint joinPoint, Object result) {
            // 切面代码的性能,例如日志输入,事务处理
            System.out.println("后置告诉:在指标办法之后执行");
            //JoinPoint 可能获取到办法的定义、参数等音讯
            System.out.println("连接点:" + joinPoint.getSourceLocation());
            //result 用来承受指标办法的返回数值
            System.out.println("指标办法的返回值:" + result);
        }
    }
  2. 在测试类中进行测试

        @Test
        public void testAfterReturning() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService = context.getBean("userService", UserService.class);
            String s = userService.doOther("2", 3);
            System.out.println("doOther 执行的返回后果:"+s);
        }

    测试胜利,测试后果:

    执行了业务办法 doOther()
    后置告诉:在指标办法之后执行
    连接点办法的参数个数:2
    指标办法的返回值: 23
    doOther 执行的返回后果:23

@Around——盘绕告诉

阐明
  • @Around——盘绕告诉
  • 作用:在指标办法执行之前、之后,来执行咱们须要执行的代码
  • 属性:

    • value——切入点表达式,示意切面执行的地位
  • 办法参数:

    • ProceedingJoinPoint ——盘绕告诉办法能够蕴含一个 ProceedingJoinPoint 类型的参数,接口 ProceedingJoinPoint 除了和其父接口 JoinPoint 雷同的获取切入点表达式、办法签名、指标对象等性能外,其还有一个 proceed()办法,用于执行指标办法。若指标办法有返回值,则该办法的返回值就是指标办法 的返回值,最初,盘绕加强办法将其返回值返回【该加强办法理论是拦挡了指标办法的执行】
  • 办法返回值:为 Object 类型,用于返回执行 proceed()办法的返回值,即指标办法的执行后果的返回值
代码演示
  1. Aspect 类的定义编写

    package xyz.rtx3090.service;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.stereotype.Component;
    
    @Component("myAspect")
    @Aspect
    public class MyAspect {@Around(value = "execution(* love(String, String))")
        public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            //ProceedingJoinPoint 也可能获取到办法的定义、参数等音讯
            Object[] args = proceedingJoinPoint.getArgs();
            System.out.println("----- 遍历指标办法的参数列表 -----");
            for (Object o :
                    args) {System.out.println(o);
            }
            // 在指标办法之前执行的加强性能
            System.out.println("盘绕告诉:在指标办法之前执行的,例如输入日志");
            // 执行指标办法的调用
            Object proceed = proceedingJoinPoint.proceed();
            // 在指标办法之后执行的加强性能
            System.out.println("盘绕告诉:在指标办法之后执行的,例如处理事务");
            return proceed;
        }
    }
  2. 在测试类中进行测试

        @Test
        public void testAround() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService = context.getBean("userService", UserService.class);
            Boolean love = userService.love("root", "admin123456");
            System.out.println(love ? "登录胜利" : "登录失败");
        }

    测试胜利,测试后果:

    —– 遍历指标办法的参数列表 —–
    root
    admin123456
    盘绕告诉:在指标办法之前执行的,例如输入日志
    执行业务办法 love()
    盘绕告诉:在指标办法之后执行的,例如处理事务
    登录胜利

@Pointcut——定义切入点

阐明
  • @Pointcut——定义切入点
  • 作用:

    • 当较多的告诉加强办法应用雷同的 execution 切入点表达式时,编写、保护均较为麻烦。AspectJ 提供了 @Pointcut 注解,用于定义 execution 切入点表达式。(相当与给定义点起别名)
    • 其用法是,将 @Pointcut 注解在一个办法之上,当前所有的 execution 的 value 属性值均 可应用该办法名作为切入点。代表的就是 @Pointcut 定义的切入点。这个应用 @Pointcut 注解 的办法个别应用 private 的标识办法,即没有理论作用的办法。
  • 属性:

    • value——切入点表达式,示意切面执行的地位
  • 办法参数:无
  • 办法返回值:void 无
代码演示
  1. Aspect 类的定义编写

    package xyz.rtx3090.service;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    @Component("myAspect")
    @Aspect
    public class MyAspect {@Pointcut(value = "execution(* xyz.rtx3090.service.UserServiceImpl.returnA(..))")
        private void myPointcut() {// 不须要写代码}
    
        @Around(value = "myPointcut()")
        public Object myAround02(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            Object obj = null;
            // 在指标办法执行之前的加强性能
            System.out.println("盘绕告诉:在指标代码之前执行的加强性能,例如打印日志");
            // 执行指标办法的调用
            obj = proceedingJoinPoint.proceed();
            // 在指标办法执行之后的加强性能
            System.out.println("盘绕告诉:在指标代码之后执行的加强性能,例如处理事务");
            return obj;
        }
    }
  2. 在测试类中进行测试

        @Test
        public void testPointcut() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService = context.getBean("userService", UserService.class);
            char a = userService.returnA();
            System.out.println("返回后果:" + a);
        }

    测试胜利,测试后果:

    盘绕告诉:在指标代码之前执行的加强性能,例如打印日志
    执行业务办法 returnA
    盘绕告诉:在指标代码之后执行的加强性能,例如处理事务
    返回后果:A

@After——最终告诉(理解)

阐明
  • @After——最终告诉
  • 作用:在指标办法执行后被执行,且无论抛出异样都会被执行
  • 属性:

    • value——切入点表达式,示意切面执行的地位
  • 办法参数:

    • JoinPoint——可用来获取切入点表达式、办法签名、指标对象等(不只 @After 前置告诉用这个办法参数,其余告诉也都有,可写可不写)
  • 办法返回值:void 无
代码演示
  1. Aspect 类的定义编写

    package xyz.rtx3090.service;
    
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    @Component("myAspect")
    @Aspect
    public class MyAspect {@After(value = "execution(public void xyz..*.fk(..))")
        public void myAfter() {
            // 切面代码的性能,例如事务处理
            System.out.println("最终告诉:总是会被执行的办法");
        }
    }
  2. 在测试类中进行测试

        @Test
        public void testAfter() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService = context.getBean("userService", UserService.class);
            userService.fk();}

    测试胜利,测试后果:

    执行业务办法 fk()
    最终告诉:总是会被执行的办法

@AfterThrowing——异样告诉(理解)

阐明
  • @AfterThrowing——异样告诉
  • 作用:用于在指标办法抛出异样后执行
  • 属性:

    • value——切入点表达式,示意切面执行的地位
    • throwing——用于指定所产生的异样类对象【与上面办法参数 throwing 名统一】
  • 办法参数:

    • JoinPoint——可用来获取切入点表达式、办法签名、指标对象等(不只 @After 前置告诉用这个办法参数,其余告诉也都有,可写可不写)
    • Throwable——用于指定所产生的异样类对象【与下面属性 throwing 值统一】
  • 办法返回值:void 无
代码演示
  1. Aspect 类的定义编写

    package xyz.rtx3090.service;
    
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    @Component("myAspect")
    @Aspect
    public class MyAspect {@AfterThrowing(value = "execution(* doSend(..))", throwing = "ex")
        public void myAfterThrowing(Throwable ex) {
            // 切面代码的性能, 当产生异样时执行
            System.out.println("异样告诉:在指标办法抛出异样时执行;\n 异样起因:" + ex.getMessage());
        }
    }
  2. 在测试类中进行测试

        @Test
        public void testAfterThrowing() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService = context.getBean("userService", UserService.class);
            int i = userService.doSend(true);
            System.out.println(i == 1 ? "抢票胜利" : "抢票失败");
        }

    测试胜利,测试后果:

    异样告诉:在指标办法抛出异样时执行;
    异样起因:/ by zero

    java.lang.ArithmeticException: / by zero

Spring 集成 Mybatis

概述

将 MyBatis 与 Spring 进行整合,次要解决的问题就是将 SqlSessionFactory 对象交由 Spring 来治理。所以,该整合,只须要将 SqlSessionFactory 的对象生成器 SqlSessionFactoryBean 注 册在 Spring 容器中,再将其注入给 Dao 的实现类即可实现整合。实现 Spring 与 MyBatis 的整合罕用的形式:扫描的 Mapper 动静代理 Spring 像插线板一样,mybatis 框架是插头,能够容易的组合到一起。插线板 spring 插 上 mybatis,两个框架就是一个整体。

代码实现

  1. 筹备数据库数据

    /* 如果数据库 mybatis 存在,则删除它 */
    drop database if exists mybatis;
    
    /* 创立名为 mybatis 的数据库 */
    create database mybatis;
    
    /* 应用为名 mybatis 的数据库 */
    use mybatis;
    
    /* 创立表 user*/
    CREATE TABLE `user`
    (`id`   int(10) NOT NULL,
        `name` varchar(20) DEFAULT NULL,
        `pwd`  varchar(50) DEFAULT NULL,
        PRIMARY KEY (`id`)
    ) ENGINE = InnoDB
      DEFAULT CHARSET = utf8;
    
    /* 向表 user 插入 4 条数据 */
    insert into mybatis.user (id, name, pwd)
    VALUES (1,'Jason','Crimson'),
           (2,'BernardoLi','Assault'),
           (3,'Mybatis','Shortcuts'),
           (4,'Spring','404NOTFOUND');
  2. 新建 Maven 我的项目,工程构造目录如图所示

  3. 编写 pom.xml 文件,导入我的项目所须要的 Jar 包依赖

      <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.11</version>
          <scope>test</scope>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.2.5.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-tx</artifactId>
          <version>5.2.5.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-jdbc</artifactId>
          <version>5.2.5.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis</artifactId>
          <version>3.5.1</version>
        </dependency>
        <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis-spring</artifactId>
          <version>1.3.1</version>
        </dependency>
        <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>
        <resources>
          <resource>
            <directory>src/main/java</directory><!-- 所在的目录 -->
            <includes><!-- 包含目录下的.properties,.xml 文件都会扫描到 -->
              <include>**/*.properties</include>
              <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
          </resource>
        </resources>
        <plugins>
          <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.0</version>
            <configuration>
              <source>1.8</source>
              <target>1.8</target>
            </configuration>
          </plugin>
        </plugins>
      </build>
  4. 编写实体类User

    package xyz.rtx3090.pojo;
    
    public class User {
        private int id;
        private String name;
        private String pwd;
    
        //constructor
        //setter and getter
    
    }
  5. 编写 Dao 接口UserDao

    package xyz.rtx3090.dao;
    
    import xyz.rtx3090.pojo.User;
    
    import java.util.List;
    
    public interface UserDao {
        // 查问全副用户信息
        List<User> selectAllUser();}
  6. 编写 Dao 接口的对应的配置文件UserDao.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="xyz.rtx3090.dao.UserDao">
        <!-- 查问全副用户信息 -->
        <select id="selectAllUser" resultType="xyz.rtx3090.pojo.User">
            select * from mybatis.user;
        </select>
    </mapper>
  7. 编写 service 接口UserService

    package xyz.rtx3090.service;
    
    import xyz.rtx3090.pojo.User;
    
    import java.util.List;
    
    public interface UserService {
        // 查问全副用户信息
        List<User> selectAllUser();}
  8. 编写 service 接口的实现类UserServiceImpl(通过 Service 接口来调用 Dao 接口)

    package xyz.rtx3090.service.impl;
    
    import xyz.rtx3090.dao.UserDao;
    import xyz.rtx3090.pojo.User;
    import xyz.rtx3090.service.UserService;
    
    import java.util.List;
    
    public class UserServiceImpl implements UserService {
          // 援用 Dao 接口
        private UserDao userDao;
    
        public void setUserDao(UserDao userDao) {this.userDao = userDao;}
    
        @Override
        public List<User> selectAllUser() {return userDao.selectAllUser();
        }
    }
  9. 编写 Mybatis 外围配置文件mybatisConfig.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>
        <!-- 设置 -->
        <settings>
            <setting name="logImpl" value="STDOUT_LOGGING"/>
        </settings>
        <!-- 类型别名 -->
        <typeAliases>
            <package name="xyz.rtx3090.pojo"/>
        </typeAliases>
        <!-- 映射文件 -->
        <mappers>
            <package name="xyz.rtx3090.dao"/>
        </mappers>
    </configuration>
  10. 编写数据库配置文件

    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8
    username=root
    password=intmain()
    max=20
  11. 编写 Spring 外围配置文件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"
           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:jdbc.properties"/>
    
        <!-- 申明数据源,作用是链接数据库 -->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
              init-method="init" destroy-method="close">
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
            <property name="maxActive" value="${max}"/>
        </bean>
    
        <!-- 配置 SqlSessionFactory-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!-- 关联数据源 -->
            <property name="dataSource" ref="dataSource"/>
            <!-- 关联 mybatis 外围配置文件 -->
            <property name="configLocation" value="classpath:mybatisConfig.xml"/>
        </bean>
    
        <!-- 注册 Mapper 扫描配置器 -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
            <!-- 指定根本扫描包,即 Dao 接口包 -->
            <property name="basePackage" value="xyz.rtx3090.dao"/>
        </bean>
    
            <!-- 注册实现类 UserServiceImpl 到 springIOC 容器中 -->
        <bean id="userServiceImpl" class="xyz.rtx3090.service.impl.UserServiceImpl">
            <property name="userDao" ref="userDao"/>
        </bean>
    </beans>
  12. 编写测试类进行测试

    package xyz.rtx3090.service;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import xyz.rtx3090.dao.UserDao;
    import xyz.rtx3090.pojo.User;
    
    import java.util.List;
    
    public class UserServiceImplTest {
        @Test
        public void testSelectAllUser() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
                    UserService userServiceImpl = context.getBean("userServiceImpl", UserService.class);
            List<User> userList = userServiceImpl.selectAllUser();
            for (User user :
                    userList) {System.out.println(user);
            }
        }
    }

    测试胜利,输入后果:

    User{id=1, name=’Jason’, pwd=’Crimson’}
    User{id=2, name=’BernardoLi’, pwd=’Assault’}
    User{id=3, name=’Mybatis’, pwd=’Shortcuts’}
    User{id=4, name=’Spring’, pwd=’404NOTFOUND’}

Spring 事务

概述

事务本来是数据库中的概念在 Dao 层,但个别状况下须要将事务晋升到业务层,即 Service 层。这样做是为了可能应用事务的个性来治理具体的业务。

在 Spring 中通常能够通过以下两种形式来实现对事务的治理:

  1. 应用 Spring 的事务注解治理事务
  2. 应用 AspectJ 的 AOP 配置管理事务

Spring 事务管理 API

Spring 的事务管理,次要用到两个事务相干的接口。

事务管理器接口

事务管理器是 PlatformTransactionManager 接口对象。其次要用于实现事务的提交、回 滚,及获取事务的状态信息。

罕用的两个实现类

PlatformTransactionManager 接口有两个罕用的实现类:

  1. DataSourceTransactionManager:应用 JDBC 或 MyBatis 进行数据库操作时应用。
  2. HibernateTransactionManager:应用 Hibernate 进行长久化数据时应用。
Spring 的回滚形式

Spring 事务的默认回滚形式是:产生运行时异样和 error 时回滚,产生受查 (编译) 异样时提交。不过对于受查异样程序员也能够手工设置其回滚形式

回顾谬误与异样

Throwable 类是 Java 语言中所有谬误或异样的超类。只有当对象是此类(或其子类之一) 的实例时,能力通过 Java 虚拟机或者 Java 的 throw 语句抛出。

Error 是程序在运行过程中呈现的无奈解决的谬误,比方 OutOfMemoryError、ThreadDeath、NoSuchMethodError 等。当这些谬误产生时,程序是无奈解决(捕捉或抛出)的,JVM 个别会终止线程。

程序在编译和运行时呈现的另一类谬误称之为异样,它是 JVM 告诉程序员的一种形式。通过这种形式,让程序员晓得曾经或可能呈现谬误,要求程序员对其进行解决。

异样分为运行时异样与 受查异样

运行时异样 是 RuntimeException 类或其子类,即只有在运行时才呈现的异样。如,NullPointerException、ArrayIndexOutOfBoundsException、IllegalArgumentException 等均属于运行时异样。这些异样由 JVM 抛出,在编译时不要求必须解决(捕捉或抛出)。但只有代码编写足够认真,程序足够强壮,运行时异样是能够防止的。

受查异样 也叫编译时异样,即在代码编写时要求必须捕捉或抛出的异样,若不解决,则无奈通过编译。如 SQLException,ClassNotFoundException,IOException 等都属于受查异样。

RuntimeException 及其子类以外的异样,均属于受查异样。当然,用户自定义的 Exception 的子类,即用户自定义的异样也属受查异样。程序员在定义异样时,只有未明确申明定义的 为 RuntimeException 的子类,那么定义的就是受查异样。

事务定义接口

事务定义接口 TransactionDefinition 中定义了事务形容相干的三类常量:事务隔离级别、事务流传行为、事务默认超时时限,及对它们的操作。

定义了五个事务隔离级别常量

这些常量均是以 ISOLATION_结尾。即形如 ISOLATION_XXX。

  1. DEFAULT:采纳 DB 默认的事务隔离级别,MySql 的默认为 REPEATABLE_READ;Oracle 默认为 READ_COMMITTED
  2. READ_UNCOMMITTED读未提交,未解决任何并发问题
  3. READ_COMMITTED:读已提交,解决脏读,存在不可反复读与幻读
  4. REPEATABLE_READ可反复读,解决脏读、不可反复读,存在幻读
  5. SERIALIZABLE:串行化。不存在并发问题。
定义了七个事务流传行为常量

所谓事务流传行为是指,处于不同事务中的办法在互相调用时,执行期间事务的保护状况。如 A 事务中的办法 doSome()调用 B 事务中的办法 doOther(),在调用执行期间事务的保护状况,就称为事务流传行为。事务流传行为是加在办法上的。

事务流传行为常量都是以 PROPAGATION_ 结尾,形如 PROPAGATION_XXX。

  1. PROPAGATION_REQUIRED【把握】

    指定的办法必须在事务内执行。若以后存在事务,就退出到以后事务中;若以后没有事务,则创立一个新事务。这种流传行为是最常见的抉择,也是 Spring 默认的事务流传行为。

    如该流传行为加在 doOther()办法上。若 doSome()办法在调用 doOther()办法时就是在事务内运行的,则 doOther()办法的执行也退出到该事务内执行。若 doSome()办法在调用 doOther()办法时没有在事务内执行,则 doOther()办法会创立一个事务,并在其中执行。

  2. PROPAGATION_REQUIRES_NEW【把握】

    指定的办法反对以后事务,但若以后没有事务,也能够以非事务形式执行。

  3. PROPAGATION_SUPPORTS【把握】

    总是新建一个事务,若以后存在事务,就将以后事务挂起,直到新事务执行结束。

  4. PROPAGATION_MANDATORY
  5. PROPAGATION_NESTED
  6. PROPAGATION_NEVER
  7. PROPAGATION_NOT_SUPPORTED
定义了默认事务超时时限

常量 TIMEOUT_DEFAULT 定义了事务底层默认的超时时限,sql 语句的执行时长。

留神,事务的超时时限起作用的条件比拟多,且超时的工夫计算点较简单。所以该值个别就应用默认值即可。

事务管理

事务管理的形式分为两种:Spring 事务注解治理 AspectJ 的 AOP 配置管理事务

Spring 事务注解治理

通过 @Transactional 注解形式,可将事务织入到相应 public 办法中,实现事务管理

@Transactional 的所有可选属性如下所示:

  • propagation:用于设置事务流传属性。该属性类型为 Propagation 枚举,默认值为Propagation.REQUIRED
  • isolation:用于设置事务的隔离级别。该属性类型为 Isolation 枚举,默认值为Isolation.DEFAULT
  • readOnly:用于设置该办法对数据库的操作是否是只读的。该属性为 boolean,默认值 为 false
  • timeout:用于设置本操作与数据库连贯的超时时限。单位为秒,类型为 int,默认值为 -1,即没有时限
  • rollbackFor:指定须要回滚的异样类。类型为Class[],默认值为空数组。当然,若只有 一个异样类时,能够不应用数组
  • rollbackForClassName:指定须要回滚的异样类类名。类型为String[],默认值为空数组。当然,若只有一个异样类时,能够不应用数组
  • noRollbackFor:指定不须要回滚的异样类。类型为Class[],默认值为空数组。当然,若只有一个异样类时,能够不应用数组
  • noRollbackForClassName:指定不须要回滚的异样类类名。类型为String[],默认值为空数组。当然,若只有一个异样类时,能够不应用数组

须要留神的是,@Transactional 若用在办法上,只能用于 public 办法上。对于其余非 public 办法,如果加上了注解 @Transactional,尽管 Spring 不会报错,但不会将指定事务织入到该 办法中。因为 Spring 会疏忽掉所有非 public 办法上的 @Transaction 注解。

若 @Transaction 注解在类上,则示意该类上所有的办法均将在执行时织入事务

AspectJ 的 AOP 配置管理事务

应用 XML 配置事务代理的形式的有余是,每个指标类都须要配置事务代理。当指标类较多,配置文件会变得十分臃肿

应用 XML 配置参谋形式能够主动为每个合乎切入点表达式的类生成事务代理。其用法 很简略,只需将后面代码中对于事务代理的配置删除,再替换为如下内容即可

事务演示

本例要实现购买商品,模仿用户下订单,向订单表增加销售记录,从商品表缩小库存。

开启事务前

  1. 筹备数据库数据

    /* 如果存在数据库 spring,则进行删除 */
    drop database if exists spring;
    
    /* 创立名为 spring 的数据库 */
    create database spring;
    
    /* 应用名为 spring 的数据库 */
    use spring;
    
    /* 创立表 sale*/
    create table `sale`
    (`id`   int(11) auto_increment,
        `gid`  int(11) not null,
        `nums` int(11),
        primary key (`id`)
    ) ENGINE = INNODB
      default charset = utf8;
    
    /* 创立表 goods*/
    create table `goods`
    (`id`     int(11) auto_increment,
        `name`   varchar(100) not null,
        `amount` int(11)      not null,
        `price`  float        not null,
        primary key (`id`)
    ) ENGINE = INNODB
      default charset = utf8;
    
    /* 向表 goods 中插入数据 */
    insert into spring.goods (id, name, amount, price)
    VALUES ('1001' , '笔记本' , '10' , '7999'),
           ('1002' , '手机' , '20' , '5999');
  2. 创立 maven 我的项目,工程目录构造如图所示

  3. 配置 pom.xml 文件

    <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.11</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.2.5.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-tx</artifactId>
                <version>5.2.5.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.2.5.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.1</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>1.3.1</version>
            </dependency>
            <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>
            <plugins>
                <!-- 规定我的项目 JDK 版本 -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
            <resources>
                <!-- 解决 Maven 动态资源过滤问题 -->
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>true</filtering>
                </resource>
                <resource>
                    <directory>src/main/resources</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>true</filtering>
                </resource>
            </resources>
        </build>
  4. 编写实体类SaleGoods

    package xyz.rtx3090.pojo;
    
    public class Sale {
        private Integer id;
        private Integer gid;
        private Integer nums;
    
        //constructor
        //setter and getter
            //toString
    }
    package xyz.rtx3090.pojo;
    
    public class Goods {
        private Integer id;
        private String name;
        private Integer amount;
        private float price;
    
        //constructor
        //setter and getter
          //toString
    }
  5. 编写 Dao 接口SaleDaoGoodsDao

    package xyz.rtx3090.dao;
    
    import xyz.rtx3090.pojo.Sale;
    
    public interface SaleDao {
        // 插入指定销售信息
        int insertSale(Sale sale);
    }
    package xyz.rtx3090.dao;
    
    import xyz.rtx3090.pojo.Goods;
    
    public interface GoodsDao {
        // 依据商品 id 号查问指定商品信息
        Goods selectGoods(Integer goodsId);
    
        // 更新指定商品信息
        int updateGoods(Goods goods);
    }
  6. 编写 Dao 接口对应的 xml 配置文件SaleDao.xmlGoodsDao.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="xyz.rtx3090.dao.SaleDao">
        <!-- 插入指定销售信息 -->
        <insert id="insertSale" parameterType="sale">
            insert into sale (gid, nums) VALUES (#{gid},#{nums});
        </insert>
    </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="xyz.rtx3090.dao.GoodsDao">
        <!-- 依据商品 id 号查问指定商品信息 -->
        <select id="selectGoods" resultType="goods">
            select *
            from spring.goods
            where id = #{id};
        </select>
    
        <!-- 更新指定商品信息 -->
        <update id="updateGoods" parameterType="goods">
            update spring.goods
            set amount = amount - #{amount}
            where id = #{id};
        </update>
    </mapper>
  7. 编写自定义异样类NotEnoughException,以便 service 调用异样时应用

    package xyz.rtx3090.exception;
    
    public class NotEnoughException extends RuntimeException{
        //constructor
        public NotEnoughException() {super();
        }
        public NotEnoughException(String msg) {super(msg);
        }
    }
  8. 编写 service 接口BuyGoodsService,及其实现类BuyGoodsServiceImpl

    package xyz.rtx3090.service;
    
    public interface BuyGoodsService {void buy(Integer gid, Integer amount);
    }
    package xyz.rtx3090.service.impl;
    
    import xyz.rtx3090.dao.GoodsDao;
    import xyz.rtx3090.dao.SaleDao;
    import xyz.rtx3090.exception.NotEnoughException;
    import xyz.rtx3090.pojo.Goods;
    import xyz.rtx3090.pojo.Sale;
    import xyz.rtx3090.service.BuyGoodsService;
    
    public class BuyGoodsServiceImpl implements BuyGoodsService {
        private GoodsDao goodsDao;
        private SaleDao saleDao;
    
        //setter and getter
        public GoodsDao getGoodsDao() {return goodsDao;}
    
        public void setGoodsDao(GoodsDao goodsDao) {this.goodsDao = goodsDao;}
    
        public SaleDao getSaleDao() {return saleDao;}
    
        public void setSaleDao(SaleDao saleDao) {this.saleDao = saleDao;}
    
        @Override
        public void buy(Integer gid, Integer amount) {
            // 更新销售表
            Sale sale = new Sale();
            sale.setGid(gid);
            sale.setNums(amount);
            saleDao.insertSale(sale);
            // 更新商品表
            Goods goods = goodsDao.selectGoods(gid);
            if (goods == null) {throw new NullPointerException("无此商品");
            } else if (goods.getAmount() < amount) {throw new NotEnoughException("库存有余");
            } else {goods = new Goods();
                goods.setId(gid);
                goods.setAmount(amount);
                goodsDao.updateGoods(goods);
            }
        }
    }
  9. 编写 mybatis 外围配置文件mybatisConfig.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>
        <!-- 设置 -->
        <settings>
            <setting name="logImpl" value="STDOUT_LOGGING"/>
        </settings>
        <!-- 类型别名 -->
        <typeAliases>
            <package name="xyz.rtx3090.pojo"/>
        </typeAliases>
        <!-- 映射门路 -->
        <mappers>
            <package name="xyz.rtx3090.dao"/>
        </mappers>
    </configuration>
  10. 编写数据库配置文件jdbc.properties

    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/spring?useSSL=false&useUnicode=true&characterEncoding=UTF-8
    username=root
    password=intmain()
    max=20
  11. 编写 spring 外围配置文件applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           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:jdbc.properties"/>
    
        <!-- 申明数据源 DataSource,应用 Druid 数据库链接池 -->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
              init-method="init" destroy-method="close">
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
            <property name="maxActive" value="${max}"/>
        </bean>
    
        <!-- 配置 SqlSessionFactory-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!-- 关联数据源 -->
            <property name="dataSource" ref="dataSource"/>
            <!-- 关联 Mybatis 外围配置文件 -->
            <property name="configLocation" value="classpath:mybatisConfig.xml"/>
        </bean>
    
        <!-- 注册 Mapper 扫描配置器 -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
            <!-- 指定根本扫描包,即 Dao 接口包 -->
            <property name="basePackage" value="xyz.rtx3090.dao"/>
        </bean>
    
        <!-- 导入 beans.xml 配置文件 -->
        <import resource="classpath:beans.xml"/>
    </beans>
  12. 编写另一个 spring 外围配置文件beans.xml(用于注册咱们自定义类到 springIOC 容器)

    <?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">
    
        <!-- 注册 BuyGoodsServiceImpl 实现类到 SpringIOC 容器中 -->
        <bean id="buyServiceImpl" class="xyz.rtx3090.service.impl.BuyGoodsServiceImpl">
            <!-- 通过 set 办法将 spring 扫描的 dao 接口注入 -->
            <property name="saleDao" ref="saleDao"/>
            <property name="goodsDao" ref="goodsDao"/>
        </bean>
    </beans>
  13. 编写测试类 BuyGoodsServiceImplTest 进行测试

    package xyz.rtx3090.service.impl;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import xyz.rtx3090.service.BuyGoodsService;
    
    public class BuyGoodsServiceImplTest {
        @Test
        public void testBuy() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            BuyGoodsService buyServiceImpl = context.getBean("buyServiceImpl", BuyGoodsService.class);
            buyServiceImpl.buy(1003,10);
        }
    }

    在测试中咱们发现:因为‘商品不存在’、‘商品有余’等起因购买失败,goods表数据未能被更新,但 sale 表的数据却被更新。这就存在了事务问题!

退出 Spring 事务注解治理

阐明:以下代码演示均在下面演示的代码【开启事务前】的根底进行改良的

  1. 在 spring 外围配置文件 application.xml,减少 事务管理器 注解驱动 相干配置(此文件无需作出其余任何批改)

        <!-- 申明事务管理器 -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
        <!-- 开启注解驱动, 关联事务管理器(留神导入束缚头时,不要抉择谬误了,要抉择:schema/tx/ 这个)-->
        <tx:annotation-driven transaction-manager="transactionManager"/>
  2. 在业务层 service 实现类 BuyGoodsServiceImpl 的 public 办法上退出事务注解

            // 前面的属性值也都能够不加,间接采纳默认值即可
            @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {NotEnoughException.class, NullPointerException.class})
        @Override
        public void buy(Integer gid, Integer amount) {
            // 更新销售表
            Sale sale = new Sale();
            sale.setGid(gid);
            sale.setNums(amount);
            saleDao.insertSale(sale);
            // 更新商品表
            Goods goods = goodsDao.selectGoods(gid);
            if (goods == null) {throw new NullPointerException("无此商品");
            } else if (goods.getAmount() < amount) {throw new NotEnoughException("库存有余");
            } else {goods = new Goods();
                goods.setId(gid);
                goods.setAmount(amount);
                goodsDao.updateGoods(goods);
            }
        }
  3. 再次在测试类中进行测试

    package xyz.rtx3090.service.impl;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import xyz.rtx3090.service.BuyGoodsService;
    
    public class BuyGoodsServiceImplTest {
        @Test
        public void testBuy() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            BuyGoodsService buyServiceImpl = context.getBean("buyServiceImpl", BuyGoodsService.class);
            buyServiceImpl.buy(1003,10);
        }
    }

    这次咱们发现 商品表 更新失败,销售表 也没有新增加数据。事务问题失去解决了。

退出 AspectJ 的 AOP 配置管理事务

阐明:以下代码演示均在下面演示的代码【开启事务前】的根底进行改良的

除了下面这种形式,咱们还能够采纳这种办法来治理事务。

  1. 新退出 AspectJ 的依赖坐标

            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>5.2.5.RELEASE</version>
            </dependency>
  2. 在 spring 外围配置文件和退出 事务管理器 事务告诉(切面)aop 配置,切入点 的配置

        <!-- 申明事务管理器 -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
        <!-- 事务告诉(切面)-->
        <tx:advice id="buyAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT" rollback-for="java.lang.NullPointerException,xyz.rtx3090.exception.NotEnoughException"/>
                <tx:method name="add*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="java.lang.Exception"/>
                <tx:method name="*" propagation="SUPPORTS"/>
            </tx:attributes>
        </tx:advice>
    
        <!--aop 配置:告诉利用的切入点 -->
        <aop:config>
            <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
            <aop:advisor advice-ref="buyAdvice" pointcut-ref="servicePt"/>
        </aop:config>
  3. 再次在测试类中进行测试

    package xyz.rtx3090.service.impl;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import xyz.rtx3090.service.BuyGoodsService;
    
    public class BuyGoodsServiceImplTest {
        @Test
        public void testBuy() {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            BuyGoodsService buyServiceImpl = context.getBean("buyServiceImpl", BuyGoodsService.class);
            buyServiceImpl.buy(1003,10);
        }
    }

    这次咱们发现 商品表 更新失败,销售表 也没有新增加数据。事务问题失去解决了。

Spring 与 Web

在 Web 我的项目中应用 Spring 框架,首先要解决在 web 层(这里指 Servlet)中获取到 Spring 容器的问题。只有在 web 层获取到了 Spring 容器,便可从容器中获取到 Service 对象。

SpringWeb 我的项目创立

具体搭建步骤

  1. 创立带 maven-archetype-webapp 模版的 maven 我的项目,工程目录如图所示

  2. 配置 pom.xml 文件

    <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.11</version>
          <scope>test</scope>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.2.5.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-tx</artifactId>
          <version>5.2.5.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-jdbc</artifactId>
          <version>5.2.5.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis</artifactId>
          <version>3.5.1</version>
        </dependency>
        <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis-spring</artifactId>
          <version>1.3.1</version>
        </dependency>
        <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>
        <!-- servlet 依赖 -->
        <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>javax.servlet-api</artifactId>
          <version>3.1.0</version>
          <scope>provided</scope>
        </dependency>
        <!-- jsp 依赖 -->
        <dependency>
          <groupId>javax.servlet.jsp</groupId>
          <artifactId>jsp-api</artifactId>
          <version>2.2.1-b03</version>
          <scope>provided</scope>
        </dependency>
      </dependencies>
    
      <build>
        <resources>
          <resource>
            <directory>src/main/java</directory><!-- 所在的目录 -->
            <includes><!-- 包含目录下的.properties,.xml 文件都会扫描到 -->
              <include>**/*.properties</include>
              <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
          </resource>
        </resources>
        <plugins>
          <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.0</version>
            <configuration>
              <source>1.8</source>
              <target>1.8</target>
            </configuration>
          </plugin>
        </plugins>
      </build>
  3. 编写实体类User

    package xyz.rtx3090.pojo;
    
    public class User {
        private int id;
        private String name;
        private String pwd;
    
        //constructor
        //setter and getter
          //toString
    }
  4. 编写 Dao 接口UserDao

    package xyz.rtx3090.dao;
    
    import xyz.rtx3090.pojo.User;
    
    public interface UserDao {
        // 注册并增加用户信息
        int insertUser(User user);
    }
  5. 编写 Dao 接口对应的 xml 配置文件UserDao.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="xyz.rtx3090.dao.UserDao">
        <!-- 注册并增加用户信息 -->
        <insert id="insertUser" parameterType="user">
            insert into mybatis.user (id, name, pwd)
            VALUES (#{id}, #{name}, #{pwd});
        </insert>
    </mapper>
  6. 编写 service 接口UserService

    package xyz.rtx3090.service;
    
    import xyz.rtx3090.pojo.User;
    
    public interface UserService {
        // 注册并增加用户信息
        int insertUser(User user);
    }
  7. 编写 service 接口实现类UserServiceImpl

    package xyz.rtx3090.service.impl;
    
    import xyz.rtx3090.dao.UserDao;
    import xyz.rtx3090.pojo.User;
    import xyz.rtx3090.service.UserService;
    
    public class UserServiceImpl implements UserService {
        private UserDao userDao;
    
        public void setUserDao(UserDao userDao) {this.userDao = userDao;}
    
        @Override
        public int insertUser(User user) {return userDao.insertUser(user);
        }
    }
  8. 编写 controller 类RegisterServlet

    package xyz.rtx3090.controller;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import xyz.rtx3090.pojo.User;
    import xyz.rtx3090.service.UserService;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    public class RegisterServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // 承受申请参数
            String name = req.getParameter("name");
            String pwd = req.getParameter("pwd");
    
            // 创立 spring 容器,获取 Service 对象
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            System.out.println("容器的信息:" + context);
            UserService userServiceImpl = context.getBean("userServiceImpl", UserService.class);
    
            User user = new User();
            user.setName(name);
            user.setPwd(pwd);
    
            // 调用 service 办法
            userServiceImpl.insertUser(user);
    
            // 显示处理结果的 jsp
            req.getRequestDispatcher("/result.jsp").forward(req,resp);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req,resp);
        }
    }
  9. 编写 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>
        <!-- 设置 -->
        <settings>
            <setting name="logImpl" value="STDOUT_LOGGING"/>
        </settings>
        <!-- 类型别名 -->
        <typeAliases>
            <package name="xyz.rtx3090.pojo"/>
        </typeAliases>
        <!-- 映射文件 -->
        <mappers>
            <package name="xyz.rtx3090.dao"/>
        </mappers>
    </configuration>
  10. 数据库配置文件jdbc.properites

    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8
    username=root
    password=intmain()
    max=20
  11. 编写 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:jdbc.properties"/>
    
        <!-- 申明数据源,作用是链接数据库 -->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
              init-method="init" destroy-method="close">
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
            <property name="maxActive" value="${max}"/>
        </bean>
    
        <!-- 配置 SqlSessionFactory-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!-- 关联数据源 -->
            <property name="dataSource" ref="dataSource"/>
            <!-- 关联 mybatis 外围配置文件 -->
            <property name="configLocation" value="classpath:mybatisConfig.xml"/>
        </bean>
    
        <!-- 注册 Mapper 扫描配置器 -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
            <!-- 指定根本扫描包,即 Dao 接口包 -->
            <property name="basePackage" value="xyz.rtx3090.dao"/>
        </bean>
    
        <!-- 导入 beans.xml 文件 -->
        <import resource="classpath:beans.xml"/>
    </beans>
  12. 编写另一个 spring 外围配置文件 beans.xml(用于注册自定义类到 springIOC 容器中)

    <?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">
    
        <!-- 注册实现类 UserServiceImpl 到 springIOC 容器中 -->
        <bean id="userServiceImpl" class="xyz.rtx3090.service.impl.UserServiceImpl">
            <property name="userDao" ref="userDao"/>
        </bean>
    </beans>
  13. 配置 Tomcat 服务器

  14. 启动 tomcat,在页面输出注册信息,若跳转到登录胜利的页面,则表明工程胜利

运行后果剖析

当表单提交,跳转到 result.jsp 后,多刷新几次页面,查看后盾输入,发现每刷新一次 页面,就 new 出一个新的 Spring 容器。即,每提交一次申请,就会创立一个新的 Spring 容 器。对于一个利用来说,只须要一个 Spring 容器即可。所以,将 Spring 容器的创立语句放 在 Servlet 的 doGet()或 doPost()办法中是有问题的。

此时,能够思考,将 Spring 容器的创立放在 Servlet 进行初始化时进行,即执行 init()方 法时执行。并且,Servlet 还是单例多线程的,即一个业务只有一个 Servlet 实例,所有执行 该业务的用户执行的都是这一个 Servlet 实例。这样,Spring 容器就具备了唯一性了。然而,Servlet 是一个业务一个 Servlet 实例,即 LoginServlet 只有一个,但还会有 StudentServlet、TeacherServlet 等。每个业务都会有一个 Servlet,都会执行本人的 init()办法,也就都会创立一个 Spring 容器了。这样一来,Spring 容器就又不惟一了。

为了下面的问题,咱们就须要用到【监听器 ContextLoaderListener】

Spring 的监听器 ContextLoaderListener

对于 Web 利用来说,ServletContext 对象是惟一的,一个 Web 利用,只有一个 ServletContext 对象,该对象是在 Web 利用装载时初始化的。若将 Spring 容器的创立机会,放在 ServletContext 初始化时,就能够保障 Spring 容器的创立只会执行一次,也就保障了 Spring 容器在整个利用中的唯一性。

当 Spring 容器创立好后,在整个利用的生命周期过程中,Spring 容器应该是随时能够被拜访的。即,Spring 容器应具备全局性。而放入 ServletContext 对象的属性,就具备利用的 全局性。所以,将创立好的 Spring 容器,以属性的模式放入到 ServletContext 的空间中,就 保障了 Spring 容器的全局性。

退出移动版