文章由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类,这个类内部创建SqlSessionFactory的
SqlSessionFactory 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.class)
MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象。
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定SqlSessionFactory对象的id-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
<!--指定包名, 包名是dao接口所在的包名。
MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行
一次getMapper()方法,得到每个接口的dao对象。
创建好的dao对象放入到spring的容器中的。 dao对象的默认名称是 接口名首字母小写
-->
<property name="basePackage" value="com.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>