Spring
Spring依赖
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
<!-- xml、properties文件加载build,将这些文件加载到类路径下-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
Spring容器创建
// 以ClassPathXmlApplicationContext为例 配置文件路径为类路径
ApplicationContext act = new ClassPathXmlApplicationContext("main.xml");
第一章 IOC
什么是IOC?
ioc即为控制反转是spring核心之一,Inversion of control
管理对象的创建、赋值、依赖关系
IOC通过管理对象,实现业务逻辑对象的解耦合
1. 核心名词
**控制:**一个对象的创建赋值等等,就是一个对象的生命周期
**正转:**一般情况下对对象的控制都有我们程序员来把握,成为控制正转
**反转:**将对象的控制权转交给spring容器
DI 依赖注入:Dependency Injection
spring通过反射创建对象或对对象进行赋值,实现依赖注入,实现IOC
2. 配置文件方式创建Bean
在这里配置你需要的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">
</beans>
注:beans后的内容是一个约束文件,也就是说在本文件定义的东西要符合文件规范
① bean创建
- bean的添加,每一个id只能被创建一次,创建成功后数据会放在spring维护的一张concurrentMap表中 map.put(id,class)
- 当配置文件执行的时候,spring会把所有的bean创建
- spring创建对象默认使用对象的无参数构造方法
② bean属性赋值
- set注入
set注入就是直接在xml文件中使用property标签配置属性
//配置简单数据类型
<bean id="student" class="org.example.entity.Student">
<property name="name" value="李四"/>
<property name="age" value="22"/>
</bean>
//配置引用数据类型,使用ref
<bean id="student" class="org.example.entity.Student">
<property name="name" value="李四"/>
<property name="age" value="22"/>
<property name="school" ref="mySchool"/>
</bean>
<bean id="mySchool" class="org.example.entity.School">
<property name="name" value="华信"/>
</bean>
注:Spring注重的是属性的Set方法,所以写了age属性名,对象中就一定要有setAge()方法
- 构造方法赋值
使用constructor-arg标签
// 使用name进行属性赋值
<bean id="student" class="org.example.entity.Student">
<constructor-arg name="myname" value="llz"/>
<constructor-arg name="myage" value="22"/>
<constructor-arg name="myschool" ref="mySchool"/>
</bean>
// 根据index属性进行赋值 index 是形参在参数列表的位置
<bean id="student" class="org.example.entity.Student">
<constructor-arg index="1" value="22"/>
<constructor-arg index="2" ref="mySchool"/>
<constructor-arg index="0" value="llz"/>
</bean>
// 在使用时 name和index可以省略但是 参数顺序要正确
<bean id="student" class="org.example.entity.Student">
<constructor-arg value="llz"/>
<constructor-arg value="22"/>
<constructor-arg ref="mySchool"/>
</bean>
- 自动注入
自动注入只能注入引用数据类型
通过定义 autowire 标签来实现
<bean id="student" class="org.example.entity.Student" autowire="byName">
<constructor-arg value="llz"/>
<constructor-arg value="22"/>
</bean>
// byName 是根据name匹配数据
先判断容器id是否与定义的属性名称一致,一致之后在此判断数据类型是否一致
// byType 根据类型来匹配
比如:1.同一个类型 2.子类 3.接口实现类
一定要注意 一个文件中只能有一个匹配,多的话会报错
③ 链接多个配置文件
当模块过于庞大时,我们可能会对配置文件进行模块分类,那么就需要链接两个配置文件
使用import标签 注意一定要是classpath路径
<import resource="classpath:配置文件路径" />
例:
<import resource="spring-school.xml"/>
<import resource="spring-student.xml"/>
路径可使用通配符
<import resource="spring-*.xml"/>
3. 注解方法创建Bean
① 相关注解
1. 注解扫描器
使用注解创建bean时必须要在配置文件中添加扫描器
这样spring才会去创建那些加注解的类
<context:component-scan base-package="org.example.test02"/>
可以多次使用该表前实现多个包扫描
2. @Component
对象创建注解 等同于xml中bean标签
该注解创建对象使用空参构造方法
@Component // 使用spring默认明明规则 类名首字母大写
@Component("MyStudent") // 自己指定类名
3. @Service
作用和Component效果一样创建bean
但是他表示这个类是业务层,可以包含事务
4. @Repository
作用和Component效果一样创建bean
但是他表示这个类是持久层,可以访问数据库
5. @Controller
作用和Component效果一样创建bean
但是他表示这个类是控制层,可以接受前端请求,也可以返回数据
② Bean属性赋值
在这之前也需要加扫描
1. @Value
赋值基本数据类型
- 加在属性之上
// 获取配置文件中Student.age的值 需要配合配置文件使用
@Value("${Student.age}")
private int age;
2. @Autowired
赋值引用数据类型
- 加在属性之上
// 默认注入方式是byType 容器中得有该类型的bean
@Autowired
private School school;
**属性1:**required
- true(默认) 注入失败时报错
- false 注入失败时不报错,注入对象为null
@Autowired(required = false)
private School school;
3. 配置文件加载
1.我们需要创建一个配置属性的文件
2.文件格式为properties
3.文件单独存在我们需要它被spring读取
<!-- 在配置文件中引入它 -->
<context:property-placeholder location="classpath:myconf.properties"/>
第二章 AOP
1. AOP简介
① 什么是AOP?
面向切面编程(Aspect oriented programming)
-
什么是切面
切面就是我们要添加的非业务功能代码,
-
怎么面向切面编程
1.找出切面,也就是我们要添加的功能代码
2.设计切面执行时间,比如说是在业务代码之前,还是之后还是什么等等
② 面向切面编程的好处
- 可以专注业务开发,提高开发效率
- 实现业务和非业务功能解耦合
- 复用切面代码
- 不用更改原来的业务代码
③ AOP中的术语
-
Aspect :切面,也就是我们要给业务方法添加的更改
-
JoinPoint :连接点,链接切面的业务方法,在这个业务方法执行时,会同时执行切面功能
-
Pointcut :切入点,一个或多个连接点的集合,也就是表示这个切面将在那些方法执行
-
target :添加切面的目标对象
-
Advice :通知,也就是确定添加的切面是要在业务之前执行还是在之后执行
其中 Advice Pointcut Aspect 最重要,意思是在什么时间、什么地方、执行那个切面
AOP是一个动态思想,在程序执行过程中创建代理(ServiceProxy),使用代理执行方法时,为业务方法增加切面功能,代理存在内存中
④ Spring中的代理方式
-
JDK动态代理
如果 target 实现了一个或多个接口
那么Spring将使用动态代理,target实现的所有接口都将被代理,所以
直接调用target方法时,会报com.sun.proxy.$Proxy16 cannot be cast to com.test01.service.impl.ServiceTest01Impl 异常
解决方法:
给xml文件开启切面加上这个东西
<aop:aspectj-autoproxy proxy-target-class="true"/> // 这样可以强制使用另一种代理方式
-
CGLIB代理
代理对象类型是target的子类
开其方法就是上面所说在XML中进行配置
2. Spring+AspectJ实现AOP
① 实现步骤(注解方式)
- 导入依赖,开启切面配置
- 编写一个Proxy(切面)
- 编写一个需要添加切面的方法
- 完善切面配置
- 调用方法验证
② 实现
1. 导入AspectJ依赖/开启切面配置
pom.xml 导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.15</version>
</dependency>
spring.xml 开启配置
<aop:aspectj-autoproxy/>
2. 编写切面
@Configuration // 表明是一个配置类(交给IOC管理)
@Aspect // 表明自己是一个切面类
public class MyProxy {
/**
* 第一要素Advice,确定该切面在什么时候执行 Before表示在业务方法之前执行
* execution(..) 切面切入点
*/
@Before(value = "execution(* com.test01.service.ServiceTest01+.*(..))")
void before(){
System.out.println("切面来了");
}
}
3. 实现业务方法
// 业务方法随便写一个就行
public void save(School school) {
school.setName("zhengzhpou1");
System.out.println(school);
}
4. 完善切面注解
已在1.1.1.2实现
5. 调用验证
public static void main(String[] args) {
ApplicationContext act = new ClassPathXmlApplicationContext("main.xml");
ServiceTest01 service = (ServiceTest01) act.getBean("serviceTest01Impl");
School school = new School();
service.save(school);
System.out.println(service.getClass().getName());
}
输出:
切面来了
School(name=zhengzhpou1)
com.sun.proxy.$Proxy16 // 可以我们执行的其实是加工过的切面类
3. Advice通知
Advice有五种
- Before :前置通知,在业务方法之前执行
- AfterReturning:后置通知,在业务方法之后执行
- Around :环绕通知,在业务方法之前之后都执行(若业务方法无返回值则不会在之后执行)
- AfterThrowing : 异常通知,只有业务方法出异常时才会通知
- After :后置通知
① Before
前置通知
就是在执行业务方法之前执行切面代码
可以搭配JoinPoint使用
@Before(value = "execution(* *..impl.*.sav*(..))")
public void before(JoinPoint joinPoint){
}
② AfterReturning
后置通知
就是在业务方法之后执行切面代码
可以搭配JoinPoint使用
此外他还有自己的参数,那就是目标方法的返回值
@AfterReturning(value = "execution(* *..service..*.*(..))",returning = "res") // returning代表返回值参数
public void afterReturning(Object res){
}
注:如果res是基本数据类型,那么在切面改变最后结果不变;如果res是引用数据类型,在切面改变之后,最终结果会变化
例如:
@AfterReturning(value = "execution(* *..service..*.*(..))",returning = "res")
public void afterReturning(Object res){
System.out.println(res);
System.out.println("后置切面来了");
Student student = (Student)res;
student.setName("lll");
}
ServiceTest01 target = (ServiceTest01) act.getBean("serviceTest01Impl");
System.out.println( target.getStudent("梁林宗"));
最后输出结果为:
Student(name=梁林宗, mySchool=null)
后置切面来了
Student(name=lll, mySchool=null) // 可以看到切面中改变最后数据会改变
③ Around
环绕方法
非常强大,可以在业务之前加代码,也可以在之后加代码,甚至可以控制目标方法
特点
- 必须要有返回值,类型与目标方法一致,或是Object
- 参数必须声明ProceedingJoinPoint
- 可以实现前置切面、后置切面、控制目标方法
// 案例一 屏蔽目标方法
@Around(value ="execution(* *..service..*.*(..))")
public Object around(ProceedingJoinPoint joinPoint){
Student student = new Student();
student.setName("llz");
return student;
}
// 案例二 执行前置切面、后置切面、最后返回目标方法结果
@Around(value ="execution(* *..service..*.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("前置切面");
Object result = joinPoint.proceed(); // 执行目标方法,返回值为目标返回结果
System.out.println("后置切面");
return result;
}
// 案例三 执行前置切面、后置切面、修改最后结果
@Around(value ="execution(* *..service..*.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("前置切面");
Object result = joinPoint.proceed();
System.out.println("后置切面");
return result + "修改最后结果";
}
注:joinPoint.proceed() 意为执行目标方法
④ AfterThrowing
异常通知
就是当目标方法抛出异常时,执行切面代码
特点:
- 必须有一个参数 Exception
- 没有返回值
// 它多一个参数 throwing = "exception"
@AfterThrowing(value ="execution(* *..service..*.*(..))",throwing = "exception")
public void around(Exception exception) {
System.out.println(exception.getMessage());
}
⑤ @After
最终通知
无论怎样,切面代码都会执行(其实就是放在finally里)
特点:
- 返回值为void
- 代码总会执行
- 在目标方法执行后执行
@After(value ="execution(* *..service..*.*(..))")
public void after() {
System.out.println("after来了");
}
输出: // 即使目标方法出现异常 该切面依旧会执行
after来了
Exception in thread "main" java.lang.ArithmeticException: / by zero
4. execution切点
切点为一个表达式,和Advice配合使用
表达式为
execution(业务方法权限 业务方法返回值类型 业务方法路径+业务方法名 业务方法抛出异常类型 )
注:其中黑体为必填
此外表达式支持通配符
符号 | 意思 |
---|---|
* | 代表0个或多个任意字符 |
… | 1.用在方法参数中,表示一个或多个参数;2.用在包名后表示 当前包以及子包路径 |
+ | 1.用在类名后 表示当前类及其子类;2.用在接口上 表示当前接口以及其实现类 |
举例
1.execution(public * *(..))
代表方法修饰符为public的所有方法
2.execution(* set*(..))
代表所有的 以set 开头的方法
3.execution(* com.test01.service.IService+.*(..))
代表该接口所有实现类的所有方法
4.execution(* com.test01.service..*(..))
代表service下的所有类以及子包中的所有子类的所有方法
5.execution(* *..impl.*.sav*(..))
任意路径下impl包下的以sav卡头的任意参数方法
5. 代理类
当你给方法添加切面后,在运行过程中,Spring会创建一个代理类XXXXProxy
@Component
public class SpringProxy {
// 切面
MyProxy aspect = new MyProxy();
//
ServiceTest01Impl target = new ServiceTest01Impl();
public void methodProxy() {
aspect.before();
target.save(new School());
}
}
大致就是这么个实现,这个类只存在内存中,我们调用业务方法时,其实调用的是该方法,所以我们获取
如下:
ServiceTest01 target = (ServiceTest01) act.getBean("serviceTest01Impl");
School school = new School();
target.save(school);
System.out.println(target.getClass().getTypeName());
输出:
School(name=zhengzhpou1)
切面来了
com.sun.proxy.$Proxy16 // 可以发现我们调用 目标业务类型是Spring加工过的类型
6. JoinPoint
我们所有的 Advice一般情况都可以有一个参数,就是JoinPoint,他必须写在参数的第一个位置
JoinPoint就是执行该切面的方法,所以我们可用通过JoinPoint做很多事
@After(value = "execution(* *..impl.*.sav*(..))")
public void before(JoinPoint joinPoint){
System.out.println(joinPoint);
System.out.println("切面来了");
}
输出:
School(name=zhengzhpou1)
execution(void com.test01.service.ServiceTest01.save(School)) // 这个就是JoinPoint我们可以清晰的拿到该方法一些参数
切面来了
com.sun.proxy.$Proxy16
JoinPoint方法
- joinPoint.getArgs() 获取该方法的参数列表
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
System.out.println(arg);
}
输出:
School(name=zhengzhpou1) // 参数内容都有
- joinPoint.getTarget() / getThis() 获取该方法所处的对象
com.test01.service.impl.ServiceTest01Impl@5049d8b2
- joinPoint.getSignature() 获取该方法信息
joinPoint.getSignature();
joinPoint.getSignature().getName();
joinPoint.getSignature().getModifiers();
joinPoint.getSignature().getDeclaringType();
joinPoint.getSignature().getDeclaringTypeName();
输出:
(JDK动态代理,调用接口方法)
void com.test01.service.ServiceTest01.save(School) // 方法
save // 方法名
1025 // 方法修饰词
interface com.test01.service.ServiceTest01 // target的类型及全类名
com.test01.service.ServiceTest01 // target全类名
(CGLIB代理,直接调用target方法)
void com.test01.service.impl.ServiceTest01Impl.save(School)
save
1
class com.test01.service.impl.ServiceTest01Impl
com.test01.service.impl.ServiceTest01Impl
7. PointCut
切入点
也就是统一管理我们的链接点,也就是 execution(* *..service..*.*(..))
当我们多个方法使用同一个链接点是,修改就需要修改多个,使用切入点就可以避免
@Before(value = "flagOne()")
public void before(JoinPoint joinPoint){
System.out.println("前置切面来了");
}
@AfterReturning(value = "flagOne()",returning = "res") // 直接使用方法名引入连接点
public void afterReturning(Object res){
System.out.println("后置切面来了");
}
@Pointcut("execution(* *..service..*.*(..))") // 切入点可以理解为一个标记,简便后续引用,同时已于修改
private void flagOne(){}
第三章 Spring集成MyBatis
核心就是把正常情况下,我们用到的 MyBatis对象交给 Spring管理
1.思路
① 导入MyBatis相关依赖
② 编写 需要与数据库交互的接口(mapper)
③ 编写 与mapper映射的mapper.xml文件
④ 编写 MyBatis配置文件
⑤ 编写 Spring配置文件集成MyBatis
⑥ 在预先准备的servcieImpl中引入mapper接口
⑦ 调用service接口方法 验证结果
2. 实现
① 导入MyBatis相关依赖
(这里展示的是全部依赖)
<!-- spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<!-- mybatis在spring中的创建依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
<!-- 数据连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<!-- SpringJdbc依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.23</version>
</dependency>
② 编写 需要与数据库交互的接口(mapper)
// 以user数据表为例 一个保存一个查询方法
package org.example.mapper;
import org.example.entity.User;
import java.util.List;
public interface UserMapper {
// 保存方法
Integer save(User user);
// 查询方法
List<User> list();
③ 编写 与mapper映射的mapper.xml文件
在 xml文件中实现我们 mapper接口中的 SQL逻辑
<!-- 头文件是MyBatis的标准。 一定要对应到相应的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="org.example.mapper.UserMapper">
<insert id="save" >
INSERT INTO user VALUE (#{id},#{Name},#{age})
</insert>
<select id="list" resultType="org.example.entity.User">
SELECT * FROM user;
</select>
</mapper>
④ 编写MyBatis配置文件
文件作用,告知 MyBatis,我们用的实体类在哪里,mapper.xml在哪里
取名为mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 实体类位置-->
<typeAliases>
<package name="org.example.entity"/>
</typeAliases>
<!--这是告诉Mybatis去哪找持久化类的映射文件,也就是 mapper.xml 的位置。注意自己使用的标签-->
<mappers>
<!-- <mapper resource="org/example/mapper/UserMapper.xml"></mapper>-->
<package name="org.example.mapper"/>
</mappers>
</configuration>
⑤ 编写 Spring配置文件集成MyBatis
核心类:SqlSessionFactory(核心) DruidDataSource MapperScannerConfigurer
<?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="org.example"/>
<!-- 数据源,配置链接数据库的内容-->
<bean id="myDataSources" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/test01"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- SqlSessionFactory mybatis核心对象 需要配置数据源,以及mybatis配置文件位置-->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="myDataSources"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!-- mybatis在spring中的配置类,需要一个SqlSession,还有mapper的位置-->
<bean id="mapperScanConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="factory"/>
<property name="basePackage" value="org.example.mapper"/>
</bean>
</beans>
⑥ 在预先准备的servcieImpl中引入mapper接口
package org.example.service.impl;
import org.example.entity.User;
import org.example.mapper.UserMapper;
import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper mapper;
@Override
public int save(User user) {
return mapper.save(user);
}
@Override
public List<User> list() {
return mapper.list();
}
}
⑦ 调用service接口方法 验证结果
public class App
{
public static void main( String[] args )
{
ApplicationContext con = new ClassPathXmlApplicationContext("spring.xml");
UserService service = (UserService) con.getBean("userServiceImpl");
System.out.println(service.list());
}
}
第四章 Spring事务
什么是事务?
控制多个sql同时成功或是失败。
1. Spring实现事务
以下实现方式在集成 MyBatis基础上实现
① 导入依赖
<!-- spring事务依赖-->
<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>
② 完善Spring配置文件
<!-- 事务管理器 需要用到数据源-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="myDataSources"/>
</bean>
<!-- 开始事务 并制定用哪个事务管理器-->
<tx:annotation-driven transaction-manager="transactionManager"/>
③ 给需要事务的方法添加注解
添加 @Transactional注解,即可实现事务。事务规则为Spring默认规则
@Override
@Transactional
public List<User> list() {
User user = new User();
user.setUse_name("c");
user.setAge("10");
user.setId(104L);
mapper.save(user);
return mapper.list();
}
2. @Transactional 及其属性
这个注解是Spring提供的事务核心
这个注解有多个属性
@Transactional(
propagation = Propagation.MANDATORY, // 事务传播方式
isolation = Isolation.DEFAULT, // 事务隔离级别
readOnly = false, // 是否只读
timeout = 20, // 超时时间
rollbackFor = {NullPointerException.class}, // 回滚异常
noRollbackFor = {ArithmeticException.class} // 不回滚异常
)
① propagation
事务的传播方式 有七种 重要的有三种
- Propagation.REQUIRED (默认) 如果当前存在事务,就加入;没有就自己创建一个事务
- Propagation.SUPPORTS 如果当前存在事务,就加入;没有就正常运行
- Propagation.REQUIRES_NEW 创建一个新的事务执行;如果当前存在事务,就挂起当前事务
- Propagation.MANDATORY 如果当前存在事务则加入事务;没有就抛出异常
- Propagation.NOT_SUPPORTED 以非事务方式运行;如果当前存在事务,就挂起当前事务
- Propagation.NEVER 以非事务放方式运行;如果存在事务就抛出异常
- Propagation. NESTED 如果当前存在事务那么就创建一个新的事务作为当前事务的嵌套事务进行运行;没有就自己创建一个事务
propagation = Propagation.MANDATORY
② isolation
设置事务的隔离级别
- Isolation.DEFAULT(默认) 连接的数据库使用什么级别,就使用什么级别
- Isolation.READ_UNCOMMITTED
- Isolation.READ_COMMITTED
- Isolation.REPEATABLE_READ
- Isolation.SERIALIZABLE
isolation = Isolation.DEFAULT
③ readOnly
设置是不是只读,一般为 false
- false(默认) 表示可以进行其他操作
- true 表示只能查
readOnly = false
④ timeout
设置事务超时时间
以毫秒为单位,一般设置为 -1 ,表示无限长时间(sql执行时间不好控制)
timeout = 20
⑤ rollback
设置回滚条件
内容是一个异常类型的集合(默认回滚条件为,触发运行是异常)
rollbackFor = {NullPointerException.class,ArithmeticException.class}
⑥ noRollbackFor
设置不会滚条件
内容是一个异常类型的集合
noRollbackFor = {ArithmeticException.class}
⑦ 注意事项
- Transactional必须加载public方法上,其他修饰词或者没有修饰词都不生效
- 内部自调用也不生效(因为AOP)
3. AspectJ实现事务方式
也就是非注解方式
相较于注解方式优点:
- 可用同时给多个方法添加事务
- 所有的配置都在配置文件中,方便管理
① 导入依赖
<!-- spring事务依赖-->
<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>
<!-- AspectJ事务依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.15</version>
</dependency>
② 完善Spring配置文件
<!--事务管理器 需要用到数据源,不同的框架有不同的事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="myDataSources"/>
</bean>
<!--1.业务方法具体事务属性配置(事务模式,隔离级别等等) 2.指定事务管理器-->
<tx:advice id="serviceAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--指定为那些方法添加事务,name可以使用 ‘*’ 通配符-->
<tx:method name="list" propagation="REQUIRED" isolation="DEFAULT" read-only="false" timeout="-1" rollback-for="{NullPointerException.class}"/>
<tx:method name="add*" propagation="REQUIRED" isolation="DEFAULT" read-only="false" timeout="-1" rollback-for="{NullPointerException.class}"/>
</tx:attributes>
</tx:advice>
<!--切入点配置-->
<aop:config>
<!--设定一个切入点-->
<aop:pointcut id="pointcut1" expression="execution(* *..service..*.*(..))"/>
<!--将切入点和已配置好的方法事务属性链接-->
<aop:advisor advice-ref="serviceAdvice" pointcut-ref="pointcut1"/>
</aop:config>
③ 测试方法即可
注意点:1.advice中方法名的指定 2.切入点表达式写法
第五章 Spring-Web
在这里只是简单接触一下,具体的要到Spring MVC
依然代码内容接集成MyBatis
1. 实现
① 导入依赖
<!-- 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>
② 创建web模块
如果使用IDEA快速创建,则会生成相应结构,不然自己手动创建
webapp(resources同一级)
webapp/WEB-INF
webapp/WEB-INF/web.xml
webapp/WEB-INF/index.jsp
③ 创建Controller(servlet)
package com.llz.test.controller;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
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 UserController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("我执行了");
// 创建spring容器 开始使用
ApplicationContext act= new ClassPathXmlApplicationContext("spring.xml");
response.getWriter().write("<h1> Hello World </h1>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("我执行了");
// 创建spring容器 开始使用
ApplicationContext act= new ClassPathXmlApplicationContext("spring.xml");
super.doPost(req, resp);
}
}
④ 配置web.xml文件
在这里配置访问跳转路径
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--一个controller对应一个请求
配置servlet-->
<servlet>
<servlet-name>UserController</servlet-name>
<servlet-class>com.llz.test.controller.UserController</servlet-class>
</servlet>
<!--将servlet和请求路径关联-->
<servlet-mapping>
<servlet-name>UserController</servlet-name>
<url-pattern>/list</url-pattern>
</servlet-mapping>
</web-app>
⑤ 编写jsp文件
编写jsp请求,跳转到当前页面
<html>
<body>
<h2>Hello World!</h2>
<div align="center">
<form action="list" method="post">
<input type="submit" value="点击" style="width: 100px;height: 100px;background-color: aqua">
</form>
</div>
</body>
</html>
⑥ 启动tomcat测试访问
正常访问
....
08-Mar-2022 23:43:55.524 淇℃伅 [main] org.apache.catalina.startup.Catalina.start Server startup in [40] milliseconds
Connected to server
[2022-03-08 11:43:55,822] Artifact webTest01: Artifact is being deployed, please wait...
[2022-03-08 11:43:56,106] Artifact webTest01: Artifact is deployed successfully
[2022-03-08 11:43:56,106] Artifact webTest01: Deploy took 284 milliseconds
我执行了
08-Mar-2022 23:44:05.529 淇℃伅 [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory 鎶妛eb 搴旂敤绋嬪簭閮ㄧ讲鍒扮洰褰� [E:\Java\tomcat\apache-tomcat-9.0.21\webapps\manager]
08-Mar-2022 23:44:05.555 淇℃伅 [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [E:\Java\tomcat\apache-tomcat-9.0.21\webapps\manager] has finished in [26] ms
第六章 Bean的创建流程
第四章 Spring事务 (老笔记)
1. 事务三大组件
① PlatformTransactionManager
org.springframework.jdbc.datasource.DataSourceTransactionManager
Spring事务总接口
特点
- 可以控制事务的提交和回滚
- 他有很多实现类,不同的数据框架有不同的实现类
- 当需要事务的时候,他会去调用相对应实现了类的方法,实现事务
TransactionDifinition
可以定义事务各种属性
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
//配置数据隔离性,默认是Default 数据库是啥他就是啥
definition.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
TransactionStatus
事务状态
TransactionStatus transaction = transactionManager.getTransaction(definition);
2. 编程式事务+XML
依赖
pom
spring依赖,spring boot不需要
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.13</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
applicationContext.xml
<!--
配置包扫描
-->
<context:component-scan base-package="com"/>
<!--
数据源
-->
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" name="dataSource">
<property name="password" value="root"/>
<property name="username" value="root"/>
<property name="url" value="jdbc:mysql:///test01?serverTimezone=GMT%2B8"/>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
</bean>
<!--
事务管理器
-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" name="transactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--
配置TransactionTemplate
-->
<bean class="org.springframework.transaction.support.TransactionTemplate" name="transactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
<!--
配置JdbcTemplte
-->
<bean class="org.springframework.jdbc.core.JdbcTemplate" name="jdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
代码实现
- 运用Transactionmanager
public void test01(){
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
TransactionStatus transaction = transactionManager.getTransaction(definition);
try{
jdbcTemplate.execute("insert into user values (1,'llz',20)");
transactionManager.commit(transaction);
}catch(Exception e){
transactionManager.rollback(transaction);
}
}
- TransactionTemplate
public void test02(){
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
jdbcTemplate.execute("insert into user values (3,'llz',20)");
int x=1/0;
} catch (DataAccessException e) {
status.setRollbackOnly();
}
}
});
}
启动/验证
public static void main(String[] args) {
//创建spring容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//拿到指定的测试类
UserService userService = context.getBean(UserService.class);
//调用方法
userService.test02();
}
3. 声明式事务+java代码配置
以在Spring Boot为例(其实springboot中只用配置一个dataSource就可以直接使用注解,进行事务了)
依赖
pom
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.13</version>
</dependency><dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
Config
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
@Configuration
public class SQLConfiguration {
//配置dataSource 可以在application中配置
@Bean
DataSource dataSource(){
DriverManagerDataSource dataSource= new DriverManagerDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/test01?serverTimezone=GMT%2B8");
dataSource.setUsername("root");
dataSource.setPassword("root");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
return dataSource;
}
//配置事务管理器 PlatformTransactionManager
@Bean
PlatformTransactionManager transactionManager(){
return new DataSourceTransactionManager(dataSource()); //记得传入dataSource
}
//配置JdbcTemplate
@Bean
JdbcTemplate jdbcTemplate(){
return new JdbcTemplate(dataSource()); //记得传入dataSource
}
}
代码实现
@Service
public class serviceUser {
@Autowired
JdbcTemplate jdbcTemplate;
@Autowired
PlatformTransactionManager transactionManager;
@Transactional //直接使用注解 开启事务
public void test01(){
jdbcTemplate.execute("insert into user values (5,'java',20)");
int x=1/0;
}
}
启动/验证
@EnableTransactionManagement //开启注解支持
@SpringBootApplication
public class Affair1Application {
public static void main(String[] args) {
//创建spring boot容器
ConfigurableApplicationContext run = SpringApplication.run(Affair1Application.class, args);
//获取指定测试类
serviceUser bean = run.getBean(serviceUser.class);
//启动测试方法
bean.test01();
}
}
事务的传播性
Spring Boot为例
- REQUIRED 如果当前存在事务就加入事务,没有就创建一个新的事务**(spring默认事务行为)**
@Transactional(propagation = Propagation.REQUIRED)
public void trans(){
}
- SUPPORTS 如果当前有事务则加入事务,没有就正常运行
@Transactional(propagation = Propagation.SUPPORTS)
public void trans(){
}
- MANDATORY 如果当前存在事务则加入事务,没有就抛出异常
@Transactional(propagation = Propagation.MANDATORY)
public void trans(){
}
- REQUIRES_NEW 创建一个新的事务,如果当前存在事务就把当前事务挂起
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void trans(){
}
- NOT_SUPPORTED 以非事务方式运行,如果存在事务就把当前事务挂起
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void trans(){
}
- NEVER 以非事务放方式运行,如果存在事务就抛出异常
@Transactional(propagation = Propagation.NEVER)
public void trans(){
}
- NESTED 如果当前存在事务那么就创建一个新的事务作为当前事务的嵌套事务进行运行,没有就当作REQUIRED
@Transactional(propagation = Propagation.NESTED)
public void trans(){
}
Transactional
事务注解
属性
@Transactional(propagation = Propagation.MANDATORY,readOnly = true,rollbackFor = IOException.class,timeout = 3000)
- propagation 配置事务的传播方式
- readOnly 设置事务为只读
- rollback 设置要回滚的异常(检查时异常默认不回滚)
- timeout 设置事务超时间
坑
- Transactional必须加载public方法上,其他修饰词或者没有修饰词都不生效
- 内部自调用也不生效(因为AOP)