Spring 学习记录

Spring 学习记录

  • 1. Spring和SpringFrameWork
    • 1.1 广义的Spring
    • 2.1 狭义的Spring
    • 2.3 SpringFrameWork / Spring框架图
  • 2. Spring IOC容器(即上图中的Core Container)
    • 2.1 相关概念 (IOC DI 容器 组件)
    • 2.2 Spring IOC容器的作用
    • 2.3 Spring IOC容器接口和具体实现类
  • 3. Spring IOC 实践
    • 3.1 IOC / DI 一般步骤
    • 3.2 基于 XML配置 方法
      • 3.2.1 组件信息声明配置 (IOC) 和 依赖注入配置 (DI)
      • 3.2.2 IOC容器创建和使用
      • 3.2.3 组件周期方法配置
      • 3.2.4 FactoryBean的使用
    • 3.3 基于 注解配置 方法
      • 3.3.1 Spring提供的常见注解
      • 3.3.2 注解中BeanName的问题
      • 3.3.2 Autowired 和 Resourc 注解
    • 3.4 基于 配置类 方法
    • 3.5 三种方法总结
  • 4. Spring AOP
    • 4.1 AOP是什么
    • 4.2 为什么引入AOP
    • 4.3 主要应用场景
    • 4.4 Spring AOP 基于注解实现
      • 4.4.1 Spring AOP 底层技术组成
      • 4.3.2 通知增强的类型
      • 4.4.3 切点表达式
      • 4.4.4 代码示例
  • 5.Spring-tx
    • 5.1什么是Spring-tx?为什么需要Spring-tx?
    • 5.2 代码示例
    • 5.3 事务属性

1. Spring和SpringFrameWork

通常情况下,我们所说的Spring是狭义概念的Spring,即SpringFrameWork框架。

1.1 广义的Spring

广义上的 Spring 泛指以 Spring Framework 为基础的 Spring 技术栈

经过十多年的发展,Spring 已经不再是一个单纯的应用框架,而是逐渐发展成为一个由多个不同子项目(模块)组成的成熟技术,例如 Spring Framework、Spring MVC、SpringBoot、Spring Cloud、Spring Data、Spring Security 等,其中 Spring Framework 是其他子项目的基础

2.1 狭义的Spring

狭义的 Spring 特指 Spring Framework,通常我们将它称为 Spring 框架
Spring Framework(Spring框架)是一个开源的应用程序框架,由SpringSource公司开发,最初是为了解决企业级开发中各种常见问题而创建的。它提供了很多功能,例如:依赖注入(Dependency Injection)、面向切面编程(AOP)、声明式事务管理(TX)等。其主要目标是使企业级应用程序的开发变得更加简单和快速,并且Spring框架被广泛应用于Java企业开发领域。

2.3 SpringFrameWork / Spring框架图

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

2. Spring IOC容器(即上图中的Core Container)

2.1 相关概念 (IOC DI 容器 组件)

  • IOC:Inversion of Control(控制反转)
    主要是针对对象的创建和调用控制而言的。当应用程序需要使用一个对象时,不再是应用程序直接创建该对象,而是由 IoC 容器来创建和管理,即控制权由应用程序转移到 IoC 容器中,也就是“反转”了控制权 。这种方式基本上是通过依赖查找的方式来实现的,即 IoC 容器维护着构成应用程序的对象,并负责创建这些对象。
  • DI: Dependency Injection(依赖注入)
    组件之间传递依赖关系的过程中,将依赖关系在容器内部进行处理,这样就不必在应用程序代码中硬编码对象之间的依赖关系,实现了对象之间的解耦合。例如在A类内部需要引用B类,不需要A类的内部创建B类的对象,而是在容器内完成。在 Spring 中,DI 是通过 XML 配置文件或注解的方式实现的。它提供了三种形式的依赖注入:构造函数注入、Setter 方法注入和接口注入
  • IOC容器:IOC是是一种思想而不是某种技术 简而言之即对象不再由应用程序创建,而是由另外的独立的模块来创建,我们把这个独立的模块称为容器,由于应用了IOC思想,所以叫做IOC容器。Spring框架应用了IOC思想将对象的创建放在了独立的模块中,称为Spring IOC容器。
  • 组件:简而言之,组件是对象。但是对象不一定是组件

在这里插入图片描述

2.2 Spring IOC容器的作用

Spring 框架中的IOC容器负责创建组件、管理组件的依赖关系、存储组件,销毁组件。减少编码压力,让程序员更加专注进行业务编写。
容器通过读取配置元数据来获取有关要实例化、配置和组装组件的指令。配置元数据以 XML、Java 注解或 Java 代码形式表现。它允许表达组成应用程序的组件以及这些组件之间丰富的相互依赖关系。
在这里插入图片描述
配置元数据的方式

  • XML配置方式:是Spring框架最早的配置方式之一,通过在XML文件中定义Bean及其依赖关系、Bean的作用域等信息,让Spring IoC容器来管理Bean之间的依赖关系。该方式从Spring框架的第一版开始提供支持。
  • 注解方式:从Spring 2.5版本开始提供支持,可以通过在Bean类上使用注解来代替XML配置文件中的配置信息。通过在Bean类上加上相应的注解(如@Component, @Service, @Autowired等),将Bean注册到Spring IoC容器中,这样Spring IoC容器就可以管理这些Bean之间的依赖关系。
  • Java配置类方式:从Spring 3.0版本开始提供支持,通过Java类来定义Bean、Bean之间的依赖关系和配置信息,从而代替XML配置文件的方式。Java配置类是一种使用Java编写配置信息的方式,通过@Configuration、@Bean等注解来实现Bean和依赖关系的配置。

2.3 Spring IOC容器接口和具体实现类

  • 接口BeanFactory接口提供了配置框架和基本功能,ApplicationContext接口继承自BeanFactory接口,添加了更多特定于企业的功能。
  • 实现类:以下实现类都继承了某些父类,这些父类实现了ApplicatiContext接口。
    在这里插入图片描述
    例如:ClassPathXmlApplicationContext的类继承关系如下:引用自该文章
    在这里插入图片描述

3. Spring IOC 实践

3.1 IOC / DI 一般步骤

元数据配置——容器读取配置并实例化——获取对应的组件
元数据配置:此步骤只是进行配置而不进行具体的实例化
容器读取配置并实例化组件:此步骤容器会对配置读取进而实例化组件
获取对应的组件:此步骤是获取所需要的组件
在这里插入图片描述

3.2 基于 XML配置 方法

3.2.1 组件信息声明配置 (IOC) 和 依赖注入配置 (DI)

  • 基于无参构造函数在这里插入图片描述
public class HappyComponent {//默认包含无参数构造函数public void doWork() {System.out.println("HappyComponent.doWork");}
}
...
<bean id="happyComponent" class="com.local.ioc_01.HappyComponent"/>
  • 基于有参的构造函数
    如果是基本类型,就用value。如果是引用类型,就用ref。所ref的类型要在配置文件中已经配置过,并且ref的是对应类型的id。
    在这里插入图片描述
public class UserDao {
}public class UserService {private UserDao userDao; private int age; private String name;public UserService(int age , String name ,UserDao userDao) {this.userDao = userDao;this.age = age;this.name = name;}
}
<bean id="userDao" class="com.local.ioc_01.UserDao"/>
<bean id="userService" class="com.local.ioc_01.UserService"><constructor-arg name="name" value="zzz"/><constructor-arg name="age" value="12"/><constructor-arg name="userDao" ref="userDao"/>
</bean>
  • 基于静态工厂方法(创建对象的方法是静态方法)
    注:工厂方法——在方法内部创建对象,外部访问的时候只需要访问方法就可以,类似在“工厂”内部对对象进行创建,而使用者不用关心在工厂内部发生了什么。
    factory-method: 指定静态工厂方法,注意,该方法必须是static方法。
public class ClientService {public static ClientService createInstance() {return new ClientService();}
}
<bean id="clientService" class="com.local.ioc_01.ClientService" factory-method="createInstance"/>
  • 基于实例工厂方法(创建对象的方法是非静态方法)
    factory-bean属性:指定当前容器中工厂Bean 的名称。
    factory-method: 指定实例工厂方法名。注意,实例方法必须是非static的
public class DefaultServiceLocator {public ClientService createClientServiceInstance() {return new ClientService();}
}
<bean id="serviceLocator" class="com.local.ioc_01.DefaultServiceLocator"/>
<bean id="clientService2" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>
  • 基于Setter方法依赖注入
public class MovieFinder {
}public class SimpleMovieLister {private MovieFinder movieFinder;private String movieName;public void setMovieFinder(MovieFinder movieFinder) {this.movieFinder = movieFinder;}public void setMovieName(String movieName){this.movieName = movieName;}
}
<bean id="movieFinder" class="com.local.ioc_02.MovieFinder"/>
<bean id="movieLister" class="com.local.ioc_02.SimpleMovieLister"><property name="movieFinder" ref="movieFinder"/><property name="movieName"   value="电影"/>
</bean>

3.2.2 IOC容器创建和使用

# spring-ioc-01.xml是要读取的配置文件
# getBean()中填写要创建的组件对应的id和对应的反射ClassPathXmlApplicationContext Context = new ClassPathXmlApplicationContext("spring-ioc-01.xml");
UserService userService = Context.getBean("userService", UserService.class);

3.2.3 组件周期方法配置

可以在组件类中定义方法,然后当IOC容器实例化和销毁组件对象的时候进行调用。这两个方法我们成为生命周期方法。
类似于Servlet的init/destroy方法,我们可以在周期方法完成初始化和释放资源等工作。
方法命名随意,但是要求方法必须是 public void 无形参列表

public class BeanOne {public void init(){System.out.println("init....");}public void destroy(){System.out.println("destroy....");}
}
<bean id="bean" class="com.local.ioc_03.BeanOne" init-method="init" destroy-method="destroy"/>
ClassPathXmlApplicationContext Context = new ClassPathXmlApplicationContext("spring-ioc-03.xml");
BeanOne bean = Context.getBean("bean", BeanOne.class);
Context.close();
//init....
//destroy....

3.2.4 FactoryBean的使用

FactoryBean 是接口。类实例化该接口后可以将创建复杂对象的过程存储在FactoryBean 的getObject方法中。
待定…

在这里插入代码片

3.3 基于 注解配置 方法

和 XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。

3.3.1 Spring提供的常见注解

@Controller、@Service、@Repository这三个注解只是在@Component注解的基础上起了三个新的名字,在语法层面没有区别。但是为了代码的可读性,不要随意乱起。
在这里插入图片描述

  • 使用注解的组件
@Controller
public class UserController {@Autowiredprivate UserService userService;}@Service
public class UserService {@Autowiredprivate UserDao userDao;
}@Repository
public class UserDao {
}
  • xml配置文件进行扫描
基本扫描
<context:component-scan base-package="com.atguigu.components"/>排除扫描
<context:component-scan base-package="com.atguigu.components"><!-- context:exclude-filter标签:指定排除规则 --><!-- type属性:指定根据什么来进行排除,annotation取值表示根据注解来排除 --><!-- expression属性:指定排除规则的表达式,对于注解来说指定全类名即可 --><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>指定扫描
<!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
<context:component-scan base-package="com.atguigu.ioc.components" use-default-filters="false"><!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 --><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
  • 获取对象
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("annotation01.xml");
UserController bean = context.getBean(UserController.class);

3.3.2 注解中BeanName的问题

使用 XML 方式管理 bean 的时候,每个 bean 都有一个唯一标识——id 属性的值,便于在其他地方引用。现在使用注解后,每个组件仍然应该有一个唯一标识。
默认情况:类名首字母小写就是 bean 的 id。例如:SoldierController 类对应的 bean 的 id 就是 soldierController。
还可以使用value属性指定:

@Controller(value = "tianDog")
public class SoldierController {
}

3.3.2 Autowired 和 Resourc 注解

  • AutoWired注解
    在成员变量上直接标记@Autowired注解即可。容器创建对象的时候会自动寻找对应的组件进行注入。注入流程如下:
    在这里插入图片描述
@Controller(value = "tianDog")
public class SoldierController {@Autowired@Qualifier(value = "maomiService222")// 根据面向接口编程思想,使用接口类型引入Service组件private ISoldierService soldierService;//依赖注入的时候,去寻找value值对应的注解
  • Resource注解
    @Resource注解默认根据 Bean名称装配,未指定name时,使用属性名作为name通过name找不到的话会自动启动通过类型装配。
    @Autowired注解默认根据类型装配,如果想根据名称装配,需要配合@Qualifier注解一起用
    @Autowired注解是Spring提供的注解,@Resource注解是JDK扩展包中的。【高于JDK11或低于JDK8需要引入以下依赖
<dependency><groupId>jakarta.annotation</groupId><artifactId>jakarta.annotation-api</artifactId><version>2.1.1</version>
</dependency>
@Controller
public class UserController {/*** 1. 如果没有指定name,先根据属性名查找IoC中组件xxxService* 2. 如果没有指定name,并且属性名没有对应的组件,会根据属性类型查找* 3. 可以指定name名称查找!  @Resource(name='test') == @Autowired + @Qualifier(value='test')*/@Resourceprivate UserService UserService;
}

3.4 基于 配置类 方法

完全注解开发:Spring 完全注解配置(Fully Annotation-based Configuration)是指通过 Java配置类 代码来配置 Spring 应用程序,使用注解来替代原本在 XML 配置文件中的配置。相对于 XML 配置,完全注解配置具有更强的类型安全性和更好的可读性

  • 配置类:使用 @Configuration 注解将一个普通的类标记为 Spring 的配置类
@Configuration
@ComponentScan(basePackages = {"com.local"})
// 上述代替了使用xml文件配置
public class configuration {
}
  • IOC容器创建对象
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(configuration.class);
UserController bean = context.getBean(UserController.class);

3.5 三种方法总结

  • XML方式配置
    在这里插入图片描述

  • XML+注解方式配置
    在这里插入图片描述

  • 完全注解方式配置
    在这里插入图片描述

4. Spring AOP

4.1 AOP是什么

AOP:Aspect Oriented Programming(面向切面编程)面向切面编程是一种思维,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

4.2 为什么引入AOP

某种程度AOP上完善和解决OOP的非核心代码冗余和不方便统一维护问题。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

4.3 主要应用场景

  1. 日志记录:在系统中记录日志是非常重要的,可以使用AOP来实现日志记录的功能,可以在方法执行前、执行后或异常抛出时记录日志。
  2. 事务处理:在数据库操作中使用事务可以保证数据的一致性,可以使用AOP来实现事务处理的功能,可以在方法开始前开启事务,在方法执行完毕后提交或回滚事务。
  3. 安全控制:在系统中包含某些需要安全控制的操作,如登录、修改密码、授权等,可以使用AOP来实现安全控制的功能。可以在方法执行前进行权限判断,如果用户没有权限,则抛出异常或转向到错误页面,以防止未经授权的访问。
  4. 性能监控:在系统运行过程中,有时需要对某些方法的性能进行监控,以找到系统的瓶颈并进行优化。可以使用AOP来实现性能监控的功能,可以在方法执行前记录时间戳,在方法执行完毕后计算方法执行时间并输出到日志中。
  5. 异常处理:系统中可能出现各种异常情况,如空指针异常、数据库连接异常等,可以使用AOP来实现异常处理的功能,在方法执行过程中,如果出现异常,则进行异常处理(如记录日志、发送邮件等)。
  6. 缓存控制:在系统中有些数据可以缓存起来以提高访问速度,可以使用AOP来实现缓存控制的功能,可以在方法执行前查询缓存中是否有数据,如果有则返回,否则执行方法并将方法返回值存入缓存中。
  7. 动态代理:AOP的实现方式之一是通过动态代理,可以代理某个类的所有方法,用于实现各种功能。

4.4 Spring AOP 基于注解实现

4.4.1 Spring AOP 底层技术组成

在这里插入图片描述

  • 动态代理(InvocationHandler):JDK原生的实现方式,需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口(兄弟两个拜把子模式)。
  • cglib:通过继承被代理的目标类(认干爹模式)实现代理,所以不需要目标类实现接口。
  • AspectJ:早期的AOP实现的框架,SpringAOP借用了AspectJ中的AOP注解。

4.3.2 通知增强的类型

即表示将切面中的方法切入到核心方法中的哪里去。

- 前置通知:在被代理的目标方法前执行 @Before
- 返回通知:在被代理的目标方法成功结束后执行 @After
- 异常通知:在被代理的目标方法异常结束后执行 @AfterThrowing
- 后置通知:在被代理的目标方法最终结束后执行 @AfterReturning
- 环绕通知:使用try...catch...finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置 @Around

4.4.3 切点表达式

  • 什么是切点表达式
    在准备好切面后,需要确定将切面切入到哪里,即切入到核心方法的哪里,切点表达式即指定说明了切入的点。
  • 切点表达式语法
  • 第一位:execution( ) 固定开头
  • 第二位:方法访问修饰符
public private 直接描述对应修饰符即可
  • 第三位:方法返回值
int String void 直接描述返回值类型
注意:特殊情况 不考虑 访问修饰符和返回值execution(* * ) 这是错误语法execution(*) == 你只要考虑返回值 或者 不考虑访问修饰符 相当于全部不考虑了
  • 第四位:指定包的地址
 固定的包: com.atguigu.api | service | dao单层的任意命名: com.atguigu.*  = com.atguigu.api  com.atguigu.dao  * = 任意一层的任意命名任意层任意命名: com.. = com.atguigu.api.erdaye com.a.a.a.a.a.a.a  ..任意层,任意命名 用在包上!注意: ..不能用作包开头   public int .. 错误语法  com..找到任何包下: *..
  • 第五位:指定类名称
固定名称: UserService
任意类名: *
部分任意: com..service.impl.*Impl
任意包任意类: *..*
  • 第六位:指定方法名称
语法和类名一致
任意访问修饰符,任意类的任意方法: * *..*.*
  • 第七位:方法参数
第七位: 方法的参数描述具体值: (String,int) != (int,String) 没有参数 ()模糊值: 任意参数 有 或者 没有 (..)  ..任意参数的意识部分具体和模糊:第一个参数是字符串的方法 (String..)最后一个参数是字符串 (..String)字符串开头,int结尾 (String..int)包含int类型(..int..)
  • 切点表达式重用
    Q:为什么要对切点表达式重用?
    A:当多个切面表达式相同时,后期进行修改维护较麻烦,例如以下:前2个和后2个的切面表达式相同,因此将切点表达式提取到同一个类中,方便重用。
@Before(value = "execution(public int *..Calculator.sub(int,int))")
..........
@AfterReturning(value = "execution(public int *..Calculator.sub(int,int))")
.........
@AfterThrowing(value = "execution(* *..*Service.*(..))")
.........
@After(value="execution(* *..*Service.*(..))")
......

Q:如何重用?如何重用后引用?

// 重用
@Component
public class PointCut {@Pointcut(value = "execution(public int *..Calculator.sub(int,int))")public void GlobalPointCut(){}@Pointcut(value = "execution(* *..*Service.*(..))")public void SecondPointCut(){}
}//引用
@Before(value = "GlobalPointCut")  //value中是方法名
public void printLogBeforeCoreOperation() {}

4.4.4 代码示例

在这里插入图片描述

//定义计算接口
public interface Calculator {int add(int i, int j);int sub(int i, int j);int mul(int i, int j);int div(int i, int j);
}//接口的核心代码实现类(只实现核心代码而忽略一些打印输出和异常处理的代码)
@Component
public class CalculatorPureImpl implements Calculator {  @Overridepublic int add(int i, int j) {return i + j;}  @Overridepublic int sub(int i, int j) {return i - j;}@Overridepublic int mul(int i, int j) {return i * j;}@Overridepublic int div(int i, int j) {return i / j;}
}//重用切面表达式
@Component
public class MyPointCut {@Pointcut(value = "execution(* service.impl.*.*(..))")public void pointCutOne(){}
}//切面
@Component
@Aspect
public class LogAdvice {@Before("PointCut.MyPointCut.pointCutOne()")public void start(){System.out.println("start");}@After("PointCut.MyPointCut.pointCutOne()")public void end(){System.out.println("end");}@AfterThrowing("PointCut.MyPointCut.pointCutOne()")public void error(){System.out.println("error");}
}//配置类扫描和开启aspectj注解
@Configuration
@ComponentScan({"service","advice","config"}) //扫描
@EnableAspectJAutoProxy //开启aspectj注解
public class JavaConfig {
}//测试
@SpringJUnitConfig(value = JavaConfig.class)
public class SpringAopTest {@Autowiredprivate Calculator calculator;@Testpublic void test1(){int res=calculator.add(1,1);System.out.println(res);}
}
//输出
start
end
2

5.Spring-tx

5.1什么是Spring-tx?为什么需要Spring-tx?

  • Spring-tx是Spring框架支持以声明性的方式管理事务,而不是编程式的方式。将事务的控制和业务逻辑分离开来,提高代码的可读性和可维护性
try {// 开启事务:关闭事务的自动提交conn.setAutoCommit(false);// 核心操作// 业务代码// 提交事务conn.commit();  
}catch(Exception e){  // 回滚事务conn.rollBack();
}finally{ // 释放数据库连接conn.close();
}
  • 编程式事务:手动编写程序来管理事务,即通过编写代码的方式直接控制事务的提交和回滚。
  • 声明式事务:使用注解或 XML 配置的方式来控制事务的提交和回滚。开发者只需要添加配置即可, 具体事务的实现由第三方框架实现,避免我们直接进行事务操作。

5.2 代码示例

在这里插入图片描述

//jdbc.properties
atguigu.url=jdbc:mysql://localhost:3306/fruitdb
atguigu.driver=com.mysql.cj.jdbc.Driver
atguigu.username=root
atguigu.password=123456//javaconfig
@Configuration
@ComponentScan({"Dao","Service"})
@PropertySource("classpath:jdbc.properties")
@EnableTransactionManagement //开启事务注解的支持
public class JavaConfig {@Value("${atguigu.driver}")private String driver;@Value("${atguigu.url}")private String url;@Value("${atguigu.username}")private String username;@Value("${atguigu.password}")private String password;//druid连接池@Beanpublic DataSource dataSource(){DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}@Bean//jdbcTemplatepublic JdbcTemplate jdbcTemplate(DataSource dataSource){JdbcTemplate jdbcTemplate = new JdbcTemplate();jdbcTemplate.setDataSource(dataSource);return jdbcTemplate;}//使用事务管理器 //事务管理器需要数据库连接信息datasource@Beanpublic DataSourceTransactionManager transactionManager(DataSource dataSource){return new DataSourceTransactionManager(dataSource);}
}//FruitDao
@Repository
public class FruitDao {@Autowiredprivate JdbcTemplate jdbcTemplate;public void updatePriceByName(String name,Integer price){String sql = "update t_fruit set price = ? where fname = ? ;";int rows = jdbcTemplate.update(sql, price,name);}public void updateRemarkByName(String name,String remark){String sql = "update t_fruit set remark= ? where fname = ? ;";jdbcTemplate.update(sql,remark,name);}
}//FruitService 
@Service
public class FruitService {@Autowiredprivate FruitDao fruitDao;@Transactional //添加事务注解public void changeInfo(){fruitDao.updatePriceByName("苹果",30);int i=1/0; //这里会报错 那么整个事务将会回滚,2次修改信息都将失败 如果没有事务 那么第一次将修改成功而第二次失败fruitDao.updateRemarkByName("苹果","ok");}
}

上述代码在FruitService的方法中添加了Transactionnal注解,该注解可以作用与类和方法上,作用于类上说明对类内的方法都生效,作用于方法则只对方法生效。

5.3 事务属性

  • 只读
    在Transactionnal注解中设置属性readOnly属性为True,默认值为False
    在这里插入图片描述
@Transactional(readOnly = true)
  • 超时时间
    程序运行过程中因为某些原因卡住占用资源,设置超时时间,事务运行的时间超过超过设置的超时时间则回滚,释放资源。通过timeout属性设置。默认值是-1,即无限。
    在这里插入图片描述
@Transactional(timeout = 3)
  • 事务异常
    关于异常的分类可以参考此文章-异常分类总结
    默认只针对运行时异常回滚,编译时异常不回滚
    rollbackForClassName:指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!
    noRollbackForClassName:指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!
public class FruitService {@Autowiredprivate FruitDao fruitDao;@Transactional(noRollbackFor = ArithmeticException.class) //添加事务注解 发生该异常时不回滚public void changeInfo(){fruitDao.updatePriceByName("苹果",30);int i=1/0; //这里会报错 但是设置为不回滚 所以第一条修改成功,但是第二条修改失败fruitDao.updateRemarkByName("苹果","ok");}
}
  • 隔离级别
    数据库事务的隔离级别是指在多个事务并发执行时,数据库系统为了保证数据一致性所遵循的规定。常见的隔离级别包括:
  1. 读未提交(Read Uncommitted):事务可以读取未被提交的数据,容易产生脏读、不可重复读和幻读等问题。实现简单但不太安全,一般不用。
  2. 读已提交(Read Committed):事务只能读取已经提交的数据,可以避免脏读问题,但可能引发不可重复读和幻读。
  3. 可重复读(Repeatable Read):在一个事务中,相同的查询将返回相同的结果集,不管其他事务对数据做了什么修改。可以避免脏读和不可重复读,但仍有幻读的问题。
  4. 串行化(Serializable):最高的隔离级别,完全禁止了并发,只允许一个事务执行完毕之后才能执行另一个事务。可以避免以上所有问题,但效率较低,不适用于高并发场景。
@Transactional(isolation = Isolation.REPEATABLE_READ)
  • 事务传播
    在这里插入图片描述
    @Transactional 注解通过 propagation 属性设置事务的传播行为。它的默认值是
Propagation propagation() default Propagation.REQUIRED;

在这里插入图片描述

//jdbc.properties
atguigu.url=jdbc:mysql://localhost:3306/fruitdb
atguigu.driver=com.mysql.cj.jdbc.Driver
atguigu.username=root
atguigu.password=123456//javaconfig
@Configuration
@ComponentScan({"Dao","Service"})
@PropertySource("classpath:jdbc.properties")
@EnableTransactionManagement //开启事务注解的支持
public class JavaConfig {@Value("${atguigu.driver}")private String driver;@Value("${atguigu.url}")private String url;@Value("${atguigu.username}")private String username;@Value("${atguigu.password}")private String password;//druid连接池@Beanpublic DataSource dataSource(){DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}@Bean//jdbcTemplatepublic JdbcTemplate jdbcTemplate(DataSource dataSource){JdbcTemplate jdbcTemplate = new JdbcTemplate();jdbcTemplate.setDataSource(dataSource);return jdbcTemplate;}//使用事务管理器 //事务管理器需要数据库连接信息datasource@Beanpublic DataSourceTransactionManager transactionManager(DataSource dataSource){return new DataSourceTransactionManager(dataSource);}
}//FruitDao
@Repository
public class FruitDao {@Autowiredprivate JdbcTemplate jdbcTemplate;public void updatePriceByName(String name,Integer price){String sql = "update t_fruit set price = ? where fname = ? ;";int rows = jdbcTemplate.update(sql, price,name);}public void updateRemarkByName(String name,String remark){String sql = "update t_fruit set remark= ? where fname = ? ;";jdbcTemplate.update(sql,remark,name);}
}//FruitService
@Service
public class FruitService {@Autowiredprivate FruitDao fruitDao;@Transactional(noRollbackFor = ArithmeticException.class,propagation = Propagation.REQUIRES_NEW) //添加事务注解 发生该异常时不回滚 并且修改默认传播public void changePrice(){fruitDao.updatePriceByName("香蕉",50);int i=1/0; //这里会报错 但是设置了不回滚}@Transactionalpublic void changeRemark(){fruitDao.updateRemarkByName("苹果","good");}
}//TopService 整合的Service
@Service
public class TopService {@Autowiredprivate FruitService fruitService;@Transactionalpublic void changeInfo(){fruitService.changePrice();fruitService.changeRemark();}
}//测试
@Test
public void test(){topService.changeInfo();
}结果:父事务默认的传播行为,changePrice方法中修改了默认的传播方法,并且遇到运行错误时不回滚。
那么changePrice方法则忽略父方法中的传播行为,独立创建事务。
结果是成功修改了price而没有修改remark。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/266548.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TDengine 研发分享:利用 Windbg 解决内存泄漏问题的实践和经验

内存泄漏是一种常见的问题&#xff0c;它会导致程序的内存占用逐渐增加&#xff0c;最终导致系统资源耗尽或程序崩溃。AddressSanitizer (ASan) 和 Valgrind 是很好的内存检测工具&#xff0c;TDengine 的 CI 过程就使用了 ASan 。不过这次内存泄漏问题发生在 Windows 下&#…

新品齐发!小牛电动打造全场景高端化产品阵列!

2 月 29 日&#xff0c;全球智能城市出行品牌小牛电动发布“新世代性能旗舰”电摩NX、电自NXT&#xff0c;以及“全场景智驾越野电摩”X3三款新品。同时&#xff0c;与知名体育电竞俱乐部——JDG京东电子竞技俱乐部携手&#xff0c;打造“英雄的联盟”超级形象&#xff0c;引领…

学习大语言模型(LLM),从这里开始

在见识了ChatGPT的各种强大能力后&#xff0c;不少 NLP一线从业人员很自然地想到&#xff0c;以后开发者只要借助 ChatGPT&#xff0c;就可以做到现在大部分NLP工程师在做的事&#xff0c;比如文本分类、实体抽取、文本推理等。甚至随着大语言模型&#xff08;largelanguagemod…

【JVM篇】什么是运行时数据区

文章目录 &#x1f354;什么是运行时数据区⭐程序计数器⭐栈&#x1f50e;Java虚拟机栈&#x1f388;栈帧的内容 &#x1f50e;本地方法栈 ⭐堆⭐方法区 &#x1f354;什么是运行时数据区 运行时数据区指的是jvm所管理的内存区域&#xff0c;其中分为两大类 线程共享&#xf…

2024亚马逊全球开店注册前需要准备什么?

在2023年出海四小龙SHEIN、Temu、速卖通AliExpress、TikTok Shop快速增长扩张&#xff0c;成为了中国跨境卖家“逃离亚马逊”的新选择。但是&#xff0c;跨境电商看亚马逊。当前&#xff0c;亚马逊仍然是跨境电商行业的绝对老大&#xff0c;占有将近70%成以上的业务份额。 作为…

【电商干货】5分钟了解电商数据API测试完整流程,建议收藏!可获取免费测试key!

电商API是什么&#xff1f; API是application programming interface&#xff08;应用程序接口&#xff09;的简称&#xff0c;是一些预先定义的函数。目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力&#xff0c;而又无需访问源码&#xff0c;或理解内部…

快速下载Huggingface的大语言模型

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、Huggingface是什么&#xff1f;二、基于官方huggingface-cli下载&#xff08;基础&#xff0c;断线风险&#xff09;1.安装hf下载环境2.配置环境变量3.注册…

WPF 【十月的寒流】学习笔记(2):MVVM中是怎么实现通知的

文章目录 前言相关链接代码仓库项目配置代码初始代码ViewPersonViewModel 尝试老办法通知解决方案ObservableCollectionBindingListICollectionView 总结 前言 我们这次详细了解一下列表通知的底层是怎么实现的 相关链接 十月的寒流 MVVM实战技巧之&#xff1a;可被观测的集合…

Appium + mitmProxy 实现APP接口稳定性测试

随着 App 用户量的不断增长&#xff0c;任何小的问题都可能放大成严重的线上事故&#xff0c;为了避免对App造成损害的任何可能性&#xff0c;我们必须从各个方面去思考 App 的稳定性建设&#xff0c;尽可能减少任何潜在的威胁。 1.背景介绍 为了保障 App 的稳定性&#xff0…

仿牛客网项目---社区首页的开发实现

从今天开始我们来写一个新项目&#xff0c;这个项目是一个完整的校园论坛的项目。主要功能模块&#xff1a;用户登录注册&#xff0c;帖子发布和热帖排行&#xff0c;点赞关注&#xff0c;发送私信&#xff0c;消息通知&#xff0c;社区搜索等。这篇文章我们先试着写一下用户的…

ELK 简介安装

1、概念介绍 日志介绍 日志就是程序产生的&#xff0c;遵循一定格式&#xff08;通常包含时间戳&#xff09;的文本数据。 通常日志由服务器生成&#xff0c;输出到不同的文件中&#xff0c;一般会有系统日志、 应用日志、安全日志。这些日志分散地存储在不同的机器上。 日志…

【Leetcode每日一刷】动态规划算法: 62. 不同路径、63. 不同路径 II

博主简介&#xff1a;努力学习和进步中的的22级计科生博主主页&#xff1a; Yaoyao2024每日一句: “ 路虽远&#xff0c;行则将至。事虽难&#xff0c;做则可成。” 前言 前言&#xff1a;动规五部曲 以下是《代码随想录》作者总结的动规五部曲 确定dp数组&#xff08;dp tab…

Flink——芒果TV的实时数仓建设实践

目录 一、芒果TV实时数仓建设历程 1.1 阶段一&#xff1a;Storm/Flink JavaSpark SQL 1.2 阶段二&#xff1a;Flink SQLSpark SQL 1.3 阶段三&#xff1a;Flink SQLStarRocks 二、自研Flink实时计算调度平台介绍 2.1 现有痛点 2.2 平台架构设计 三、Flink SQL实时数仓分…

AI智能分析网关V4:抽烟/打电话/玩手机行为AI算法及场景应用

抽烟、打电话、玩手机是人们在日常生活中常见的行为&#xff0c;但这些行为在某些场合下可能会带来安全风险。因此&#xff0c;对于这些行为的检测技术及应用就变得尤为重要。今天来给大家介绍一下TSINGSEE青犀AI智能分析网关V4抽烟/打电话/玩手机检测算法及其应用场景。 将监控…

输入一个字符串,将其中的数字字符移动到非数字字符之后

输入一个字符串&#xff0c;将其中的数字字符移动到非数字字符之后&#xff0c;并保持数字字符贺非数字字符输入时的顺序。 代码&#xff1a; #include <cstdio> #include <queue> using namespace std; int main() {char str[200];fgets(str, 200, stdin);//读入…

每周一算法:双端队列广搜

题目链接 电路维修 题目描述 达达是来自异世界的魔女&#xff0c;她在漫无目的地四处漂流的时候&#xff0c;遇到了善良的少女翰翰&#xff0c;从而被收留在地球上。翰翰的家里有一辆飞行车。有一天飞行车的电路板突然出现了故障&#xff0c;导致无法启动。 电路板的整体结…

【学习心得】Python调用JS的三种常用方法

在做JS逆向的时候&#xff0c;一种情况是直接用Python代码复现JS代码的功能&#xff0c;达成目的。但很多时候这种方法有明显的缺点&#xff0c;那就是一旦JS代码逻辑发生了更改&#xff0c;你就得重写Python的代码逻辑非常不便。于是第二种情况就出现了&#xff0c;我直接得到…

vue项目从后端下载文件显示进度条或者loading

//API接口 export const exportDownload (params?: Object, peCallback?: Function) > {return new Promise((resolve, reject) > {axios({method: get,url: ,headers: {access_token: ${getToken()},},responseType: blob,params,onDownloadProgress: (pe) > {peC…

10分钟SkyWalking与SpringBoot融合并整合到Linux中

1.依赖配置 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.2.0.RELEASE</version></dependency><dependency><groupId>org.springframe…

IP源防攻击IPSG(IP Source Guard)

IP源防攻击IPSG&#xff08;IP Source Guard&#xff09;是一种基于二层接口的源IP地址过滤技术&#xff0c;它能够防止恶意主机伪造合法主机的IP地址来仿冒合法主机&#xff0c;还能确保非授权主机不能通过自己指定IP地址的方式来访问网络或攻击网络。 2.1 IPSG基本原理 绑定…