Spring笔记

1.初识Spring

1.1 Spring 简介

Spring是由Rod Johnson组织和开发的一个分层的Java SE/EE full-stack(一站式)轻量级开源框架,它以IoC(Inversoin of Control,控制反转)和AOP(Aspect Oriented Programming,面向切面编程)为内核,使用基本的JavaBean来完成以前只可能由EJB(Enterprise Java Beans,Java企业Bean)完成的工作.取代了EJB的臃肿,低效的开发模式哟 !

1.2 Spring 框架优点

Spring 具有简单,可测试和松耦合等特点,从这个角度出发,Spring可适用于服务器端/任何Java应用的开发.

1.非侵入式设计
2.方便耦合,简化开发
3.支持AOP
4.支持声明式事务处理
5.方便程序的测试
6.方便集成各种优秀的框架
7.降低Java EE API的使用难度

1.3 Spring 核心容器

Spring框架的主要功能是通过其核心容器来实现.Spring框架提供了两种核心容器,分别是BeanFatory和ApplicatoinContext.

BeanFactory
BeanFactory为基础类型的Ioc容器,简单的说就是一个管理Bean的工厂,它主要负责初始化各种Bean,并调用它们的生命周期方法.

ApplicationContext
ApplicationContext是BeanFactory的子接口,也称为应用上下文,不仅包含了BeanFactory的所有功能,还添加了对国际化,资源访问,事件传播等方面的支持.有两种创建ApplicatoinContext接口实例的方法.

通过ClassPathXmlApplicationContext创建 通过类路径classPath中寻找指定的XML配置文件,找到并装载完成ApplicationContext的实例化工作.

通过FileSystemXmlApplicatoinContext创建 通过指定的文件系统路径(绝对路径)中寻找指定的XML配置文件,找到并装载完成ApplicationContext的实例化工作.

1.4 依赖注入

依赖注入的作用就是在使用Spring框架创建对象时,动态地将其所依赖的对象注入Bean组件中,其实现方式通常有两种,一种是属性setter方法注入,另一种是构造方法注入.

属性setter方法注入
指Spring容器使用setter方法注入被依赖的实例.通过调用无参的构造器或无参静态工厂方法实例化Bean后,调用该Bean的setter方法,即可实现基于setter方法的依赖注入.

构造方法注入
指Spring容器使用的构造方法注入被依赖的实例.基于构造方法的依赖注入通过带参数的构造方法来实现,每个参数代表着一个依赖.

1.5 Spring 简单示例程序

创建一个简单的Java bean

public class HiSpring5 {

	private String name;

	public HiSpring5() {
		System.out.println("The Constructor  be called !");
	}

	public void setName(String name) {
		System.out.println("setName(String name) method be called !");
		this.name = name;
	}

	public void ouputName() {
		System.out.println("ouputName() method: my name is " + name + " !");
	}
}

创建Spring的配置文件: 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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!--
	id: IOC容器中Bean的唯一标识
	class: 通过反射机制在IOC容器中创建Bean(所以要求Bean中必须有无参的构造器).
	-->
	<bean id="SpringTestID" class="具体包名">
		<!-- 初始化HiSpring类中的属性名为name的值 -->
		<property name="name" value="Spring"></property>
	</bean>

</beans>

测试类

public class Spring5Test {

	@Test
	public void test() {

		
        // 创建Spring的IOC对象
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

		// 从IOC容器中获取Bean实例
		HiSpring5 hiSpring5 = (HiSpring5) applicationContext.getBean("SpringTestID");
        
		hiSpring5.ouputName();

	}

}

程序运行结果如下

The Constructor  be called !
setName(String name) method be called !
ouputName() method: my name is Spring !

2.Spring注入

简单来说,依赖注入的情况如下 :

public class A {
    public void importantMethod() {
        B b = ... // get an instance of B
        b.usefulMethod();
        ...
    }
    ...
}

如果要使用B,类A必须先获得组件B的实例引用,若B是一个具体类,则可以通过new关键字直接创建组件B实例.但是,如果B是接口,且有多个实现,则问题就变得复杂了,我们固然可以任意选择接口B的一个实现类,但这也意味着A的可重用性大大降低了,因为无法采用B的其他实现.

依赖注入是这样处理此类情景的 : 接管对象的创建工作,并将该对象的引用注入需要该对象的组件.以上述例子为例,依赖注入框架会分别创建对象A和对象B,将对象B注入到对象A中.为了能让框架进行依赖注入,程序员需要编写特定的set方法或构建方法,例如: 为了能将B注入到A中,类A会被修改成如下形式 :

public class A {
    private B b;
    public void importantMethod() {
        // no need to worry about creating B anymore.
        // B b = ... // get an instance of B.
        b.usefulMethod();
        ...
    }
    public void setB(B b) {
        this.b = b;
    }
}

修改后的类A新增了一个setter方法,该方法将会被框架调用,以注入一个B的实例.由于对象依赖由依赖注入,类A的importantMethod()方法不再需要在调用B的usefulMethod()方法前去创建一个B的实例.

当然,也可以采用构造器方式注入,如下所示 : (本例中,Spring会先创建B的实例,再创建实例A,然后把B注入到实例A中.)

public class A {
    private B b;

    public A(B b){
        this.b = b;
    }
    public void importantMethod() {
        // no need to worry about creating B anymore.
        // B b = ... // get an instance of B.
        b.usefulMethod();
        ...
    }
}

注意 : Spring 管理的对象称为beans .

通过提供一个控制反转器(或者依赖注入器IOC),Spring为我们提供一种可以’聪明’地管理Java对象的依赖关系的方法.
其优雅之处在于 : 程序员无须了解Spring框架的存在,更不需要引入任何Spring类型.
使用Spring,程序几乎将所有重要对象的创建工作移交给了Spring,并配置如何注入依赖.Spring支持XML注解两种配置方式.此外还需要创建是一个ApplicationContext对象,其代表一个Spring控制反转器,org.springframework. context.ApplicationContext接口有多个实现,包括ClassPathXmlApplicationContextFileSystemXmlApplicationContext.
这两个实现都需要至少一个包含beans信息的XML文件.

ClassPathXmlApplicationContext : 在类加载路径中加载配置文件.
FileSystemXmlApplicationContext : 在文件系统路径中加载文件.

下面为从类路径中加载applicationContext1.xml和applicationContext2.xml的ApplicationContext创建的一个代码示例 :

ApplicationContext context = new ClassPathXmlApplicationContext(
    new String[]{"applicationContext1.xml","applicationContext2.xml"})

//可以通过调用`ApplicationContext`的`getBean`方法获得对象
//getBean方法会查询`id`为`product`且类型为`Product`的`bean`对象.
Product product = context.getBean("product",Product.class);

3.Spring Bean

3.1 Bean 的配置

简介 : Spring可以被看成一个大型工厂,其作用是生产和管理Spring容器中的Bean,前提是需要在Sring配置文件中进行配置. Spring容器支持XML和Properties两种格式的配置文件.其前者最为常用哟! 在配置文件中,通常为一个Bean配置id或name和class两个属性即可,注意的是如果Bean中未指定id和name,则Spring会将class值当做id使用.

3.2 Bean 的实例化

简介 : 在Spring中,想要使用容器中的Bean时需要先将其实例化,其实例化的方式有三种,如下所示.

构造器实例化(最常用) : Spring容器通过Bean对应类中默认的无参构造方法来实例化Bean.
静态工厂方式实例化 : 将Bean配置中的class属性指定静态工厂类,然后使用factory-method属性来指定所创建的静态工厂方法.
实例工厂方式实例化 : 将需要实例化的Bean通过factory-bean属性指向配置的实例工厂,然后使用factory-method属性确定使用工厂中的具体方法.

Bean 的实例化案例
通过一个示例程序来演示实例化Bean的三种方式.

  1. Bean类
//构造器实例化
public class Bean1 {

}

//静态工厂方式实例化  
public class Bean2 {

}

//实例工厂方式实例化
public class Bean3 {

}

2.MyBean2Factory.java : 静态工厂

public class MyBean2Factory {

	public static Bean2 createBean() {
		return new Bean2();
	}
}

3.MyBean3Factory.java : 实例工厂

public class MyBean3Factory {

	public MyBean3Factory() {
        System.out.println("this is constructor ~");
	}

	public Bean3 createBean() {
		return new Bean3();
	}
}

4.applicationContext.xml : 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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- 构造器实例化 -->
	<bean id="bean1" class="完全限定名"/>

	<!-- 静态工厂方式实例化 -->
	<!-- factory-method: 指定工厂方法 -->
	<bean id="bean2" class="完全限定名" factory-method="createBean"/>

	<!-- 实例工厂方式实例化 -->
	<!-- factory-bean:指定配置的实例工厂 -->
	<bean id="myBean3Factory" class="完全限定名"/>
	<bean id="bean3" factory-bean="myBean3Factory" factory-method="createBean"/>
	
</beans>

5.InstanceTest.java : 测试类

public class InstanceTest {

	private static ApplicationContext applicationContext;

	@BeforeClass
	public static void init() {
		applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
	}

	@Test
	@Ignore
	// 构造器实例化
	public void instanceTest1() {
		Bean1 bean1 = applicationContext.getBean(Bean1.class);

		System.out.println(bean1);
	}

	@Test
	@Ignore
	// 静态工厂方式实例化
	public void instanceTest2() {
		System.out.println(applicationContext.getBean("bean2"));
	}

	@Test
	@Ignore
	// 实例工厂方式实例化
	public void instanceTest3() {
		System.out.println(applicationContext.getBean("bean3"));
	}

}

3.3 Bean 的作用域

简介 : 在Spring 4.3中为Bean定义了七种作用域,如下所示.

1.Singleton(单例) : 使用Singleton定义的Bean在Spring容器中将只有一个实例,也就是说,无论有多少个Bean引用它,始终指向同一个对象,其也是Spring默认的作用域.
2.prototype(原型) : 每次通过Spring容器获取prototype定义的Bean时,容器都将创建一个新的Bean实例.
3.request : …
4.session : …
5.globalSession : …
6.application : …
7.websocket : …

Bean 的作用域案例
下面通过简单程序来分别演示singleton和prototype作用域

1.Bean类

//Bean的作用域: singleton 
public class Bean4 {
}


//Bean的作用域: prototype
public class Bean5 {
}

2.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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- singleton(default) -->
	<bean id="bean4" class="完全限定名" scope="singleton"/>
	<!-- prototype -->
	<bean id="bean5" class="完全限定名" scope="prototype"/>

</beans>

3.测试类

public class ScopeTest {

	private static ApplicationContext applicationContext;

	@BeforeClass
	public static void init() {
		applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
	}

	@Test
	@Ignore
	// Bean的作用域: singleton
	public void singletonTest() {
		System.out.println(applicationContext.getBean("bean4"));
		System.out.println(applicationContext.getBean("bean4"));
	}

	@Test
	@Ignore
	// Bean的作用域: prototype
	public void prototypeTest() {
		System.out.println(applicationContext.getBean("bean5"));
	}

}

3.4 Bean 的装配方式

简介 : Bean的装配方式可以理解为依赖注入,Bean的装配方式既Bean依赖注入的方式. Spring容器支持多种形式的Bean的装配方式,如基于XML的装配,基于Annotation的装配等…

基于XML的装配
Spring提供了两种基于XML的装配方式: 设值注入(Setter Injection)和构造注入(Constructor Injection). 在Spring实例化Bean的过程中,Spring首先会调用Bean的默认构造方法来实例化Bean对象,然后通过反射的方式调用setter方式来注入属性值.因此,设值注入要求一个Bean必须满足一下两点要求.

  • 1.Bean类必须提供一个默认的无参构造方法.
  • 2.Bean类必须为需要注入的属性提供对应的setter方法.

下面通过一个简单示例程序来展示基于XML的Bean的装配方式.

1.User.java : Java bean

public class User {

	private String name;
	private List<String> phone;

	public User() {

	}

	public User(String name, List<String> phone) {
		super();
		this.name = name;
		this.phone = phone;
	}

	public String getName() {
		return name;
	}

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

	public List<String> getPhone() {
		return phone;
	}

	public void setPhone(List<String> phone) {
		this.phone = phone;
	}

	@Override
	public String toString() {
		return "User [name=" + name + ", phone=" + phone + "]";
	}

}

2.applicationContext.xml : 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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	 <!-- 利用构造注入方式装配User实例 -->
	 <bean id="user1" class="完全限定名">
	 	<constructor-arg index="0" value="YUbuntu0109-1"/>
	 	<constructor-arg index="1">
	 		<list>
	 			<value>"15111111111"</value>
	 			<value>"15211111111</value>
	 		</list>
	 	</constructor-arg>
	 </bean>
	 <!-- 使用设值注入方式装配User实例 -->
	 <bean id="user2" class="完全限定名">
	 	<property name="name" value="YUbuntu0109-2"/>
	 	<property name="phone">
	 		<list>
	 			<value>"13111111111"</value>
	 			<value>"18111111111"</value>
	 		</list>
	 	</property>
	 </bean>
</beans>

3.AssembleTest.java : 测试类

public class AssembleTest {

	private static ApplicationContext applicationContext;

	@BeforeClass
	public static void init() {
		applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
	}

	@Test
	// @Ignore
	// 测试基于XML的Bean配置
	public void xmlBeanAssembleTest() {

		// result: User [name=YUbuntu0109-1, phone=["15111111111", "15211111111]]
		System.out.println(applicationContext.getBean("user1"));
		// result: User [name=YUbuntu0109-2, phone=["13111111111", "18111111111"]]
		System.out.println(applicationContext.getBean("user2"));
	}

}

基于Annotation的装配
简介 : 通过注解(Annotation)来实现Bean的装配工作可以解决XML配置文件过于臃肿的问题,且便于后期维护. 其常用注解如下.

@Repository : 用于将数据访问层(DAO层)的类标识为Spring中的Bean.
@Service : 通常作用在业务层(Service层),用于将业务层的类标识为Spring中的Bean.
@Controller : 通过作用在控制层(如Spring MVC的Controller),用于将控制层的类标识为Spring中的Bean.
@Autowired : 用于对Bean的属性变量,属性的setter方法及构造方法进行标注,配合对用的注解处理器来完成Bean的自动配置工作.
下面通过一个简单案例来演示如何通过这些注解来装配Bean.

1.UserDao.java

public interface UserDao {

	public void save();
}

2.UserDaoImple.java

@Repository("userDao") // 将UserDaoImpl类标识为Spring中的Bean
public class UserDaoImpl implements UserDao {

	@Override
	public void save() {
		System.out.println("UserDao : Save function ~");
	}

}

其中使用@Respository注解将UserDaoImpl类标识为Spring中的Bean,其写法相当于配置文件中的

<bean id="userDao" class="完全限定名"/>

3.UserService.java

public interface UserService {

	public void save();
}

4.UserServiceImpl.java

@Service // 将UserServiceImpl类标识为Spring中的Bean
public class UserServiceImpl implements UserService {

	@Autowired // 自动配置Bean
	private UserDao userDao;

	@Override
	public void save() {
		userDao.save();
		System.out.println("UserService : Save function ~");
	}

}

5.UserController.java

@Repository /// 将UserController类标识为Spring中的Bean
public class UserController {

	@Autowired // 自动配置Bean
	private UserService userService;

	public void save() {
		userService.save();
		System.out.println("UserController : Save function ~");
	}

}

6.AssembleTest.java : 测试类


public class AssembleTest {

	private static ApplicationContext applicationContext;

	@BeforeClass
	public static void init() {
		applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
	}

	@Test
	public void annotationAssembleTest() {
		UserController userController = (UserController) applicationContext.getBean("userController");
		userController.save();
	}

}

7.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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
	 
	 <!-- 扫描指定包下的所有Bean类,进行注解解析 -->
	 <context:component-scan base-package="要扫描的具体包名"/>
	 
</beans>

8.程序运行结果

UserDao : Save function ~
UserService : Save function ~
UserController : Save function ~

4.Spring AOP

4.1 什么是 AOP

1.面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
2.通俗描述:不通过修改源代码方式,在主干功能里面添加新功能

4.2 AOP中的相关概念

  • Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
  • Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
  • Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
  • Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
  • Target(目标对象):织入 Advice 的目标对象.。
  • Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程
    在这里插入图片描述

4.3 AOP使用场景

AOP用来封装横切关注点,具体可以在下面的场景中使用:

Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging  调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence  持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务

5.Spring 数据库开发

Spring JDBC
简介 : Spring框架降低了Java EE API的使用难度,例如JDBC. Spring的JDBC模块负责数据库资源管理和错误处理,简化了开发人员对数据库的操作,从而将更多的精力投入到编写业务逻辑中.

Spring JdbcTemplate 的解析
针对数据库操作,Spring框架提供了JdbcTemplate类,该类是Spring框架数据抽象层的基础,Spring JDBC的核心类. 它继承自抽象类JdbcAccessor,同时实现了JdbcOperations接口.

  • 1.JdbcAccessor : 该类为子类提供了一些访问是数据库时使用的公共属性.
  • 2.JdbcOperations : 该接口定义了在JdbcTemplate类中可以使用的操作集合,包括增删改查等操作.

Spring JDBC 的配置
Spring JDBC模块主要由4个包组成,如下所示哟 ~

1.core(核心包) : 包含了JDBC的核心功能,包括JdbcTemplate,SimpleJdbcInsert,SimpleJdbcCall类,以及NamedParameterJdbcTemplate类.
2.dataSource(数据源包) : 访问数据源的实用工具类,它有多种数据源的实现,可以在Java EE容器外部测试JDBC代码.
3.object(对象包) : 以面向对象的方式访问数据库,它允许执行查询并将返回结果作为业务对象,可以在数据表的列和业务对象的属性之间映射查询结果.
4.support(支持包) : 包含了core和object包的支持类,例如: 提供异常转换功能的SQLException类.

由此可知,Spring对数据库的操作都封装在这几个包中,Spring JDBC的配置是在配置文件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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- 1: 配置数据源 -->
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<!-- 数据库驱动 -->
		<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
		<!-- 连接数据库的url -->
		<property name="url"
			value="jdbc:mysql://localhost:3306/xxxxxx"/>
		<!-- 连接数据库的用户名 -->
		<property name="username" value="xxxxxx" />
		<!-- 连接数据库的密码 -->
		<property name="password" value="xxxxxx" />
	</bean>

	<!-- 2: 配置JDBC模板 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<!-- 默认必须使用数据源 -->
		<property name="dataSource" ref="dataSource" />
	</bean>

	<!-- 3: 配置注入类 -->
	<bean id="xxxxxx" class="xxxxxx">
		<property name="jdbcTemplate" ref="jdbcTemplate" />
	</bean>

    ······
</beans>

定义JdbcTemplate时,需要将dataSource注入到JdbcTemplate中,而其它需要使用JdbcTemplate的Bean,也需要将JdbcTemplate注入到该Bean中(通常注入到Dao类中,在Dao类中进行与数据库的相关操作).

Spring JdbcTempalte 的常用方法
JdbcTemplate类中提供了大量的操作数据库的方法,下面通过一个简单的CURE来体现一下Spring JDBC代码的简洁美 ~

1.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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- 1: 配置数据源 -->
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<!-- 数据库驱动 -->
		<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
		<!-- 连接数据库的url -->
		<property name="url" value="jdbc:mysql://localhost/Spring?useSSL=false&amp;serverTimezone=UTC&amp;allowPublicKeyRetrieval=true" />
		<!-- 连接数据库的用户名 -->
		<property name="username" value="xxxxxx" />
		<!-- 连接数据库的密码 -->
		<property name="password" value="xxxxxx" />
	</bean>

	<!-- 2: 配置JDBC模板 -->
	<bean id="jdbcTemplate"
		class="org.springframework.jdbc.core.JdbcTemplate">
		<!-- 默认必须使用数据源 -->
		<property name="dataSource" ref="dataSource" />
	</bean>

	<!-- 3: 配置注入类 -->
	<bean id="accountDao" class="pers.huangyuhui.spring.jdbc.dao.impl.AccountDaoImpl">
		<!-- 将jbdcTemplate注入到accountDao实例中 -->
		<property name="jdbcTemplate" ref="jdbcTemplate" />
	</bean>

</beans>

2.Account.java : 封装用户账户信息

public class Account {

	private Integer id;
	private String username;
	private Double balance;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public Double getBalance() {
		return balance;
	}

	public void setBalance(Double balance) {
		this.balance = balance;
	}

	@Override
	public String toString() {
		return "Account [id=" + id + ", username=" + username + ", balance=" + balance + "]";
	}

}

3.AccountDao.java : 操作账户信息的接口

public interface AccountDao {

	/**
	 * @Title: createTable
	 * @Description: 创建数据表
	 * @param: sql
	 * @return: void
	 */
	public void createTable(String sql);

	/**
	 * @Title: findAccountById
	 * @Description: 查找表数据
	 * @param: id
	 * @return: Account
	 */
	public Account findAccountById(int id);

	/**
	 * @Title: findAllAccount
	 * @Description: 查找全部表数据
	 * @return: List<Account>
	 */
	public List<Account> findAllAccount();

	/**
	 * @Title: addAccount
	 * @Description: 添加表数据
	 * @param: account
	 * @return: int
	 */
	public int addAccount(Account account);

	/**
	 * @Title: updateAccount
	 * @Description: 更新表数据
	 * @param: account
	 * @return: int
	 */
	public int updateAccount(Account account);

	/**
	 * @Title: deleteAccount
	 * @Description: 删除表数据
	 * @param: id
	 * @return: int
	 */
	public int deleteAccount(int id);
}

4.AccountDaoImpl.java : AccountDao的实现类

public class AccountDaoImpl implements AccountDao {

	// 声明JdbcTemplate属性及其setter方法
	private JdbcTemplate jdbcTemplate;

	// 获取JdbcTemplate实例
	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	@Override
	public void createTable(String sql) {

		jdbcTemplate.execute(sql);
	}

	@Override
	public int addAccount(Account account) {
		String sql = "insert into account(username,balance) value(?,?)";

		// 定义数组来存储SQL语句中的参数. Good idea ~
		Object[] objects = new Object[] { account.getUsername(), account.getBalance() };

		// 执行添加操作,返回受SQL语句影响的条数
		return jdbcTemplate.update(sql, objects);
	}

	@Override
	public int updateAccount(Account account) {
		String sql = "update account set username=? , balance=?  where id = ?";

		// 注意: `?`需与设置的参数顺序对应哟 !
		Object[] objects = new Object[] { account.getUsername(), account.getBalance(), account.getId() };

		return this.jdbcTemplate.update(sql, objects);
	}

	@Override
	public int deleteAccount(int id) {
		String sql = "delete from account where id = ?";

		return this.jdbcTemplate.update(sql, id);
	}

	@Override
	public Account findAccountById(int id) {
		String sql = "select id,username,balance from account where id = ?";

		// 创建BeanPropertyRowMapper对象
		// 它可以自动地将数据表中的数据映射到用户自定义的类中(前提是:用户自定义类中的字段要与数据表中的字段相对应)
		RowMapper<Account> rowMapper = new BeanPropertyRowMapper<Account>(Account.class);

		// 将id绑定到SQL语句中,并通过RowMapper返回一个Object类型的单行记录
		return this.jdbcTemplate.queryForObject(sql, rowMapper, id);
	}

	@Override
	public List<Account> findAllAccount() {
		String sql = "select id,username,balance from account";

		RowMapper<Account> rowMapper = new BeanPropertyRowMapper<Account>(Account.class);

		// 执行静态的SQL查询,并通过RowMapper返回结果集
		return this.jdbcTemplate.query(sql, rowMapper);

	}

}

5.CURETest.java : 测试类

public class CURDTest {

	private static Account account;
	private static AccountDao accountDao;
	private static ApplicationContext applicationContext;

	@BeforeClass
	public static void init() {
		account = new Account();
		// 加载配置文件
		applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
		// 获取AccountDao实例
		accountDao = (AccountDao) applicationContext.getBean("accountDao");
	}

	@Test
	// 创建用户账户信息数据表
	public void createTableTest() {
		String sql = 
				"create table account\r\n" + 
				"(\r\n" + 
				"	id int primary key auto_increment,\r\n" + 
				"	username varchar(10) not null,\r\n" + 
				"	balance double not null\r\n" + 
				")";

		accountDao.createTable(sql);
		System.out.println("success to create the table of account ~");
	}

	@Ignore
	@Test
	// 添加账户信息
	public void addAccountTest() {

		// 向Account对象中添加数据
		account.setUsername("YUbuntu0109");
		account.setBalance(666666.0);

		// 获取添加操作返回的结果
		int num = accountDao.addAccount(account);
		if (num > 0) {
			System.out.println("成功添加了 " + num + " 条数据哟 ~");
		} else {
			System.out.println("添加数据失败 !");
		}

	}

	@Ignore
	@Test
	// 更新指定账户信息
	public void updateAccountTest() {

		account.setId(3);
		account.setUsername("update");
		account.setBalance(999999.0);

		int num = accountDao.updateAccount(account);
		if (num > 0) {
			System.out.println("更新成功了 " + num + " 条数据哟 ~");
		} else {
			System.out.println("更新数据失败 ! ");
		}

	}

	@Ignore
	@Test
	// 删除指定账户信息
	public void deleteAccountTest() {

		int num = accountDao.deleteAccount(1);
		if (num > 0) {
			System.out.println("删除了 " + num + " 条数据哟 ~");
		} else {
			System.out.println("删除数据失败 !");
		}
	}

	@Ignore
	@Test
	// 查询指定账户信息
	public void findAccountByIdTest() {
		account = accountDao.findAccountById(2);

		System.out.println(account);
	}

	@Ignore
	@Test
	// 查询所有账户信息
	public void findAllAccount() {
		List<Account> accountInfo = accountDao.findAllAccount();

		System.out.println(accountInfo);

	}

}

6.Spring 事务管理

6.1事务管理

一个数据库事务是一个被视为单一的工作单元的操作序列。这些操作应该要么完整地执行,要么完全不执行。事务管理是一个重要组成部分,RDBMS 面向企业应用程序,以确保数据完整性和一致性。事务的概念可以描述为具有以下四个关键属性说成是 ACID:

1.原子性:事务应该当作一个单独单元的操作,这意味着整个序列操作要么是成功,要么是失败的。
2.一致性:这表示数据库的引用完整性的一致性,表中唯一的主键等。
3.隔离性:可能同时处理很多有相同的数据集的事务,每个事务应该与其他事务隔离,以防止数据损坏。
4.持久性:一个事务一旦完成全部操作后,这个事务的结果必须是永久性的,不能因系统故障而从数据库中删除。

一个真正的 RDBMS 数据库系统将为每个事务保证所有的四个属性。使用 SQL 发布到数据库中的事务的简单视图如下:

1.使用 begin transaction 命令开始事务。
2.使用 SQL 查询语句执行各种删除、更新或插入操作。
3.如果所有的操作都成功,则执行提交操作,否则回滚所有操作。

Spring 框架在不同的底层事务管理 APIs 的顶部提供了一个抽象层。Spring 的事务支持旨在通过添加事务能力到 POJOs 来提供给 EJB 事务一个选择方案。Spring 支持编程式和声明式事务管理。EJBs 需要一个应用程序服务器,但 Spring 事务管理可以在不需要应用程序服务器的情况下实现。

6.2 局部事务 vs. 全局事务

局部事务是特定于一个单一的事务资源,如一个 JDBC 连接,而全局事务可以跨多个事务资源事务,如在一个分布式系统中的事务。

局部事务管理在一个集中的计算环境中是有用的,该计算环境中应用程序组件和资源位于一个单位点,而事务管理只涉及到一个运行在一个单一机器中的本地数据管理器。局部事务更容易实现。

全局事务管理需要在分布式计算环境中,所有的资源都分布在多个系统中。在这种情况下事务管理需要同时在局部和全局范围内进行。分布式或全局事务跨多个系统执行,它的执行需要全局事务管理系统和所有相关系统的局部数据管理人员之间的协调。

6.3 编程式 vs. 声明式

Spring 支持两种类型的事务管理:

编程式事务管理 :这意味着你在编程的帮助下有管理事务。这给了你极大的灵活性,但却很难维护。
声明式事务管理 :这意味着你从业务代码中分离事务管理。你仅仅使用注释或 XML 配置来管理事务。

声明式事务管理比编程式事务管理更可取,尽管它不如编程式事务管理灵活,但它允许你通过代码控制事务。但作为一种横切关注点,声明式事务管理可以使用 AOP 方法进行模块化。Spring 支持使用 Spring AOP 框架的声明式事务管理。

6.4 Spring 事务抽象

Spring事务管理的五大属性:隔离级别、传播行为、是否只读、事务超时、回滚规则

Spring 事务抽象的关键是由 org.springframework.transaction.PlatformTransactionManager 接口定义,如下所示:

public interface PlatformTransactionManager {
   TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
   void commit(TransactionStatus status) throws TransactionException;
   void rollback(TransactionStatus status) throws TransactionException;
}
序号方法 & 描述
1TransactionStatus getTransaction(TransactionDefinition definition)
根据指定的传播行为,该方法返回当前活动事务或创建一个新的事务。
2void commit(TransactionStatus status)
该方法提交给定的事务和关于它的状态。
3void rollback(TransactionStatus status)
该方法执行一个给定事务的回滚。

TransactionDefinition 是在 Spring 中事务支持的核心接口,它的定义如下:

public interface TransactionDefinition {
   int getPropagationBehavior();
   int getIsolationLevel();
   String getName();
   int getTimeout();
   boolean isReadOnly();
}
序号方法 & 描述
1int getPropagationBehavior()
该方法返回传播行为。Spring 提供了与 EJB CMT 类似的所有的事务传播选项。
2int getIsolationLevel()
该方法返回该事务独立于其他事务的工作的程度。
3String getName()
该方法返回该事务的名称。
4int getTimeout()
该方法返回以秒为单位的时间间隔,事务必须在该时间间隔内完成。
5boolean isReadOnly()
该方法返回该事务是否是只读的。

下面是隔离级别的可能值:

序号隔离 & 描述
1TransactionDefinition.ISOLATION_DEFAULT
这是默认的隔离级别。
2TransactionDefinition.ISOLATION_READ_COMMITTED
表明能够阻止误读;可以发生不可重复读和虚读。
3TransactionDefinition.ISOLATION_READ_UNCOMMITTED
表明可以发生误读、不可重复读和虚读。
4TransactionDefinition.ISOLATION_REPEATABLE_READ
表明能够阻止误读和不可重复读;可以发生虚读。
5TransactionDefinition.ISOLATION_SERIALIZABLE
表明能够阻止误读、不可重复读和虚读。

下面是传播类型的可能值:

序号传播 & 描述
1TransactionDefinition.PROPAGATION_MANDATORY
支持当前事务;如果不存在当前事务,则抛出一个异常。
2TransactionDefinition.PROPAGATION_NESTED
如果存在当前事务,则在一个嵌套的事务中执行。
3TransactionDefinition.PROPAGATION_NEVER
不支持当前事务;如果存在当前事务,则抛出一个异常。
4TransactionDefinition.PROPAGATION_NOT_SUPPORTED
不支持当前事务;而总是执行非事务性。
5TransactionDefinition.PROPAGATION_REQUIRED
支持当前事务;如果不存在事务,则创建一个新的事务。
6TransactionDefinition.PROPAGATION_REQUIRES_NEW
创建一个新事务,如果存在一个事务,则把当前事务挂起。
7TransactionDefinition.PROPAGATION_SUPPORTS
支持当前事务;如果不存在,则执行非事务性。
8TransactionDefinition.TIMEOUT_DEFAULT
使用默认超时的底层事务系统,或者如果不支持超时则没有。

TransactionStatus 接口为事务代码提供了一个简单的方法来控制事务的执行和查询事务状态。

public interface TransactionStatus extends SavepointManager {
   boolean isNewTransaction();
   boolean hasSavepoint();
   void setRollbackOnly();
   boolean isRollbackOnly();
   boolean isCompleted();
}
序号方法 & 描述
1boolean hasSavepoint()
该方法返回该事务内部是否有一个保存点,也就是说,基于一个保存点已经创建了嵌套事务。
2boolean isCompleted()
该方法返回该事务是否完成,也就是说,它是否已经提交或回滚。
3boolean isNewTransaction()
在当前事务时新的情况下,该方法返回 true。
4boolean isRollbackOnly()
该方法返回该事务是否已标记为 rollback-only。
5void setRollbackOnly()
该方法设置该事务为 rollback-only 标记。

6.5 事务管理实现

基于配置文件实现事务管理

<!--1 创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<!--注入数据源-->
	<property name="dataSource" ref="dataSource"></property>
</bean>
<!--2 配置通知-->
<tx:advice id="txadvice">
	<!--配置事务参数-->
	<tx:attributes>
		<!--指定哪种规则的方法上面添加事务-->
		<tx:method name="accountMoney" propagation="REQUIRED"/>
		<!--<tx:method name="account*"/>-->
	</tx:attributes>
</tx:advice>
<!--3 配置切入点和切面-->
<aop:config>
	<!--配置切入点-->
	<aop:pointcut id="pt" expression="execution(* spring5.service.UserService.*(..))"/>
	 <!--配置切面-->
	 <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>

基于注解

创建配置类,使用配置类替代 xml 配置文件
@Configuration //配置类
@ComponentScan(basePackages = "集体包名") //组件扫描
@EnableTransactionManagement //开启事务
public class TxConfig {
	 //创建数据库连接池
	 @Bean
	 public DruidDataSource getDruidDataSource() {
		 DruidDataSource dataSource = new DruidDataSource();
		 dataSource.setDriverClassName("com.mysql.jdbc.Driver");
		 dataSource.setUrl("jdbc:mysql:///数据库名");
		 dataSource.setUsername("**");
		 dataSource.setPassword("**");
		 return dataSource;
	 }
	 //创建 JdbcTemplate 对象
	 @Bean
	 public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
		 //到 ioc 容器中根据类型找到 dataSource
		 JdbcTemplate jdbcTemplate = new JdbcTemplate();
		 //注入 dataSource
		 jdbcTemplate.setDataSource(dataSource);
		 return jdbcTemplate;
	 }
	 //创建事务管理器
	 @Bean
	 public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
		 DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
		 transactionManager.setDataSource(dataSource);
		 return transactionManager;
	 }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Listen·Rain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值