Web应用技术(第十四周/END)

本次练习基于how2j和课本,初步认识Spring。
以后我每周只写一篇Web的博客,所有的作业内容会在这篇博客中持续更新。。。
Alt

    • 一、Spring基础
      • 1.Spring概述:
      • 2.Sring组成:
      • 3.BeanFactory:
      • 4.控制反转:
      • 5.依赖注入:
      • 6.JavaBean与Spring的Bean:
      • 7.Java的一般程序的生命周期与SpringBean的生命周期:
        • (1)Java程序的生命周期:
        • (2)SpringBean的生命周期:
        • (3)Java程序的生命周期与SpringBean的生命周期的区别和联系:
        • (4)JVM(Java虚拟机):
      • 8.Java的一般容器与Spring中的容器:
      • 9.耦合和内聚:
    • 二、第一个基于Spring的程序:
      • (1)创建一个`Java Project`
      • (2)新建一个`lib`文件夹:
      • (3)配置环境:
      • (4)配置文件:
      • (5)实体类:
      • (6)测试类:
      • (7)运行查看:
    • 三、依赖注入的方式:
      • (1)构造器注入:
      • (2)Setter注入:
      • (3)构造方法的示例代码:
      • (4)构造器注入的`<constructor-arg>`标签解析:
    • 四、Spring AOP概述和基于AOP的第一个JavaSpring程序:
      • (1)Java代理机制:
      • (2)Spring AOP:
      • (3)AOP的简单实现:
        • 目标对象:
        • 通知:
        • 代理对象:
        • 运行效果:
    • 五、Spring持久化:
      • 1.持久化
      • 2.DAO模式:
      • 3.Spring的DAO理念:
      • 3.事务管理:
        • (1)概述:
          • 传播行为(Propagation Behavior)
          • 只读属性(Read-Only)
          • 隔离级别(Isolation Level)
        • (2)编程式事务管理:
        • (3)声明式事务管理:
        • (4)两种事务管理方式的对比:
      • 4.JdbcTemple操作数据库:
        • 实例
        • JdbcTemplate 对象操作数据库有以下好处:
    • 六、MyBatis与Spring初步整合:

一、Spring基础

1.Spring概述:

Spring框架是一个开源的Java企业应用程序开发框架,它提供了一系列的解决方案,帮助开发者在开发企业级应用时更加简单、高效、安全。Spring框架主要由IOC容器AOP数据访问Web开发等模块组成。

举个例子,假设我们要开发一个Web应用,需要连接数据库并进行数据操作。使用Spring框架,我们可以使用Spring的IOC容器来管理对象的创建和依赖关系,使用Spring的数据访问模块来连接数据库并进行数据操作,同时还可以使用Spring的Web开发模块来快速开发Web应用。这样,我们可以更加高效、简单地开发出一个功能完善的Web应用。

2.Sring组成:

  1. SpringCore模块:提供Spring框架的基础功能,如=依赖注入控制反转==。它包含了Spring框架的核心组件,如BeanFactory和ApplicationContext等。

  2. SpringContext模块:建立在SpringCore模块之上,提供了更广泛的应用程序上下文支持,如国际化、事件传播、资源加载、应用程序配置等。它还提供了许多企业级服务,如JNDI、EJB、JMS等。

  3. SpringAOP模块:提供了面向切面编程的支持,可以将横切关注点(如日志记录、性能监视、事务管理等)从业务逻辑中分离出来。它使用代理模式实现,可以在不修改业务逻辑的情况下为应用程序添加新的功能。

  4. SpringDAO模块:提供了对数据访问技术的支持,如JDBC、ORM、事务管理等。它提供了一组通用的异常层次结构,以及一些模板类,可以大大简化数据访问代码的编写。

  5. SpringORM模块:提供了对ORM框架的支持,如Hibernate、JPA等。它可以将实体对象映射到数据库表中,从而使得开发人员可以使用面向对象的方式来操作数据库。

  6. SpringWeb模块:提供了对Web应用程序的支持,如Spring MVC框架、WebSocket、REST等。它可以帮助开发人员快速构建Web应用程序,并提供了许多与Web相关的功能,如文件上传、表单处理、安全性等。

举一个例子,假设我们正在开发一个在线商城,我们可以使用Spring框架来实现这个应用程序。我们可以使用SpringCore模块来管理我们的对象,使用SpringContext模块来加载配置文件和资源,使用SpringAOP模块来处理安全和日志记录,使用SpringDAO模块来访问数据库,使用SpringORM模块来映射实体对象和数据库表,使用SpringWeb模块来处理Web请求和响应。这样,我们可以更快地开发出高质量的在线商城,并且更容易维护和扩展。

3.BeanFactory:

SpringCore模块中,BeanFactory是一个接口,它是Spring框架中最基本的接口之一,用于==管理和维护Java对象(也称为Bean)==的创建、配置和生命周期。BeanFactory接口定义了一些基本的方法,包括获取Bean实例、销毁Bean实例等。

BeanFactory的主要作用是提供了一种机制,使得Java对象的创建和管理变得更加灵活和可配置。通过BeanFactory,我们可以将Java对象的创建和配置过程从应用程序中分离出来,从而实现了应用程序的松耦合高内聚

下面是一个简单的代码例子,演示了如何使用BeanFactory来创建和获取Java对象:

public class MainApp {
    public static void main(String[] args) {
        // 创建BeanFactory对象
        BeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

        // 获取HelloWorld对象
        HelloWorld obj = (HelloWorld) factory.getBean("helloWorld");

        // 调用HelloWorld对象的方法
        obj.getMessage();
    }
}

在上面的代码中,我们首先创建了一个BeanFactory对象,然后通过调用getBean方法,获取了名为"helloWorld"的Bean实例。这个Bean实例是在配置文件applicationContext.xml中定义的,它是一个HelloWorld对象,其getMessage方法会输出一段字符串。

通过使用BeanFactory,我们可以将Java对象的创建和配置过程从应用程序中分离出来,从而实现了应用程序的松耦合和高内聚。同时,我们还可以通过修改配置文件,来改变Java对象的创建和配置过程,从而实现了应用程序的可配置性。

4.控制反转:

控制反转(Inversion of Control,IoC)是Spring框架的一个核心概念,指的是将对象的创建组装管理等控制权从应用程序代码中转移到框架或容器中,从而实现松耦合、可扩展和可维护的设计。
在传统的应用程序设计中,应用程序代码通常会负责创建和管理对象,这样会导致应用程序代码和对象之间的紧耦合,难以进行单元测试和代码重构。而控制反转则是将这些控制权转移到框架或容器中,让它们来负责对象的创建和管理,从而实现应用程序代码和对象之间的解耦和灵活性。

举一个简单的例子:

// OrderDAO类
public class OrderDAO {
    // 数据访问实现
}

// OrderService类
public class OrderService {
    private OrderDAO orderDAO;  // 依赖注入的对象

    public void setOrderDAO(OrderDAO orderDAO) {  // Setter方法注入
        this.orderDAO = orderDAO;
    }

    // 其他业务方法
}

// Spring配置文件
<bean id="orderService" class="com.example.OrderService">  <!-- 定义OrderService对象 -->
    <property name="orderDAO" ref="orderDAO" />  <!-- 通过依赖注入方式使用OrderDAO对象 -->

上述的例子中,假设有一个订单服务类OrderService,它需要依赖一个订单数据访问对象OrderDAO来实现订单数据的持久化。在传统的设计中,OrderService需要自己创建和管理OrderDAO对象,这样会导致OrderService和OrderDAO之间的紧耦合,难以进行单元测试和代码重构。而在使用Spring框架的控制反转功能后,可以将OrderDAO的创建和管理交给Spring容器来完成,然后在OrderService中通过依赖注入的方式来使用OrderDAO对象,从而实现了松耦合和可测试的设计。

5.依赖注入:

依赖注入(Dependency Injection,DI)是一种实现控制反转(Inversion of Control,IoC)的方式,它是Spring框架的核心功能之一。
简单来说,依赖注入指的是要实例化的对象的相关配置已经写好,我们只是通过某些手段(见依赖注入的两种方式)将其注入到相应的属性或在方法中,而不需要再重新new了。

可参考控制反转的示例进行理解。

6.JavaBean与Spring的Bean:

JavaBean是指符合一定规范的Java类,它通常具有以下特征:

  1. 类是公共的
  2. 有一个无参构造方法
  3. 属性通过getter和setter方法进行访问
  4. 实现了Serializable接口

JavaBean的主要用途是封装数据,它可以将数据和行为封装在一个类中,从而实现了数据的安全性和可维护性。JavaBean通常用于在不同的层之间传递数据

例如在MVC架构中,Controller层可以通过JavaBean来获取View层传递过来的数据,然后将数据传递给Model层进行处理。

Spring中的Bean是指在Spring容器中管理的Java对象,它们可以通过配置文件或注解来定义和创建。

Spring中的Bean与一般的JavaBean相比,有以下区别和联系:

区别:

  1. Spring中的Bean通常由Spring容器来创建和管理,而一般的JavaBean通常由程序员手动创建和管理。
  2. Spring中的Bean可以通过配置文件或注解来定义和创建,从而实现了更大程度的可配置性和灵活性。
  3. Spring中的Bean通常具有更多的功能和特性,例如依赖注入、面向切面编程、事务管理等。

联系:

  1. Spring中的Bean本质上也是JavaBean,它们都是Java类。
  2. Spring中的Bean也可以用于在不同的层之间传递数据,例如在MVC架构中,Controller层可以通过Spring中的Bean来获取View层传递过来的数据,然后将数据传递给Model层进行处理。

下面是一个简单的例子,演示了如何使用JavaBean和Spring中的Bean:

// 一般的JavaBean
public class User {
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

}

在上面的例子中,我们定义了一个一般的JavaBean User,它具有name和age两个属性,并且通过getter和setter方法进行访问。然后,我们定义了一个Spring中的Bean UserService,它依赖于UserDao对象,并且提供了addUser方法,用于添加用户。在Spring容器中配置UserService时,我们可以通过注入UserDao对象来实现依赖注入。这样,我们就可以在调用addUser方法时,直接使用userDao对象,而不需要在代码中显式地创建它。

7.Java的一般程序的生命周期与SpringBean的生命周期:

(1)Java程序的生命周期:

  1. 编写Java源代码,通过编译器将其编译成字节码文件。

  2. 将字节码文件加载到JVM中,并通过类加载器将其转换成Java类。

  3. JVM将Java类转换成机器码,并执行程序。

  4. 程序执行完毕后,JVM将释放内存并结束程序。

(2)SpringBean的生命周期:

  1. 实例化:Spring容器通过反射机制创建一个Bean的实例。

  2. 属性赋值:Spring容器为Bean的属性赋值,可以通过XML配置或注解方式。

  3. 初始化:Spring容器调用Bean的初始化方法,可以通过XML配置或注解方式。

  4. 使用:Bean可以被其他对象引用,被调用。

  5. 销毁:Spring容器调用Bean的销毁方法,可以通过XML配置或注解方式。

(3)Java程序的生命周期与SpringBean的生命周期的区别和联系:

区别:

  1. Java程序的生命周期是从编写代码到程序结束,而SpringBean的生命周期是从实例化到销毁

  2. Java程序的生命周期是由JVM控制,而SpringBean的生命周期是由Spring容器控制

联系:

  1. Java程序和SpringBean都有实例化、初始化、使用和销毁的过程。

  2. Spring容器可以管理Java程序中的Bean,使得Java程序更加灵活和可扩展。

举例:

Java程序的生命周期:一个简单的Java程序,包括一个main方法和一些其他的方法。当我们运行这个程序时,JVM会先加载这个程序的字节码文件,然后执行main方法。当main方法执行完毕后,JVM会释放内存并结束程序。

SpringBean的生命周期:一个简单的SpringBean,包括一个属性和一个初始化方法。当Spring容器启动时,会通过反射机制创建这个Bean的实例,并为其属性赋值。然后调用Bean的初始化方法。当Bean被其他对象引用并使用时,就会执行相关的方法。当Spring容器关闭时,会调用Bean的销毁方法。

(4)JVM(Java虚拟机):

Java程序的运行环境,它是一个软件程序,负责将Java源代码编译成字节码=并执行。JVM提供了内存管理、垃圾回收、安全性和线程管理等基础服务,以及实现了Java语言的跨平台特性。

例如,当我们在开发Java程序时,将Java源代码编译成.class文件,然后在JVM上运行。JVM会将字节码转换成机器码并在计算机上执行。由于JVM的跨平台特性,我们可以在不同的操作系统上运行相同的Java程序。

下面是一个简单的Java程序示例:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

在命令行中,我们可以使用以下命令编译和运行该程序:

javac HelloWorld.java
java HelloWorld

在编译过程中,JVM会将Java源代码编译成字节码文件HelloWorld.class。在运行过程中,JVM会加载并执行该字节码文件,并输出字符串“Hello, World!”。

8.Java的一般容器与Spring中的容器:

  • Java的一般容器指的是Java集合框架中的容器,如List、Map、Set等。这些容器可以存储和管理Java对象,提供了方便的数据结构和算法。

  • Spring中的容器一般指的是Spring IOC容器,它是Spring框架的核心,用于管理Java对象的创建、配置、依赖注入和生命周期管理等。Spring容器可以帮助我们解耦和组织Java应用程序的各个模块,提高代码的可维护性和可扩展性。

举例:

Java的一般容器示例:

List<String> list = new ArrayList<>(); // 创建一个List容器
list.add("Java");
list.add("Spring");
list.add("MySQL");
System.out.println(list); // 输出:[Java, Spring, MySQL]

Map<String, String> map = new HashMap<>(); // 创建一个Map容器
map.put("name", "Tom");
map.put("age", "18");
map.put("gender", "male");
System.out.println(map); // 输出:{age=18, name=Tom, gender=male}

Spring中的容器示例:

1. 创建Spring容器

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

2. 获取Bean对象

UserService userService = context.getBean("userService", UserService.class);

3. 使用Bean对象

User user = userService.getUserById(1);
System.out.println(user);

4. 销毁Spring容器

((ClassPathXmlApplicationContext) context).close();

在上述示例中,我们通过Spring容器创建了一个UserService对象,并使用它获取了一个User对象。在程序执行完毕后,我们通过close()方法销毁了Spring容器。这样,Spring容器就会自动管理UserService和User对象的生命周期,包括它们的创建、初始化、依赖注入和销毁等。

9.耦合和内聚:

  • 耦合是指两个或多个模块之间的相互依赖关系。在软件开发中,模块之间的耦合性越高,就越难以维护和扩展。因此,我们通常希望模块之间的耦合性尽可能地低,从而实现模块之间的松耦合。

  • 松耦合是指两个或多个模块之间的相互依赖关系较弱,模块之间的影响较小。在软件开发中,模块之间的松耦合能够提高代码的灵活性和可维护性,从而降低开发成本和维护成本。

  • Spring框架的设计目标之一就是实现松耦合的组件之间的依赖关系,以提高系统的可维护性和可扩展性。Spring通过依赖注入(Dependency Injection)和控制反转(Inversion of Control)等技术来实现松耦合。

举个例子,假设我们正在开发一个电子商务网站,我们需要实现用户注册和登录功能。如果我们的代码是紧耦合的,那么用户注册和登录的功能可能会写在同一个类中,这样就会导致代码难以维护和扩展。如果我们的代码是松耦合的,那么我们可以将用户注册和登录的功能分别写在不同的类中,从而使代码更加清晰和易于维护。

二、第一个基于Spring的程序:

本程序通过控制反转依赖注入实现

(1)创建一个Java Project

在这里插入图片描述

(2)新建一个lib文件夹:

在这里插入图片描述

(3)配置环境:

将jar包放到lib下后,Built Path

在这里插入图片描述

(4)配置文件:

在src下new一个xml文件
applicationContext.xml:

<?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:aop="http://www.springframework.org/schema/aop"  
    xmlns:tx="http://www.springframework.org/schema/tx" 
    xmlns:context="http://www.springframework.org/schema/context"  
    xsi:schemaLocation="
   http://www.springframework.org/schema/beans 
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/aop 
   http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
   http://www.springframework.org/schema/tx 
   http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
   http://www.springframework.org/schema/context      
   http://www.springframework.org/schema/context/spring-context-3.0.xsd">  <!-- 声明schema文件路径 -->
 
    <bean name="c" class="com.how2java.pojo.Category">  <!-- 定义一个名为"c"的bean,类型为Category -->
        <property name="name" value="category 1" />  <!-- 设置属性name的值为"category 1" -->
    </bean>
 
</beans>

property选择要注入的属性,value确定值。

(5)实体类:

在scr的包com.how2java.pojo下new一个class
Category.java:

package com.how2java.pojo;
 
public class Category {
 
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    private int id;
    private String name;
}

(6)测试类:

在scr的包com.how2java.test下new一个class
TestSpring.java:

package com.how2java.test;  // 包名

import org.springframework.context.ApplicationContext;  // 导入类 ApplicationContext
import org.springframework.context.support.ClassPathXmlApplicationContext;  // 导入类 ClassPathXmlApplicationContext

import com.how2java.pojo.Category;  // 导入类 Category

public class TestSpring {  // 定义一个类 TestSpring

    public static void main(String[] args) {  // 定义一个公有静态方法 main()
        ApplicationContext context = new ClassPathXmlApplicationContext(  // 创建一个 ApplicationContext 对象 context
                new String[] { "applicationContext.xml" });  // 从 applicationContext.xml 配置文件中读取配置信息

        Category c = (Category) context.getBean("c");  // 从容器中获取 id 为 "c" 的 bean,转换为 Category 类型的对象 c

        System.out.println(c.getName());  // 输出 c 对象的 name 属性值
    }
}

(7)运行查看:

在这里插入图片描述

三、依赖注入的方式:

(1)构造器注入:

构造器注入是一种依赖注入的方式,它通过调用类的构造方法来实现对依赖对象的注入。在 Spring 中,我们可以通过配置 XML文件来实现构造器注入。该注入方式只能注入一次。

举一个简单的例子:

package com.how2java.pojo;

public class Product {
    private String name;
    private Category category;

    public Product(String name, Category category) {
        this.name = name;
        this.category = category;
    }

    public String getName() {
        return name;
    }

    public Category getCategory() {
        return category;
    }
}

上述代码中,我们可以通过含参的构造方法将JavaBean注入Product的实例对象中,具体在需要使用Product类的实例化对象时,我们通过getBean()方法直接从Spring的容器(即 ApplicationContext 对象 context)中获取。

(2)Setter注入:

顾名思义,即通过实体类(JavaBean)的setter方法,根据<bean>标签的配置,实例化这个实体类。

举一个简单的例子:

package com.how2java.pojo;

public class Product {

	private int id;
	private String name;
	private Category category;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Category getCategory() {
		return category;
	}
	public void setCategory(Category category) {
		this.category = category;
	}
}

上述代码通过setName()和setCategory()方法可借助Spring实现依赖注入,具体在需要使用Product类的实例化对象时,我们通过getBean()方法直接从Spring的容器(即 ApplicationContext 对象 context)中获取,代码如下:

 ApplicationContext context = new ClassPathXmlApplicationContext(  // 创建一个 ApplicationContext 对象 context
                new String[] { "applicationContext.xml" });  // 从 applicationContext.xml 配置文件中读取配置信息

        Product p = (Product) context.getBean("p");  // 从容器中获取 id 为 "c" 的 bean,转换为 Category 类型的对象 c

(3)构造方法的示例代码:

applicationContext.xml:

<?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:aop="http://www.springframework.org/schema/aop"  
    xmlns:tx="http://www.springframework.org/schema/tx" 
    xmlns:context="http://www.springframework.org/schema/context"  
    xsi:schemaLocation="
   http://www.springframework.org/schema/beans 
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/aop 
   http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
   http://www.springframework.org/schema/tx 
   http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
   http://www.springframework.org/schema/context      
   http://www.springframework.org/schema/context/spring-context-3.0.xsd">  <!-- 声明schema文件路径 -->
 
    <bean name="c" class="com.how2java.pojo.Category">  <!-- 定义一个名为"c"的bean,类型为Category -->
        <property name="name" value="category 1" />  <!-- 设置属性name的值为"category 1" -->
        
    </bean>
        <bean name="d" class="com.how2java.pojo.Category">  <!-- 定义一个名为"c"的bean,类型为Category -->
        <property name="name" value="春风的测试" />  <!-- 设置属性name的值为"category 1" -->
        
    </bean>
    <!-- <bean name="p" class="com.how2java.pojo.Product">
		<property name="name" value="product1" />
		<property name="category" ref="d" />
	</bean> -->
	 <bean id="p" class="com.how2java.pojo.Product">
        <constructor-arg value="春风的测试"/>
        <constructor-arg ref="c"/>
    </bean>
 
</beans>

Category.java:

package com.how2java.pojo;  // 定义包名

public class Category {  // 定义Category类

    private int id;  // 私有属性id
    private String name;  // 私有属性name

    public int getId() {  // 公有方法getId,用于获取id属性的值
        return id;
    }
    public void setId(int id) {  // 公有方法setId,用于设置id属性的值
        this.id = id;
    }
    public String getName() {  // 公有方法getName,用于获取name属性的值
        return name;
    }
    public void setName(String name) {  // 公有方法setName,用于设置name属性的值
        this.name = name;
    }
}

Product.java:


package com.how2java.pojo;

public class Product {
    private String name;
    private Category category;

    public Product(String name, Category category) {
        this.name = name;
        this.category = category;
    }

    public String getName() {
        return name;
    }

    public Category getCategory() {
        return category;
    }
}

TestSpring.java:

package com.how2java.test;  // 包名

import org.springframework.context.ApplicationContext;  // 导入类 ApplicationContext
import org.springframework.context.support.ClassPathXmlApplicationContext;  // 导入类 ClassPathXmlApplicationContext

import com.how2java.pojo.Category;  // 导入类 Category
import com.how2java.pojo.Product;

public class TestSpring {  // 定义一个类 TestSpring

    public static void main(String[] args) {  // 定义一个公有静态方法 main()
        ApplicationContext context = new ClassPathXmlApplicationContext(  // 创建一个 ApplicationContext 对象 context
                new String[] { "applicationContext.xml" });  // 从 applicationContext.xml 配置文件中读取配置信息

        Product p = (Product) context.getBean("p![在这里插入图片描述](https://img-blog.csdnimg.cn/7d6e064b3bd14976906b3f007b5acb79.png)
");  // 从容器中获取 id 为 "c" 的 bean,转换为 Category 类型的对象 c

        System.out.println(p.getName());  // 输出 c 对象的 name 属性值
        System.out.println(p.getCategory().getName()); 
    }
}

运行效果:

在这里插入图片描述

setter注入可以参考how2j的教程。

(4)构造器注入的<constructor-arg>标签解析:

<constructor-arg>标签是Spring配置文件中用于传递构造函数参数的标签,它可以有以下属性:

  1. index:指定参数在构造函数中的位置,从0开始计数。
  2. type:指定参数的类型,如果有多个同类型的参数,可以使用该属性区分。
  3. name:指定参数的名称,可以与构造函数中的参数名对应。
  4. value:指定参数的值,可以是基本类型、字符串、引用类型等。
  5. ref:指定参数的引用,可以引用其他Bean的ID。

以下是一个使用<constructor-arg>标签传递构造函数参数的例子:

<bean id="user" class="com.example.User">
    <constructor-arg index="0" value="Tom"/>
    <constructor-arg index="1" value="18"/>
    <constructor-arg index="2" ref="address"/>
</bean>

<bean id="address" class="com.example.Address">
    <constructor-arg index="0" value="Shanghai"/>
    <constructor-arg index="1" value="China"/>
</bean>

上面的例子中,我们定义了一个名为"user"的Bean,它的类是com.example.User,有三个构造函数参数,分别是姓名、年龄和地址。其中,姓名和年龄使用了value属性设置参数的值,地址使用了ref属性引用了另一个Bean的ID。我们还定义了一个名为"address"的Bean,它的类是com.example.Address,有两个构造函数参数,分别是城市和国家。

四、Spring AOP概述和基于AOP的第一个JavaSpring程序:

(1)Java代理机制:

Java代理机制是一种允许程序在运行时创建一个代理对象,代理对象可以代替原始对象进行一些操作,同时还可以在原始对象的基础上添加一些额外的功能。Java 代理机制可以通过反射机制动态代理技术来实现。

Java代理机制的相关术语:

  • == 原始对象==(Real Object):原始对象是我们要代理的对象,它是程序中的一个普通对象。比如一辆汽车就是一个原始对象。
  • 代理对象(Proxy Object):代理对象是一个由程序动态创建的对象,它可以代替原始对象进行一些操作,同时还可以在原始对象的基础上添加一些额外的功能。比如一辆租赁汽车就是一个代理对象,它可以代替我们去驾驶原始的汽车,同时还可以添加一些额外的服务,比如保险、导航等。
  • 接口(Interface):接口是一个抽象的概念,它定义了一组方法的签名,但没有具体的实现。比如租赁汽车的服务就是一个接口,它定义了一组方法的名称和作用,但具体的实现还需要由原始汽车来完成。
  • InvocationHandler:InvocationHandler是一个接口,它定义了一个invoke()方法,该方法用于处理代理对象的方法调用。比如汽车制造商的合作伙伴是InvocationHandler,负责为汽车提供一些额外的服务,比如空调、音响等。
  • Proxy:Proxy是一个用于创建代理对象的类,它提供了一组静态方法来创建不同类型的代理对象。比如租赁汽车的服务提供商就是一个Proxy,它可以根据用户的需求来创建不同类型的代理汽车。

(2)Spring AOP:

Spring AOP 是 Spring 框架的一个模块,它提供了面向切面编程的支持。Spring AOP可以让我们将应用程序中的关注点(如日志记录、事务管理、安全控制等)从业务逻辑中分离出来,然后通过特定的技术将这些关注点模块化,最终将其组合成一个完整的应用程序。

Spring AOP相关术语:

  • 切面(Aspect):切面是一个模块化的关注点,它可以横向切割应用程序,将不同的关注点分离出来,比如日志记录、事务管理、安全控制等。可类比于一个瑞士军刀,每个刀片都是不同的关注点,可以单独使用,也可以组合使用。
  • 连接点(Join Point):连接点是程序执行过程中可以插入切面的点,比如方法的调用、异常的抛出等。可类比于电路中的插头,连接点就是插头的位置。
  • 通知(Advice):通知是在连接点上执行的操作,它可以在连接点的前、后、或者抛出异常时执行。比如在方法执行前记录日志、在方法执行后进行事务提交等。可类比于电器的开关,通知就是开关的操作。
  • 切入点(Pointcut):切入点是连接点的集合,它定义了哪些连接点需要被切入切面。比如对所有的 Service 层方法进行事务管理。可类比于筛子,切点就是筛子的孔。
  • 目标对象(Target):所有被通知的对象(也可以理解为被代理的对象)都是目标对象,目标对象及其属性改变、行为调用和方法传参的变化被AOP所关注。
  • 织入(Weaving):织入是指将切面与目标对象结合起来,创建一个新的代理对象的过程。
  • 引入(Introduction):引入是为类添加新的方法或属性等。比如为一个类添加一个新的接口。可类比于人的学习能力,引入就是学习新技能。

(3)AOP的简单实现:

目标对象:

Target.java:

package com.mr.target;

public class Target {
    //程序执行的方法
    public void execute(String name){
        System.out.println("执行execute()方法:" + name);//输出信息
    }
}

通知:

LoggerExcute.java:

package com.mr.log;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class LoggerExecute implements MethodInterceptor {
    // 实现 MethodInterceptor 接口,该接口提供了拦截方法调用的方法
    public Object invoke(MethodInvocation invocation) throws Throwable {
        before();  // 执行前置通知
        invocation.proceed();  // 执行原始方法
        return null;  // 返回 null
    }
    // 前置通知方法
    private void before() {
        System.out.println("程序开始执行!");
    }
}

代理对象:

Manager.java:

package com.mr.main;

import org.springframework.aop.framework.ProxyFactory;   //导入Spring AOP的ProxyFactory类

import com.mr.log.LoggerExecute;                        //导入日志记录的Advice类
import com.mr.target.Target;                            //导入目标类

public class Manager {
    //创建代理
    public static void main(String[] args) {
        Target target = new Target();                 //创建目标对象
        ProxyFactory di=new ProxyFactory();            //创建ProxyFactory对象
        di.addAdvice(new LoggerExecute());             //添加Advice对象
        di.setTarget(target);                          //设置目标对象
        Target proxy=(Target)di.getProxy();            //获取代理对象
        proxy.execute("AOP的简单实现");               //代理执行execute()方法
    } 
}

当我们运行主类Manager时,首先会创建一个目标对象,我们所要实现的核心功能包含在这个目标对象中,接着实例化一个工厂类 ProxyFactory,即di我们可以通过它来设置目标对象、添加 Advice 对象等来创建代理对象,在这里,我们通过 di.setTarget(target) 方法设置了目标对象,通过 di.addAdvice(new LoggerExecute()) 方法添加了一个 Advice 对象,最后通过 di.getProxy() 方法获取代理对象,即一个“四肢健全”的程序,有核心部分,有细枝末节,最后调用代理对象的excute()方法,实现一次输出。代理对象是目标对象的扩展,所以它仍然具备目标对象的方法。

运行效果:

五、Spring持久化:

1.持久化

持久化技术就是将内存中的临时数据保存到储存设备中,即使在手机或电脑关机的情况下,这些数据仍然不会丢失,保存在内存中的数据是瞬时状态,保存在设备中的数据则是持久状态的;持久化数据就是让数据在瞬时与持久状态之间进行转换的

举例来说,我们可以将一个Java对象保存到数据库中,这样即使程序结束或者重启,这个对象的数据也不会丢失,而且我们可以通过数据库查询语句来检索和处理这个对象的数据。

以下是一个Java对象持久化到MySQL数据库的示例:

  1. 定义一个Java对象
public class User {
    private int id;
    private String name;
    private int age;
    // 省略getter和setter方法
}
  1. 创建一个数据库表
CREATE TABLE user (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(20) NOT NULL,
  age int(11) NOT NULL,
  PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=UTF-8;
  1. 使用JDBC将Java对象保存到数据库中
public void saveUser(User user) {
    Connection conn = null;
    PreparedStatement ps = null;
    try {
        conn = getConnection();
        String sql = "INSERT INTO user (name, age) VALUES (?, ?)";
        ps = conn.prepareStatement(sql);
        ps.setString(1, user.getName());
        ps.setInt(2, user.getAge());
        ps.executeUpdate();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        close(conn, ps, null);
    }
}
  1. 使用JDBC从数据库中读取Java对象
public User getUserById(int id) {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    User user = null;
    try {
        conn = getConnection();
        String sql = "SELECT * FROM user WHERE id = ?";
        ps = conn.prepareStatement(sql);
        ps.setInt(1, id);
        rs = ps.executeQuery();
        if (rs.next()) {
            user = new User();
            user.setId(rs.getInt("id"));
            user.setName(rs.getString("name"));
            user.setAge(rs.getInt("age"));
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        close(conn, ps, rs);
    }
    return user;
}

在上述示例中,我们通过JDBC将Java对象User保存到MySQL数据库中,并且能够从数据库中读取User对象的数据。这样,就实现了Java对象的持久化。

2.DAO模式:

在Java中,DAO是指数据访问对象(Data Access Object)模式,它是一种创建可重用性高的数据访问层的设计模式。 该模式的主要目标是将数据存取逻辑与业务逻辑分离。这样,在应用程序中,业务逻辑可以专注于实现应用程序的功能和业务需求,而数据访问层负责提供数据库访问接口,以便在不同的数据源之间进行切换,并且使用相同的数据访问代码。DAO模式将所有的CRUD操作封装在一个对象中,这个对象通常被称为DAO。DAO通过公共接口向业务逻辑层提供对数据的访问,而具体的数据存取细节则由底层的数据访问技术(如JDBC或Hibernate)来实现。在Java中,使用DAO模式可以使得我们的代码更加清晰、易于维护,同时也方便了单元测试的编写。

举一个简单的例子:
假设我们正在开发一个图书管理系统,需要实现以下功能:

  • 添加新书籍
  • 根据ID获取书籍信息
  • 获取所有书籍的列表

首先,我们定义一个Book类,它包含书籍的ID、标题、作者和出版日期。

public class Book {
    private long id;
    private String title;
    private String author;
    private Date publishDate;
`
    // getters and setters
}

然后,我们创建一个BookDao接口,它定义了对Book对象进行操作的方法。

public interface BookDao {
    public void addBook(Book book);
    public Book getBookById(long id);
    public List<Book> getAllBooks();
}

接下来,我们实现BookDao接口,使用JDBC访问数据库。

public class JdbcBookDao implements BookDao {
    // JDBC连接和查询语句的定义

    public void addBook(Book book) {
        // 执行SQL插入语句,将书籍信息保存到数据库中
    }

    public Book getBookById(long id) {
        // 执行SQL查询语句,获取指定ID的书籍信息
        // 将结果封装成一个Book对象,并返回
    }

    public List<Book> getAllBooks() {
        // 执行SQL查询语句,获取所有书籍的信息
        // 将结果封装成一个List<Book>对象,并返回
    }
}

最后,我们创建一个BookService组件,它负责协调BookDao和Book之间的交互,并执行业务逻辑。

public class BookService {
    private BookDao bookDao;

    public BookService(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    public void addBook(Book book) {
        bookDao.addBook(book);
    }

    public Book getBookById(long id) {
        return bookDao.getBookById(id);
    }

    public List<Book> getAllBooks() {
        return bookDao.getAllBooks();
    }
}

现在,我们可以在应用程序中使用BookService组件,执行添加、获取和列出书籍等操作。例如:

BookDao bookDao = new JdbcBookDao();
BookService bookService = new BookService(bookDao);

// 添加新书籍
Book book = new Book();
book.setTitle("Java编程思想");
book.setAuthor("Bruce Eckel");
book.setPublishDate(new Date());
bookService.addBook(book);

// 获取指定ID的书籍信息
long id = 1;
Book bookById = bookService.getBookById(id);

// 获取所有书籍的列表
List<Book> allBooks = bookService.getAllBooks();

3.Spring的DAO理念:

个人感觉Spring的DAO模式和普通Java程序的DAO模式是有很大很大的相似之处的,具体来说,
都是先有一个需要与数据库进行交互实体类,然后然后有一个接口来定义要对这个实体类进行的操作,然后有一个实现接口的类(即DAO),在这个类中连接数据库,实现CRUD,最后有一个测试类,用于协调最初的实体类和DAO。唯一不同的地方就是初始化JDBC,在这里我们使用了反转控制依赖注入,而在普通的Java程序中,这是手工的。

3.事务管理:

(1)概述:

Spring的事务管理是一种简单而强大的机制,用于处理应用程序中的数据库事务。它支持多种事务定义和传播行为,并提供了一种统一的编程模型,使得开发人员可以轻松地管理和控制数据访问操作的原子性、一致性、隔离性和持久性。

Spring的事务管理是基于AOP(面向切面编程)实现的。它使用代理模式对DAO或Service层对象进行增强,从而自动包装数据库操作在一个事务中。Spring的事务管理还允许声明式事务,通过对方法添加注解或XML配置来指定哪些数据访问操作需要在事务内执行。

Spring 的事务管理提供了一些属性来控制事务的行为,其中包括传播行为、隔离级别、只读和超时属性,具体解释如下:

传播行为(Propagation Behavior)

定义:指一个方法调用其他方法时,事务应该如何进行传播。
属性值:

  • REQUIRED:如果当前没有事务,则开启一个新事务并在其中运行;如果当前已经存在事务,则在当前事务中运行。
  • SUPPORTS:如果当前存在事务,则在当前事务中运行;如果当前没有事务,则不开启事务。
  • MANDATORY:如果当前已经存在事务,则在当前事务中运行;如果当前没有事务,则抛出异常。
  • REQUIRES_NEW:无论当前是否存在事务,都开启一个新的事务并在其中运行。
  • NOT_SUPPORTED:当前方法不应该在事务中运行;如果当前存在事务,则挂起该事务并执行该方法,执行完后再恢复该事务。
  • NEVER:当前方法不应该在事务中运行;如果当前存在事务,则抛出异常。
  • NESTED:如果当前存在事务,则在事务的嵌套事务中运行,否则与 REQUIRED 类似。
    举例代码示例:

生活中的类比:在香港,地铁票和公交车票是两种不同的交通工具,假设你从火车站到公司需要先坐地铁再坐公交车,在你坐地铁时,如果你需要换乘公交车,你就必须离开地铁站再去买公交车票,这就好比 REQUIRES_NEW 的传播行为;如果你已经有了一张公交车票,那么你可以直接从地铁站出口上车,这就好比 REQUIRED 的传播行为。


只读属性(Read-Only)

定义:指该事务是否只读,如果设置为只读,则在该事务中不能进行任何写操作。

属性值:

  • true:只读。
  • false:非只读(默认)。

生活中的类比:假设你去图书馆借书,你只能从书架上取书并阅读,但不能修改或者添加书籍,这就好比只读模式下的数据库事务。

隔离级别(Isolation Level)

定义:指处理并发访问时,数据库事务之间隔离程度的不同程度。
属性值:

  • DEFAULT:使用默认的隔离级别(如果没有设置,则使用数据库默认的隔离级别)。
  • READ_UNCOMMITTED:读取未提交的数据,可能会看到其他事务尚未提交的数据,存在脏读、幻读和不可重复读等问题。
  • READ_COMMITTED:只能读取已经提交的数据,可以避免脏读问题,但仍然存在幻读和不可重复读等问题。
  • REPEATABLE_READ:可重复读,保证多次读取同一记录时,结果始终一致,但仍然存在幻读问题。
  • SERIALIZABLE:序列化,最高的隔离级别,通过强制事务串行执行来避免所有并发问题,但会影响性能。

生活中的类比:炒菜时需要多个人共用一个厨房和锅碗瓢盆等工具,如果大家都要使用同一把勺子,就有可能会出现争抢勺子的情况。隔离级别就好比每个人拥有自己的勺子,避免了争抢的发生。

(2)编程式事务管理:

编程式事务管理在代码中明确地开启事务、提交或回滚事务,需要手动编写相关代码才能实现。它需要通过Spring提供的TransactionTemplate来实现。

示例:
手工写的TransactionTemplate或者TransactionManager对象:

package com.mr.transaction;

import java.sql.Connection;
import java.sql.Statement;

import javax.sql.DataSource;

import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

public class TransactionExample {
    DataSource dataSource;//注入数据源
    PlatformTransactionManager transactionManager;//注入事务管理器
    TransactionTemplate transactionTemplate;//注入TransactionTemplate模板
    
    public DataSource getDataSource() {
        return dataSource;
    }
    
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    
    public PlatformTransactionManager getTransactionManager() {
        return transactionManager;
    }
    
    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }
    
    public TransactionTemplate getTransactionTemplate() {
        return transactionTemplate;
    }
    
    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }
    
    public void transactionOperation() {
        transactionTemplate.execute(new TransactionCallback() { //使用transactionTemplate的execute方法进行事务操作
            public Object doInTransaction(TransactionStatus status) { //使用TransactionCallback的doInTransaction方法实现具体的数据库操作
                Connection conn = DataSourceUtils.getConnection(dataSource);//获得数据库连接
                try {
                    Statement stmt = conn.createStatement();
                    //执行两次添加方法
                    stmt.execute("insert into tb_user(name,age,sex) values('小强','26','男')");
                    int a=0;//制造异常测试事务是否配置成功
                    
                    stmt.execute("insert into tb_user(name,age,sex) values('小红','22','女')");
                    System.out.println("操作执行成功!");
                } catch (Exception e) {
                    transactionManager.rollback(status);//事务回滚
                    System.out.println("操作执行失败,事务回滚!");
                    System.out.println("原因:"+e.getMessage());
                }
                return null;
            }
        });
    }
}

上述代码中,我们定义了三个属性:DataSource、PlatformTransactionManager、TransactionTemplate transaction,接着为它们设置getter和setter方法以便进行依赖注入,接着定义了一个进行编程式事务管理操作的方法transactionOperation,在其中进行连接数据库,对数据表进行操作,对事物进行管理(出现异常则回滚)。


编码式事务管理的xml配置文件:
<?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-2.5.xsd">
    
    <!-- 配置数据源 -->
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName">
            <value>com.mysql.jdbc.Driver</value>
        </property>
        <property name="url">
            <value>jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8</value>
            <!-- 数据库连接地址 -->
        </property>
        <property name="username">
            <value>root</value>
            <!-- 数据库用户名 -->
        </property>
        <property name="password">
            <value>admin</value>
            <!-- 数据库密码 -->
        </property>
    </bean>
    
    
    
        <!-- 定义事务管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource">
            <ref bean="dataSource" />
            <!-- 使用数据源实现事务管理器的功能 -->
        </property>
    </bean>
    
    
    <!-- 定义TransactionTemplate模板 -->
    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager">
            <ref bean="transactionManager"/>
            <!-- 使用事务管理器实现TransactionTemplate的功能 -->
        </property>
        <property name="propagationBehaviorName">
            <value>PROPAGATION_REQUIRED</value>
            <!-- 设置事务传播行为为REQUIRED -->
        </property>
    </bean>
    

    
    <!-- 为TransactionExample注入数据源、事务管理器、TransactionTemplate模板 -->
    <bean id="transactionExample"
        class="com.mr.transaction.TransactionExample">
        <property name="dataSource">
            <ref bean="dataSource" />
            <!-- 注入数据源 -->
        </property>
        <property name="transactionManager">
            <ref bean="transactionManager" />
            <!-- 注入事务管理器 -->
        </property>
        <property name="transactionTemplate">
            <ref bean="transactionTemplate"/>
            <!-- 注入TransactionTemplate模板 -->
        </property>
    </bean>
</beans>

上述代码是一个Spring的XML配置文件。XML 标签中的属性用于配置 Spring 容器中的对象和它们的依赖关系,具体而言:

  • “xmlns”、“xmlns:xsi” 和 “xsi:schemaLocation” 属性定义了 XML 的命名空间和 XSD Schema 的位置;-

  • “dataSource” 是一个数据源对象,使用 JDBC 驱动程序从数据库获取连接;

  • “transactionManager” 是一个事务管理器对象,用于管理事务的生命周期;

  • “transactionTemplate” 是一个事务模板,用于在代码中执行事务性操作;-

  • “transactionExample” 是一个示例 bean,用于演示如何将数据源、事务管理器和事务模板注入到实际业务逻辑代码中。

在 Spring 中,“TransactionTemplate” 是对事务管理器进行封装的一个模板对象,它使用事务管理器 “TransactionManager” 来执行事务性操作。具体而言,“TransactionTemplate” 提供了一些封装好的常用方法,如 execute(),可以让开发者更轻松地实现事务性数据库操作,而不必直接与低级别的事务 API 打交道。因此,“transactionManager” 和 “transactionTemplate” 在Spring事务控制中具有协同的作用,前者负责管理事务的整个生命周期,后者则在手动提交或回滚事务时提供更加便捷的编程模板。


测试类:

// 导入需要的包
package com.mr.main;

// 导入Spring的ApplicationContext接口和ClassPathXmlApplicationContext实现类
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

// 导入自定义的TransactionExample类
import com.mr.transaction.TransactionExample;

// 定义Manager类
public class Manager {

    // 主方法
    public static void main(String[] args) {
        // 创建ApplicationContext对象,装载配置文件
        ApplicationContext factory = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 获取TransactionExample对象
        TransactionExample transactionExample = (TransactionExample) factory.getBean("transactionExample");
        // 调用TransactionExample对象的transactionOperation方法,执行添加操作
        transactionExample.transactionOperation();
    }
}

上述代码中,我们首先创建了一个ApplicationContext对象,并使用ClassPathXmlApplicationContext实现类来装载配置文件。然后,我们获取了一个TransactionExample对象,并调用其transactionOperation方法来执行添加操作。

(3)声明式事务管理:

实体类:

package com.mr.user;

public class User {
    
    private String name;//姓名
    
    private Integer age;//年龄
    
    private String sex;//性别

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

DAO:

package com.mr.dao;

import org.springframework.jdbc.core.support.JdbcDaoSupport;
import com.mr.user.User;

public class AddDAO extends JdbcDaoSupport {

    //添加用户的方法
    public void addUser(User user){
        //执行添加方法的sql语句
        String sql="insert into tb_user (name,age,sex) values('" + 
                    user.getName() + "','" + user.getAge()+ "','" + user.getSex()+ "')";
                    
        // 执行两次添加方法,故意制造异常测试事务是否配置成功
        getJdbcTemplate().execute(sql);
        int a=0;// 制造异常测试事务是否配置成功
        a=9/a;  // 故意制造算术异常
        getJdbcTemplate().execute(sql); // 如果配置正确,这里插入操作应该会回滚
    }
}

上述代码中,我们使用JdbcTemplate的execute方法执行sql语句,将User对象的属性值插入到数据库中。


声明式事务管理的xml配置文件:

<?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-2.5.xsd">

    <!-- 配置数据源 -->
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName">
            <value>com.mysql.jdbc.Driver</value>
        </property>
        <property name="url">
            <value>jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8</value>
            <!-- 连接数据库的 URL,指定了数据库的名称和编码方式 -->
        </property>
        <property name="username">
            <value>root</value>
            <!-- 数据库账号 -->
        </property>
        <property name="password">
            <value>admin</value>
            <!-- 数据库密码 -->
        </property>
    </bean>

    <!-- 定义事务管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource">
            <ref bean="dataSource" />
            <!-- 使用定义好的 dataSource -->
        </property>
    </bean>

    <!-- 定义 TransactionProxy -->
    <bean id="transactionProxy"
        class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="transactionManager">
            <ref local="transactionManager" />
            <!-- 使用定义好的 transactionManager -->
        </property>
        <property name="target">
            <bean id="addDAO" class="com.mr.dao.AddDAO">
                <property name="dataSource">
                    <ref local="dataSource" />
                    <!-- 使用定义好的 dataSource -->
                </property>
            </bean>
        </property>
        <property name="proxyTargetClass" value="true" />
        <!-- 表示使用 CGLIB 动态代理 -->
        <property name="transactionAttributes">
            <props>
                <prop key="add*">PROPAGATION_REQUIRED</prop>
                <!-- 设置 add* 方法使用 PROPAGATION_REQUIRED 事务传播机制 -->
            </props>
        </property>
    </bean>

</beans>

上述代码的核心部分(执行事务管理功能的部分)是transactionProxy的transactionAttributes属性,它实现了将DAO类中加上事务管理,即对于 add* 开头的方法,使用 PROPAGATION_REQUIRED 的事务传播机制,保证在添加用户时出现异常时,能够回滚事务,不会将数据插入到数据库中。


测试类:

package com.mr.main;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.mr.dao.AddDAO;
import com.mr.user.User;

public class Manager {
    public static void main(String[] args) {
    	ApplicationContext factory = new ClassPathXmlApplicationContext("applicationContext.xml");  //装载配置文件
        AddDAO addDAO = (AddDAO)factory.getBean("transactionProxy");//获取AddDAO
        User user = new User();//实例化User实体对象
        user.setName("明日");//设置姓名
        user.setAge(30);//设置年龄
        user.setSex("男");//设置性别
        addDAO.addUser(user);//执行数据库添加方法
    }
}

通过读取 applicationContext.xml 配置文件中的内容来构建 Spring 容器,并从容器中获取到名为 transactionProxy 的 Bean 对象,然后调用该对象的 addUser() 方法向数据库插入一条用户记录。


运行效果:
数据表中多了一条数据:id,明日,30,男

(4)两种事务管理方式的对比:

很明显,使用声明式在代码量上会少很多,代码简洁,并且在耦合程度上是低耦合的,不像编码式要写多个类,使用多个包,实现了业务逻辑和数据层的分离,便于代码的维护和重构。而编码式事务管理在灵活程度上比声明式要高出很多,因为它可以手动开启、选择要管理的事务,适合处理一些特殊的业务需求。

4.JdbcTemple操作数据库:

实例

在声明式事务管理的实例中,就是用了JdbcTemplate来操作数据库。

具体来说,AddDao 类继承自 Spring 框架提供的 JdbcDaoSupport 类,因此可以使用其提供的 getJdbcTemplate() 方法获取 JDBC 模板对象,并通过该对象执行 SQL 语句。

在 addUser() 方法中,通过拼接字符串的方式构造一个 SQL 语句,并将其作为参数传递给 getJdbcTemplate().execute(sql) 方法。该方法会直接执行 SQL 语句,并返回相应的结果。例如:

String sql="insert into tb_user (name,age,sex) values('" + user.getName() + "','" + user.getAge()+ "','" + user.getSex()+ "')";
getJdbcTemplate().execute(sql);

除了 execute() 方法之外,JdbcTemplate 还提供了许多其他的便捷方法,如 queryForObject()、update()、batchUpdate() 等,用于执行不同类型的 SQL 语句。通过这些方法的封装,我们可以更方便地操作数据库,而不需要手动编写繁琐的 JDBC 代码。

JdbcTemplate和JdbcDaoSupport是两个常用的类。

JdbcTemplate是Spring提供的用于简化JDBC操作的核心类之一,可以方便地执行SQL语句并获取结果集。
JdbcDaoSupport是Spring提供的用于支持DAO层访问数据库的辅助类之一,它提供了一些基本的JDBC操作方法,例如查询、更新、插入等等。
它们之间的关系是,JdbcDaoSupport类是通过组合的方式来使用JdbcTemplate的。也就是说,JdbcDaoSupport类内部包含一个JdbcTemplate对象,并且封装了一些常见的JDBC操作方法,例如查询、更新、插入等等,以便于DAO层直接调用。

因此,当我们需要实现一个DAO类时,可以继承JdbcDaoSupport类,从而获得一些基本的JDBC操作方法,同时也可以使用JdbcTemplate对象来完成更加复杂的操作。这样,我们就可以更加轻松地访问数据库,从而提高代码的可读性和可维护性。

简单来说,JdbcTemplate和JdbcDaoSupport都是Spring提供的用于支持JDBC操作的类,其中JdbcDaoSupport是基于JdbcTemplate实现的,可以方便地封装一些常见的JDBC操作方法,使得我们可以更加轻松地访问数据库。

JdbcTemplate 对象操作数据库有以下好处:

  • 简化了 JDBC 编程
    传统的 JDBC 编程需要手动创建连接、预处理语句、设置参数、执行查询、释放资源等。而使用 JdbcTemplate 对象,我们可以更加简单地执行这些操作,只需要构建 SQL 语句和对应的参数即可。

  • 提高了代码质量
    使用 JdbcTemplate 可以将一些通用的业务逻辑封装在 DAO 层中,从而提高了代码的重复利用率和可维护性。同时,也使得代码更加可读易懂,并且避免了一些常见的错误,如 SQL 注入等。

  • 提供了事务管理支持
    JdbcTemplate 对象本身就支持事务管理,并且可以与 Spring 框架集成来实现声明式事务管理。通过配置数据源和事务管理器,我们就可以在 JdbcTemplate 中完成事务的提交和回滚等操作。

  • 可以用于多种数据库
    JdbcTemplate 支持各种不同类型的数据库,包括 MySQL、Oracle、SQL Server 等。而且,由于其采用了标准的 Jdbc API,因此可以方便地切换不同的数据库供应商,而不会影响到原有的代码实现。

  • 提高了性能和扩展性
    JdbcTemplate 内部采用了连接池技术和 PreparedStatement 缓存技术等优化手段,从而提高了程序的性能。同时,它也可以与其他 Spring 框架提供的 ORM 工具(如 Hibernate、MyBatis 等)相结合,来进行更加复杂的数据库操作。

六、MyBatis与Spring初步整合:

具体程序见how2j的教程,下面我将对Mybatis和Spring是如何进行整合的进行分析。

我认为实现二者整合功能的,主要是spring框架下的一个jar包SqlSessionFactoryBean,即org.mybatis.spring.SqlSessionFactoryBean,粗看这个jar包,便可以猜到它的作用,sql前缀,表面的数据库的CRUD有一定关系,FactoryBean则表明它是Sprig的一个工厂类,用来进进行IoC和DI等一些操作,它主要有以下功能:

  • 配置数据源:在SqlSessionFactoryBean中可以设置数据源相关属性(如driverClassName、url、username、password等),使得MyBatis能够正确连接数据库。

  • 指定Mapper文件位置:SqlSessionFactoryBean还可以通过设置mapperLocations属性,指定MyBatis Mapper文件所在路径,从而让MyBatis正确地读取Mapper文件中的SQL语句。

  • 自动扫描类型别名:SqlSessionFactoryBean通过设置typeAliasesPackage属性,自动扫描指定包下的JavaBean对象,将其注册为MyBatis中的类型别名,从而可以方便地在Mapper文件中使用JavaBean对象。

  • 配置其他属性:SqlSessionFactoryBean还可以设置其他属性,比如配置插件(plugins)、本地缓存(localCacheScope)、全局配置(configuration)等。

how2j的项目中,对SqlSessionFactoryBean属性进行设置的代码如下:

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean">
	    <!-- 配置MyBatis映射器接口对应的实体类所在包名 -->
		<property name="typeAliasesPackage" value="com.how2java.pojo" />

		<!-- 配置MyBatis映射器文件所在位置 -->
		<property name="mapperLocations" value="classpath:com/how2java/mapper/*.xml"/>

		<!-- 注入数据源 -->
		<property name="dataSource" ref="dataSource"/>
	</bean>
	

上述代码中,

  • typeAliasesPackage用于配置MyBatis映射器接口对应的实体类所在包名,可以通过设置该属性,让MyBatis自动扫描指定包下的JavaBean对象并将其注册为类型别名,便于在Mapper文件中使用JavaBean对象;
  • mapperLocations用于配置MyBatis映射器文件的路径。该属性使用了value属性,指定了映射器文件所在的路径,可以通过通配符匹配多个Mapper文件;
  • dataSource则是基于Spring的持久化操作。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【六一 iKun】Happy LiuYi, iKuns

六一了&#xff0c;放松下。 Python iKun from turtle import * screensize(1000,1000) speed(6)#把衣服画出来&#xff0c;从右肩膀开始#领子 penup() goto(-141,-179) pensize(3) fillcolor("black") pencolor("black") begin_fill() pendown() left(1)…

30天从入门到精通TensorFlow1.x 第二天,变量 tf.Variable()

文章目录 一&#xff0c;接前一天&#xff08;1&#xff09;.内容前先弄清楚 sess.run() 函数a. 该函数干嘛的b. 该函数有哪些参数c. 该函数的使用 &#xff08;2&#xff09;.由库函数创建张量&#xff08;3&#xff09;.由库函数创建张量 二、变量tf.Variable()&#xff08;1…

Dart语法学习

最近在学习flutter相关方面的知识&#xff0c;里面用到了Dart语言&#xff0c;于是写下这篇博客记录学习的一门过程。如果你有其他编程语言的经验&#xff08;尤其是Java和JavaScript&#xff09;&#xff0c;可以很快的上手Dart语言&#xff0c;Dart 在设计时应该是同时借鉴了…

AI注册流程

1、首先需要有一个OpenAI账号&#xff0c;如果有方法的&#xff0c;就可以自己先注册一下。如果没有方法的&#xff0c;还有一个付费版本的可以备选&#xff0c;亲测可用。 2、注册建议使用谷歌账号关联登录&#xff0c;最方便。微软账号太慢了&#xff0c;也可以使用。注册使用…

Git的安装和环境变量的配置

目录 前言一、下载Git二、安装Git三、检查是否安装成功四、 配置用户名和邮箱五、环境变量配置1. 获取git的安装路径2. 设置环境变量 前言 当我们第一次在新电脑上使用git命令的时候&#xff0c;会报错【git 不是内部或外部命令&#xff0c;也不是可运行的程序 或批处理文件】…

企业工程项目管理系统源码-专注项目数字化管理-Java工程管理-二次开发

工程项目各模块及其功能点清单 一、系统管理 1、数据字典&#xff1a;实现对数据字典标签的增删改查操作 2、编码管理&#xff1a;实现对系统编码的增删改查操作 3、用户管理&#xff1a;管理和查看用户角色 4、菜单管理&#xff1a;实现对系统菜单的增删改查操…

javaWeb 酒店民宿预定信息管理系统myeclipse开发mysql数据库MVC模式java编程计算机网页设计

一、源码特点 java ssh酒店民宿预定信息管理系统是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为T…

【十一】设计模式~~~结构型模式~~~代理模式(Java)

【学习难度&#xff1a;★★★☆☆&#xff0c;使用频率&#xff1a;★★★★☆】 6.1. 模式动机 在某些情况下&#xff0c;一个客户不想或者不能直接引用一个对 象&#xff0c;此时可以通过一个称之为“代理”的第三者来实现 间接引用。代理对象可以在客户端和目标对象之间起…

CTR预估之DNN系列模型:FNN/PNN/DeepCrossing

前言 在上一篇文章中 CTR预估之FMs系列模型:FM/FFM/FwFM/FEFM&#xff0c;介绍了FMs系列模型的发展过程&#xff0c;开启了CTR预估系列篇章的学习。FMs模型是由线性项和二阶交互特征组成&#xff0c;虽然有自动学习二阶特征组合的能力&#xff0c;一定程度上避免了人工组合特征…

Springboot中使用mail邮件

Springboot中使用mail邮件发送 1、配置邮箱的POP3/SMTP服务和IMAP/SMTP服务2、导入依赖和一些默认#配置新的3、发送邮件4、整合工具类 1、配置邮箱的POP3/SMTP服务和IMAP/SMTP服务 这里使用的是QQ邮箱,进入设置-账户&#xff0c;开启下服务。 开启后获取授权码&#xff0c;保存…

智能路由器开发之OpenWrt简介

智能路由器开发之OpenWrt简介 1. 引言 1.1 智能路由器的重要性和应用场景 智能路由器作为网络通信的核心设备&#xff0c;具有重要的地位和广泛的应用场景。传统的路由器主要提供基本的网络连接功能&#xff0c;但随着智能家居、物联网和大数据应用的快速发展&#xff0c;对于…

Typora+PicGo+阿里云OSS搭建博客图床

✅作者简介&#xff1a;大家好&#xff0c;我是Cisyam&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Cisyam-Shark的博客 &#x1f49e;当前专栏&#xff1a; 程序日常 ✨特色专栏&…

每日一题——删除字符串中的所有相邻重复项

每日一题 删除字符串中的所有相邻重复项 题目链接 思路 这是一道用栈解决的典型题目 我们先来看看栈的基本性质&#xff1a; 栈&#xff1a;是一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素的操作。进行数据插入和删除操作的一端称为栈顶&#xff0c…

【PC迁移与管理】上海道宁为每个用户和每个 PC 传输和迁移场景提供解决方案——PCmover

PCmover 是一款 可以自动将所有选定文件、 文件夹、设置、用户配置文件 甚至应用程序 从旧PC传输、恢复和升级到 新PC或操作系统的软件 而且由于 大多数迁移的应用程序 都已安装在新PC上即可使用 通常无需查找旧CD 以前下载的程序 序列号或许可证代码 开发商介绍 La…

Zookeeper学习---2、客户端API操作、客户端向服务端写数据流程

1、客户端API操作 1.1 IDEA 环境搭建 前提&#xff1a;保证 hadoop102、hadoop103、hadoop104 服务器上 Zookeeper 集群服务端启动。 1、创建一个工程&#xff1a;Zookeeper 2、添加pom文件 <?xml version"1.0" encoding"UTF-8"?> <project …

Android Studio 2022.3 新版 flamingo 安装步骤及遇到的问题

下载地址: https://developer.android.google.cn/studio D盘中新建一个 Android 文件夹, 用来存储 Android studio 和 SDK 文件. 下载好之后, 运行 exe 文件, 点击 next 注意这个路径最好不要有空格,比如 program files这种目录,不然后面安装sdk的时候会有问题. 点击 instal…

【Linux】线程概述、创建线程、终止线程

目录 线程概述1、创建线程函数解析代码举例 2、终止线程函数解析代码举例 橙色 线程概述 与进程类似&#xff0c;线程是允许应用程序并发执行多个任务的一种机制。一个进程可以包含多个线程。 进程是 CPU 分配资源的最小单位&#xff0c;线程是操作系统调度执行的最小单位。…

Qt与Excel:从底层原理到上层应用的全面探索

Qt与Excel&#xff1a;从底层原理到上层应用的全面探索 一、Qt与Excel文件的交互基础&#xff08;Basics of Qt and Excel Interaction&#xff09;1.1 Qt与Excel文件的基本概念&#xff08;Basic Concepts of Qt and Excel Files&#xff09;1.2 Qt读取Excel文件的基本方法&am…

Python文件打包成exe文件

文章目录 背景安装pyinstaller开始打包总结 背景 今天因为在线将pdf转为word被收费了&#xff0c;有点不爽&#xff0c;所以自己动手撸一个pdf转word的小工具&#xff0c;想着打包成exe给朋友使用&#xff0c;万一哪天会用到呢&#xff1f; 安装pyinstaller 打开cmd命令窗口…

Homeassistant --openwrt docker 安装

openwrt homeassistant安装教程 前提&#xff1a;在N1盒子上面烧录 f大的openwrt系统 (安装81o 或者82o都可以) 一.进入openwrt系统 通常为192.168.1.1 打开网络配置 点击网络点击接口然后修改 这样网络是属于旁路由上网了 可以联通网络了 主要需要填写正确 二.点击docker …