spring5高效学习笔记

文章由b站动力节点spring视频课程整理而成,可作为笔记参考。

文章目录

目录

第1章 Spring概述

1.1 Spring框架是什么

  • Spring 是于 2003 年兴起的一个轻量级的 Java 开发框架,它是为了解决企业应用开发 的复杂性而创建的。Spring的核心是控制反转(IoC)和面向切面编程(AOP)。Spring 是可 以在 Java SE/EE 中使用的轻量级开源框架。

  • Spring 的主要作用就是为代码“解耦”,降低代码间的耦合度。就是让对象和对象(模块和模块)之间关系不是使用代码关联,而是通过配置来说明。即在 Spring 中说明对象(模块)的关系。

  • Spring 根据代码的功能特点,使用 Ioc 降低业务对象之间耦合度。IoC 使得主业务在相互调用过程中,不用再自己维护关系了,即不用再自己创建要使用的对象了。而是由 Spring容器统一管理,自动“注入”,注入即赋值。 而 AOP 使得系统级服务得到了最大复用,且不用再由程序员手工将系统级服务“混杂”到主业务逻辑中了,而是由 Spring 容器统一完成“注入”。

1.2 Spring优点?

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

  • (1) 轻量:Spring 框架使用的 jar 都比较小,一般在 1M 以下或者几百 kb。Spring 核心功能的所需的 jar总共在 3M 左右。Spring 框架运行占用的资源少,运行效率高。不依赖其他 jar

  • (2) 针对接口编程,解耦合:Spring 提供了 Ioc 控制反转,由容器管理对象,对象的依赖关系。原来在程序代码中的对象创建方式,现在由容器完成。对象之间的依赖解耦合。

  • (3) AOP 编程的支持:通过 Spring 提供的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付在 Spring 中,开发人员可以从繁杂的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。

  • (4) 方便集成各种优秀框架
    Spring 不排斥各种优秀的开源框架,相反 Spring 可以降低各种框架的使用难度,Spring
    提供了对各种优秀框架(如 Struts,Hibernate、MyBatis)等的直接支持。

1.3 Spring体系结构

在这里插入图片描述

 Spring 由 20 多个模块组成,它们可以分为数据访问/集成(Data Access/Integration)、 Web、面向切面编程(AOP, Aspects)、
 提供JVM的代理(Instrumentation)、消息发送(Messaging)、核心容器(Core Container)和测试(Test)。

第2章 IoC控制反转

  • IoC (Inversion of Control) : 控制反转, 是一个理论,概念,思想.

  • 描述的:把对象的创建,赋值,管理工作都交给代码之外的容器实现, 也就是对象的创建是有其它外部资源完成。

  • 控制: 创建对象,对象的属性赋值,对象之间的关系管理。

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

  • 正转:由开发人员在代码中,使用new 构造方法创建对象, 开发人员主动管理对象。

  • 例如:

  public static void main(String args[]){
            Student student = new Student(); // 在代码中, 创建对象。--正转。

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

  • 为什么要使用 ioc : 目的就是减少对代码的改动, 也能实现不同的功能。 实现解耦合。

2.1 开发工具准备

  • 开发工具:idea2017 以上

  • 依赖管理:maven3 以上

  • jdk:1.8 以上

  • 自行设置maven仓库

2.2 Spring的第一个程序

2.2.1 创建maven项目
2.2.2 引入 maven 依赖 pom.xml
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    插件
    <build>
        <plugins>
            <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
            </plugin>
        </plugins>
    </build>
2.2.3 定义接口与实体类
    public interface SomeService {
        void doSome();
    }
    public class SomeServiceImpl implements SomeService {
        public SomeServiceImpl() {
            super();
            System.out.println("SomeServiceImpl无参数构造方法");
        }
        @Override
        public void doSome() {
            System.out.println("====业务方法doSome()===");
        }
    }
2.2.4创建 Spring 配置文件
  • 在 src/main/resources/目录现创建一个 xml 文件,文件名可以随意,但 Spring 建议的名称为 applicationContext.xml。
    spring 配置中需要加入约束文件才能正常使用,约束文件是 xsd 扩展名。
<?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="someService" class="com.wjw.service.impl.SomeServiceImpl"></bean>
</beans>
2.2.5定义测试类
@Test
    public  void test02(){
        String config="application.xml";
        ApplicationContext ac=new ClassPathXmlApplicationContext(config);
        SomeService someService= (SomeService) ac.getBean("someService");
        someService.doSome();
    }
2.2.6使用 spring 创建非自定义类对象
  • spring 配置文件加入 java.util.Date 定义:<bean id="myDate" class="java.util.Date" />

  • MyTest 测试类中:调用 getBean(“myDate”) 获取日期类对象。

2.3 基于XML的DI

2.3.1 注入分类
  • bean 实例在调用无参构造器创建对象后,就要对 bean 对象的属性进行初始化。

  • 初始化是由容器自动完成的,称为注入。根据注入方式的不同,常用的有两类:set 注入、构造注入。

(1) set 注入

A、简单类型注入(通过set方法注入)

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

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

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
<bean id="stu" class="com.wjw.ba01.Student">
        <property name="name" value="李四"/>
        <property name="age" value="20"/>
    </bean>

    <bean id="myDate" class="java.util.Date">
        <property name="time" value="12321323"/>
    </bean>

B、引用类型注入

<bean id="stu2" class="com.wjw.ba02.Student">
        <property name="name" value="李四"/>
        <property name="age" value="20"/>
        <property name="school" ref="mySchool"/>
    </bean>

    <bean id="mySchool" class="com.wjw.ba02.School">
        <property name="name" value="北京大学"/>
        <property name="address" value="北京海淀市"/>
    </bean>
  • 差别就是ref引入一个新的bean对象

(2) 构造注入(通过构造方法注入)

  • 构造注入是指,在构造调用者实例的同时,完成被调用者的实例化。即,使用构造器设置依赖关系。
public class Student {
    private String name;
    private int age;
    private School school;


    public Student(String name, int age, School school) {
        this.name = name;
        this.age = age;
        this.school = school;
    }
}
    <bean id="stu2" class="com.wjw.ba03.Student">
        <constructor-arg name="name" value="小明"/>
        <constructor-arg name="age" value="19"/>
        <constructor-arg name="school" ref="mySchool"/>
    </bean>

    <bean id="stu3" class="com.wjw.ba03.Student">
        <constructor-arg index="0" value="小红"/>
        <constructor-arg index="1" value="17"/>
        <constructor-arg index="2" ref="mySchool"/>
    </bean>

    <bean id="stu4" class="com.wjw.ba03.Student">
        <constructor-arg  value="小红22"/>
        <constructor-arg  value="17"/>
        <constructor-arg  ref="mySchool"/>
    </bean>

<constructor-arg />标签中用于指定参数的属性有:

  • ➢ name:指定参数名称。
  • ➢ index:指明该参数对应着构造器的第几个参数,从 0 开始。不过,该属性不要也行,但要注意,若参数类型相同,或之间有包含关系,则需要保证赋值顺序要与构造器中的参数顺序一致。
2.3.2 引用类型属性自动注入
  • 对于引用类型属性的注入,也可不在配置文件中显示的注入。可以通过为标签设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属性)。
  • 根据自动注入判断标准的不同,可以分为两种:
    byName:根据名称自动注入
    byType: 根据类型自动注入
    <bean id="stu" class="com.wjw.ba04.Student" autowire="byName">
        <property name="name" value="小明"/>
        <property name="age" value="22"/>
    <!--        <property name="school" ref="school"/>-->
    </bean>

    <bean id="school" class="com.wjw.ba04.School">
        <property name="name" value="北京大学"/>
        <property name="address" value="北京海淀市"/>
    </bean>
2.3.3 引入多个 Spring 配置文件
  • 多个配置文件中有一个总文件,总配置文件将各其它子文件通过<import/>引入。

2.4 基于注解的DI

  • 对于 DI 使用注解,将不再需要在 Spring 配置文件中声明 bean 实例。Spring 中使用注解,需要在原有 Spring 运行环境基础上再做一些改变。
  • 需要在 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="com.wjw.ba06"/>
</beans>

指定多个包的三种方式:

  • 1.使用多个 context:component-scan 指定不同的包路径
    在这里插入图片描述

  • 2.指定 base-package 的值使用分隔符

  •  分隔符可以使用逗号(,)分号(;)还可以使用空格,不建议使用空格。
    

在这里插入图片描述

  • 3.base-package 是指定到父包名
  • base-package 的值表是基本包,容器启动会扫描包及其子包中的注解,当然也会扫描到子包下级的子包。所以 base-package 可以指定一个父包就可以。
    

在这里插入图片描述

2.4.1定义 Bean 的注解@Component
  • 需要在类上使用注解@Component,该注解的 value 属性用于指定该 bean 的 id 值。
@Component("myStudent")
public class Student {
    private String name;
    private Integer age;

@Component(value = "myStudent")等同于<bean id="myStudent" class="com.wjw.ba01.Student" />
@Component 不指定 value 属性,bean 的 id 是类名的首字母小写。

spring中和@Component功能一致,创建对象的注解还有:

  • 1.@Repository(用在持久层类的上面) :
  • 放在dao的实现类上面,表示创建dao对象,dao对象是能访问数据库的。
    
  • 2.@Service(用在业务层类的上面):
  • 放在service的实现类上面,创建service对象,service对象是做业务处理,可以有事务等功能的。
    
  • 3.@Controller(用在控制器的上面):
  •  放在控制器(处理器)类的上面,创建控制器对象的,控制器对象,能够接受用户提交的参数,显示请求的处理结果。
    

在这里插入图片描述

2.4.2简单类型属性注入@Value
  • 需要在属性上使用注解@Value,该注解的 value 属性用于指定要注入的值。在这里插入图片描述

  • 使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上。

2.4.3 byType 自动注入@Autowired
  • 需要在引用属性上使用注解@Autowired,该注解默认使用按类型自动装配 Bean 的方式。使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上。

在这里插入图片描述

2.4.4 byName 自动注入@Autowired 与@Qualifier
  • 需要在引用属性上联合使用注解@Autowired 与@Qualifier。@Qualifier 的 value 属性用于指定要匹配的 Bean 的 id 值。类中无需 set 方法,也可加到 set 方法上。
    在这里插入图片描述
2.4.5 JDK 注解@Resource 自动注入
  • Spring提供了对 jdk中@Resource注解的支持。@Resource 注解既可以按名称匹配Bean,也可以按类型匹配 Bean。默认是按名称注入。使用该注解,要求 JDK 必须是 6 及以上版本。@Resource 可在属性上,也可在 set 方法上。

(1) byType 注入引用类型属性

  • @Resource 注解若不带任何参数,采用默认按名称的方式注入,按名称不能注入 bean,则会按照类型进行 Bean 的匹配注入。
    在这里插入图片描述
    (2) byName 注入引用类型属性
    @Resource 注解指定其 name 属性,则 name 的值即为按照名称进行匹配的 Bean 的 id
    在这里插入图片描述
2.4.6 注解与 XML 的对比
  • 1.注解优点是:
    ⚫ 方便
    ⚫ 直观
    ⚫ 高效(代码少,没有配置文件的书写那么复杂)。
  • 其弊端也显而易见:以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的。
  • 2.XML 方式优点是:
    ⚫ 配置和代码是分离的
    ⚫ 在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。
  • xml 的缺点是:编写麻烦,效率低,大型项目过于复杂。

第3章 AOP面向切面编程

3.1 不使用AOP的开发方式

3.2 动态代理

- 1.动态代理

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

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

- 2.动态代理的作用:
1)在目标类源代码不改变的情况下,增加功能。
2)减少代码的重复
3)专注业务逻辑代码
4)解耦合,让你的业务功能和日志,事务非业务功能分离。

3.3 AOP简介

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

AOP(Aspect Orient Programming)面向切面编程

  • Aspect: 切面,给你的目标类增加的功能,就是切面。 像上面用的日志,事务都是切面。
  • 切面的特点: 一般都是非业务方法,独立使用的。
  • Orient:面向, 对着。
  • Programming:编程

3.4 面向切面编程对有什么好处?

  • 1.减少重复;
  • 2.专注业务;
    注意:面向切面编程只是面向对象编程的一种补充。

3.5 AOP编程术语

(1) 切面(Aspect)

  • 表示增强的功能, 就是一堆代码,完成某个一个功能。非业务功能,常见的切面功能有日志, 事务, 统计信息, 参数检查, 权限验证。

(2) 连接点(JoinPoint)

  • 连接点 ,连接业务方法和切面的位置。 就某类中的业务方法

(3) 切入点(Pointcut)

  • 切入点 ,指多个连接点方法的集合。多个方法

(4) 目标对象(Target)

  • 给哪个类的方法增加功能, 这个类就是目标对象

(5) 通知(Advice)

  • 通知,通知表示切面功能执行的时间。

3.6 AspectJ对AOP的实现

aop是一个规范,是动态的一个规范化,一个标准
aop的技术实现框架:

  • 1.spring:spring在内部实现了aop规范,能做aop的工作。
    spring主要在事务处理时使用aop。
    我们项目开发中很少使用spring的aop实现。 因为spring的aop比较笨重。
  • 2.aspectJ: 一个开源的专门做aop的框架。spring框架中集成了aspectj框架,通过spring就能使用aspectj的功能。
    aspectJ框架实现aop有两种方式:
    • 使用xml的配置文件 : 配置全局事务
    • 使用注解,我们在项目中要做aop功能,一般都使用注解, aspectj有5个注解。
    • 1)@Before
      2)@AfterReturning
      3)@Around
      4)@AfterThrowing
      5)@After
3.6.1 AspectJ 的通知类型

AspectJ 中常用的通知有五种类型:
(1)前置通知
(2)后置通知
(3)环绕通知
(4)异常通知
(5)最终通知

3.6.2 AspectJ 的切入点表达式
  • AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:
execution(modifiers-pattern? ret-type-pattern
		 eclaring-type-pattern?name-pattern(param-pattern)
         throws-pattern?)

解释:

  • modifiers-pattern] 访问权限类型
    ret-type-pattern 返回值类型
    declaring-type-pattern 包名类名
    name-pattern(param-pattern) 方法名(参数类型和参数个数)
    throws-pattern 抛出异常类型
    ?表示可选的部分

以上表达式共 4 个部分。
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中明显就
是方法的签名。注意,表达式中黑色文字表示可省略部分,各部分间用空格分开。在其中可
以使用以下符号:
在这里插入图片描述

3.6.3 AspectJ 的开发环境

(1) maven 依赖

	<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.9.RELEASE</version>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-aspects</artifactId>
		<version>5.2.9.RELEASE</version>
	</dependency>
	
	<build>
	<plugins>
		 <plugin>
		 	<artifactId>maven-compiler-plugin</artifactId>
		 	<version>3.1</version>
		 <configuration>
			 <source>1.8</source>
			 <target>1.8</target>
		 </configuration>
		 </plugin>
	</plugins>
	</build>
3.6.4 AspectJ 基于注解的 AOP 实现

(1) 实现步骤
A、Step1:定义业务接口与实现类
在这里插入图片描述
B、 Step2:定义切面类

  • 类中定义了若干普通方法,将作为不同的通知方法,用来增强功能。
    在这里插入图片描述
    C、 Step3:声明目标对象切面类对象
    在这里插入图片描述
    D、Step4:注册 AspectJ 的自动代理

  • 在定义好切面 Aspect 后,需要通知 Spring 容器,让容器生成“目标类+ 切面”的代理对象。这个代理是由容器自动生成的。只需要在 Spring 配置文件中注册一个基于 aspectj 的自动代理生成器,其就会自动扫描到@Aspect 注解,并按通知类型与切入点,将其织入,并生成代理。
    在这里插入图片描述

  • <aop:aspectj-autoproxy/>的底层是由 AnnotationAwareAspectJAutoProxyCreator 实现的。从其类名就可看出,是基于 AspectJ 的注解适配自动代理生成器。

  • 其工作原理是,<aop:aspectj-autoproxy/>通过扫描找到@Aspect 定义的切面类,再由切面类根据切入点找到目标类的目标方法,再由通知类型找到切入的时间点。
    E、 Step5:测试类中使用目标对象的 id
    在这里插入图片描述
    (2) [掌握]@Before 前置通知-方法有 JoinPoint 参数

  • 在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参
    数。该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、
    目标对象等。
    不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该
    参数。
    在这里插入图片描述
    (3) [掌握]@AfterReturning 后置通知-注解有 returning 属性

  • 在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目标方法的返回值。该注解的 returning 属性就是用于指定接收方法返回值的变量名的。所以,被注解为后置通知的方法,除了可以包含 JoinPoint 参数外,还可以包含用于接收返回值的变量。该变量最好为 Object 类型,因为目标方法的返回值可能是任何类型。

在这里插入图片描述
(4) [掌握]@Around 环绕通知-增强方法有 ProceedingJoinPoint参数

  • 在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值,Object 类型。并且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法的返回值。最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。接口增加方法:
    在这里插入图片描述
    (5) [了解]@AfterThrowing 异常通知-注解中有 throwing 属性
  • 在目标方法抛出异常后执行。该注解的 throwing 属性用于指定所发生的异常类对象。当然,被注解为异常通知的方法可以包含一个参数 Throwable,参数名称为 throwing 指定的名称,表示发生的异常对象。
    在这里插入图片描述
    (6) [了解]@After 最终通知
    在这里插入图片描述
    (7) @Pointcut 定义切入点
  • 当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。
  • 其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。这个使用@Pointcut 注解的方法一般使用 private 的标识方法,即没有实际作用的方法。
    在这里插入图片描述

第4章 Spring集成MyBatis

  • 将 MyBatis 与 Spring 进行整合,主要解决的问题就是将 SqlSessionFactory 对象交由 Spring
    来管理。所以,该整合,只需要将 SqlSessionFactory 的对象生成器 SqlSessionFactoryBean 注
    册在 Spring 容器中,再将其注入给 Dao 的实现类即可完成整合。

4.1.1 MySQL创建数据库springdb,新建表Student

4.1.2 maven依赖pom.xml

 <!--做spring事务用到的-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.2.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.9.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>
插件:
  <build>
        <!--目的是把src/main/java目录中的xml文件包含到输出结果中。输出到classes目录中-->
        <resources>
            <resource>
                <directory>src/main/java</directory><!--所在的目录-->
                <includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

4.1.3 定义实体类Student

public class Student {
    private Integer id;
    private String name;
    private String email;
    private Integer age;

4.1.4 定义StudentDao接口

public interface StudentDao {

    int insertStudent(Student student);
    List<Student> selectStudents();
}

4.1.5 定义映射文件mapper

  • 在 Dao 接口的包中创建 MyBatis 的映射文件 mapper,命名与接口名相同,本例为StudentDao.xml。mapper 中的 namespace 取值也为 Dao 接口的全限定性名。
<?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.wjw.dao.StudentDao">
    <insert id="insertStudent">
        insert into student values(#{id},#{name},#{email},#{age})
    </insert>

    <select id="selectStudents" resultType="com.wjw.domain.Student">
        select id,name,email,age from student order by id desc
    </select>
</mapper>

4.1.6 定义Service接口和实现类

接口定义:

public interface StudentService {
    int addStudent(Student student);
    List<Student> queryStudent();

}

类定义:

public class StudentServiceImpl implements StudentService {
    //引用类型
    private StudentDao studentDao;

    //使用set注入,赋值
    public void setStudentDao(StudentDao studentDao) {
        this.studentDao = studentDao;
    }


    public int addStudent(Student student) {
        int nums = studentDao.insertStudent(student);
        return nums;
    }

    public List<Student> queryStudent() {
        List<Student> students = studentDao.selectStudents();
        return students;
    }
}

4.1.7 定义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:控制mybatis全局行为-->
    <settings>
        <!--设置mybatis输出日志-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <!--设置别名-->
    <typeAliases>
        <!--name:实体类所在的包名
            表示com.bjpowernode.domain包中的列名就是别名
            你可以使用Student表示com.bjpowenrode.domain.Student
        -->
        <package name="com.wjw.domain"/>
    </typeAliases>

    <!-- sql mapper(sql映射文件)的位置-->
    <mappers>
        <!--
         name:是包名, 这个包中的所有mapper.xml一次都能加载
       -->
        <package name="com.wjw.dao"/>
    </mappers>
    
</configuration>

4.1.8 修改Spring配置文件

(1) 数据源的配置(掌握)
Druid 数据源 DruidDataSource

  • Druid 是阿里的开源数据库连接池。是 Java 语言中最好的数据库连接池。Druid 能
    够提供强大的监控和扩展功能。
  • 官网:https://github.com/alibaba/druid

配置连接池:
在这里插入图片描述
Spring 配置文件:

<!--声明数据源DataSource, 作用是连接数据库的-->
    <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <!--set注入给DruidDataSource提供连接数据库信息 -->
        <!--    使用属性配置文件中的数据,语法 ${key} -->
        <property name="url" value="${jdbc.url}" /><!--setUrl()-->
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.passwd}" />
        <property name="maxActive" value="${jdbc.max}" />
    </bean>

(2) 从属性文件读取数据库连接信息

  • 为了便于维护,可以将数据库连接信息写入到属性文件中,使 Spring 配置文件从中读取
    数据。
    属性文件名称自定义,但一般都是放在 src 下。
    在这里插入图片描述
    该属性文件若要被 Spring 配置文件读取,其必须在配置文件中进行注册。使用标签。
    <context:property-placeholder/>方式(掌握)
    该方式要求在 Spring 配置文件头部加入 spring-context.xsd 约束文件
    <context:property-placeholder/>标签中有一个属性 location,用于指定属性文件的位置。
 <!--
        把数据库的配置信息,写在一个独立的文件,编译修改数据库的配置内容
        spring知道jdbc.properties文件的位置
     -->
    <context:property-placeholder location="classpath:jdbc.properties" />

(3) 注册 SqlSessionFactoryBean

<!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactorySqlSessionFactory  sqlSessionFactory = new ..
    -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--set注入,把数据库连接池付给了dataSource属性-->
        <property name="dataSource" ref="myDataSource" />
        <!--mybatis主配置文件的位置
           configLocation属性是Resource类型,读取配置文件
           它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置
        -->
        <property name="configLocation" value="classpath:mybatis.xml" />
    </bean>

(4) 定义 Mapper 扫描配置器 MapperScannerConfigurer

  • Mapper 扫描配置器 MapperScannerConfigurer 会自动生成指定的基本包中 mapper 的代理对象。该 Bean 无需设置 id 属性。basePackage 使用分号或逗号设置多个包
<!--创建dao对象,使用SqlSession的getMapper(StudentDao.classMapperScannerConfigurer:在内部调用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.wjw.dao"/>
    </bean>

4.1.9 向Service注入接口名

  <!--声明service-->
    <bean id="studentService" class="com.wjw.service.impl.StudentServiceImpl">
        <property name="studentDao" ref="studentDao" />
    </bean>

4.1.10 Spring配置文件全部配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       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:property-placeholder location="classpath:jdbc.properties" />
    <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.url}" /><!--setUrl()-->
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.passwd}" />
        <property name="maxActive" value="${jdbc.max}" />
    </bean>
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="myDataSource" />
        <property name="configLocation" value="classpath:mybatis.xml" />
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
        <property name="basePackage" value="com.wjw.dao"/>
    </bean>
    <bean id="studentService" class="com.wjw.service.impl.StudentServiceImpl">
        <property name="studentDao" ref="studentDao" />
    </bean>
</beans>

第5章 Spring事务

5.1 Spring的事务管理

  • 事务原本是数据库中的概念,在 Dao 层。但一般情况下,需要将事务提升到业务层,即 Service 层。这样做是为了能够使用事务的特性来管理具体的业务。
  • 在 Spring 中通常可以通过以下两种方式来实现对事务的管理:
    (1)使用 Spring 的事务注解管理事务
    (2)使用 AspectJ 的 AOP 配置管理事务

5.2 Spring事务管理API

(1) 事务管理器接口(重点)

  • 事务管理器是 PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回滚,及获取事务的状态信息。
    在这里插入图片描述
    A、常用的两个实现类
  • PlatformTransactionManager 接口有两个常用的实现类:
    ➢ DataSourceTransactionManager:使用 JDBC 或 MyBatis 进行数据库操作时使用。
    ➢ HibernateTransactionManager:使用 Hibernate 进行持久化数据时使用。

(2) 事务定义接口

  • 事务定义接口 TransactionDefinition 中定义了事务描述相关的三类常量:事务隔离级别、事务传播行为、事务默认超时时限,及对它们的操作。
    在这里插入图片描述
    A、定义了五个事务隔离级别常量(掌握)
    在这里插入图片描述
    B、 定义了七个事务传播行为常量(掌握)

事务传播行为常量都是以 PROPAGATION_ 开头,形如 PROPAGATION_XXX。

PROPAGATION_REQUIRED
PROPAGATION_REQUIRES_NEW
PROPAGATION_SUPPORTS

前面三个需要掌握,后面基本上很少出现。

PROPAGATION_MANDATORY
PROPAGATION_NESTED
PROPAGATION_NEVER
PROPAGATION_NOT_SUPPORTED

a、 PROPAGATION_REQUIRED:

  • 指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事务,则创建一个新事务。这种传播行为是最常见的选择,也是 Spring 默认的事务传播行为。
  • 如该传播行为加在 doOther()方法上。若 doSome()方法在调用 doOther()方法时就是在事务内运行的,则 doOther()方法的执行也加入到该事务内执行。若 doSome()方法在调用doOther()方法时没有在事务内执行,则 doOther()方法会创建一个事务,并在其中执行。
    在这里插入图片描述
    b、PROPAGATION_SUPPORTS
    指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。
    在这里插入图片描述
    c、 PROPAGATION_REQUIRES_NEW
    总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕。
    在这里插入图片描述
    C、 定义了默认事务超时时限
  • 常量 TIMEOUT_DEFAULT 定义了事务底层默认的超时时限,sql 语句的执行时长。
    注意,事务的超时时限起作用的条件比较多,且超时的时间计算点较复杂。所以,该值一般就使用默认值即可。

5.3 使用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[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。

- 1.声明事务管理器、开启事务注解驱动(在application中配置)

 <!--    使用spring的事务管理器-->
    <!-- 1. 声明事务管理器  -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--    连接数据库 指定数据源    -->
        <property name="dataSource" ref="myDataSource"/>
    </bean>

    <!--2. 开启事务注解驱动,告诉spring使用注解管理事务,创建代理对象
           transaction-manager:事务管理器对象的id
    -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

注意:tx驱动导入
在这里插入图片描述
- 2.在方法或类上面加@Transactional注解

public class BuyGoodsServiceImpl implements BuyGoodsService {

    private SaleDao saleDao;
    private GoodsDao goodsDao;
    /**
     *
     * rollbackFor:表示发生指定的异常一定回滚.
     *   处理逻辑是:
     *     1) spring框架会首先检查方法抛出的异常是不是在rollbackFor的属性值中
     *         如果异常在rollbackFor列表中,不管是什么类型的异常,一定回滚。
     *     2) 如果你的抛出的异常不在rollbackFor列表中,spring会判断异常是不是RuntimeException,
     *         如果是一定回滚。
     *
     */
// 默认
//    @Transactional(
//            propagation = Propagation.REQUIRED,
//            isolation = Isolation.DEFAULT,
//            readOnly = false,
//            rollbackFor = {
//                    NullPointerException.class,  NoEnoughException.class
//            }
//    )
    //使用的是事务控制的默认值, 默认的传播行为是REQUIRED,默认的隔离级别DEFAULT
    //默认抛出运行时异常,回滚事务。
    @Transactional
    public void buy(Integer goodsId, Integer nums) {
        System.out.println("buy方法开始==========");
        //想sale表添加记录
        Sale sale=new Sale();
        sale.setGid(goodsId);
        sale.setNums(nums);
        saleDao.insertSale(sale);

        //查询goods商品
        Goods goods=goodsDao.selectGoods(goodsId);
        if(goods==null){
            //没有找到商品
            throw new NoEnoughException("编号"+goods+"没有找到商品");
        }else if(goods.getAmount()<nums){
            //商品不足
            throw new NoEnoughException("编号"+goods+"商品不足");
        }

        //更新列表
        Goods goods1=new Goods();
        goods1.setId(goodsId);
        goods1.setAmount(nums);
        goodsDao.updateGoods(goods1);
        System.out.println("buy方法结束==========");
    }

    public void setSaleDao(SaleDao saleDao) {
        this.saleDao = saleDao;
    }

    public void setGoodsDao(GoodsDao goodsDao) {
        this.goodsDao = goodsDao;
    }
}

注:这个注解只适合小型项目

5.5 使用AspectJ的AOP配置管理事务(掌握)

  • 适合大型项目,有很多的类,方法,需要大量的配置事务,使用aspectj框架功能,在spring配置文件中声明类,方法需要的事务。这种方式业务方法和事务配置完全分离。
    实现步骤: 都是在xml配置文件中实现。

1)要使用的是aspectj框架,需要加入依赖

	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-aspects</artifactId>
		<version>5.2.9.RELEASE</version>
	</dependency>

2)声明事务管理器对象

    <bean id="xx" class="DataSourceTransactionManager">

3) 声明方法需要的事务类型(配置方法的事务属性【隔离级别,传播行为,超时】)

4) 配置aop:指定哪些哪类要创建代理。

模板:

<!--    使用spring的事务管理器-->
    <!-- 1. 声明事务管理器  -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--    连接数据库 指定数据源    -->
        <property name="dataSource" ref="myDataSource"/>
    </bean>

    <!--2.声明业务方法它的事务属性(隔离级别,传播行为,超时时间)
         id:自定义名称,表示 <tx:advice></tx:advice>之间的配置内容的
         transaction-manager:事务管理器对象的id
   -->
    <tx:advice id="myAdvice" transaction-manager="transactionManager">
        <!--tx:attributes:配置事务属性-->
        <tx:attributes>
            <!--tx:method:给具体的方法配置事务属性,method可以有多个,分别给不同的方法设置事务属性
              name:方法名称,1)完整的方法名称,不带有包和类。
                            2)方法可以使用通配符,* 表示任意字符
              propagation:传播行为,枚举值
              isolation:隔离级别
              rollback-for:你指定的异常类名,全限定类名。 发生异常一定回滚
          -->
            <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
                       rollback-for="java.lang.NullPointerException,com.wjw.excep.NoEnoughException"/>

            <!--使用通配符,指定很多的方法-->
            <tx:method name="add*" propagation="REQUIRES_NEW" />
            <!--指定修改方法-->
            <tx:method name="modify*" />
            <!--删除方法-->
            <tx:method name="remove*" />
            <!--查询方法,query,search,find-->
            <tx:method name="*" propagation="SUPPORTS" read-only="true" />
        </tx:attributes>
    </tx:advice>

    <!--配置aop-->
    <aop:config>
        <!--配置切入点表达式:指定哪些包中类,要使用事务
           id:切入点表达式的名称,唯一值
           expression:切入点表达式,指定哪些类要使用事务,aspectj会创建代理对象

           com.bjpowernode.service
           com.crm.service
           com.service
        -->
        <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
        <!--配置增强器:关联adivce和pointcut
             advice-ref:通知,上面tx:advice哪里的配置
             pointcut-ref:切入点表达式的id
         -->
        <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt" />
    </aop:config>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值