本次练习基于how2j和课本,初步认识Spring。
以后我每周只写一篇Web的博客,所有的作业内容会在这篇博客中持续更新。。。
一、Spring基础
1.Spring概述:
Spring框架是一个开源的Java企业应用程序开发框架,它提供了一系列的解决方案,帮助开发者在开发企业级应用时更加简单、高效、安全。Spring框架主要由IOC容器、AOP、数据访问、Web开发等模块组成。
举个例子,假设我们要开发一个Web应用,需要连接数据库并进行数据操作。使用Spring框架,我们可以使用Spring的IOC容器来管理对象的创建和依赖关系,使用Spring的数据访问模块来连接数据库并进行数据操作,同时还可以使用Spring的Web开发模块来快速开发Web应用。这样,我们可以更加高效、简单地开发出一个功能完善的Web应用。
2.Sring组成:
-
SpringCore模块:提供Spring框架的基础功能,如=依赖注入和控制反转==。它包含了Spring框架的核心组件,如BeanFactory和ApplicationContext等。
-
SpringContext模块:建立在SpringCore模块之上,提供了更广泛的应用程序上下文支持,如国际化、事件传播、资源加载、应用程序配置等。它还提供了许多企业级服务,如JNDI、EJB、JMS等。
-
SpringAOP模块:提供了面向切面编程的支持,可以将横切关注点(如日志记录、性能监视、事务管理等)从业务逻辑中分离出来。它使用代理模式实现,可以在不修改业务逻辑的情况下为应用程序添加新的功能。
-
SpringDAO模块:提供了对数据访问技术的支持,如JDBC、ORM、事务管理等。它提供了一组通用的异常层次结构,以及一些模板类,可以大大简化数据访问代码的编写。
-
SpringORM模块:提供了对ORM框架的支持,如Hibernate、JPA等。它可以将实体对象映射到数据库表中,从而使得开发人员可以使用面向对象的方式来操作数据库。
-
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类,它通常具有以下特征:
- 类是公共的
- 有一个无参构造方法
- 属性通过getter和setter方法进行访问
- 实现了Serializable接口
JavaBean的主要用途是封装数据,它可以将数据和行为封装在一个类中,从而实现了数据的安全性和可维护性。JavaBean通常用于在不同的层之间传递数据,
例如在MVC架构中,Controller层可以通过JavaBean来获取View层传递过来的数据,然后将数据传递给Model层进行处理。
Spring中的Bean是指在Spring容器中管理的Java对象,它们可以通过配置文件或注解来定义和创建。
Spring中的Bean与一般的JavaBean相比,有以下区别和联系:
区别:
- Spring中的Bean通常由Spring容器来创建和管理,而一般的JavaBean通常由程序员手动创建和管理。
- Spring中的Bean可以通过配置文件或注解来定义和创建,从而实现了更大程度的可配置性和灵活性。
- Spring中的Bean通常具有更多的功能和特性,例如依赖注入、面向切面编程、事务管理等。
联系:
- Spring中的Bean本质上也是JavaBean,它们都是Java类。
- 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程序的生命周期:
编写Java源代码,通过编译器将其编译成字节码文件。
将字节码文件加载到JVM中,并通过类加载器将其转换成Java类。
JVM将Java类转换成机器码,并执行程序。
程序执行完毕后,JVM将释放内存并结束程序。
(2)SpringBean的生命周期:
实例化:Spring容器通过反射机制创建一个Bean的实例。
属性赋值:Spring容器为Bean的属性赋值,可以通过XML配置或注解方式。
初始化:Spring容器调用Bean的初始化方法,可以通过XML配置或注解方式。
使用:Bean可以被其他对象引用,被调用。
销毁:Spring容器调用Bean的销毁方法,可以通过XML配置或注解方式。
(3)Java程序的生命周期与SpringBean的生命周期的区别和联系:
区别:
Java程序的生命周期是从编写代码到程序结束,而SpringBean的生命周期是从实例化到销毁。
Java程序的生命周期是由JVM控制,而SpringBean的生命周期是由Spring容器控制。
联系:
Java程序和SpringBean都有实例化、初始化、使用和销毁的过程。
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配置文件中用于传递构造函数参数的标签,它可以有以下属性:
- index:指定参数在构造函数中的位置,从0开始计数。
- type:指定参数的类型,如果有多个同类型的参数,可以使用该属性区分。
- name:指定参数的名称,可以与构造函数中的参数名对应。
- value:指定参数的值,可以是基本类型、字符串、引用类型等。
- 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数据库的示例:
- 定义一个Java对象
public class User {
private int id;
private String name;
private int age;
// 省略getter和setter方法
}
- 创建一个数据库表
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;
- 使用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);
}
}
- 使用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的持久化操作。