Spring随手笔记

Spring 其实学的是SpringFramework Spring是一组框架

Spring 弹簧;春天,春季;

1 关于spring文件中的jar包说明

image-20230829195626194


image-20230829204148413

image-20230829204111661

 

ioc 容器对象是一个重量级的对象 很大 创建很耗费资源,因此一般只创建一个就够了

 


2 Spring 容器创建

把容器配置文件 放在src目录下面去 不要乱放

 

image-20230830134139652

文件的名字是随意的 这里老韩取名为beans

 

image-20230830135722698

 

image-20230830134504041

image-20230830134621236

unmapped 映射;非映射

image-20230830134736911

image-20230830134909087

image-20230830135038336

做完这些之后 就不再有提示了

image-20230830135250712

 


3 类加载路径

image-20230830145934217

默认会把src 当作类的加载路径 但是程序运行过后 真实走的是out目录那条线


 

4 class.getResource() 和 classLoader.getResource() 是不同的

类的加载器中的

public URL getResource(String name) {

Class对象中的

public java.net.URL getResource(String name) {


String path = HspTomcatV3.class.getResource("/").getPath();

 

参考代码位置:
D:\Java_developer_tools\javase\JavaSenior2\day8\src\com\hsp\io\FileInformation.java
D:\Java_developer_tools\javaweb\jiaju_mall\src\com\hspedu\furns\utils\JDBCUtilsByDruid.java
D:\Java_developer_tools\javaweb\hsptomcat\ydtomcat\src\main\java\com\hspedu\tomcat\HspTomcatV3.java
D:\Java_developer_tools\ssm\my_spring\spring\src\com\hspedu\spring\annotation\HspSpringApplicationContext.java
   
           /*
        * 结论:默认情况下 是把 out module 的根目录 当作默认的类加载路径 即out/production/day8/
        * 但是Class.getResource() 可以只放一个斜杠 / class.getResource("/") 来获取项目当前的真实的
        * 运行路径 根路径
        * 而classLoader.getResource("/") 不可以放入一个斜杠 会返回一个Null
        但是在Tomcat下path是否以’/'开头无所谓,可以以斜杠开头也可以不以斜杠开头 都可以获取到项目的加载路径
即在Tomcat下可以 使用classLoader.getResource("/")
获取到类加载路径 //classPath= file:/D:/Java_developer_tools/ssm/my_springmvc/hsp_springmvc/target/my_springmvc/WEB-INF/classes/  
       
        * classLoader.getResource("/") 这种方式只可以写相对路径 路径前不可以写斜杠 而是从
        * 运行路径下直接写 即 classLoader.getResource("com/hsp/io/FileInformation.class")
        *
        * 而 Class.getResource() 需要在路径前提供一个斜杠才能解析到
        * class.getResource("/com/hsp/io/FileInformation.class")
        * 不可以写相对路径前面不加斜杠
        *
        * */
   

public static void main(String[] args) {
       // 1.得到类的加载器 使用任何一个类的.class 都可以得到类的加载器
       ClassLoader classLoader = FileInformation.class.getClassLoader();

       // 2.通过类的加载器获取到要扫描的包的资源 url
       // classLoader.getResource("com/hspedu/spring/component"); 默认是按照斜杠 / 来间隔各级文件目录的

       /*
        * 下面返回为null 在这里 即不可以写绝对路径 也不可写/   在手写hsptomcat 时 写的是
        * String path = HspTomcatV3.class.getResource("/").getPath();// 得到的是工作目录,而不是源码目录
        * System.out.println("path= " + path);
        * 这里的区别是
        * 手写tomcat时 是用 class对象.getResource() 返回的是也是URL对象
        * 类加载器.getResource() 返回的是URL对象
        * 但是用法不同 加载器中的 不可以写 "/" 和 绝对路径
        * */
       URL resource = classLoader.getResource("/"); // null
       URL resource2 = classLoader.getResource("day8/com/hsp/io"); // null
       URL resource3 = classLoader.getResource("/com/hsp/io"); // null
       URL resource4 = classLoader.getResource("com/hsp/io"); //
       URL resource5 = classLoader.getResource("com/hsp/io/FileInformation.java"); // null
       URL resource6 = classLoader.getResource("com/hsp/io/FileInformation.class"); //
       URL resource7 = classLoader.getResource("day8/com/hsp/io/FileInformation.class"); // null
       System.out.println("resource= " + resource);// null
       System.out.println("resource2= " + resource2);// null
       System.out.println("resource3= " + resource3);// null
       System.out.println("resource4= " + resource4);// resource4= file:/D:/Java_developer_tools/javase/JavaSenior2/out/production/day8/com/hsp/io
       System.out.println("resource5= " + resource5);// resource5= null
       System.out.println("resource6= " + resource6);// resource6= file:/D:/Java_developer_tools/javase/JavaSenior2/out/production/day8/com/hsp/io/FileInformation.class
       System.out.println("resource7= " + resource7);// resource7= null
       /*
       * 结论:默认情况下 是把 out module 的根目录 当作默认的类加载路径 即out/production/day8/
       * */

       //String resourcePath = classLoader.getResource("/").getPath(); // NullPointerException


       System.out.println("=========================");

       URL classResource = FileReader.class.getResource("/"); // classResource= file:/D:/Java_developer_tools/javase/JavaSenior2/out/production/day8/
       URL classResource2 = FileReader.class.getResource("src/com/hsp/io/FileInformation"); // classResource2= null
       // 绝对路径 放进去 得不到 返回null 下面这两种方式的绝对路径 都不行
       URL classResource3 = FileReader.class.getResource("file:/D:\\Java_developer_tools\\javase\\JavaSenior2\\day8\\src\\com\\hsp\\io"); // classResource3= null
       //URL classResource3 = FileReader.class.getResource("D:\\Java_developer_tools\\javase\\JavaSenior2\\day8\\src\\com\\hsp\\io"); // classResource3= null

       URL classResource4 = FileReader.class.getResource("/com/hsp/io/FileInformation.java"); // classResource5= null
       URL classResource5 = FileReader.class.getResource("/com/hsp/io/FileInformation.class"); // 这个格式正确!! classResource4= file:/D:/Java_developer_tools/javase/JavaSenior2/out/production/day8/com/hsp/io/FileInformation.class
       URL classResource6 = FileReader.class.getResource("com/hsp/io/FileInformation.class"); // 这个格式不对 需要使用/ 开头 解析成项目真实的运行路径
       URL classResource7 = FileReader.class.getResource("day8/com/hsp/io/FileInformation.class"); // classResource6= null
       System.out.println("classResource= " + classResource); // file:/D:/Java_developer_tools/javase/JavaSenior2/out/production/day8/
       System.out.println("classResource2= " + classResource2); // null
       System.out.println("classResource3= " + classResource3); // null
       System.out.println("classResource4= " + classResource4); // null
       System.out.println("classResource5= " + classResource5); // file:/D:/Java_developer_tools/javase/JavaSenior2/out/production/day8/com/hsp/io/FileInformation.class
       System.out.println("classResource6= " + classResource6); // classResource6= null
       System.out.println("classResource7= " + classResource7); // classResource7= null
       /*
        * 结论:默认情况下 是把 out module 的根目录 当作默认的类加载路径 即out/production/day8/
        * 但是Class.getResource() 可以只放一个斜杠 / class.getResource("/") 来获取项目当前的真实的
        * 运行路径 根路径
        * 而classLoader.getResource("/") 不可以放入一个斜杠 会返回一个Null
        * classLoader.getResource("/") 这种方式只可以写相对路径 路径前不可以写斜杠 而是从
        * 运行路径下直接写 即 classLoader.getResource("com/hsp/io/FileInformation.class")
        *
        * 而 Class.getResource() 需要在路径前提供一个斜杠才能解析到
        * class.getResource("/com/hsp/io/FileInformation.class")
        * 不可以写相对路径前面不加斜杠
        *
        * */

  }

@Test
public void test1(){
   //先创建文件对象
   File file = new File("e:\\news1.txt");

   //调用相应的方法,得到对应的信息
   System.out.println("文件名:" + file.getName());
   System.out.println("文件绝对路径:" + file.getAbsolutePath());
   System.out.println("文件父级目录:" + file.getParent());
   System.out.println("文件大小(字节):" + file.length());
   System.out.println("文件是否存在:" + file.exists());
   System.out.println("是否为文件:" + file.isFile());
   System.out.println("是否是一个目录:" + file.isDirectory());//文件夹


   // 下面类似的方式 在hsptomcat 中也写了 还有jiaju_mall项目中也有少量应用

   /*
    * 下面返回为null 在这里 即不可以写绝对路径 也不可写/   在手写hsptomcat 时 写的是
    * String path = HspTomcatV3.class.getResource("/").getPath();// 得到的是工作目录,而不是源码目录
    * System.out.println("path= " + path);
    * 这里的区别是
加载器中的 不可以写 "/" 和 绝对路径
    *
    * jiaju_mall 项目中的使用方式是类的加载器.getResourceAsStream()
    * //因为我们是web项目,他的工作目录在out, 文件的加载,需要使用类加载器
       //找到我们的工作目录
       properties.load(JDBCUtilsByDruid.class.getClassLoader().getResourceAsStream("druid.properties"));
    * */

   // 1.得到类的加载器 使用任何一个类的.class 都可以得到类的加载器
   ClassLoader classLoader = FileInformation.class.getClassLoader();

   // 2.通过类的加载器获取到要扫描的包的资源 url
   // classLoader.getResource("com/hspedu/spring/component"); 默认是按照斜杠 / 来间隔各级文件目录的
   URL resource = classLoader.getResource("/"); // null
   String resourcePath = classLoader.getResource("/").getPath();
   System.out.println("resource= " + resource);
   System.out.println("resourcePath= " + resourcePath);

   //File file2 = new File(resource.getPath());
   //       //if (file2.isDirectory()){
   //       //   File[] files = file2.listFiles();
   //       //   for (File f : files) {
   //       //       System.out.println("============");
   //       //       System.out.println(f.getAbsolutePath());
   //       //   }
   //       //}
}

url.getPath() / url.getFile() 可以去除前缀file:



URL classGetResource = this.getClass().getResource("/");
System.out.println("classGetResource= " + classGetResource);
// url.getPath() / url.getFile() 可以去除前缀file:
String classGetResourcePath = classGetResource.getPath();
System.out.println("classGetResourcePath= " + classGetResourcePath);
String classGetResourceFile = classGetResource.getFile();
System.out.println("classGetResourceFile= " + classGetResourceFile);
输出的结果如下:
classGetResource= file:/D:/Java_developer_tools/ssm/my_springmvc/hsp_springmvc/target/test-classes/
classGetResourcePath= /D:/Java_developer_tools/ssm/my_springmvc/hsp_springmvc/target/test-classes/
classGetResourceFile= /D:/Java_developer_tools/ssm/my_springmvc/hsp_springmvc/target/test-classes/

 

4.1 Java中getResourceAsStream的用法

首先,Java中的getResourceAsStream有以下几种: \1. Class.getResourceAsStream(String path) : path 不以’/'开头时默认是从此类所在的包下取资源,以’/'开头则是从ClassPath根下获取。其只是通过path构造一个绝对路径,最终还是由ClassLoader获取资源。

\2. Class.getClassLoader.getResourceAsStream(String path) :默认则是从ClassPath根下获取,path不能以’/'开头,最终是由ClassLoader获取资源。

\3. ServletContext. getResourceAsStream(String path):默认从WebAPP根目录下取资源,Tomcat下path是否以’/'开头无所谓,当然这和具体的容器实现有关。

\4. Jsp下的application内置对象就是上面的ServletContext的一种实现。

其次,getResourceAsStream 用法大致有以下几种:

第一: 要加载的文件和.class文件在同一目录下,例如:com.x.y 下有类me.class ,同时有资源文件myfile.xml

那么,应该有如下代码:

me.class.getResourceAsStream("myfile.xml");

第二:在me.class目录的子目录下,例如:com.x.y 下有类me.class ,同时在 com.x.y.file 目录下有资源文件myfile.xml

那么,应该有如下代码:

me.class.getResourceAsStream("file/myfile.xml");

第三:不在me.class目录下,也不在子目录下,例如:com.x.y 下有类me.class ,同时在 com.x.file 目录下有资源文件myfile.xml

那么,应该有如下代码:

me.class.getResourceAsStream("/com/x/file/myfile.xml");

总结一下,可能只是两种写法

第一:前面有 “ / ”

“ / ”代表了工程的根目录,例如工程名叫做myproject,“ / ”代表了myproject

me.class.getResourceAsStream("/com/x/file/myfile.xml");

第二:前面没有 “ / ”

代表当前类的目录

me.class.getResourceAsStream("myfile.xml");

me.class.getResourceAsStream("file/myfile.xml");

最后,自己的理解: getResourceAsStream读取的文件路径只局限与工程的源文件夹中,包括在工程src根目录下,以及类包里面任何位置,但是如果配置文件路径是在除了源文件夹之外的其他文件夹中时,该方法是用不了的。

 


Java的Class.getClassLoader().getResourceAsStream()与Class.getResourceAsStream()理解

两者都可以实现从classPath路径读取指定资源的输入流。

为什么是classPath而不是src,这是因为web项目运行时,IDE编译器会把src下的一些资源文件移至WEB-INF/classes,classes目录就是classPath目录。该目录放的一般是web项目运行时的class文件、资源文件(xml,properties等)。

  1. Class.getClassLoader().getResourceAsStream() Class是当前类的Class对象,Class.getClassLoader()是获取当前类的类加载器。类加载器的大概作用是当需要使用一个类时,加载该类的".class"文件,并创建对应的class对象,将class文件加载到虚拟机的内存。getResourceAsStream()是获取资源的输入流。类加载器默认是从classPath路径加载资源。

因此,当使用Class.getClassLoader.getResourceAsStream()加载资源时,是从classPath路径下进行加载,放在resources下的文件加载时不能加(“/”)。

image-20230930232647577

InputStream in = PropertiesUtil.class.getClassLoader().getResourceAsStream("xx.properties");

 

  1. Class.getResourceAsStream()

//当前类的URI目录,不包括自己
Class.getResourceAsStream("");
//当前的classpath的绝对URI路径
Class.getResourceAsStream("/");

 

 

在使用 Class.getResourceAsStream()时,一定注意要加载的资源路径与当前类所在包的路径是否一致【使用时注意子目录】。 (1)要加载的资源路径与当前类所在包的路径一致

image-20230930232713311

InputStream in = PropertiesUtil.class.getResourceAsStream("xx.properties");

(2)要加载的资源路径在resources下

image-20230930232726764

InputStream in = PropertiesUtil.class.getResourceAsStream("/xx.properties");

 

 


5 回顾泛型指定的时机

image-20230905202217597

image-20230905202132032

 

 


6 Spring AOP debug 调试 导错包,容器中找不到实现子类 报错

注意:当把一个实现子类复制到另一个包下 时 ,这个实现子类引的接口,还是原来包下的接口!!容易出现该报错!!

image-20230908182142611

image-20230908182621066

package com.hspedu.spring.aop.aspectj;

// 下面这里的包引错了 导致报错
// expected single matching bean but found 2: com.hspedu.spring.test.Man#0,com.hspedu.spring.test.Man#1
// 解决 方法 不写导包import 默认找同包下的SmartAnimalable类
import com.hspedu.spring.aop.proxy3.SmartAnimalable;


import org.springframework.stereotype.Component;

/**
* @author 韩顺平
* @version 1.0
*/
@Component //使用@Component 当spring容器启动时,将 SmartDog注入到容器
public class SmartDog implements SmartAnimalable {
@Override
public float getSum(float i, float j) {
//System.out.println("日志-方法名-getSum-参数 " + i + " " + j);
float result = i + j;
System.out.println("方法内部打印result = " + result);
//System.out.println("日志-方法名-getSum-结果result= " + result);
return result;
}

@Override
public float getSub(float i, float j) {
//System.out.println("日志-方法名-getSub-参数 " + i + " " + j);
float result = i - j;
System.out.println("方法内部打印result = " + result);
//System.out.println("日志-方法名-getSub-结果result= " + result);
return result;
}
}

 

 

image-20230908182048508


7 老师补充: 动态代理 jdk的 Proxy与Spring的CGlib

1. 为什么要使用动态代理?

动态代理:在不改变原有代码的情况下上进行对象功能增强 使用代理对象代替原来的对象完成功能 进而达到拓展功能的目的

2.JDK Proxy 动态代理面向接口的动态代理

特点:

  1. 一定要有接口和实现类的存在 代理对象增强的是实现类 在实现接口的方法重写的方法

  2. 生成的代理对象只能转换成 接口的不能转换成 被代理类

  3. 代理对象只能增强接口中定义的方法 实现类中其他和接口无关的方法是无法增强的

  4. 代理对象只能读取到接口中方法上的注解 不能读取到实现类方法上的注解 使用方法:

 

public class Test1 {
public static void main(String[] args) {
Dinner dinner=new Person("张三");
// 通过Porxy动态代理获得一个代理对象,在代理对象中,对某个方法进行增强
// ClassLoader loader,被代理的对象的类加载器
ClassLoader classLoader = dinner.getClass().getClassLoader();
// Class<?>[] interfaces,被代理对象所实现的所有接口
Class[] interaces= dinner.getClass().getInterfaces();
// InvocationHandler h,执行处理器对象,专门用于定义增强的规则
InvocationHandler handler = new InvocationHandler(){
// invoke 当我们让代理对象调用任何方法时,都会触发invoke方法的执行
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Object proxy, 代理对象
// Method method,被代理的方法
// Object[] args,被代理方法运行时的实参
Object res=null;
if(method.getName().equals("eat")){
System.out.println("饭前洗手");
// 让原有的eat的方法去运行
res =method.invoke(dinner, args);
System.out.println("饭后刷碗");
}else{
// 如果是其他方法,那么正常执行就可以了
res =method.invoke(dinner, args);
}
return res;
}
};
Dinner dinnerProxy =(Dinner) Proxy.newProxyInstance(classLoader,interaces,handler);
//dinnerProxy.eat("包子");
dinnerProxy.drink();
}
}
interface Dinner{
void eat(String foodName);
void drink();
}
class Person implements Dinner{
private String name;
public Person(String name) {
this.name = name;
}
@Override
public void eat(String foodName) {
System.out.println(name+"正在吃"+foodName);
}
@Override
public void drink( ) {
System.out.println(name+"正在喝茶");
}
}
class Student implements Dinner{
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void eat(String foodName) {
System.out.println(name+"正在食堂吃"+foodName);
}
@Override
public void drink( ) {
System.out.println(name+"正在喝可乐");
}
}

3.CGlib动态代理

cglib动态代理模式是面向父类

特点:

  1. 面向父类的和接口没有直接关系 2.不仅可以增强接口中定义的方法还可以增强其他方法 3.可以读取父类中方法上的所有注解

  2. 使用实例

 

public class Test1 {
@Test
public void testCglib(){
Person person =new Person();
// 获取一个Person的代理对象
// 1 获得一个Enhancer对象
Enhancer enhancer=new Enhancer();
// 2 设置父类字节码
enhancer.setSuperclass(person.getClass());
// 3 获取MethodIntercepter对象 用于定义增强规则
MethodInterceptor methodInterceptor=new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
/*Object o, 生成之后的代理对象 personProxy
Method method, 父类中原本要执行的方法 Person>>> eat()
Object[] objects, 方法在调用时传入的实参数组
MethodProxy methodProxy 子类中重写父类的方法 personProxy >>> eat()
*/
Object res =null;
if(method.getName().equals("eat")){
// 如果是eat方法 则增强并运行
System.out.println("饭前洗手");
res=methodProxy.invokeSuper(o,objects);
System.out.println("饭后刷碗");
}else{
// 如果是其他方法 不增强运行
res=methodProxy.invokeSuper(o,objects); // 子类对象方法在执行,默认会调用父类对应被重写的方法
}
return res;
}
};
// 4 设置methodInterceptor
enhancer.setCallback(methodInterceptor);
// 5 获得代理对象
Person personProxy = (Person)enhancer.create();
// 6 使用代理对象完成功能
personProxy.eat("包子");
}
}
class Person {
public Person( ) {
}
public void eat(String foodName) {
System.out.println("张三正在吃"+foodName);
}
}

4.两个动态代理的区别

  1. JDK动态代理是面向接口的,只能增强实现类中接口中存在的方法。CGlib是面向父类的,可以增强父类的所有方法

  2. JDK得到的对象是JDK代理对象实例,而CGlib得到的对象是被代理对象的子类


8 Spring 版本报错java: Compilation failed: internal java compiler error java:编译失败:内部java编译器错误

临时解决方案:不想改pom文件

pom.xml 中引入新的jar包 就会在下面两个位置自动切换成language 5 和

Target bytecode version 1.5 需要改成 8 和 1.8

image-20230911172146354

image-20230911172353792

image-20230911172255055


9 要获取注解的类对象不可以加@!!!

 

// 注意要获取注解的类对象不可以加@!!!
//if(declaredField.isAnnotationPresent(@Autowired.class)){
// 下面这个是正确的!
if(declaredField.isAnnotationPresent(Autowired.class)){

10 关于方法返回值 手动抛出一个异常可以替代return 即可以不用再return

// 编写方法 返回容器中的对象
public Object getBean(String name) {


// 抛出一个异常 说明传进来的name 不存在 是瞎传了一个name
// 抛出异常可以代替返回值 就不用在返回了 return
throw new NullPointerException("没有该bean");
//
//return null;
}

11 判断clazz对象 的类 有没有实现某个接口 ,使用 某个接口.class.isAssignableFrom(clazz)

image-20230915144229566

 

 

 


12 判断实例对象 的类 有没有实现某个接口 ,使用 instanceof

 


13aspectClass.getDeclaredMethod() 方法 在获取无参数的方法时注意细节

 

image-20230917164136421

image-20230917164202570

// 通过方法名获取该方法
// 下面这个可以找到 注意小细节 第二个参数在该方法无参的情况下
// 不可以带类型【(Class<?>) null】 不可以强转后传入 而是应该直接传一个null !!!!
//Method declaredMethod1 = aspectClass.getDeclaredMethod(name, (Class<?>) null);
Method declaredMethod1 = aspectClass.getDeclaredMethod(name, null);
//Method declaredMethod1 = aspectClass.getDeclaredMethod("showBeginLog", null);
//进行反射调用
//declaredMethod1.invoke(aspectClass.newInstance(),null);
declaredMethod1.invoke(null,null);

 

 


14 resource.getPath() 和 resource.getFile() 类似都返回一个String类型

使用的位置如下:

D:\Java_developer_tools\ssm\my_spring\spring\src\com\hspedu\spring\annotation2\YdSpringApplicationContext.java

 // 通过类的加载器 得到要扫描的包的全路径
ClassLoader classLoader = YdSpringApplicationContext.class.getClassLoader();
//URL resource = classLoader.getResource("com/hspedu/spring/component");
URL resource = classLoader.getResource(path);
//System.out.println("resource= " + resource);
//resource= file:/D:/Java_developer_tools/ssm/my_spring/spring/out/production/spring/com/hspedu/spring/component

// 得到要扫描的包的文件 因为此时的url 不是 String 类型
// 因此需要通过getPath()方法 转换为String类型

//File file = new File(resource.getPath());
File file = new File(resource.getFile());

 


15 spring 声明式事务嵌套时出现死锁 一直转圈的情况

 

 

 

C:\jdk\jdk1.8\bin\java.exe -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Java_developer_tools\developer_tools_IDEA\IntelliJ IDEA 2020.2.1\lib\idea_rt.jar=62800:D:\Java_developer_tools\developer_tools_IDEA\IntelliJ IDEA 2020.2.1\bin" -Dfile.encoding=UTF-8 -classpath "D:\Java_developer_tools\developer_tools_IDEA\IntelliJ IDEA 2020.2.1\lib\idea_rt.jar;C:\Users\yangd\.m2\repository\org\junit\platform\junit-platform-launcher\1.4.2\junit-platform-launcher-1.4.2.jar;D:\Java_developer_tools\developer_tools_IDEA\IntelliJ IDEA 2020.2.1\plugins\junit\lib\junit5-rt.jar;D:\Java_developer_tools\developer_tools_IDEA\IntelliJ IDEA 2020.2.1\plugins\junit\lib\junit-rt.jar;C:\jdk\jdk1.8\jre\lib\charsets.jar;C:\jdk\jdk1.8\jre\lib\deploy.jar;C:\jdk\jdk1.8\jre\lib\ext\access-bridge-64.jar;C:\jdk\jdk1.8\jre\lib\ext\cldrdata.jar;C:\jdk\jdk1.8\jre\lib\ext\dnsns.jar;C:\jdk\jdk1.8\jre\lib\ext\jaccess.jar;C:\jdk\jdk1.8\jre\lib\ext\jfxrt.jar;C:\jdk\jdk1.8\jre\lib\ext\localedata.jar;C:\jdk\jdk1.8\jre\lib\ext\nashorn.jar;C:\jdk\jdk1.8\jre\lib\ext\sunec.jar;C:\jdk\jdk1.8\jre\lib\ext\sunjce_provider.jar;C:\jdk\jdk1.8\jre\lib\ext\sunmscapi.jar;C:\jdk\jdk1.8\jre\lib\ext\sunpkcs11.jar;C:\jdk\jdk1.8\jre\lib\ext\zipfs.jar;C:\jdk\jdk1.8\jre\lib\javaws.jar;C:\jdk\jdk1.8\jre\lib\jce.jar;C:\jdk\jdk1.8\jre\lib\jfr.jar;C:\jdk\jdk1.8\jre\lib\jfxswt.jar;C:\jdk\jdk1.8\jre\lib\jsse.jar;C:\jdk\jdk1.8\jre\lib\management-agent.jar;C:\jdk\jdk1.8\jre\lib\plugin.jar;C:\jdk\jdk1.8\jre\lib\resources.jar;C:\jdk\jdk1.8\jre\lib\rt.jar;D:\Java_developer_tools\ssm\my_spring\spring\out\production\spring;D:\Java_developer_tools\ssm\my_spring\spring\lib\commons-logging-1.1.3.jar;D:\Java_developer_tools\ssm\my_spring\spring\lib\spring-beans-5.3.8.jar;D:\Java_developer_tools\ssm\my_spring\spring\lib\spring-context-5.3.8.jar;D:\Java_developer_tools\ssm\my_spring\spring\lib\spring-core-5.3.8.jar;D:\Java_developer_tools\ssm\my_spring\spring\lib\spring-expression-5.3.8.jar;C:\Users\yangd\.m2\repository\org\junit\jupiter\junit-jupiter\5.4.2\junit-jupiter-5.4.2.jar;C:\Users\yangd\.m2\repository\org\junit\jupiter\junit-jupiter-api\5.4.2\junit-jupiter-api-5.4.2.jar;C:\Users\yangd\.m2\repository\org\apiguardian\apiguardian-api\1.0.0\apiguardian-api-1.0.0.jar;C:\Users\yangd\.m2\repository\org\opentest4j\opentest4j\1.1.1\opentest4j-1.1.1.jar;C:\Users\yangd\.m2\repository\org\junit\platform\junit-platform-commons\1.4.2\junit-platform-commons-1.4.2.jar;C:\Users\yangd\.m2\repository\org\junit\jupiter\junit-jupiter-params\5.4.2\junit-jupiter-params-5.4.2.jar;C:\Users\yangd\.m2\repository\org\junit\jupiter\junit-jupiter-engine\5.4.2\junit-jupiter-engine-5.4.2.jar;C:\Users\yangd\.m2\repository\org\junit\platform\junit-platform-engine\1.4.2\junit-platform-engine-1.4.2.jar;D:\Java_developer_tools\ssm\my_spring\spring\lib\dom4j-1.6.1.jar;D:\Java_developer_tools\ssm\my_spring\spring\lib\spring-aop-5.3.8.jar;D:\Java_developer_tools\ssm\my_spring\spring\lib\com.springsource.org.aopalliance-1.0.0.jar;D:\Java_developer_tools\ssm\my_spring\spring\lib\com.springsource.net.sf.cglib-2.2.0.jar;D:\Java_developer_tools\ssm\my_spring\spring\lib\com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar;D:\Java_developer_tools\ssm\my_spring\spring\lib\spring-aspects-5.3.8.jar;D:\Java_developer_tools\ssm\my_spring\spring\lib\c3p0-0.9.1.2.jar;D:\Java_developer_tools\ssm\my_spring\spring\lib\mysql-connector-java-5.1.7-bin.jar;D:\Java_developer_tools\ssm\my_spring\spring\lib\spring-jdbc-5.3.8.jar;D:\Java_developer_tools\ssm\my_spring\spring\lib\spring-orm-5.3.8.jar;D:\Java_developer_tools\ssm\my_spring\spring\lib\spring-tx-5.3.8.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit5 com.hspedu.spring.tx.homework.HomeworkTest,queryPriceByIdTest

九月 21, 2023 10:15:33 上午 com.mchange.v2.log.MLog <clinit>
信息: MLog clients using java 1.4+ standard logging.
九月 21, 2023 10:15:34 上午 com.mchange.v2.c3p0.C3P0Registry banner
信息: Initializing c3p0-0.9.1.2 [built 21-May-2007 15:04:56; debug? true; trace: 10]
九月 21, 2023 10:15:34 上午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager
信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1hgf2oaay1ed5w8zu66ezx|2fb3536e, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1hgf2oaay1ed5w8zu66ezx|2fb3536e, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/spring, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
用户购买信息 userId= 1 goodsId= 1 amount= 1
第一个方法正常执行完
用户购买信息 userId= 1 goodsId= 1 amount= 1


org.springframework.dao.CannotAcquireLockException: PreparedStatementCallback; SQL [UPDATE user_account SET money=money-? Where user_id=?]; Lock wait timeout exceeded; try restarting transaction; nested exception is java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction

at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:267)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:70)
at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1541)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:667)
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:960)
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:1015)
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:1025)
at com.hspedu.spring.tx.homework.dao.GoodsDao.updateBalance2(GoodsDao.java:73)
at com.hspedu.spring.tx.homework.service.GoodsService.buyGoodsBuyTx2(GoodsService.java:102)
at com.hspedu.spring.tx.homework.service.GoodsService$$FastClassBySpringCGLIB$$6d10e5bd.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692)
at com.hspedu.spring.tx.homework.service.GoodsService$$EnhancerBySpringCGLIB$$a3efd790.buyGoodsBuyTx2(<generated>)
at com.hspedu.spring.tx.homework.service.MultiplyService.multiBuyGoodsByTx(MultiplyService.java:52)
at com.hspedu.spring.tx.homework.service.MultiplyService$$FastClassBySpringCGLIB$$b640abf3.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692)
at com.hspedu.spring.tx.homework.service.MultiplyService$$EnhancerBySpringCGLIB$$a359bbf2.multiBuyGoodsByTx(<generated>)
at com.hspedu.spring.tx.homework.HomeworkTest.queryPriceByIdTest(HomeworkTest.java:45)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:628)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:117)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:184)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:180)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:127)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)
at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)
at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
Caused by: java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1055)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3515)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3447)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1951)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2101)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2554)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1761)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2046)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1964)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1949)
at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeUpdate(NewProxyPreparedStatement.java:105)
at org.springframework.jdbc.core.JdbcTemplate.lambda$update$2(JdbcTemplate.java:965)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:651)
... 82 more



Process finished with exit code -1

 

16 开启声明式事务的两种方式 xml和注解

xml方式的开启方式可以参考spring的学习项目

在这两段代码中,@EnableTransactionManagement 注解和 XML 配置 <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/> 实际上是同一功能的两种不同表现形式,它们都用于启用 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:tx="http://www.springframework.org/schema/tx"
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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">



<!-- 想要
@Resource
private JdbcTemplate jdbcTemplate; 生效
首先要在.java 文件中加上自动装配的注解 @Resource , 还要在ioc容器的.xml文件中如下配置
-->
<!-- 配置要扫描的包-->
<context:component-scan base-package="com.hspedu.spring.tx.homework.dao"/>
<context:component-scan base-package="com.hspedu.spring.tx.homework.service"/>

<!--配置JdbcTemplate-->
<!--首先要配置一个数据源对象-->
<!--引入外部的jdbc.properties文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置数据源对象DataSource-->
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
<!--给数据源对象配置属性值-->
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.pwd}"/>
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
</bean>

<!--再正式配置JdbcTemplate-->
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<!--给JdbcTemplate对象配置dataSource属性
name="dataSource" 是JdbcTemplate对象的一个属性
ref="dataSource" 指的是上面配置好的 id="dataSource" 的一个bean
-->
<property name="dataSource" ref="dataSource"/>
</bean>

<!--配置@Transactional 要想让声明式事务的注解好用需要配置下面liangz
相关的配置 (1)配置事务管理器对象 (2)开启基于注解的声明式事务管理功能-->
<!--配置事务管理器对象 数据源事务管理器
1.DataSourceTransactionManager 这个对象是进行事务管理
2.一定要配置数据源属性,这样指定该事务管理器 是对哪个数据源进行事务控制
-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="dataSourceTransactionManager">
<!--指定dataSource 要指定的数据源dataSource和jdbcTemplate
的数据源dataSource必须是同一个!! 数据源事务管理器最终能够控制事务
能够提交能够回滚 还是针对数据源中取出来的连接 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置/开启基于注解的声明式事务管理功能 并指定你启用的是哪一个数据源事务管理器/对象
一定要引入结尾为 tx 包下的driven
xmlns:tx="http://www.springframework.org/schema/tx"
-->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>


</beans>

 

 

package com.hspedu.hspliving.commodity.config;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
* @author yangda
* @create 2024-03-19-17:14
* @description:
*/
@Configuration
@EnableTransactionManagement //开启事务
@MapperScan("com.hspedu.hspliving.commodity.dao")
public class MyBatisConfig {

//引入分页插件
@Bean
public PaginationInterceptor paginationInterceptor(){
PaginationInterceptor paginationInterceptor =
new PaginationInterceptor();

//做基本设置
// 这个设置是指当当前页码超出总页数时,是否自动跳转回第一页。设置为 true 表示允许这样做。
paginationInterceptor.setOverflow(true);
//单页限制 100 条,小于 0 如 -1 不受限制
paginationInterceptor.setLimit(100);

return paginationInterceptor;
}
}

 

  1. XML 配置方式

    • 在 XML 文件中,通过 <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/> 来启用基于注解的声明式事务管理。

    • 这个配置指示 Spring 使用 <bean id="dataSourceTransactionManager"> 中定义的事务管理器来处理标有 @Transactional 注解的类或方法。

    • 这是传统 Spring 项目中常用的配置方式。

  2. Java 配置方式

    • 在 Java 配置类中,通过添加 @EnableTransactionManagement 注解来启用基于注解的声明式事务管理。

    • 当你使用这个注解时,Spring 会在背景中进行和 XML 方式相同的配置。

    • @EnableTransactionManagement 是 Spring Boot 和现代 Spring 应用推荐的方式,因为它减少了配置的复杂性,并且更加符合 Java 配置的风格。

两种方式都可以实现相同的功能,即启用 Spring 的声明式事务管理。你可以根据自己的项目需求和个人喜好选择使用哪一种。如果你的项目完全基于 Java 配置(常见于 Spring Boot 项目),那么使用 @EnableTransactionManagement 会更加方便。如果你的项目是基于传统的 XML 配置,那么使用 XML 方式会更熟悉。但是,在一个项目中,通常只需要选择一种方式来启用事务管理。

因此,@EnableTransactionManagement 注解和 XML 中的 <tx:annotation-driven> 实际上是两种实现相同功能的不同方法,它们都用于开启 Spring 的声明式事务管理支持。

17 <tx:annotation-driven> 和 @EnableTransactionManagement这两种方式底层都是通过org.springframework.jdbc.datasource.DataSourceTransactionManager 进行事务控制的吗?

是的,无论是通过 @EnableTransactionManagement 注解还是通过 XML 配置的 <tx:annotation-driven> 来启用 Spring 的声明式事务管理,底层默认都是使用 DataSourceTransactionManager (对于 JDBC 或者 MyBatis)进行事务控制的,当然这也取决于你的具体配置和所使用的数据访问技术。

  1. DataSourceTransactionManager:

    • 主要用于 JDBC 或 MyBatis 等基于数据源的事务管理。

    • 当你配置了一个 DataSourceTransactionManager 并启用了声明式事务管理,Spring 会使用这个事务管理器来管理与指定数据源相关的事务。

  2. 使用 @EnableTransactionManagement 注解:

    • 当你在配置类上使用此注解时,Spring 会自动检测到应用上下文中可用的 PlatformTransactionManager 实现。

    • 如果你有自定义的事务管理器 bean(如 DataSourceTransactionManager),Spring 会使用它;如果没有,Spring 将尝试自动配置一个默认的事务管理器。

    • 在大多数基于 Spring Boot 的应用中,如果你配置了数据源,Spring Boot 会自动配置 DataSourceTransactionManager

  3. 使用 XML 配置:

    • 在 XML 中,你可以通过 <bean> 元素明确地声明一个 DataSourceTransactionManager 实例,并通过 <tx:annotation-driven> 元素将其指定为要使用的事务管理器。

    • 你需要确保 <tx:annotation-driven> 元素的 transaction-manager 属性引用的是你声明的事务管理器的 bean ID。

无论哪种方式,最终 Spring 都需要一个 PlatformTransactionManager 实现来实际管理事务。DataSourceTransactionManager 只是 PlatformTransactionManager 的一个具体实现,主要用于 JDBC 访问数据库。如果你使用其他数据访问技术,例如 JPA 或 Hibernate,那么可能会用到 JpaTransactionManagerHibernateTransactionManager 等其他实现。但在具体使用 JDBC 或 MyBatis 时,通常是 DataSourceTransactionManager 被用于事务控制。

posted @   卡牌300  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示