Spring 概述
TIPS:
建议去 Spring 官网 学习!!!
Spring 是什么 Spring
是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control:反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核, 提供了展现层 SpringMVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的 JavaEE 企业应用开源框架。
Spring 的发展历程 Spring 是从 EJB 时代发展过来的,它剔除了 EJB 的很多诟病。 所以,我们需要了解一下它的发展历程,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 + 1997年 IBM 提出了 EJB 的思想 + 1998年,SUN 制定开发标准规范 EJB1.0 + 1999年,EJB1.1 发布 + 2001年,EJB2.0 发布 + 2003年,EJB2.1 发布 + 2006年,EJB3.0 发布 + Rod Johnson(Spring 之父) + Expert One-to-One J2EE Design and Development(2002) 阐述了 J2EE 使用 EJB 开发设计的优点及解决方案 + Expert One-to-One J2EE Development without EJB(2004) 阐述了 J2EE 开发不使用 EJB 的解决方式(Spring 雏形) + 2017 年 9 月份发布了 Spring 的最新版本 Spring 5.0 通用版(GA)
Spring 的优势 那么我们学习 Spring 有什么好处呢?那么接下来就介绍一下 Spring 的优势。 其优势如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 + 通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合。 + 用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。 + 通过 Spring 的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付。 + 可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。 + 可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。 + Spring 可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz 等)的直接支持。 + Spring 对 JavaEE-API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的使用难度大为降低。 + Spring 的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对 Java 设计模式灵活运用以及对 Java 技术的高深造诣。 + 它的源代码无疑是 Java 技术的最佳实践的范例。
Spring 的体系结构 既然 Spring 如此重要,那就先来看看它的体系结构。 如下图:
由上图可知,Spring
是由多个模块组成的。那么它们的具体功能是干什么的呢? 详情如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 + Spring 的核心容器是其他模块建立的基础,有 Spring-core、Spring-beans、Spring-context、Spring-context-support 和 Spring-expression(String 表达式语言)等模块组成。 + Spring-core 模块:提供了框架的基本组成部分,包括控制反转(Inversion of Control,IOC)和依赖注入(Dependency Injection,DI)功能。 + Spring-beans 模块:提供了 BeanFactory,是工厂模式的一个经典实现,Spring 将管理对象称为 Bean 。 + Spring-context 模块:建立在 Core 和 Beans 模块的基础之上,提供一个框架式的对象访问方式,是访问定义和配置的任何对象的媒介。ApplicationContext 接口是 Context 模块的焦点。 + Spring-context-support 模块:支持整合第三方库到Spring应用程序上下文,特别是用于高速缓存(EhCache、JCache)和任务调度(CommonJ、Quartz)的支持。 + Spring-expression 模块:提供了强大的表达式语言去支持运行时查询和操作对象图。这是对JSP2.1规范中规定的统一表达式语言(Unified EL)的扩展。 + 该语言支持设置和获取属性值、属性分配、方法调用、访问数组、集合和索引器的内容、逻辑和算术运算、变量命名以及从Spring的IOC容器中以名称检索对象。 + 它还支持列表投影、选择以及常用的列表聚合。 + Spring-aop 模块:提供了一个符合 AOP 要求的面向切面的编程实现,允许定义方法拦截器和切入点,将代码按照功能进行分离,以便干净地解耦。 + Spring-aspects 模块:提供了与 AspectJ 的集成功能,AspectJ 是一个功能强大且成熟的 AOP 框架。 + Spring-instrument 模块:提供了类植入(Instrumentation)支持和类加载器的实现,可以在特定的应用服务器中使用。 + Spring4.0 以后新增了消息(Spring-messaging)模块,该模块提供了对消息传递体系结构和协议的支持。 + 数据访问/集成层由 JDBC、ORM、OXM、JMS 和事务模块组成。 + Spring-jdbc 模块:提供了一个 JDBC 的抽象层,消除了烦琐的 JDBC 编码和数据库厂商特有的错误代码解析。 + Spring-orm 模块:为流行的对象关系映射(Object-Relational Mapping)API 提供集成层,包括 JPA 和 Hibernate。 + 使用 Spring-orm 模块可以将这些 O/R 映射框架与 Spring 提供的所有其他功能结合使用,例如声明式事务管理功能。 + Spring-oxm 模块:提供了一个支持 对象/XML 映射的抽象层实现,例如 JAXB、Castor、JiBX 和 XStream。 + Spring-jms 模块(Java Messaging Service):指 Java 消息传递服务,包含用于生产和使用消息的功能。自 Spring4.1 以后,提供了与 Spring-messaging 模块的集成。 + Spring-tx模块(事务模块):支持用于实现特殊接口和所有 POJO(普通 Java 对象)类的编程和声明式事务管理。 + Web 层由 Spring-web、Spring-webmvc、Spring-websocket 和 Portlet 模块组成。 + Spring-web 模块:提供了基本的 Web 开发集成功能,例如多文件上传功能、使用 Servlet 监听器初始化一个 IOC 容器以及 Web 应用上下文。 + Spring-webmvc 模块:也称为 Web-Servlet 模块,包含用于web应用程序的Spring MVC和REST Web Services实现。 + Spring MVC 框架提供了领域模型代码和 Web 表单之间的清晰分离,并与 Spring Framework 的所有其他功能集成。 + Spring-websocket 模块:Spring4.0 以后新增的模块,它提供了 WebSocket 和 SocketJS 的实现。 + Portlet 模块:类似于 Servlet 模块的功能,提供了 Portlet 环境下的 MVC 实现。 + Spring-test 模块支持使用 JUnit 或 TestNG 对 Spring 组件进行单元测试和集成测试。
工厂模式解耦 在实际开发中,我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法通过读取配置文件,把这些对象创建出来并存起来。 在接下来使用的时候,直接拿过来用就好了。那么,这个读取配置文件、创建和获取三层对象的类就是工厂,使用这种方式实现解耦的模式也称之为 工厂模式
。
上述介绍了 工厂模式实现解耦 ,那么就来操作一下。第一步:
新建 Maven 工程,完善包结构。 如下图:
第二步:
导入如下依赖到 pom.xml
文件中。 依赖如下:
pom.xml 1 2 3 4 5 6 7 8 9 10 <packaging > jar</packaging > <dependencies > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.47</version > </dependency > </dependencies >
第三步:
在 dao
包下新建 IAccountDao
接口,然后在 dao/impl
包下新建 AccountDaoImpl
实现类。 代码如下:
IAccountDao.java 1 2 3 4 5 6 7 8 9 10 11 12 13 package club.guoshizhan.dao;public interface IAccountDao { void saveAccount () ; }
AccountDaoImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package club.guoshizhan.dao.impl;import club.guoshizhan.dao.IAccountDao;public class AccountDaoImpl implements IAccountDao { public void saveAccount () { System.out.println("保存了账户" ); } }
第四步:
在 service
包下新建 IAccountService
接口,然后在 service/impl
包下新建 AccountServiceImpl
实现类。 代码如下:
IAccountService.java 1 2 3 4 5 6 7 8 9 10 11 12 13 package club.guoshizhan.service;public interface IAccountService { void saveAccount () ; }
AccountServiceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package club.guoshizhan.service.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.factory.BeanFactory;import club.guoshizhan.service.IAccountService;public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao" ); public void saveAccount () { int i = 1 ; accountDao.saveAccount(); System.out.println(i); i++; } }
第五步:
在 resources
目录下新建 bean.properties
配置文件。 代码如下:
bean.properties 1 2 accountService =club.guoshizhan.service.impl.AccountServiceImpl accountDao =club.guoshizhan.dao.impl.AccountDaoImpl
第六步:
在 factory
包下新建 BeanFactory
工厂类。 代码如下:
BeanFactory.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 package club.guoshizhan.factory;import java.io.InputStream;import java.util.Enumeration;import java.util.HashMap;import java.util.Map;import java.util.Properties;public class BeanFactory { private static Properties props; private static Map<String, Object> beans; static { try { props = new Properties(); InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties"); props.load(in); beans = new HashMap<>(); Enumeration keys = props.keys(); while (keys.hasMoreElements()) { String key = keys.nextElement().toString(); String beanPath = props.getProperty(key); Object value = Class.forName(beanPath).newInstance(); beans.put(key, value); } } catch (Exception e) { throw new ExceptionInInitializerError("初始化properties失败!" ); } } public static Object getBean (String beanName) { return beans.get(beanName); } }
第七步:
在 controller
包下新建 Client
类,用于测试我们是否解耦成功。 代码如下:
Client.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package club.guoshizhan.controller;import club.guoshizhan.factory.BeanFactory;import club.guoshizhan.service.IAccountService;public class Client { public static void main (String[] args) { for (int i = 0 ; i < 5 ; i++) { IAccountService as = (IAccountService) BeanFactory.getBean("accountService" ); System.out.println(as); as.saveAccount(); } } }
执行上述 main
方法即可查看到结果。 结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 club.guoshizhan.service.impl.AccountServiceImpl@140e19 d 保存了账户 1 club.guoshizhan.service.impl.AccountServiceImpl@140e19 d 保存了账户 1 club.guoshizhan.service.impl.AccountServiceImpl@140e19 d 保存了账户 1 club.guoshizhan.service.impl.AccountServiceImpl@140e19 d 保存了账户 1 club.guoshizhan.service.impl.AccountServiceImpl@140e19 d 保存了账户 1
由结果可知,工厂模式
解耦成功(我们没有 new 对象)。 那我们再回过头来看如下问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 + 当我们使用 JDBC 的时候,是通过反射来注册驱动的,代码: Class.forName("com.mysql.jdbc.Driver"); // 此处只是一个字符串 + 反射注册驱动的好处是,我们的类中不再依赖具体的驱动类,此时就算删除 MySQL 的 jar 包,依然可以编译(运行就不要想了,没有驱动不可能运行成功的)。 - 但是,也产生了一个新的问题,MySQL 驱动的全限定类名字符串是在 java 类中写死的,一旦需要修改,还是要修改源码。 + 解决这个问题也很简单,使用配置文件配置(xml 或者是 properties)。如我们的 bean.properties 配置文件。 + 在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法通过读取配置文件,把这些对象同时创建出来并存起来。 + 在接下来使用的时候,直接可以把这些保存好了的对象拿过来用。那么,这个读取配置文件、创建和获取三层对象的类就是工厂,这种模式就叫工厂模式。
TIPS:
Spring 概述 到此结束!!!
Spring 中的 IoC 遗留问题 我们已经知道了 工厂模式,因此在学习 IoC
之前,先来解决工厂模式遗留下下来的问题。 问题如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 + 由于工厂中有很多对象,肯定要找个集合来存。这时候有 Map 和 List 供选择。 + 到底选 Map 集合还是 List 集合,就看我们有没有查找需求。如果有查找需求,选 Map 。 + 所以我们的答案就是在应用加载时,创建一个 Map 集合,用于存放三层对象。我们把这个 map 称之为容器。 + 所以,IoC 容器就和我们的 map 是一样的,只不过它比我们的容器更加复杂。 + 工厂就是负责给我们从容器中获取指定对象的类。这时候我们获取对象的方式发生了改变,变成了被动获取对象。 + 而不是我们在程序中去 new 对象(主动获取对象),而是通过工厂去创建对象,并且通过工厂去获取对象。 + 这种被动接收的方式获取对象的思想就是控制反转(IOC),它是 Spring 框架的核心之一。
主动获取对象和被动获取对象 图示如下:
IoC 概念和作用 控制反转
(Inversion of Control,缩写为 IoC
),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。 其中最常见的方式叫做 依赖注入(Dependency Injection,简称 DI) ,还有一种方式叫 依赖查找(Dependency Lookup) 。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
上述介绍来自于 百度百科 ,解释得很官方。以下是通俗一点的解释:
1 2 3 4 5 6 7 8 + 传统的 Java 程序设计过程中,如果一个类依赖与另一个类的话,需要通 new 进行创建对象,这个过程是程序主动去创建依赖对象。 + 而 IoC 是通过一个容器创建这些对象,即有 Ioc 容器来控制对象的创建。即 IoC 容器控制了对象。 + 其次,正常的方法是主动控制获取依赖对象,这个正转;而反转是由容器来创建以及注入依赖对象,这个就是反转!
IoC 的作用:
削减计算机程序的耦合(解除我们代码中的依赖关系),将对象的创建和调用都交给 Spring 容器去处理。
IoC 环境搭建 第一步:
新建 Maven 工程,完善包结构。(和上面的 工厂解耦
一模一样) 然后导入如下依赖:
pom.xml 1 2 3 4 5 6 7 8 9 10 <packaging > jar</packaging > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.2.1.RELEASE</version > </dependency > </dependencies >
第二步:
在 resources
目录下新建 bean.xml
配置文件,这个就是 Spring 的 IoC 配置文件。 代码如下:
bean.xml 1 2 3 4 5 6 7 8 9 10 <?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 ="accountService" class ="club.guoshizhan.service.impl.AccountServiceImpl" > </bean > <bean id ="accountDao" class ="club.guoshizhan.dao.impl.AccountDaoImpl" > </bean > </beans >
第三步:
把以下的每一个类放到对应包结构中。(具体放到哪一个包参考 package 语句) 各个类的代码如下:
IAccountDao.java 1 2 3 4 5 6 7 8 9 10 11 12 13 package club.guoshizhan.dao;public interface IAccountDao { void saveAccount () ; }
AccountDaoImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package club.guoshizhan.dao.impl;import club.guoshizhan.dao.IAccountDao;public class AccountDaoImpl implements IAccountDao { public void saveAccount () { System.out.println("保存了账户" ); } }
IAccountService.java 1 2 3 4 5 6 7 8 9 10 11 12 13 package club.guoshizhan.service;public interface IAccountService { void saveAccount () ; }
AccountServiceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package club.guoshizhan.service.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.dao.impl.AccountDaoImpl;import club.guoshizhan.service.IAccountService;public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao = new AccountDaoImpl(); public void saveAccount () { accountDao.saveAccount(); } }
Client.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package club.guoshizhan.controller;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.service.IAccountService;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Client { public static void main (String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml" ); IAccountService as = (IAccountService) ac.getBean("accountService" ); IAccountDao dao = ac.getBean("accountDao" , IAccountDao.class ) ; System.out.println(as); System.out.println(dao); as.saveAccount(); } }
最后我们运行 main 方法即可。 结果如下:
1 2 3 club.guoshizhan.service.impl.AccountServiceImpl@14 ba872 club.guoshizhan.dao.impl.AccountDaoImpl@6 ba0ac 保存了账户
TIPS:
以上的过程就是 通过 Spring 的 IoC 容器来创建对象,这样就降低了程序之间的耦合度。IoC 环境搭建 到此结束!!!
ApplicationContext 的实现类 ApplicationContext
有三个实现类, 具体介绍以及使用如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 + 1、ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了。(更常用) + 2、FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须有访问权限) + 3、AnnotationConfigApplicationContext:它是用于读取注解创建容器的,后面会说到。 + 1、ApplicationContext: 它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。 + 也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。(适用范围:单例对象适用,一般情况下采用此接口) + 2、BeanFactory: 它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。 + 也就是说,什么时候根据 id 获取对象了,什么时候才真正的创建对象。(适用范围:多例对象使用)
前两个实现类的使用方法如下:
Client.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package club.guoshizhan.controller;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.service.IAccountService;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Client { public static void main (String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml" ); IAccountService as = (IAccountService) ac.getBean("accountService" ); IAccountDao dao = ac.getBean("accountDao" , IAccountDao.class ) ; System.out.println(as); System.out.println(dao); as.saveAccount(); } }
Spring 中 bean 的细节 三种创建 bean 的方式 第一步:
先创建一个 SimulationFactory
模拟工厂类,方便后续介绍。 代码如下:
SimulationFactory.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package club.guoshizhan.factory;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.dao.impl.AccountDaoImpl;import club.guoshizhan.service.IAccountService;import club.guoshizhan.service.impl.AccountServiceImpl;public class SimulationFactory { public IAccountDao getAccountDao () { return new AccountDaoImpl(); } public static IAccountService getAccountService () { return new AccountServiceImpl(); } }
第二步:
在 bean.xml
配置文件中使用三种创建 bean 的方式。 详情如下:
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?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 ="accountService" class ="club.guoshizhan.service.impl.AccountServiceImpl" > </bean > <bean id ="simulationFactory" class ="club.guoshizhan.factory.SimulationFactory" > </bean > <bean id ="accountDaoFactory" factory-bean ="simulationFactory" factory-method ="getAccountDao" > </bean > <bean id ="accountServiceFactory" class ="club.guoshizhan.factory.SimulationFactory" factory-method ="getAccountService" > </bean > </beans >
作用范围 在 Spring 中,那些组成应用程序的主体及由 Spring IoC 容器所管理的对象,被称之为 bean
。 其作用范围的详细介绍如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?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 ="accountService" class ="club.guoshizhan.service.impl.AccountServiceImpl" scope ="prototype" > </bean > </beans >
生命周期 Spring Bean 的完整生命周期从创建 Spring 容器开始,直到最终 Spring 容器销毁 Bean ,这其中包含了一系列关键点。 如下图:
Spring Bean 的生命周期的相关介绍以及配置如下:
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?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 ="accountService" class ="club.guoshizhan.service.impl.AccountServiceImpl" scope ="prototype" init-method ="init" destroy-method ="destroy" > </bean > </beans >
Spring 中的依赖注入 Spring 中的依赖注入详情介绍如下: 如需更多详情,请参考: 关于Spring IOC (DI-依赖注入)你需要知道的一切
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + 依赖注入:Dependency Injection + IOC 的作用:降低程序间的耦合(依赖关系) + 依赖关系的管理:以后都交给 spring 来维护。在当前类需要用到其他类的对象,由 spring 为我们提供,我们只需要在配置文件中说明即可。 + 这种依赖关系的维护就称之为依赖注入。 + 1、基本类型和 String + 2、其他 bean 类型(在配置文件中或者注解配置过的 bean) + 3、复杂类型/集合类型 + 1、第一种:使用构造方法提供 + 2、第二种:使用 set 方法提供 + 3、第三种:使用注解提供
构造方法注入 第一步:
先创建一个 User 类,用于方便后续的注入操作。 代码如下:
User.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package club.guoshizhan.domain;import java.util.Date;public class User { private String name; private Integer age; private Date birthday; public User (String name, Integer age, Date birthday) { this .name = name; this .age = age; this .birthday = birthday; } }
第二步:
编写 bean.xml
配置文件(构造方法注入的详细介绍写于注释之中)。 代码如下:
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?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 ="user" class ="club.guoshizhan.domain.User" > <constructor-arg name ="name" value ="James Gosling" > </constructor-arg > <constructor-arg name ="age" value ="58" > </constructor-arg > <constructor-arg name ="birthday" ref ="now" > </constructor-arg > </bean > <bean id ="now" class ="java.util.Date" > </bean > </beans >
第三步:
编写 Client
类进行测试。 代码如下:
Client.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package club.guoshizhan.controller;import club.guoshizhan.domain.User;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Client { public static void main (String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml" ); User user = (User) ac.getBean("user" ); user.show(); } }
第四步:
运行 main 方法,测试结果如下:
1 2 3 name = James Gosling age = 58 birthday = Mon Sep 07 18 :50 :18 CST 2020
TIPS:
由于构造方法的弊端,导致它无法成为常用的注入方式。
set 方法注入 第一步:
修改 User 类的代码,把原来的构造方法删掉,然后新加 setXXX 方法。 代码如下:
User.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package club.guoshizhan.domain;import java.util.Date;public class User { private String name; private Integer age; private Date birthday; public void setName (String name) { this .name = name; } public void setAge (Integer age) { this .age = age; } public void setBirthday (Date birthday) { this .birthday = birthday; } public void show () { System.out.println("name = " + name); System.out.println("age = " + age); System.out.println("birthday = " + birthday); } }
第二步:
编写 bean.xml
配置文件(set 注入的详细介绍写于注释之中)。 代码如下:
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?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 ="user" class ="club.guoshizhan.domain.User" > <property name ="name" value ="Java" > </property > <property name ="age" value ="25" > </property > <property name ="birthday" ref ="now" > </property > </bean > <bean id ="now" class ="java.util.Date" > </bean > </beans >
第三步:
运行 Client
类中的 main
方法,测试结果如下:
1 2 3 name = Java age = 25 birthday = Mon Sep 07 19 :05 :51 CST 2020
TIPS:
由于 set 注入的灵活性,因此它是常用的注入方式。
集合类型注入 第一步:
修改 User 类的代码,加入复杂的属性,并提供 setXXX 方法。 代码如下:
User.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package club.guoshizhan.domain;import java.util.*;public class User { private String[] arrStr; private List<String> list; private Set<String> set; private Map<String,String > map; private Properties properties; public void setArrStr (String[] arrStr) { this .arrStr = arrStr; } public void setList (List<String> list) { this .list = list; } public void setSet (Set<String> set) { this .set = set; } public void setMap (Map<String, String> map) { this .map = map; } public void setProperties (Properties properties) { this .properties = properties; } public void show () { System.out.println("arrStr = " + Arrays.toString(arrStr)); System.out.println("list = " + list); System.out.println("set = " + set); System.out.println("map = " + map); System.out.println("properties = " + properties); } }
第二步:
编写 bean.xml
配置文件(集合类型注入的详细介绍写于注释之中)。 代码如下:
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 <?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 ="user" class ="club.guoshizhan.domain.User" > <property name ="arrStr" > <set > <value > AAA</value > <value > BBB</value > <value > CCC</value > </set > </property > <property name ="list" > <array > <value > Lisa</value > <value > Jack</value > <value > Lucy</value > </array > </property > <property name ="set" > <list > <value > SET1</value > <value > SET2</value > <value > SET3</value > </list > </property > <property name ="map" > <props > <prop key ="MAP1" > MAP1</prop > <prop key ="MAP2" > MAP2</prop > </props > </property > <property name ="properties" > <map > <entry key ="PRO1" value ="PRO1" > </entry > <entry key ="PRO2" > <value > PRO2</value > </entry > </map > </property > </bean > </beans >
第三步:
运行 Client
类中的 main
方法,测试结果如下:
1 2 3 4 5 arrStr = [AAA, BBB, CCC] list = [Lisa, Jack, Lucy] set = [SET1, SET2, SET3] map = {MAP2=MAP2, MAP1=MAP1} properties = {PRO2=PRO2, PRO1=PRO1}
TIPS:
集合类型注入 到此结束!!!
基于注解的 IoC
写在前面:
学习基于注解的 IoC 配置,大家脑海里首先得有一个认知,即注解配置和 xml 配置要实现的功能都是一样的,都是要降低程序间的耦合,只是配置的形式不一样。 关于实际的开发中到底使用 xml 还是注解,我们不得而知,所以这两种配置方式我们都需要掌握。
注解 IoC 的环境搭建 第一步:
新建 Maven 工程,完善包结构。 然后导入如下依赖:
pom.xml 1 2 3 4 5 6 7 8 9 10 <packaging > jar</packaging > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.2.1.RELEASE</version > </dependency > </dependencies >
第二步:
在 resources
目录下新建 bean.xml
配置文件。 代码如下:
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 <?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.xsd" > <context:component-scan base-package ="club.guoshizhan" > </context:component-scan > </beans >
第三步:
把以下的每一个类放到对应包结构中。(具体放到哪一个包参考 package 语句) 各个类的代码如下:
IAccountDao.java 1 2 3 4 5 6 7 8 9 10 11 12 13 package club.guoshizhan.dao;public interface IAccountDao { void saveAccount () ; }
AccountDaoImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package club.guoshizhan.dao.impl;import club.guoshizhan.dao.IAccountDao;import org.springframework.stereotype.Repository;@Repository ("accountDao" )public class AccountDaoImpl implements IAccountDao { public void saveAccount () { System.out.println("保存了账户" ); } }
IAccountService.java 1 2 3 4 5 6 7 8 9 10 11 12 13 package club.guoshizhan.service;public interface IAccountService { void saveAccount () ; }
AccountServiceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package club.guoshizhan.service.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.dao.impl.AccountDaoImpl;import club.guoshizhan.service.IAccountService;import org.springframework.stereotype.Component;@Component public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao = new AccountDaoImpl(); public void saveAccount () { accountDao.saveAccount(); } public void init () { System.out.println("init..." ); } public void destroy () { System.out.println("destroy..." ); } }
Client.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package club.guoshizhan.controller;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.service.impl.AccountServiceImpl;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Client { public static void main (String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml" ); AccountServiceImpl accountServiceImpl = (AccountServiceImpl) ac.getBean("accountServiceImpl" ); IAccountDao accountDao = ac.getBean("accountDao" , IAccountDao.class ) ; System.out.println(accountServiceImpl); System.out.println(accountDao); accountServiceImpl.saveAccount(); accountDao.saveAccount(); } }
最后我们运行 main
方法即可。 结果如下:
1 2 3 4 club.guoshizhan.service.impl.AccountServiceImpl@57643 e club.guoshizhan.dao.impl.AccountDaoImpl@13e16f d 保存了账户 保存了账户
TIPS:
以上的过程就是 注解 IoC 的环境搭建 ,那么环境搭建到此结束!!!
常用注解详解 用于创建对象的注解 接下来介绍一下 Spring 中的 常用注解 ,具体详情如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 + @Component 注解: + 作用:用于把当前类对象存入 spring 容器中 + 属性:value:用于指定 bean 的 id 。当我们不写时,它的默认值就是当前类名,且首字母改小写。 + @Controller 注解:一般用在表现层 + @Service 注解:一般用在业务层 + @Repository 注解:一般用在持久层
用于注入数据的注解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 + @Autowired 注解: + 作用:自动按照类型注入。只要容器中有唯一的一个 bean 对象类型和要注入的变量类型匹配,就可以注入成功 + 如果 IoC 容器中没有任何 bean 的类型和要注入的变量类型匹配,则报错。 + 如果 IoC 容器中有多个类型匹配时:那么 Spring 会先去匹配数据类型,然后再根据变量名去确定哪一个 bean + 出现位置:可以是变量上,也可以是方法上 + 细节:在使用注解注入时,set 方法就不是必须的了。 + @Qualifier 注解: + 作用:在按照类注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是在给方法参数注入时可以 + 属性:value 属性用于指定注入 bean 的 id 。 + @Resource 注解: + 作用:直接按照 bean 的 id 注入,它可以独立使用 + 属性:name 属性用于指定 bean 的 id 。 + @Value 注解: + 作用:用于注入基本类型和 String 类型的数据 + 属性:value 属性用于指定数据的值。它可以使用 spring 中 SpEL(也就是 spring 的 el 表达式)。SpEL 的写法:${表达式}
用于改变作用范围的注解 1 2 3 4 5 6 7 8 + @Scope 注解: + 作用:用于指定 bean 的作用范围。可以写在类的上方 + 属性:value 属性指定范围的取值。常用取值:singleton / prototype
生命周期相关的注解 1 2 3 4 5 6 7 + @PreDestroy 注解:作用就是用于指定 bean 的销毁方法。此注解写在销毁的方法上 + @PostConstruct 注解:作用就是用于指定 bean 的初始化方法。此注解写在初始化的方法上
基于 XML 的 IoC 案例 第一步:
创建数据库和数据表。 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 CREATE DATABASE spring;CREATE TABLE account ( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR (40 ), money FLOAT )CHARACTER SET utf8 COLLATE utf8_general_ci; INSERT INTO account (NAME ,money) VALUES ('Jack' ,1000 );INSERT INTO account (NAME ,money) VALUES ('Lucy' ,1000 );INSERT INTO account (NAME ,money) VALUES ('Lisa' ,1000 );
第二步:
新建一个 Maven 工程。 导入如下依赖:
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <packaging > jar</packaging > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.2.1.RELEASE</version > </dependency > <dependency > <groupId > commons-dbutils</groupId > <artifactId > commons-dbutils</artifactId > <version > 1.4</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.6</version > </dependency > <dependency > <groupId > com.mchange</groupId > <artifactId > c3p0</artifactId > <version > 0.9.5.5</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13</version > <scope > test</scope > </dependency > </dependencies >
第三步:
创建如下各个接口和类,根据 package
语句将类或接口放到对应的包中。 接口和类的代码如下:
Account.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package club.guoshizhan.domain;import java.io.Serializable;public class Account implements Serializable { private Integer id; private String name; private Float money; public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public Float getMoney () { return money; } public void setMoney (Float money) { this .money = money; } @Override public String toString () { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}' ; } }
IAccountDao.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package club.guoshizhan.dao;import club.guoshizhan.domain.Account;import java.util.List;public interface IAccountDao { List<Account> findAll () ; Account findById (Integer id) ; void save (Account account) ; void update (Account account) ; void delete (Integer id) ; }
AccountDaoImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 package club.guoshizhan.dao.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.handlers.BeanHandler;import org.apache.commons.dbutils.handlers.BeanListHandler;import java.util.List;public class AccountDaoImpl implements IAccountDao { private QueryRunner runner; public void setRunner (QueryRunner runner) { this .runner = runner; } public List<Account> findAll () { try { return runner.query("select * from account" , new BeanListHandler<Account>(Account.class )) ; } catch (Exception e) { throw new RuntimeException(e); } } public Account findById (Integer id) { try { return runner.query("select * from account where id = ?" , new BeanHandler<Account>(Account.class ), id ) ; } catch (Exception e) { throw new RuntimeException(e); } } public void save (Account account) { try { runner.update("insert into account(name,money) values (?,?)" , account.getName(), account.getMoney()); } catch (Exception e) { throw new RuntimeException(e); } } public void update (Account account) { try { runner.update("update account set name=?,money=? where id=?" , account.getName(), account.getMoney(), account.getId()); } catch (Exception e) { throw new RuntimeException(e); } } public void delete (Integer id) { try { runner.update("delete from account where id=?" , id); } catch (Exception e) { throw new RuntimeException(e); } } }
IAccountService.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package club.guoshizhan.service;import club.guoshizhan.domain.Account;import java.util.List;public interface IAccountService { List<Account> findAll () ; Account findById (Integer id) ; void save (Account account) ; void update (Account account) ; void delete (Integer id) ; }
AccountServiceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package club.guoshizhan.service.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import club.guoshizhan.service.IAccountService;import java.util.List;public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao; public void setAccountDao (IAccountDao accountDao) { this .accountDao = accountDao; } public List<Account> findAll () { return accountDao.findAll(); } public Account findById (Integer id) { return accountDao.findById(id); } public void save (Account account) { accountDao.save(account); } public void update (Account account) { accountDao.update(account); } public void delete (Integer id) { accountDao.delete(id); } }
第四步:
在 resources
目录下新建 bean.xml
配置文件。 代码如下:
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 <?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 ="accountService" class ="club.guoshizhan.service.impl.AccountServiceImpl" > <property name ="accountDao" ref ="accountDao" > </property > </bean > <bean id ="accountDao" class ="club.guoshizhan.dao.impl.AccountDaoImpl" > <property name ="runner" ref ="runner" > </property > </bean > <bean id ="runner" class ="org.apache.commons.dbutils.QueryRunner" scope ="prototype" > <constructor-arg name ="ds" ref ="dataSource" > </constructor-arg > </bean > <bean id ="dataSource" class ="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name ="driverClass" value ="com.mysql.jdbc.Driver" > </property > <property name ="jdbcUrl" value ="jdbc:mysql://localhost:3306/spring" > </property > <property name ="user" value ="root" > </property > <property name ="password" value ="root" > </property > </bean > </beans >
第五步:
在 test
目录下新建测试类,包名根据 package 语句自行创建。 代码如下:
AccountServiceTest.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package club.guoshizhan.test;import club.guoshizhan.domain.Account;import club.guoshizhan.service.IAccountService;import org.junit.Before;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import java.util.List;public class AccountServiceTest { private ApplicationContext ac; private IAccountService accountService; @Before public void init () { ac = new ClassPathXmlApplicationContext("bean.xml" ); accountService = ac.getBean("accountService" , IAccountService.class ) ; } @Test public void testFindAll () { List<Account> accounts = accountService.findAll(); for (Account account : accounts){ System.out.println(account); } } @Test public void testFindOne () { Account account = accountService.findById(1 ); System.out.println(account); } @Test public void testSave () { Account account = new Account(); account.setName("test" ); account.setMoney(12345f ); accountService.save(account); } @Test public void testUpdate () { Account account = accountService.findById(4 ); account.setMoney(23456f ); accountService.update(account); } @Test public void testDelete () { accountService.delete(4 ); } }
TIPS:
依次执行每一个测试方法即可,结果完全正确!!!
基于注解的 IoC 案例
这部分的操作是在 XML 案例
的基础上进行的操作。就是只需要修改部分代码即可。那就操作一下吧!!!
第一步:
把 bean.xml
配置文件修改成如下代码:
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <?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.xsd" > <context:component-scan base-package ="club.guoshizhan" > </context:component-scan > <bean id ="runner" class ="org.apache.commons.dbutils.QueryRunner" scope ="prototype" > <constructor-arg name ="ds" ref ="dataSource" > </constructor-arg > </bean > <bean id ="dataSource" class ="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name ="driverClass" value ="com.mysql.jdbc.Driver" > </property > <property name ="jdbcUrl" value ="jdbc:mysql://localhost:3306/spring" > </property > <property name ="user" value ="root" > </property > <property name ="password" value ="root" > </property > </bean > </beans >
第二步:
修改 AccountDaoImpl
类,+
代表增加代码,-
代表删除代码。 修改如下:
AccountDaoImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 /** * 账户持久层实现类 */ + @Repository("accountDao") public class AccountDaoImpl implements IAccountDao { + @Autowired private QueryRunner runner; - public void setRunner(QueryRunner runner) { - this.runner = runner; - }
第三步:
修改 AccountServiceImpl
类,+
代表增加代码,-
代表删除代码。 修改如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 /** * 账号的业务层实现类 */ + @Service("accountService") public class AccountServiceImpl implements IAccountService { + @Autowired private IAccountDao accountDao; - public void setAccountDao(IAccountDao accountDao) { - this.accountDao = accountDao; - }
第四步:
去测试类进行测试(我测试的是 findAll
方法,只要能测试成功就表示你的配置没有问题)。 结果如下:
1 2 3 4 5 6 7 8 9 10 11 九月 08 , 2020 11 :09 :08 上午 com.mchange.v2.log.MLog 信息: MLog clients using java 1.4 + standard logging. 九月 08 , 2020 11 :09 :09 上午 com.mchange.v2.c3p0.C3P0Registry 信息: Initializing c3p0-0.9 .5 .5 [built 11 -December-2019 22 :18 :33 -0800 ; debug? true ; trace: 10 ] 九月 08 , 2020 11 :09 :09 上午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource 信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3 , acquireRetryAttempts -> 30 , acquireRetryDelay -> 1000 , autoCommitOnClose -> false , automaticTestTable -> null , breakAfterAcquireFailure -> false , checkoutTimeout -> 0 , connectionCustomizerClassName -> null , connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1 hgeby9actvo5zi1fbj4vu|114 c583, debugUnreturnedConnectionStackTraces -> false , description -> null , driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null , forceIgnoreUnresolvedTransactions -> false , forceSynchronousCheckins -> false , forceUseNamedDriverClass -> false , identityToken -> 1 hgeby9actvo5zi1fbj4vu|114 c583, idleConnectionTestPeriod -> 0 , initialPoolSize -> 3 , jdbcUrl -> jdbc:mysql: Account{id=1 , name='Jack' , money=1000.0 } Account{id=2 , name='Lucy' , money=1000.0 } Account{id=3 , name='Lisa' , money=1000.0 } Process finished with exit code 0
上面的注解操作虽然简化了一点点,但是还是需要 bean.xml
这个配置文件。那么问题来了,能不能删除这个配置文件呢?安排!!!
第一步:
删除 bean.xml
配置文件,新建 SpringConfiguration
配置类(注解介绍地很详细)。 代码如下:
SpringConfiguration.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package club.guoshizhan.config;import com.mchange.v2.c3p0.ComboPooledDataSource;import org.apache.commons.dbutils.QueryRunner;import org.springframework.context.annotation.*;import javax.sql.DataSource;@Configuration @ComponentScan ("club.guoshizhan" )public class SpringConfiguration { @Bean @Scope ("prototype" ) public QueryRunner queryRunner (DataSource dataSource) { return new QueryRunner(dataSource); } @Bean public DataSource createDataSource () { ComboPooledDataSource dataSource = new ComboPooledDataSource(); try { dataSource.setDriverClass("com.mysql.jdbc.Driver" ); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/spring" ); dataSource.setUser("root" ); dataSource.setPassword("root" ); return dataSource; } catch (Exception e) { throw new RuntimeException(e); } } }
第二步:
把测试类 AccountServiceTest
修改成下面的样子(只要一个 testFindAll 方法即可)。 代码如下:
AccountServiceTest.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package club.guoshizhan.test;import club.guoshizhan.config.SpringConfiguration;import club.guoshizhan.domain.Account;import club.guoshizhan.service.IAccountService;import org.junit.Before;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.AnnotationConfigApplicationContext;import java.util.List;public class AccountServiceTest { private ApplicationContext ac; private IAccountService accountService; @Before public void init () { ac = new AnnotationConfigApplicationContext(SpringConfiguration.class ) ; accountService = ac.getBean("accountService" , IAccountService.class ) ; } @Test public void testFindAll () { List<Account> accounts = accountService.findAll(); for (Account account : accounts){ System.out.println(account); } } }
第三步:
执行测试类中的 testFindAll
方法。 结果如下:
1 2 3 4 5 6 7 8 9 10 11 九月 08 , 2020 12 :49 :39 下午 com.mchange.v2.log.MLog 信息: MLog clients using java 1.4 + standard logging. 九月 08 , 2020 12 :49 :39 下午 com.mchange.v2.c3p0.C3P0Registry 信息: Initializing c3p0-0.9 .5 .5 [built 11 -December-2019 22 :18 :33 -0800 ; debug? true ; trace: 10 ] 九月 08 , 2020 12 :49 :39 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource 信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3 , acquireRetryAttempts -> 30 , acquireRetryDelay -> 1000 , autoCommitOnClose -> false , automaticTestTable -> null , breakAfterAcquireFailure -> false , checkoutTimeout -> 0 , connectionCustomizerClassName -> null , connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1 hgeby9actz9f0b1jp9fll|514 af7, debugUnreturnedConnectionStackTraces -> false , description -> null , driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null , forceIgnoreUnresolvedTransactions -> false , forceSynchronousCheckins -> false , forceUseNamedDriverClass -> false , identityToken -> 1 hgeby9actz9f0b1jp9fll|514 af7, idleConnectionTestPeriod -> 0 , initialPoolSize -> 3 , jdbcUrl -> jdbc:mysql: Account{id=1 , name='Jack' , money=1000.0 } Account{id=2 , name='Lucy' , money=1000.0 } Account{id=3 , name='Lisa' , money=1000.0 } Process finished with exit code 0
假设有很多个配置类,而 SpringConfiguration
作为主配置类,那么它该如何去加载其他的配置类呢?
第一步:
新建 JdbcConfig
配置类,用于数据库相关的配置。 代码如下:
JdbcConfig.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package club.guoshizhan.config;import com.mchange.v2.c3p0.ComboPooledDataSource;import org.apache.commons.dbutils.QueryRunner;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Scope;import javax.sql.DataSource;public class JdbcConfig { @Bean @Scope ("prototype" ) public QueryRunner queryRunner (DataSource dataSource1) { return new QueryRunner(dataSource1); } @Bean public DataSource createDataSource () { ComboPooledDataSource dataSource = new ComboPooledDataSource(); try { dataSource.setDriverClass("com.mysql.jdbc.Driver" ); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/spring" ); dataSource.setUser("root" ); dataSource.setPassword("root" ); return dataSource; } catch (Exception e) { throw new RuntimeException(e); } } }
第二步:
把主配置类修改为如下的样子(里面什么都没有,但是使用 @Import 注解加载了其他的配置类)。 代码如下:
SpringConfiguration.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package club.guoshizhan.config;import org.springframework.context.annotation.*;@ComponentScan ("club.guoshizhan" )@Import (JdbcConfig.class ) public class SpringConfiguration {}
TIPS:
然后执行测试类的测试方法也可实现相关的操作。
那么问题又来了,数据库的相关信息的配置都写在代码里面了,这就造成了硬编码,最终导致无法灵活的实现配置。 因此,我们需要把它写到配置文件里面,然后从配置文件中读取相关的信息。那么,接下来就在 resources
目录下创建 jdbcConfig.properties
配置文件。 代码如下:
jdbcConfig.properties 1 2 3 4 jdbc.driver =com.mysql.jdbc.Driver jdbc.url =jdbc:mysql://localhost:3306/spring jdbc.username =root jdbc.password =root
接下来把 JdbcConfig
配置类修改成如下样子。 代码如下:
JdbcConfig.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package club.guoshizhan.config;import com.mchange.v2.c3p0.ComboPooledDataSource;import org.apache.commons.dbutils.QueryRunner;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Scope;import javax.sql.DataSource;public class JdbcConfig { @Value ("${jdbc.driver}" ) private String driver; @Value ("${jdbc.url}" ) private String url; @Value ("${jdbc.username}" ) private String username; @Value ("${jdbc.password}" ) private String password; @Bean @Scope ("prototype" ) public QueryRunner queryRunner (DataSource dataSource1) { return new QueryRunner(dataSource1); } @Bean public DataSource createDataSource () { ComboPooledDataSource dataSource = new ComboPooledDataSource(); try { dataSource.setDriverClass(driver); dataSource.setJdbcUrl(url); dataSource.setUser(username); dataSource.setPassword(password); return dataSource; } catch (Exception e) { throw new RuntimeException(e); } } }
然后去 SpringConfiguration
主配置文件添加如下代码:
1 2 3 4 5 6 @ComponentScan("club.guoshizhan") @Import(JdbcConfig.class) + @PropertySource("classpath:jdbcConfig.properties") public class SpringConfiguration { }
TIPS:
最后去测试类测试,仍然可以执行成功。到此为止就完全实现了注解的配置,而不再需要 XML 文件了。
Spring 和 Junit 的整合 Spring 和 Junit 的整合 的步骤如下:
1 2 3 4 5 6 7 8 9 10 11 + Spring 使用 Junit 单元测试:测试我们的配置。步骤如下: + 1、导入 Spring 整合 junit 的 jar 包(坐标) + 2、使用 Junit 提供的 @RunWith 注解把原有的 main 方法替换了,替换成 Spring 提供的 + 3、使用 @ContextConfiguration 注解告知 Spring 的运行器 ==> Spring 和 ioc 创建是基于 xml 还是注解的,并且说明位置 + locations:指定 xml 文件的位置,加上 classpath 关键字,表示在类路径下 + classes:指定注解类所在地位置 + 4、注意事项:当我们使用 Spring 5.x 版本的时候,要求 junit 的 jar 包版本必须是 4.12 及以上
第一步:
导入相关依赖。 代码如下:
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13</version > <scope > test</scope > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-test</artifactId > <version > 5.2.2.RELEASE</version > </dependency >
第二步:
编写测试类(有空可以自己去对比一下之前的测试类,看一下不同点)。 代码如下:
AccountServiceTest.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package club.guoshizhan.test;import club.guoshizhan.config.SpringConfiguration;import club.guoshizhan.domain.Account;import club.guoshizhan.service.IAccountService;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import java.util.List;@RunWith (SpringJUnit4ClassRunner.class ) @ContextConfiguration (classes = SpringConfiguration.class ) /*// XML 方式的配置 @ContextConfiguration (locations = "classpath:bean.xml" )*/public class AccountServiceTest { @Autowired private IAccountService accountService; @Test public void testFindAll () { List<Account> accounts = accountService.findAll(); for (Account account : accounts){ System.out.println(account); } } }
TIPS:
运行测试类的方法,即可看到对应的结果。Spring 整合 Junit 到此结束!!!
Spring 中的 AOP 转账小案例 第一步:
新建一个 Maven 工程,导入如下依赖。 代码如下:
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <packaging > jar</packaging > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.2.1.RELEASE</version > </dependency > <dependency > <groupId > commons-dbutils</groupId > <artifactId > commons-dbutils</artifactId > <version > 1.4</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.6</version > </dependency > <dependency > <groupId > com.mchange</groupId > <artifactId > c3p0</artifactId > <version > 0.9.5.5</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13</version > <scope > test</scope > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-test</artifactId > <version > 5.2.2.RELEASE</version > </dependency > </dependencies >
第二步:
创建如下各个接口和类,根据 package
语句将类或接口放到对应的包中。 接口和类的代码如下:
Account.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package club.guoshizhan.domain;import java.io.Serializable;public class Account implements Serializable { private Integer id; private String name; private Float money; public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public Float getMoney () { return money; } public void setMoney (Float money) { this .money = money; } @Override public String toString () { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}' ; } }
IAccountDao.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package club.guoshizhan.dao;import club.guoshizhan.domain.Account;import java.util.List;public interface IAccountDao { List<Account> findAll () ; Account findById (Integer id) ; void save (Account account) ; void update (Account account) ; void delete (Integer id) ; Account findByName (String name) ; }
AccountDaoImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 package club.guoshizhan.dao.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.handlers.BeanHandler;import org.apache.commons.dbutils.handlers.BeanListHandler;import java.util.List;public class AccountDaoImpl implements IAccountDao { private QueryRunner runner; public void setRunner (QueryRunner runner) { this .runner = runner; } @Override public List<Account> findAll () { try { return runner.query("select * from account" , new BeanListHandler<Account>(Account.class )) ; } catch (Exception e) { throw new RuntimeException(e); } } @Override public Account findById (Integer id) { try { return runner.query("select * from account where id = ?" , new BeanHandler<Account>(Account.class ), id ) ; } catch (Exception e) { throw new RuntimeException(e); } } @Override public void save (Account account) { try { runner.update("insert into account(name,money) values (?,?)" , account.getName(), account.getMoney()); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void update (Account account) { try { runner.update("update account set name=?,money=? where id=?" , account.getName(), account.getMoney(), account.getId()); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void delete (Integer id) { try { runner.update("delete from account where id=?" , id); } catch (Exception e) { throw new RuntimeException(e); } } @Override public Account findByName (String name) { try { List<Account> accounts = runner.query("select * from account where name = ?" , new BeanListHandler<>(Account.class ), name ) ; if (accounts == null || accounts.size() == 0 ) { return null ; } if (accounts.size() > 1 ) { throw new RuntimeException("结果集不唯一,数据有问题" ); } return accounts.get(0 ); } catch (Exception e) { throw new RuntimeException(e); } } }
IAccountService.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package club.guoshizhan.service;import club.guoshizhan.domain.Account;import java.util.List;public interface IAccountService { List<Account> findAll () ; Account findById (Integer id) ; void save (Account account) ; void update (Account account) ; void delete (Integer id) ; void transfer (String sourceName, String targetName, Float money) ; }
AccountServiceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 package club.guoshizhan.service.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import club.guoshizhan.service.IAccountService;import java.util.List;public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao; public void setAccountDao (IAccountDao accountDao) { this .accountDao = accountDao; } @Override public List<Account> findAll () { return accountDao.findAll(); } @Override public Account findById (Integer id) { return accountDao.findById(id); } @Override public void save (Account account) { accountDao.save(account); } @Override public void update (Account account) { accountDao.update(account); } @Override public void delete (Integer id) { accountDao.delete(id); } @Override public void transfer (String sourceName, String targetName, Float money) { Account source = accountDao.findByName(sourceName); Account target = accountDao.findByName(targetName); source.setMoney(source.getMoney() - money); target.setMoney(target.getMoney() + money); accountDao.update(source); accountDao.update(target); } }
第三步:
在 resources
目录下新建 bean.xml
配置文件。 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 <?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 ="accountService" class ="club.guoshizhan.service.impl.AccountServiceImpl" > <property name ="accountDao" ref ="accountDao" > </property > </bean > <bean id ="accountDao" class ="club.guoshizhan.dao.impl.AccountDaoImpl" > <property name ="runner" ref ="runner" > </property > </bean > <bean id ="runner" class ="org.apache.commons.dbutils.QueryRunner" scope ="prototype" > <constructor-arg name ="ds" ref ="dataSource" > </constructor-arg > </bean > <bean id ="dataSource" class ="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name ="driverClass" value ="com.mysql.jdbc.Driver" > </property > <property name ="jdbcUrl" value ="jdbc:mysql://localhost:3306/spring" > </property > <property name ="user" value ="root" > </property > <property name ="password" value ="root" > </property > </bean > </beans >
第四步:
在 test
目录下新建 AccountServiceTest
测试类,包名根据 package
语句自行创建。 代码如下:
AccountServiceTest.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package club.guoshizhan.test;import club.guoshizhan.domain.Account;import club.guoshizhan.service.IAccountService;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import java.util.List;@RunWith (SpringJUnit4ClassRunner.class ) @ContextConfiguration (locations = "classpath:bean.xml" )public class AccountServiceTest { @Autowired private IAccountService accountService; @Test public void transferTest () { accountService.transfer("Jack" , "Lucy" , 100F ); List<Account> accounts = accountService.findAll(); for (Account account : accounts) { System.out.println(account); } } }
第五步:
执行 transferTest
方法,然后查看结果。 结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 九月 08 , 2020 10 :42 :04 下午 org.springframework.test.context.support.AbstractTestContextBootstrapper getDefaultTestExecutionListenerClassNames 信息: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener] 九月 08 , 2020 10 :42 :04 下午 org.springframework.test.context.support.AbstractTestContextBootstrapper getTestExecutionListeners 信息: Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@d 25987, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@101 b7af, org.springframework.test.context.support.DirtiesContextTestExecutionListener@3884 b2, org.springframework.test.context.event.EventPublishingTestExecutionListener@1275 dab] 九月 08 , 2020 10 :42 :05 下午 com.mchange.v2.log.MLog 信息: MLog clients using java 1.4 + standard logging. 九月 08 , 2020 10 :42 :05 下午 com.mchange.v2.c3p0.C3P0Registry 信息: Initializing c3p0-0.9 .5 .5 [built 11 -December-2019 22 :18 :33 -0800 ; debug? true ; trace: 10 ] 九月 08 , 2020 10 :42 :06 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource 信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3 , acquireRetryAttempts -> 30 , acquireRetryDelay -> 1000 , autoCommitOnClose -> false , automaticTestTable -> null , breakAfterAcquireFailure -> false , checkoutTimeout -> 0 , connectionCustomizerClassName -> null , connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1 hgeby9acukfaofbdzec9|90 b489, debugUnreturnedConnectionStackTraces -> false , description -> null , driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null , forceIgnoreUnresolvedTransactions -> false , forceSynchronousCheckins -> false , forceUseNamedDriverClass -> false , identityToken -> 1 hgeby9acukfaofbdzec9|90 b489, idleConnectionTestPeriod -> 0 , initialPoolSize -> 3 , jdbcUrl -> jdbc:mysql: Account{id=1 , name='Jack' , money=900.0 } Account{id=2 , name='Lucy' , money=1100.0 } Account{id=3 , name='Lisa' , money=1000.0 } Process finished with exit code 0
由上述结果可知:Jack 用户少了 100 元,所以他的余额为 900 元;而 Lucy 用户多了 100 元,所以她的余额为 1100 元。 这结果没毛病,那是因为程序没有发生异常的情况。如果程序有异常,那结果就不一样了。往下看!!!
我们给 AccountServiceImpl
类中的 transfer
方法加一行代码。 详情如下:
AccountServiceImpl's transfer method 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Override public void transfer(String sourceName, String targetName, Float money) { Account source = accountDao.findByName(sourceName); // 根据名称查询转出账户 Account target = accountDao.findByName(targetName); // 根据名称查询转入账户 source.setMoney(source.getMoney() - money); // 转出账户减钱 target.setMoney(target.getMoney() + money); // 转入账户加钱 accountDao.update(source); // 更新转出账户 + int i = 10 / 0; accountDao.update(target); // 更新转入账户 }
再次执行 transferTest
方法,然后查看结果发现,程序报错了。 结果如下:
1 2 3 4 5 6 7 8 9 10 11 信息: MLog clients using java 1.4 + standard logging. 九月 08 , 2020 11 :22 :38 下午 com.mchange.v2.c3p0.C3P0Registry 信息: Initializing c3p0-0.9 .5 .5 [built 11 -December-2019 22 :18 :33 -0800 ; debug? true ; trace: 10 ] 九月 08 , 2020 11 :22 :38 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource 信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3 , acquireRetryAttempts -> 30 , acquireRetryDelay -> 1000 , autoCommitOnClose -> false , automaticTestTable -> null , breakAfterAcquireFailure -> false , checkoutTimeout -> 0 , connectionCustomizerClassName -> null , connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1 hgeby9aculvfii1d1svyq|90 b489, debugUnreturnedConnectionStackTraces -> false , description -> null , driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null , forceIgnoreUnresolvedTransactions -> false , forceSynchronousCheckins -> false , forceUseNamedDriverClass -> false , identityToken -> 1 hgeby9aculvfii1d1svyq|90 b489, idleConnectionTestPeriod -> 0 , initialPoolSize -> 3 , jdbcUrl -> jdbc:mysql: java.lang.ArithmeticException: / by zero at club.guoshizhan.service.impl.AccountServiceImpl.transfer(AccountServiceImpl.java:54 ) at club.guoshizhan.test.AccountServiceTest.transfer(AccountServiceTest.java:25 ) ...
程序报错了没关系,调嘛!但是呢,你报错就报错嘛,可是 Jack 用户少了 100 元,而 Lucy 却没有收到 Jack 的 100 元,这就难受了。 那么这是什么原因造成的呢?有人可能会说没有事务,如果没有事务的话,增删改的方法就不起作用。显然不是这个原因。 那么接下来就去找出原因,并解决这个问题。
我们通过代码发现:每执行一次操作,就会有一个新的连接对象。 这个就是原因。详情如下图:
那我们如何解决呢?第一步:
新建两个工具类。 代码如下:
ConnectionUtils.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package club.guoshizhan.utils;import javax.sql.DataSource;import java.sql.Connection;public class ConnectionUtils { private ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); private DataSource dataSource; public void setDataSource (DataSource dataSource) { this .dataSource = dataSource; } public Connection getThreadConnection () { try { Connection conn = threadLocal.get(); if (conn == null ) { conn = dataSource.getConnection(); threadLocal.set(conn); } return conn; } catch (Exception e) { throw new RuntimeException(e); } } public void removeConnection () { threadLocal.remove(); } }
TransactionManager.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package club.guoshizhan.utils;public class TransactionManager { private ConnectionUtils connectionUtils; public void setConnectionUtils (ConnectionUtils connectionUtils) { this .connectionUtils = connectionUtils; } public void beginTransaction () { try { connectionUtils.getThreadConnection().setAutoCommit(false ); } catch (Exception e) { e.printStackTrace(); } } public void commit () { try { connectionUtils.getThreadConnection().commit(); } catch (Exception e) { e.printStackTrace(); } } public void rollback () { try { connectionUtils.getThreadConnection().rollback(); } catch (Exception e) { e.printStackTrace(); } } public void release () { try { connectionUtils.getThreadConnection().close(); connectionUtils.removeConnection(); } catch (Exception e) { e.printStackTrace(); } } }
第二步:
修改 AccountServiceImpl
类。 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 package club.guoshizhan.service.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import club.guoshizhan.service.IAccountService;import club.guoshizhan.utils.TransactionManager;import java.util.List;public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao; private TransactionManager txManager; public void setTxManager (TransactionManager txManager) { this .txManager = txManager; } public void setAccountDao (IAccountDao accountDao) { this .accountDao = accountDao; } @Override public List<Account> findAll () { try { txManager.beginTransaction(); List<Account> accounts = accountDao.findAll(); txManager.commit(); return accounts; }catch (Exception e){ txManager.rollback(); throw new RuntimeException(e); }finally { txManager.release(); } } @Override public Account findById (Integer id) { try { txManager.beginTransaction(); Account accounts = accountDao.findById(id); txManager.commit(); return accounts; }catch (Exception e){ txManager.rollback(); throw new RuntimeException(e); }finally { txManager.release(); } } @Override public void save (Account account) { try { txManager.beginTransaction(); accountDao.save(account); txManager.commit(); }catch (Exception e){ txManager.rollback(); throw new RuntimeException(e); }finally { txManager.release(); } } @Override public void update (Account account) { try { txManager.beginTransaction(); accountDao.update(account); txManager.commit(); }catch (Exception e){ txManager.rollback(); throw new RuntimeException(e); }finally { txManager.release(); } } @Override public void delete (Integer id) { try { txManager.beginTransaction(); accountDao.delete(id); txManager.commit(); }catch (Exception e){ txManager.rollback(); throw new RuntimeException(e); }finally { txManager.release(); } } @Override public void transfer (String sourceName, String targetName, Float money) { try { txManager.beginTransaction(); Account source = accountDao.findByName(sourceName); Account target = accountDao.findByName(targetName); source.setMoney(source.getMoney() - money); target.setMoney(target.getMoney() + money); accountDao.update(source); int i = 10 / 0 ; accountDao.update(target); txManager.commit(); }catch (Exception e){ txManager.rollback(); throw new RuntimeException(e); }finally { txManager.release(); } } }
第三步:
修改 AccountDaoImpl
类。 代码如下:
AccountDaoImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 package club.guoshizhan.dao.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import club.guoshizhan.utils.ConnectionUtils;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.handlers.BeanHandler;import org.apache.commons.dbutils.handlers.BeanListHandler;import java.util.List;public class AccountDaoImpl implements IAccountDao { private QueryRunner runner; private ConnectionUtils connectionUtils; public void setConnectionUtils (ConnectionUtils connectionUtils) { this .connectionUtils = connectionUtils; } public void setRunner (QueryRunner runner) { this .runner = runner; } @Override public List<Account> findAll () { try { return runner.query(connectionUtils.getThreadConnection(), "select * from account" , new BeanListHandler<Account>(Account.class )) ; } catch (Exception e) { throw new RuntimeException(e); } } @Override public Account findById (Integer id) { try { return runner.query(connectionUtils.getThreadConnection(), "select * from account where id = ?" , new BeanHandler<Account>(Account.class ), id ) ; } catch (Exception e) { throw new RuntimeException(e); } } @Override public void save (Account account) { try { runner.update(connectionUtils.getThreadConnection(), "insert into account(name,money) values (?,?)" , account.getName(), account.getMoney()); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void update (Account account) { try { runner.update(connectionUtils.getThreadConnection(), "update account set name=?,money=? where id=?" , account.getName(), account.getMoney(), account.getId()); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void delete (Integer id) { try { runner.update(connectionUtils.getThreadConnection(), "delete from account where id=?" , id); } catch (Exception e) { throw new RuntimeException(e); } } @Override public Account findByName (String name) { try { List<Account> accounts = runner.query(connectionUtils.getThreadConnection(), "select * from account where name = ?" , new BeanListHandler<>(Account.class ), name ) ; if (accounts == null || accounts.size() == 0 ) { return null ; } if (accounts.size() > 1 ) { throw new RuntimeException("结果集不唯一,数据有问题" ); } return accounts.get(0 ); } catch (Exception e) { throw new RuntimeException(e); } } }
第四步:
修改 bean.xml
配置文件。 代码如下:
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 <?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 ="accountService" class ="club.guoshizhan.service.impl.AccountServiceImpl" > <property name ="accountDao" ref ="accountDao" > </property > <property name ="txManager" ref ="txManager" > </property > </bean > <bean id ="accountDao" class ="club.guoshizhan.dao.impl.AccountDaoImpl" > <property name ="runner" ref ="runner" > </property > <property name ="connectionUtils" ref ="connectionUtils" > </property > </bean > <bean id ="runner" class ="org.apache.commons.dbutils.QueryRunner" scope ="prototype" > </bean > <bean id ="dataSource" class ="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name ="driverClass" value ="com.mysql.jdbc.Driver" > </property > <property name ="jdbcUrl" value ="jdbc:mysql://localhost:3306/spring" > </property > <property name ="user" value ="root" > </property > <property name ="password" value ="root" > </property > </bean > <bean id ="connectionUtils" class ="club.guoshizhan.utils.ConnectionUtils" > <property name ="dataSource" ref ="dataSource" > </property > </bean > <bean id ="txManager" class ="club.guoshizhan.utils.TransactionManager" > <property name ="connectionUtils" ref ="connectionUtils" > </property > </bean > </beans >
第五步:
执行测试类中的 transferTest
方法。 结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 九月 09 , 2020 2 :14 :15 下午 org.springframework.test.context.support.AbstractTestContextBootstrapper getDefaultTestExecutionListenerClassNames 信息: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener] 九月 09 , 2020 2 :14 :15 下午 org.springframework.test.context.support.AbstractTestContextBootstrapper getTestExecutionListeners 信息: Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@d 25987, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@101 b7af, org.springframework.test.context.support.DirtiesContextTestExecutionListener@3884 b2, org.springframework.test.context.event.EventPublishingTestExecutionListener@1275 dab] 九月 09 , 2020 2 :14 :16 下午 com.mchange.v2.log.MLog 信息: MLog clients using java 1.4 + standard logging. 九月 09 , 2020 2 :14 :17 下午 com.mchange.v2.c3p0.C3P0Registry 信息: Initializing c3p0-0.9 .5 .5 [built 11 -December-2019 22 :18 :33 -0800 ; debug? true ; trace: 10 ] 九月 09 , 2020 2 :14 :17 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource 信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3 , acquireRetryAttempts -> 30 , acquireRetryDelay -> 1000 , autoCommitOnClose -> false , automaticTestTable -> null , breakAfterAcquireFailure -> false , checkoutTimeout -> 0 , connectionCustomizerClassName -> null , connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1 hgeby9acvhq3mgqa4tsv|15 d6a01, debugUnreturnedConnectionStackTraces -> false , description -> null , driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null , forceIgnoreUnresolvedTransactions -> false , forceSynchronousCheckins -> false , forceUseNamedDriverClass -> false , identityToken -> 1 hgeby9acvhq3mgqa4tsv|15 d6a01, idleConnectionTestPeriod -> 0 , initialPoolSize -> 3 , jdbcUrl -> jdbc:mysql: java.lang.RuntimeException: java.lang.ArithmeticException: / by zero at club.guoshizhan.service.impl.AccountServiceImpl.transfer(AccountServiceImpl.java:118 )
由上述结果可知:程序报错了。但是,我们去数据库查一下,发现 Jack 用户没有少钱,Lucy 也没有多钱,这说明事务控制住了。那么转账小案例的操作到此就结束了!!! 接下来介绍一下 动态代理 相关的知识,用于解决上面还存在的一些问题(别以为事务解决了就没有问题了)。
动态代理实现事务控制 基于接口的动态代理 新建如下三个类,然后执行 Client 中的 main 方法即可。 代码如下:
IProducer.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package club.guoshizhan.proxy;public interface IProducer { public void saleProduct (float money) ; public void afterService (float money) ; }
Producer.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package club.guoshizhan.proxy;public class Producer implements IProducer { public void saleProduct (float money) { System.out.println("销售产品,并拿到钱:" + money); } public void afterService (float money) { System.out.println("提供售后服务,并拿到钱:" + money); } }
Client.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package club.guoshizhan.proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class Client { public static void main (String[] args) { final Producer producer = new Producer(); IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(), producer.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { Object returnValue = null ; Float money = (Float) args[0 ]; if ("saleProduct" .equals(method.getName())) { returnValue = method.invoke(producer, money * 0.8f ); } return returnValue; } }); proxyProducer.saleProduct(10000f ); } }
TIPS:
基于接口的动态代理 到此结束!!!
基于子类的动态代理 新建如下两个类,然后执行 Client 中的 main 方法即可。(注意:这些类和上面的类不在同一个包) 代码如下:
Producer.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package club.guoshizhan.cglib;public class Producer { public void saleProduct (float money) { System.out.println("销售产品,并拿到钱:" + money); } public void afterService (float money) { System.out.println("提供售后服务,并拿到钱:" + money); } }
Client.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package club.guoshizhan.cglib;import org.springframework.cglib.proxy.Enhancer;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class Client { public static void main (String[] args) { final Producer producer = new Producer(); Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() { @Override public Object intercept (Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object returnValue = null ; Float money = (Float) args[0 ]; if ("saleProduct" .equals(method.getName())) { returnValue = method.invoke(producer, money * 0.8f ); } return returnValue; } }); cglibProducer.saleProduct(12000f ); } }
TIPS:
基于子类的动态代理 到此结束!!!
AOP 的概念及使用 在软件业,AOP
为 Aspect Oriented Programming 的缩写,意为:面向切面编程
,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP 是 OOP 的延续,是软件开发中的一个热点,也是 Spring 框架中的一个重要内容,是函数式编程的一种衍生范型。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
简单地说:它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。 AOP 的作用:
在程序运行期间,不修改源码对已有方法进行增强。 AOP 的优势:
1、减少重复代码。2、提高开发效率。3、维护方便。 AOP 的实现方式:
使用动态代理技术。
接下来介绍一下 AOP
相关的术语及细节,方便以后查看文档时能够更好的理解。 相关术语如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点。 + Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。 + 所有的切入点都是连接点,但并不是所有的连接点都是切入点。那些被增强了的方法才能叫做切入点。 + Advice(通知/增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。 + Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field 。 + Target(目标对象):代理的目标对象。 + Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。 + Proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类。 + Aspect(切面):是切入点和通知(引介)的结合。
基于 XML 的 AOP 配置 第一步:
新建 Maven 工程,完善包结构。 然后导入如下依赖:
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <packaging > jar</packaging > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.2.1.RELEASE</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.8.7</version > </dependency > </dependencies >
第二步:
创建如下各个接口和类,根据 package
语句将类或接口放到对应的包中。 接口和类的代码如下:
IAccountService.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package club.guoshizhan.service;public interface IAccountService { void save () ; void update (int id) ; int delete () ; }
AccountServiceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package club.guoshizhan.service.impl;import club.guoshizhan.service.IAccountService;public class AccountServiceImpl implements IAccountService { @Override public void save () { System.out.println("执行了保存操作……" ); } @Override public void update (int id) { System.out.println("执行了更新操作……" ); } @Override public int delete () { System.out.println("执行了删除操作……" ); return 0 ; } }
Logger.java 1 2 3 4 5 6 7 8 9 10 11 12 13 package club.guoshizhan.utils;public class Logger { public void printLog () { System.out.println("Logger 类中的 printLog 方法开始记录日志了……" ); } }
第三步:
在 resources
目录下新建 bean.xml
配置文件。 代码如下:
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 <?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" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" > <bean id ="accountService" class ="club.guoshizhan.service.impl.AccountServiceImpl" > </bean > <bean id ="logger" class ="club.guoshizhan.utils.Logger" > </bean > <aop:config > <aop:aspect id ="logAdvice" ref ="logger" > <aop:before method ="printLog" pointcut ="execution(* club.guoshizhan.service.impl.*.*(..))" > </aop:before > </aop:aspect > </aop:config > </beans >
第四步:
在 test
目录下新建测试类,包名根据 package
语句自行创建。 代码如下:
AOPTest.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package club.guoshizhan.test;import club.guoshizhan.service.IAccountService;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class AOPTest { public static void main (String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml" ); IAccountService as = (IAccountService)ac.getBean("accountService" ); as.save(); as.update(1 ); as.delete(); } }
执行 AOPTest
测试类,结果如下:
1 2 3 4 5 6 7 8 Logger 类中的 printLog 方法开始记录日志了…… 执行了保存操作…… Logger 类中的 printLog 方法开始记录日志了…… 执行了更新操作…… Logger 类中的 printLog 方法开始记录日志了…… 执行了删除操作…… Process finished with exit code 0
由上述结果可知:我们已经成功实现了 AOP 的配置
。这部分核心内容就是如何去配置,以及 切入点表达式
的各种写法。 这个部分内容不多,重点关注 如何配置 以及 切入点表达式 即可。接下来介绍 四种常用的通知类型 。接着往下看!!!
四种常用的通知类型 第一步:
新建 Maven 工程,完善包结构。 然后导入如下依赖:
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <packaging > jar</packaging > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.2.1.RELEASE</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.8.7</version > </dependency > </dependencies >
第二步:
创建如下各个接口和类,根据 package
语句将类或接口放到对应的包中。 接口和类的代码如下:
IAccountService.java 1 2 3 4 5 6 7 8 9 10 11 package club.guoshizhan.service;public interface IAccountService { void save () ; }
AccountServiceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package club.guoshizhan.service.impl;import club.guoshizhan.service.IAccountService;public class AccountServiceImpl implements IAccountService { @Override public void save () { System.out.println("执行了保存操作……" ); } }
Logger.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package club.guoshizhan.utils;public class Logger { public void beforePrintLog () { System.out.println("前置通知:Logger 类中的 beforePrintLog 方法开始记录日志了……" ); } public void afterPrintLog () { System.out.println("后置通知:Logger 类中的 afterPrintLog 方法开始记录日志了……" ); } public void exceptionPrintLog () { System.out.println("异常通知:Logger 类中的 exceptionPrintLog 方法开始记录日志了……" ); } public void finalPrintLog () { System.out.println("最终通知:Logger 类中的 finalPrintLog 方法开始记录日志了……" ); } }
第三步:
在 resources
目录下新建 bean.xml
配置文件。 代码如下:
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <?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" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" > <bean id ="accountService" class ="club.guoshizhan.service.impl.AccountServiceImpl" > </bean > <bean id ="logger" class ="club.guoshizhan.utils.Logger" > </bean > <aop:config > <aop:aspect id ="logAdvice" ref ="logger" > <aop:before method ="beforePrintLog" pointcut ="execution(* club.guoshizhan.service.impl.*.*(..))" > </aop:before > <aop:after-returning method ="afterPrintLog" pointcut ="execution(* club.guoshizhan.service.impl.*.*(..))" > </aop:after-returning > <aop:after-throwing method ="exceptionPrintLog" pointcut ="execution(* club.guoshizhan.service.impl.*.*(..))" > </aop:after-throwing > <aop:after method ="finalPrintLog" pointcut ="execution(* club.guoshizhan.service.impl.*.*(..))" > </aop:after > </aop:aspect > </aop:config > </beans >
第四步:
在 test
目录下新建测试类,包名根据 package
语句自行创建。 代码如下:
AOPTest.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package club.guoshizhan.test;import club.guoshizhan.service.IAccountService;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class AOPTest { public static void main (String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml" ); IAccountService as = (IAccountService)ac.getBean("accountService" ); as.save(); } }
执行 AOPTest
测试类,结果如下:
1 2 3 4 5 6 前置通知:Logger 类中的 beforePrintLog 方法开始记录日志了…… 执行了保存操作…… 后置通知:Logger 类中的 afterPrintLog 方法开始记录日志了…… 最终通知:Logger 类中的 finalPrintLog 方法开始记录日志了…… Process finished with exit code 0
由上述结果可知:我们已经成功实现了 四种常用的通知类型的配置
。 但是还有点小问题:在配置文件中配置各种通知时,里面有这么一个属性: apointcut="execution(* club.guoshizhan.service.impl.*.*(..))"
, 我觉得这太繁琐了,能不能优化一下呢?安排!!!(把配置文件按照如下修改,再运行测试方法即可)
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <aop:config > <aop:aspect id ="logAdvice" ref ="logger" > <aop:before method ="beforePrintLog" pointcut-ref ="springAdvice" > </aop:before > <aop:after-returning method ="afterPrintLog" pointcut-ref ="springAdvice" > </aop:after-returning > <aop:after-throwing method ="exceptionPrintLog" pointcut-ref ="springAdvice" > </aop:after-throwing > <aop:after method ="finalPrintLog" pointcut-ref ="springAdvice" > </aop:after > <aop:pointcut id ="springAdvice" expression ="execution(* club.guoshizhan.service.impl.*.*(..))" > </aop:pointcut > </aop:aspect > </aop:config >
TIPS:
修改完配置之后,运行测试类即可。到此为止,四种常用的通知类型
到此结束!!!(接下来介绍 环绕通知
)
Spring 中的环绕通知 第一步:
在 Logger
类中加入如下方法。 代码如下:
Logger.java 1 2 3 public void aroundPrintLog () { System.out.println("Logger 类中的 aroundPrintLog 方法开始记录日志了……" ); }
第二步:
在 bean.xml
配置文件中,把配置切面部分改为如下配置。 代码如下:
bean.xml 1 2 3 4 5 6 <aop:aspect id ="logAdvice" ref ="logger" > <aop:pointcut id ="springAdvice" expression ="execution(* club.guoshizhan.service.impl.*.*(..))" > </aop:pointcut > <aop:around method ="aroundPrintLog" pointcut-ref ="springAdvice" > </aop:around > </aop:aspect >
第三步:
执行测试类。 结果如下:
1 2 3 Logger 类中的 aroundPrintLog 方法开始记录日志了…… Process finished with exit code 0
由上面结果发现:save 方法中的语句并没有被执行,这是为什么呢? 接下来就解决这个问题。
把上面的 aroundPrintLog
方法修改为下面代码(问题原因和解决方式写于注释之中):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public Object aroundPrintLog (ProceedingJoinPoint pjp) { Object rtValue = null ; try { Object[] args = pjp.getArgs(); System.out.println("Logger 类中的 aroundPrintLog 方法开始记录日志了……前置通知" ); rtValue = pjp.proceed(args); System.out.println("Logger 类中的 aroundPrintLog 方法开始记录日志了……后置通知" ); return rtValue; } catch (Throwable t) { System.out.println("Logger 类中的 aroundPrintLog 方法开始记录日志了……异常通知" ); throw new RuntimeException(t); } finally { System.out.println("Logger 类中的 aroundPrintLog 方法开始记录日志了……最终通知" ); } }
修改完成之后,我们去测试类进行测试。 结果如下:
1 2 3 4 5 6 Logger 类中的 aroundPrintLog 方法开始记录日志了……前置通知 执行了保存操作…… Logger 类中的 aroundPrintLog 方法开始记录日志了……后置通知 Logger 类中的 aroundPrintLog 方法开始记录日志了……最终通知 Process finished with exit code 0
TIPS:
运行结果非常 OK ,完全没有问题。 那么 环绕通知
就到此结束了!!!接下来介绍 基于注解的 AOP 配置。
基于注解的 AOP 配置 第一步:
新建 Maven 工程,完善包结构。 然后导入如下依赖:
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <packaging > jar</packaging > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.2.1.RELEASE</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.8.7</version > </dependency > </dependencies >
第二步:
创建如下各个接口和类(注解 AOP 就配置在下面的类中)。 接口和类的代码如下:
IAccountService.java 1 2 3 4 5 6 7 8 9 10 11 package club.guoshizhan.service;public interface IAccountService { void save () ; }
AccountServiceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package club.guoshizhan.service.impl;import club.guoshizhan.service.IAccountService;import org.springframework.stereotype.Service;@Service ("accountService" )public class AccountServiceImpl implements IAccountService { @Override public void save () { System.out.println("执行了保存操作……" ); } }
Logger.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package club.guoshizhan.utils;import org.aspectj.lang.annotation.*;import org.springframework.stereotype.Component;@Component ("logger" )@Aspect public class Logger { @Pointcut ("execution(* club.guoshizhan.service.impl.*.*(..))" ) private void springAdvice () {} @Before ("springAdvice()" ) public void beforePrintLog () { System.out.println("前置通知:Logger 类中的 beforePrintLog 方法开始记录日志了……" ); } @AfterReturning ("springAdvice()" ) public void afterPrintLog () { System.out.println("后置通知:Logger 类中的 afterPrintLog 方法开始记录日志了……" ); } @AfterThrowing ("springAdvice()" ) public void exceptionPrintLog () { System.out.println("异常通知:Logger 类中的 exceptionPrintLog 方法开始记录日志了……" ); } @After ("springAdvice()" ) public void finalPrintLog () { System.out.println("最终通知:Logger 类中的 finalPrintLog 方法开始记录日志了……" ); } }
第三步:
在 resources
目录下新建 bean.xml
配置文件。 代码如下:
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <context:component-scan base-package ="club.guoshizhan" > </context:component-scan > <aop:aspectj-autoproxy > </aop:aspectj-autoproxy > </beans >
第四步:
在 test
目录下新建测试类,包名根据 package
语句自行创建。 代码如下:
AOPTest.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package club.guoshizhan.test;import club.guoshizhan.service.IAccountService;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class AOPTest { public static void main (String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml" ); IAccountService as = (IAccountService)ac.getBean("accountService" ); as.save(); } }
执行 AOPTest
测试类,结果如下:
1 2 3 4 5 6 前置通知:Logger 类中的 beforePrintLog 方法开始记录日志了…… 执行了保存操作…… 最终通知:Logger 类中的 finalPrintLog 方法开始记录日志了…… 后置通知:Logger 类中的 afterPrintLog 方法开始记录日志了…… Process finished with exit code 0
由上述结果可知:我们已经成功实现了 基于注解的 AOP 配置
。 但是还有点小问题:在结果中,最终通知在后置通知的前面,不知道你有没有发现。 这个小问题是存在的,没办法解决,官方的 bug (调用顺序错误)。所以在选择使用注解配置还是 XML 配置时,这个小问题需要考虑一下。 如果使用 环绕通知
,会出现这个小 bug 吗?接着往下看!!!
把 Logger
类修改为如下代码(使用环绕通知):
Logger.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package club.guoshizhan.utils;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.springframework.stereotype.Component;@Component ("logger" )@Aspect public class Logger { @Pointcut ("execution(* club.guoshizhan.service.impl.*.*(..))" ) private void springAdvice () {} @Around ("springAdvice()" ) public Object aroundPrintLog (ProceedingJoinPoint pjp) { Object rtValue = null ; try { Object[] args = pjp.getArgs(); System.out.println("Logger 类中的 aroundPrintLog 方法开始记录日志了……前置通知" ); rtValue = pjp.proceed(args); System.out.println("Logger 类中的 aroundPrintLog 方法开始记录日志了……后置通知" ); return rtValue; } catch (Throwable t) { System.out.println("Logger 类中的 aroundPrintLog 方法开始记录日志了……异常通知" ); throw new RuntimeException(t); } finally { System.out.println("Logger 类中的 aroundPrintLog 方法开始记录日志了……最终通知" ); } } }
然后执行 AOPTest
测试类,结果如下:
1 2 3 4 5 6 Logger 类中的 aroundPrintLog 方法开始记录日志了……前置通知 执行了保存操作…… Logger 类中的 aroundPrintLog 方法开始记录日志了……后置通知 Logger 类中的 aroundPrintLog 方法开始记录日志了……最终通知 Process finished with exit code 0
由结果可知:
使用环绕通知不会出现上述的问题(毕竟是我们自己写的代码,自己定义的) 。到此为止,基于注解的 AOP 配置 到此结束!!!
TIPS:
能实现纯注解配置吗?当然可以。配置如下:
SpringConfiguration.java 1 2 3 4 @Configuration @ComponentScan (basePackages="club.guoshizhan" )@EnableAspectJAutoProxy public class SpringConfiguration {}
Spring 中的 JdbcTemplate JdbcTemplate 环境搭建 第一步:
新建 Maven 工程,完善包结构。 然后导入如下依赖:
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <packaging > jar</packaging > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.2.1.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-jdbc</artifactId > <version > 5.2.1.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-tx</artifactId > <version > 5.2.1.RELEASE</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.46</version > </dependency > </dependencies >
第二步:
创建如下 Account
实体类和 JdbcTemplateTest
测试类。 接口和类的代码如下:
Account.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package club.guoshizhan.domain;import java.io.Serializable;public class Account implements Serializable { private Integer id; private String name; private Float money; public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public Float getMoney () { return money; } public void setMoney (Float money) { this .money = money; } @Override public String toString () { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}' ; } }
JdbcTemplateTest.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package club.guoshizhan.jdbctemplate;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.datasource.DriverManagerDataSource;public class JdbcTemplateTest { public static void main (String[] args) { DriverManagerDataSource ds = new DriverManagerDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver" ); ds.setUrl("jdbc:mysql://localhost:3306/spring" ); ds.setUsername("root" ); ds.setPassword("root" ); JdbcTemplate jt = new JdbcTemplate(); jt.setDataSource(ds); jt.execute("insert into account(name, money) values ('Mary', 1000)" ); } }
TIPS:
执行测试类中的 main 方法,然后去数据库查看一下,发现新加了一条数据。 即上述代码完成了添加操作。那么上述代码有没有问题呢?能否改呢? 上述代码的数据源写死了,应该写在配置文件中。 那就来操作一下吧!!!
第三步:
新建 dao
接口和实现类。 接口和类的代码如下:
IAccountDao.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package club.guoshizhan.dao;import club.guoshizhan.domain.Account;public interface IAccountDao { Account findById (Integer accountId) ; Account findByName (String accountName) ; void update (Account account) ; }
AccountDaoImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package club.guoshizhan.dao.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import org.springframework.jdbc.core.BeanPropertyRowMapper;import org.springframework.jdbc.core.JdbcTemplate;import java.util.List;public class AccountDaoImpl implements IAccountDao { private JdbcTemplate jdbcTemplate; public void setJdbcTemplate (JdbcTemplate jdbcTemplate) { this .jdbcTemplate = jdbcTemplate; } @Override public Account findById (Integer accountId) { List<Account> accounts = jdbcTemplate.query("select * from account where id = ?" , new BeanPropertyRowMapper<>(Account.class ), accountId ) ; return accounts.isEmpty() ? null : accounts.get(0 ); } @Override public Account findByName (String accountName) { List<Account> accounts = jdbcTemplate.query("select * from account where name = ?" , new BeanPropertyRowMapper<>(Account.class ), accountName ) ; if (accounts.isEmpty()) { return null ; } if (accounts.size() > 1 ) { throw new RuntimeException("结果集不唯一!" ); } return accounts.get(0 ); } @Override public void update (Account account) { jdbcTemplate.update("update account set name=?,money=? where id=?" , account.getName(), account.getMoney(), account.getId()); } }
第四步:
在 resources
目录下新建 bean.xml
配置文件。 代码如下:
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?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 ="accountDao" class ="club.guoshizhan.dao.impl.AccountDaoImpl" > <property name ="jdbcTemplate" ref ="jdbcTemplate" > </property > </bean > <bean id ="jdbcTemplate" class ="org.springframework.jdbc.core.JdbcTemplate" > <property name ="dataSource" ref ="dataSource" > </property > </bean > <bean id ="dataSource" class ="org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name ="driverClassName" value ="com.mysql.jdbc.Driver" > </property > <property name ="url" value ="jdbc:mysql://localhost:3306/spring" > </property > <property name ="username" value ="root" > </property > <property name ="password" value ="root" > </property > </bean > </beans >
第五步:
修改 JdbcTemplateTest
测试类。 代码如下:
AOPTest.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package club.guoshizhan.jdbctemplate;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class JdbcTemplateTest { public static void main (String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml" ); IAccountDao accountDao = ac.getBean("accountDao" , IAccountDao.class ) ; Account account = accountDao.findById(1 ); System.out.println(account); account.setMoney(2000f ); accountDao.update(account); Account jack = accountDao.findByName("Mary" ); System.out.println(jack); } }
执行测试类中的 main
方法,结果如下(数据库也是发生了改变的):。
1 2 3 4 Account{id=1 , name='Jack' , money=2000.0 } Account{id=4 , name='Mary' , money=1000.0 } Process finished with exit code 0
TIPS:
到此为止,JdbcTemplate 环境搭建 就告一段落了!!!接下来介绍 JdbcTemplate 实现 CRUD 的操作。继续往下看!!!
JdbcTemplate 实现 CRUD 我们使用上述工程的代码,修改 JdbcTemplateTest 测试类即可。 代码如下:
JdbcTemplateTest.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 package club.guoshizhan.jdbctemplate;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.core.RowMapper;import java.sql.ResultSet;import java.sql.SQLException;public class JdbcTemplateTest { public static void main (String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml" ); JdbcTemplate jt = ac.getBean("jdbcTemplate" , JdbcTemplate.class ) ; Long count = jt.queryForObject("select count(*) from account where money > ?" , Long.class , 900f ) ; System.out.println(count); } } class AccountRowMapper implements RowMapper <Account > { @Override public Account mapRow (ResultSet rs, int rowNum) throws SQLException { Account account = new Account(); account.setId(rs.getInt("id" )); account.setName(rs.getString("name" )); account.setMoney(rs.getFloat("money" )); return account; } }
TIPS:
需要执行哪一个操作,就放开哪一个操作。 这就实现了 JdbcTemplate 的 CRUD 操作。
基于 XML 的 AOP 事务控制 第一步:
新建 Maven 工程,完善包结构。 然后导入如下依赖:
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <packaging > jar</packaging > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-test</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > commons-dbutils</groupId > <artifactId > commons-dbutils</artifactId > <version > 1.4</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.6</version > </dependency > <dependency > <groupId > c3p0</groupId > <artifactId > c3p0</artifactId > <version > 0.9.1.2</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.8.7</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-tx</artifactId > <version > 5.0.2.RELEASE</version > </dependency > </dependencies >
第二步:
创建如下各个接口和类,根据 package
语句将类或接口放到对应的包中。 接口和类的代码如下:
Account.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package club.guoshizhan.domain;import java.io.Serializable;public class Account implements Serializable { private Integer id; private String name; private Float money; public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public Float getMoney () { return money; } public void setMoney (Float money) { this .money = money; } @Override public String toString () { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}' ; } }
ConnectionUtils.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package club.guoshizhan.utils;import javax.sql.DataSource;import java.sql.Connection;public class ConnectionUtils { private ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>(); private DataSource dataSource; public void setDataSource (DataSource dataSource) { this .dataSource = dataSource; } public Connection getThreadConnection () { try { Connection conn = threadLocal.get(); if (conn == null ) { conn = dataSource.getConnection(); threadLocal.set(conn); } return conn; } catch (Exception e) { throw new RuntimeException(e); } } public void removeConnection () { threadLocal.remove(); } }
TransactionManager.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package club.guoshizhan.utils;public class TransactionManager { private ConnectionUtils connectionUtils; public void setConnectionUtils (ConnectionUtils connectionUtils) { this .connectionUtils = connectionUtils; } public void beginTransaction () { try { connectionUtils.getThreadConnection().setAutoCommit(false ); } catch (Exception e) { e.printStackTrace(); } } public void commit () { try { connectionUtils.getThreadConnection().commit(); } catch (Exception e) { e.printStackTrace(); } } public void rollback () { try { connectionUtils.getThreadConnection().rollback(); } catch (Exception e) { e.printStackTrace(); } } public void release () { try { connectionUtils.getThreadConnection().close(); connectionUtils.removeConnection(); } catch (Exception e) { e.printStackTrace(); } } }
IAccountDao.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package club.guoshizhan.dao;import club.guoshizhan.domain.Account;import java.util.List;public interface IAccountDao { List<Account> findAll () ; Account findById (Integer accountId) ; void save (Account account) ; void update (Account account) ; void delete (Integer acccountId) ; Account findByName (String accountName) ; }
AccountDaoImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 package club.guoshizhan.dao.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import club.guoshizhan.utils.ConnectionUtils;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.handlers.BeanHandler;import org.apache.commons.dbutils.handlers.BeanListHandler;import java.util.List;public class AccountDaoImpl implements IAccountDao { private QueryRunner runner; private ConnectionUtils connectionUtils; public void setRunner (QueryRunner runner) { this .runner = runner; } public void setConnectionUtils (ConnectionUtils connectionUtils) { this .connectionUtils = connectionUtils; } @Override public List<Account> findAll () { try { return runner.query(connectionUtils.getThreadConnection(), "select * from account" , new BeanListHandler<Account>(Account.class )) ; } catch (Exception e) { throw new RuntimeException(e); } } @Override public Account findById (Integer accountId) { try { return runner.query(connectionUtils.getThreadConnection(), "select * from account where id = ? " , new BeanHandler<Account>(Account.class ), accountId ) ; } catch (Exception e) { throw new RuntimeException(e); } } @Override public void save (Account account) { try { runner.update(connectionUtils.getThreadConnection(), "insert into account(name,money)values(?,?)" , account.getName(), account.getMoney()); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void update (Account account) { try { runner.update(connectionUtils.getThreadConnection(), "update account set name=?,money=? where id=?" , account.getName(), account.getMoney(), account.getId()); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void delete (Integer accountId) { try { runner.update(connectionUtils.getThreadConnection(), "delete from account where id=?" , accountId); } catch (Exception e) { throw new RuntimeException(e); } } @Override public Account findByName (String accountName) { try { List<Account> accounts = runner.query(connectionUtils.getThreadConnection(), "select * from account where name = ? " , new BeanListHandler<>(Account.class ), accountName ) ; if (accounts == null || accounts.size() == 0 ) { return null ; } if (accounts.size() > 1 ) { throw new RuntimeException("结果集不唯一,数据有问题" ); } return accounts.get(0 ); } catch (Exception e) { throw new RuntimeException(e); } } }
IAccountService.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package club.guoshizhan.service;import club.guoshizhan.domain.Account;import java.util.List;public interface IAccountService { List<Account> findAllAccount () ; Account findAccountById (Integer accountId) ; void saveAccount (Account account) ; void updateAccount (Account account) ; void deleteAccount (Integer acccountId) ; void transfer (String sourceName, String targetName, Float money) ; void test () ; }
AccountServiceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 package club.guoshizhan.service.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import club.guoshizhan.service.IAccountService;import java.util.List;public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao; public void setAccountDao (IAccountDao accountDao) { this .accountDao = accountDao; } @Override public List<Account> findAllAccount () { return accountDao.findAll(); } @Override public Account findAccountById (Integer id) { return accountDao.findById(id); } @Override public void saveAccount (Account account) { accountDao.save(account); } @Override public void updateAccount (Account account) { accountDao.update(account); } @Override public void deleteAccount (Integer id) { accountDao.delete(id); } @Override public void transfer (String sourceName, String targetName, Float money) { Account source = accountDao.findByName(sourceName); Account target = accountDao.findByName(targetName); source.setMoney(source.getMoney() - money); target.setMoney(target.getMoney() + money); accountDao.update(source); accountDao.update(target); } @Override public void test () { System.out.println("test……" ); } }
第三步:
在 resources
目录下新建 bean.xml
配置文件。 代码如下:
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 <?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" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" > <bean id ="accountService" class ="club.guoshizhan.service.impl.AccountServiceImpl" > <property name ="accountDao" ref ="accountDao" > </property > </bean > <bean id ="accountDao" class ="club.guoshizhan.dao.impl.AccountDaoImpl" > <property name ="runner" ref ="runner" > </property > <property name ="connectionUtils" ref ="connectionUtils" > </property > </bean > <bean id ="runner" class ="org.apache.commons.dbutils.QueryRunner" scope ="prototype" > </bean > <bean id ="dataSource" class ="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name ="driverClass" value ="com.mysql.jdbc.Driver" > </property > <property name ="jdbcUrl" value ="jdbc:mysql://localhost:3306/spring" > </property > <property name ="user" value ="root" > </property > <property name ="password" value ="root" > </property > </bean > <bean id ="connectionUtils" class ="club.guoshizhan.utils.ConnectionUtils" > <property name ="dataSource" ref ="dataSource" > </property > </bean > <bean id ="txManager" class ="club.guoshizhan.utils.TransactionManager" > <property name ="connectionUtils" ref ="connectionUtils" > </property > </bean > <aop:config > <aop:pointcut id ="pt1" expression ="execution(* club.guoshizhan.service.impl.*.*(..))" > </aop:pointcut > <aop:aspect id ="txAdvice" ref ="txManager" > <aop:before method ="beginTransaction" pointcut-ref ="pt1" > </aop:before > <aop:after-returning method ="commit" pointcut-ref ="pt1" > </aop:after-returning > <aop:after-throwing method ="rollback" pointcut-ref ="pt1" > </aop:after-throwing > <aop:after method ="release" pointcut-ref ="pt1" > </aop:after > </aop:aspect > </aop:config > </beans >
第四步:
在 test
目录下新建测试类,包名根据 package
语句自行创建。 代码如下:
AOPTest.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package club.guoshizhan.test;import club.guoshizhan.service.IAccountService;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith (SpringJUnit4ClassRunner.class ) @ContextConfiguration (locations = "classpath:bean.xml" )public class AOPTest { @Autowired private IAccountService accountService; @Test public void testTransfer () { accountService.transfer("Jack" , "Mary" , 500f ); } }
执行 AOPTest
测试类中的 testTransfer
方法,结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 九月 11 , 2020 10 :47 :04 上午 org.springframework.test.context.support.AbstractTestContextBootstrapper getDefaultTestExecutionListenerClassNames 信息: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener] 九月 11 , 2020 10 :47 :04 上午 org.springframework.test.context.support.AbstractTestContextBootstrapper instantiateListeners 信息: Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext] 九月 11, 2020 10:47:04 上午 org.springframework.test.context.support.AbstractTestContextBootstrapper getTestExecutionListeners 信息: Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@190c9c3, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@e6e9c3, org.springframework.test.context.support.DirtiesContextTestExecutionListener@56dfcb, org.springframework.test.context.transaction.TransactionalTestExecutionListener@174b225, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@69b199] 九月 11, 2020 10:47:04 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [bean.xml] 九月 11, 2020 10:47:05 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.GenericApplicationContext@d62f43: startup date [Fri Sep 11 10:47:05 CST 2020] ; root of context hierarchy九月 11 , 2020 10 :47 :05 上午 com.mchange.v2.log.MLog <clinit> 信息: MLog clients using java 1.4 + standard logging. 九月 11 , 2020 10 :47 :06 上午 com.mchange.v2.c3p0.C3P0Registry banner 信息: Initializing c3p0-0.9 .1 .2 [built 21 -May-2007 15 :04 :56 ; debug? true ; trace: 10 ] 九月 11 , 2020 10 :47 :06 上午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager 信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3 , acquireRetryAttempts -> 30 , acquireRetryDelay -> 1000 , autoCommitOnClose -> false , automaticTestTable -> null , breakAfterAcquireFailure -> false , checkoutTimeout -> 0 , connectionCustomizerClassName -> null , connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1 hgeby9acy57d3k19bnfa0|167 eb2a, debugUnreturnedConnectionStackTraces -> false , description -> null , driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null , forceIgnoreUnresolvedTransactions -> false , identityToken -> 1 hgeby9acy57d3k19bnfa0|167 eb2a, idleConnectionTestPeriod -> 0 , initialPoolSize -> 3 , jdbcUrl -> jdbc:mysql: Process finished with exit code 0
TIPS:
然后去数据库查询一下,发现转账成功。 基于 XML 的 AOP 事务控制
到此结束!!!
基于注解的 AOP 事务控制 第一步:
新建 Maven 工程,完善包结构。 然后导入如下依赖:
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <packaging > jar</packaging > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-test</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > commons-dbutils</groupId > <artifactId > commons-dbutils</artifactId > <version > 1.4</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.6</version > </dependency > <dependency > <groupId > c3p0</groupId > <artifactId > c3p0</artifactId > <version > 0.9.1.2</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.8.7</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-tx</artifactId > <version > 5.0.2.RELEASE</version > </dependency > </dependencies >
第二步:
创建如下各个接口和类,根据 package
语句将类或接口放到对应的包中。 接口和类的代码如下:
Account.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package club.guoshizhan.domain;import java.io.Serializable;public class Account implements Serializable { private Integer id; private String name; private Float money; public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public Float getMoney () { return money; } public void setMoney (Float money) { this .money = money; } @Override public String toString () { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}' ; } }
ConnectionUtils.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package club.guoshizhan.utils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import javax.sql.DataSource;import java.sql.Connection;@Component ("connectionUtils" )public class ConnectionUtils { private ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); @Autowired private DataSource dataSource; public Connection getThreadConnection () { try { Connection conn = threadLocal.get(); if (conn == null ) { conn = dataSource.getConnection(); threadLocal.set(conn); } return conn; } catch (Exception e) { throw new RuntimeException(e); } } public void removeConnection () { threadLocal.remove(); } }
TransactionManager.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 package club.guoshizhan.utils;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;@Component ("txManager" )@Aspect public class TransactionManager { @Autowired private ConnectionUtils connectionUtils; @Pointcut ("execution(* club.guoshizhan.service.impl.*.*(..))" ) private void springAdvice () { } public void beginTransaction () { try { connectionUtils.getThreadConnection().setAutoCommit(false ); } catch (Exception e) { e.printStackTrace(); } } public void commit () { try { connectionUtils.getThreadConnection().commit(); } catch (Exception e) { e.printStackTrace(); } } public void rollback () { try { connectionUtils.getThreadConnection().rollback(); } catch (Exception e) { e.printStackTrace(); } } public void release () { try { connectionUtils.getThreadConnection().close(); connectionUtils.removeConnection(); } catch (Exception e) { e.printStackTrace(); } } @Around ("springAdvice()" ) public Object aroundPrintLog (ProceedingJoinPoint pjp) { Object rtValue = null ; try { Object[] args = pjp.getArgs(); this .beginTransaction(); rtValue = pjp.proceed(args); this .commit(); return rtValue; } catch (Throwable t) { this .rollback(); throw new RuntimeException(t); } finally { this .release(); } } }
AccountDaoImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 package club.guoshizhan.dao.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import club.guoshizhan.utils.ConnectionUtils;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.handlers.BeanHandler;import org.apache.commons.dbutils.handlers.BeanListHandler;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Repository;import java.util.List;@Repository ("accountDao" )public class AccountDaoImpl implements IAccountDao { @Autowired private QueryRunner runner; @Autowired private ConnectionUtils connectionUtils; @Override public List<Account> findAll () { try { return runner.query(connectionUtils.getThreadConnection(), "select * from account" , new BeanListHandler<>(Account.class )) ; } catch (Exception e) { throw new RuntimeException(e); } } @Override public Account findById (Integer accountId) { try { return runner.query(connectionUtils.getThreadConnection(), "select * from account where id = ? " , new BeanHandler<>(Account.class ), accountId ) ; } catch (Exception e) { throw new RuntimeException(e); } } @Override public void save (Account account) { try { runner.update(connectionUtils.getThreadConnection(), "insert into account(name,money)values(?,?)" , account.getName(), account.getMoney()); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void update (Account account) { try { runner.update(connectionUtils.getThreadConnection(), "update account set name=?,money=? where id=?" , account.getName(), account.getMoney(), account.getId()); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void delete (Integer accountId) { try { runner.update(connectionUtils.getThreadConnection(), "delete from account where id=?" , accountId); } catch (Exception e) { throw new RuntimeException(e); } } @Override public Account findByName (String accountName) { try { List<Account> accounts = runner.query(connectionUtils.getThreadConnection(), "select * from account where name = ? " , new BeanListHandler<>(Account.class ), accountName ) ; if (accounts == null || accounts.size() == 0 ) { return null ; } if (accounts.size() > 1 ) { throw new RuntimeException("结果集不唯一,数据有问题" ); } return accounts.get(0 ); } catch (Exception e) { throw new RuntimeException(e); } } }
IAccountService.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package club.guoshizhan.service;import club.guoshizhan.domain.Account;import java.util.List;public interface IAccountService { List<Account> findAllAccount () ; Account findAccountById (Integer accountId) ; void saveAccount (Account account) ; void updateAccount (Account account) ; void deleteAccount (Integer acccountId) ; void transfer (String sourceName, String targetName, Float money) ; void test () ; }
AccountServiceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 package club.guoshizhan.service.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import club.guoshizhan.service.IAccountService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Service ("accountService" )public class AccountServiceImpl implements IAccountService { @Autowired private IAccountDao accountDao; @Override public List<Account> findAllAccount () { return accountDao.findAll(); } @Override public Account findAccountById (Integer id) { return accountDao.findById(id); } @Override public void saveAccount (Account account) { accountDao.save(account); } @Override public void updateAccount (Account account) { accountDao.update(account); } @Override public void deleteAccount (Integer id) { accountDao.delete(id); } @Override public void transfer (String sourceName, String targetName, Float money) { Account source = accountDao.findByName(sourceName); Account target = accountDao.findByName(targetName); source.setMoney(source.getMoney() - money); target.setMoney(target.getMoney() + money); accountDao.update(source); accountDao.update(target); } @Override public void test () { System.out.println("test……" ); } }
第三步:
在 resources
目录下新建 bean.xml
配置文件。 代码如下:
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <?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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <context:component-scan base-package ="club.guoshizhan" > </context:component-scan > <bean id ="runner" class ="org.apache.commons.dbutils.QueryRunner" scope ="prototype" > </bean > <bean id ="dataSource" class ="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name ="driverClass" value ="com.mysql.jdbc.Driver" > </property > <property name ="jdbcUrl" value ="jdbc:mysql://localhost:3306/spring" > </property > <property name ="user" value ="root" > </property > <property name ="password" value ="root" > </property > </bean > <aop:aspectj-autoproxy > </aop:aspectj-autoproxy > </beans >
第四步:
在 test
目录下新建测试类,包名根据 package
语句自行创建。 代码如下:
AOPTest.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package club.guoshizhan.test;import club.guoshizhan.service.IAccountService;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith (SpringJUnit4ClassRunner.class ) @ContextConfiguration (locations = "classpath:bean.xml" )public class AOPTest { @Autowired private IAccountService accountService; @Test public void testTransfer () { accountService.transfer("Lucy" , "Lisa" , 100f ); } }
执行 AOPTest
测试类中的 testTransfer
方法,结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 九月 11 , 2020 2 :04 :32 下午 org.springframework.test.context.support.AbstractTestContextBootstrapper getDefaultTestExecutionListenerClassNames 信息: Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener] 九月 11 , 2020 2 :04 :32 下午 org.springframework.test.context.support.AbstractTestContextBootstrapper instantiateListeners 信息: Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext] 九月 11, 2020 2:04:32 下午 org.springframework.test.context.support.AbstractTestContextBootstrapper getTestExecutionListeners 信息: Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@190c9c3, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@e6e9c3, org.springframework.test.context.support.DirtiesContextTestExecutionListener@56dfcb, org.springframework.test.context.transaction.TransactionalTestExecutionListener@174b225, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@69b199] 九月 11, 2020 2:04:32 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [bean.xml] 九月 11, 2020 2:04:33 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.GenericApplicationContext@d62f43: startup date [Fri Sep 11 14:04:32 CST 2020] ; root of context hierarchy九月 11 , 2020 2 :04 :33 下午 com.mchange.v2.log.MLog <clinit> 信息: MLog clients using java 1.4 + standard logging. 九月 11 , 2020 2 :04 :34 下午 com.mchange.v2.c3p0.C3P0Registry banner 信息: Initializing c3p0-0.9 .1 .2 [built 21 -May-2007 15 :04 :56 ; debug? true ; trace: 10 ] 九月 11 , 2020 2 :04 :34 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager 信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3 , acquireRetryAttempts -> 30 , acquireRetryDelay -> 1000 , autoCommitOnClose -> false , automaticTestTable -> null , breakAfterAcquireFailure -> false , checkoutTimeout -> 0 , connectionCustomizerClassName -> null , connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1 hgeby9acyc9b0k1ixj7bx|3 bfa16, debugUnreturnedConnectionStackTraces -> false , description -> null , driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null , forceIgnoreUnresolvedTransactions -> false , identityToken -> 1 hgeby9acyc9b0k1ixj7bx|3 bfa16, idleConnectionTestPeriod -> 0 , initialPoolSize -> 3 , jdbcUrl -> jdbc:mysql: Process finished with exit code 0
TIPS:
然后去数据库查询一下,发现转账成功。(重点是需要关注环绕通知,其他的四种通知有顺序问题,从而会导致程序执行失败。) 基于注解的 AOP 事务控制
到此结束!!!
Spring 中的声明式事务 环境搭建 第一步:
新建 Maven 工程,完善包结构。 然后导入如下依赖:
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <packaging > jar</packaging > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-jdbc</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-tx</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.46</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.8.7</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-test</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13</version > <scope > test</scope > </dependency > </dependencies >
第二步:
创建如下各个接口和类,根据 package
语句将类或接口放到对应的包中。 接口和类的代码如下:
Account.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package club.guoshizhan.domain;import java.io.Serializable;public class Account implements Serializable { private Integer id; private String name; private Float money; public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public Float getMoney () { return money; } public void setMoney (Float money) { this .money = money; } @Override public String toString () { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}' ; } }
IAccountDao.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package club.guoshizhan.dao;import club.guoshizhan.domain.Account;public interface IAccountDao { Account findAccountById (Integer accountId) ; Account findAccountByName (String accountName) ; void updateAccount (Account account) ; }
AccountDaoImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package club.guoshizhan.dao.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import org.springframework.jdbc.core.BeanPropertyRowMapper;import org.springframework.jdbc.core.support.JdbcDaoSupport;import java.util.List;public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao { @Override public Account findAccountById (Integer accountId) { List<Account> accounts = super .getJdbcTemplate().query("select * from account where id = ?" , new BeanPropertyRowMapper<Account>(Account.class ), accountId ) ; return accounts.isEmpty() ? null : accounts.get(0 ); } @Override public Account findAccountByName (String accountName) { List<Account> accounts = super .getJdbcTemplate().query("select * from account where name = ?" , new BeanPropertyRowMapper<Account>(Account.class ), accountName ) ; if (accounts.isEmpty()) { return null ; } if (accounts.size() > 1 ) { throw new RuntimeException("结果集不唯一" ); } return accounts.get(0 ); } @Override public void updateAccount (Account account) { super .getJdbcTemplate().update("update account set name=?,money=? where id=?" , account.getName(), account.getMoney(), account.getId()); } }
IAccountService.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package club.guoshizhan.service;import club.guoshizhan.domain.Account;public interface IAccountService { Account findAccountById (Integer accountId) ; void transfer (String sourceName, String targetName, Float money) ; }
AccountServiceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package club.guoshizhan.service.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import club.guoshizhan.service.IAccountService;public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao; public void setAccountDao (IAccountDao accountDao) { this .accountDao = accountDao; } @Override public Account findAccountById (Integer accountId) { return accountDao.findAccountById(accountId); } @Override public void transfer (String sourceName, String targetName, Float money) { Account source = accountDao.findAccountByName(sourceName); Account target = accountDao.findAccountByName(targetName); source.setMoney(source.getMoney() - money); target.setMoney(target.getMoney() + money); accountDao.updateAccount(source); accountDao.updateAccount(target); } }
第三步:
在 resources
目录下新建 bean.xml
配置文件。 代码如下:
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <?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 ="accountService" class ="club.guoshizhan.service.impl.AccountServiceImpl" > <property name ="accountDao" ref ="accountDao" > </property > </bean > <bean id ="accountDao" class ="club.guoshizhan.dao.impl.AccountDaoImpl" > <property name ="dataSource" ref ="dataSource" > </property > </bean > <bean id ="dataSource" class ="org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name ="driverClassName" value ="com.mysql.jdbc.Driver" > </property > <property name ="url" value ="jdbc:mysql://localhost:3306/spring" > </property > <property name ="username" value ="root" > </property > <property name ="password" value ="root" > </property > </bean > </beans >
第四步:
在 test
目录下新建测试类,包名根据 package
语句自行创建。 代码如下:
TransactionTest.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package club.guoshizhan.test;import club.guoshizhan.service.IAccountService;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith (SpringJUnit4ClassRunner.class ) @ContextConfiguration (locations = "classpath:bean.xml" )public class TransactionTest { @Autowired private IAccountService accountService; @Test public void testTransfer () { accountService.transfer("Lisa" , "Lucy" , 100f ); } }
TIPS:
执行 TransactionTest 测试类中的 testTransfer 方法, 然后去数据库查询结果即可。 注意:
现在还不能控制事务。
基于 XML 的声明式事务 第一步:
新建 Maven 工程,完善包结构。 然后导入如下依赖:
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <packaging > jar</packaging > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-jdbc</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-tx</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.46</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.8.7</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-test</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13</version > <scope > test</scope > </dependency > </dependencies >
第二步:
创建如下各个接口和类,根据 package
语句将类或接口放到对应的包中。 接口和类的代码如下:
Account.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package club.guoshizhan.domain;import java.io.Serializable;public class Account implements Serializable { private Integer id; private String name; private Float money; public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public Float getMoney () { return money; } public void setMoney (Float money) { this .money = money; } @Override public String toString () { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}' ; } }
IAccountDao.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package club.guoshizhan.dao;import club.guoshizhan.domain.Account;public interface IAccountDao { Account findAccountById (Integer accountId) ; Account findAccountByName (String accountName) ; void updateAccount (Account account) ; }
AccountDaoImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package club.guoshizhan.dao.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import org.springframework.jdbc.core.BeanPropertyRowMapper;import org.springframework.jdbc.core.support.JdbcDaoSupport;import java.util.List;public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao { @Override public Account findAccountById (Integer accountId) { List<Account> accounts = super .getJdbcTemplate().query("select * from account where id = ?" , new BeanPropertyRowMapper<Account>(Account.class ), accountId ) ; return accounts.isEmpty() ? null : accounts.get(0 ); } @Override public Account findAccountByName (String accountName) { List<Account> accounts = super .getJdbcTemplate().query("select * from account where name = ?" , new BeanPropertyRowMapper<Account>(Account.class ), accountName ) ; if (accounts.isEmpty()) { return null ; } if (accounts.size() > 1 ) { throw new RuntimeException("结果集不唯一" ); } return accounts.get(0 ); } @Override public void updateAccount (Account account) { super .getJdbcTemplate().update("update account set name=?,money=? where id=?" , account.getName(), account.getMoney(), account.getId()); } }
IAccountService.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package club.guoshizhan.service;import club.guoshizhan.domain.Account;public interface IAccountService { Account findAccountById (Integer accountId) ; void transfer (String sourceName, String targetName, Float money) ; }
AccountServiceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package club.guoshizhan.service.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import club.guoshizhan.service.IAccountService;public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao; public void setAccountDao (IAccountDao accountDao) { this .accountDao = accountDao; } @Override public Account findAccountById (Integer accountId) { return accountDao.findAccountById(accountId); } @Override public void transfer (String sourceName, String targetName, Float money) { Account source = accountDao.findAccountByName(sourceName); Account target = accountDao.findAccountByName(targetName); source.setMoney(source.getMoney() - money); target.setMoney(target.getMoney() + money); accountDao.updateAccount(source); accountDao.updateAccount(target); } }
第三步:
在 resources
目录下新建 bean.xml
配置文件。 代码如下:
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 <?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" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" > <bean id ="accountService" class ="club.guoshizhan.service.impl.AccountServiceImpl" > <property name ="accountDao" ref ="accountDao" > </property > </bean > <bean id ="accountDao" class ="club.guoshizhan.dao.impl.AccountDaoImpl" > <property name ="dataSource" ref ="dataSource" > </property > </bean > <bean id ="dataSource" class ="org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name ="driverClassName" value ="com.mysql.jdbc.Driver" > </property > <property name ="url" value ="jdbc:mysql://localhost:3306/spring" > </property > <property name ="username" value ="root" > </property > <property name ="password" value ="root" > </property > </bean > <bean id ="transactionManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name ="dataSource" ref ="dataSource" > </property > </bean > <tx:advice id ="txAdvice" transaction-manager ="transactionManager" > <tx:attributes > <tx:method name ="*" propagation ="REQUIRED" read-only ="false" /> <tx:method name ="find*" propagation ="SUPPORTS" read-only ="true" > </tx:method > </tx:attributes > </tx:advice > <aop:config > <aop:pointcut id ="springAdvice" expression ="execution(* club.guoshizhan.service.impl.*.*(..))" > </aop:pointcut > <aop:advisor advice-ref ="txAdvice" pointcut-ref ="springAdvice" > </aop:advisor > </aop:config > </beans >
第四步:
在 test
目录下新建测试类,包名根据 package
语句自行创建。 代码如下:
TransactionTest.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package club.guoshizhan.test;import club.guoshizhan.service.IAccountService;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith (SpringJUnit4ClassRunner.class ) @ContextConfiguration (locations = "classpath:bean.xml" )public class TransactionTest { @Autowired private IAccountService accountService; @Test public void testTransfer () { accountService.transfer("Lisa" , "Lucy" , 100f ); } }
TIPS:
执行 TransactionTest
测试类中的 testTransfer
方法, 然后去数据库查询结果即可(此时已经配置好了事务)。
基于注解的声明式事务 第一步:
新建 Maven 工程,完善包结构。 然后导入如下依赖:
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <packaging > jar</packaging > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-jdbc</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-tx</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.46</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.8.7</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-test</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13</version > <scope > test</scope > </dependency > </dependencies >
第二步:
创建如下各个接口和类,根据 package
语句将类或接口放到对应的包中。 接口和类的代码如下:
Account.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package club.guoshizhan.domain;import java.io.Serializable;public class Account implements Serializable { private Integer id; private String name; private Float money; public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public Float getMoney () { return money; } public void setMoney (Float money) { this .money = money; } @Override public String toString () { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}' ; } }
IAccountDao.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package club.guoshizhan.dao;import club.guoshizhan.domain.Account;public interface IAccountDao { Account findAccountById (Integer accountId) ; Account findAccountByName (String accountName) ; void updateAccount (Account account) ; }
AccountDaoImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package club.guoshizhan.dao.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.BeanPropertyRowMapper;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Repository;import java.util.List;@Repository ("accountDao" )public class AccountDaoImpl implements IAccountDao { @Autowired private JdbcTemplate jdbcTemplate; @Override public Account findAccountById (Integer accountId) { List<Account> accounts = jdbcTemplate.query("select * from account where id = ?" , new BeanPropertyRowMapper<Account>(Account.class ), accountId ) ; return accounts.isEmpty() ? null : accounts.get(0 ); } @Override public Account findAccountByName (String accountName) { List<Account> accounts = jdbcTemplate.query("select * from account where name = ?" , new BeanPropertyRowMapper<Account>(Account.class ), accountName ) ; if (accounts.isEmpty()) { return null ; } if (accounts.size() > 1 ) { throw new RuntimeException("结果集不唯一" ); } return accounts.get(0 ); } @Override public void updateAccount (Account account) { jdbcTemplate.update("update account set name=?,money=? where id=?" , account.getName(), account.getMoney(), account.getId()); } }
IAccountService.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package club.guoshizhan.service;import club.guoshizhan.domain.Account;public interface IAccountService { Account findAccountById (Integer accountId) ; void transfer (String sourceName, String targetName, Float money) ; }
AccountServiceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package club.guoshizhan.service.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import club.guoshizhan.service.IAccountService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;@Service ("accountService" )@Transactional public class AccountServiceImpl implements IAccountService { @Autowired private IAccountDao accountDao; @Override public Account findAccountById (Integer accountId) { return accountDao.findAccountById(accountId); } @Override public void transfer (String sourceName, String targetName, Float money) { Account source = accountDao.findAccountByName(sourceName); Account target = accountDao.findAccountByName(targetName); source.setMoney(source.getMoney() - money); target.setMoney(target.getMoney() + money); accountDao.updateAccount(source); accountDao.updateAccount(target); } }
第三步:
在 resources
目录下新建 bean.xml
配置文件。 代码如下:
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 <?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: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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <context:component-scan base-package ="club.guoshizhan" > </context:component-scan > <bean id ="jdbcTemplate" class ="org.springframework.jdbc.core.JdbcTemplate" > <property name ="dataSource" ref ="dataSource" > </property > </bean > <bean id ="dataSource" class ="org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name ="driverClassName" value ="com.mysql.jdbc.Driver" > </property > <property name ="url" value ="jdbc:mysql://localhost:3306/spring" > </property > <property name ="username" value ="root" > </property > <property name ="password" value ="root" > </property > </bean > <bean id ="transactionManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name ="dataSource" ref ="dataSource" > </property > </bean > <tx:annotation-driven transaction-manager ="transactionManager" > </tx:annotation-driven > </beans >
第四步:
在 test
目录下新建测试类,包名根据 package
语句自行创建。 代码如下:
TransactionTest.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package club.guoshizhan.test;import club.guoshizhan.service.IAccountService;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith (SpringJUnit4ClassRunner.class ) @ContextConfiguration (locations = "classpath:bean.xml" )public class TransactionTest { @Autowired private IAccountService accountService; @Test public void testTransfer () { accountService.transfer("Lisa" , "Lucy" , 100f ); } }
TIPS:
执行 TransactionTest
测试类中的 testTransfer
方法, 然后去数据库查询结果即可(此时已经配置好了事务)。
基于纯注解的声明式事务 第一步:
新建 Maven 工程,完善包结构。 然后导入如下依赖:
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <packaging > jar</packaging > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-jdbc</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-tx</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.46</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.8.7</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-test</artifactId > <version > 5.0.2.RELEASE</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13</version > <scope > test</scope > </dependency > </dependencies >
第二步:
创建如下各个接口和类,根据 package
语句将类或接口放到对应的包中。 接口和类的代码如下:
Account.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package club.guoshizhan.domain;import java.io.Serializable;public class Account implements Serializable { private Integer id; private String name; private Float money; public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public Float getMoney () { return money; } public void setMoney (Float money) { this .money = money; } @Override public String toString () { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}' ; } }
IAccountDao.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package club.guoshizhan.dao;import club.guoshizhan.domain.Account;public interface IAccountDao { Account findAccountById (Integer accountId) ; Account findAccountByName (String accountName) ; void updateAccount (Account account) ; }
AccountDaoImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package club.guoshizhan.dao.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.BeanPropertyRowMapper;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Repository;import java.util.List;@Repository ("accountDao" )public class AccountDaoImpl implements IAccountDao { @Autowired private JdbcTemplate jdbcTemplate; @Override public Account findAccountById (Integer accountId) { List<Account> accounts = jdbcTemplate.query("select * from account where id = ?" , new BeanPropertyRowMapper<Account>(Account.class ), accountId ) ; return accounts.isEmpty() ? null : accounts.get(0 ); } @Override public Account findAccountByName (String accountName) { List<Account> accounts = jdbcTemplate.query("select * from account where name = ?" , new BeanPropertyRowMapper<Account>(Account.class ), accountName ) ; if (accounts.isEmpty()) { return null ; } if (accounts.size() > 1 ) { throw new RuntimeException("结果集不唯一" ); } return accounts.get(0 ); } @Override public void updateAccount (Account account) { jdbcTemplate.update("update account set name=?,money=? where id=?" , account.getName(), account.getMoney(), account.getId()); } }
IAccountService.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package club.guoshizhan.service;import club.guoshizhan.domain.Account;public interface IAccountService { Account findAccountById (Integer accountId) ; void transfer (String sourceName, String targetName, Float money) ; }
AccountServiceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package club.guoshizhan.service.impl;import club.guoshizhan.dao.IAccountDao;import club.guoshizhan.domain.Account;import club.guoshizhan.service.IAccountService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;@Service ("accountService" )@Transactional public class AccountServiceImpl implements IAccountService { @Autowired private IAccountDao accountDao; @Override public Account findAccountById (Integer accountId) { return accountDao.findAccountById(accountId); } @Override public void transfer (String sourceName, String targetName, Float money) { Account source = accountDao.findAccountByName(sourceName); Account target = accountDao.findAccountByName(targetName); source.setMoney(source.getMoney() - money); target.setMoney(target.getMoney() + money); accountDao.updateAccount(source); accountDao.updateAccount(target); } }
第三步:
在 resources
目录下新建 jdbcConfig.properties
数据库配置文件。 代码如下:
jdbcConfig.properties 1 2 3 4 jdbc.driver =com.mysql.jdbc.Driver jdbc.url =jdbc:mysql://localhost:3306/spring jdbc.username =root jdbc.password =root
第四步:
在 club 包下新建 config
包(用于存放 Spring 的各种配置类)。然后编写各种配置类。 代码如下:
JdbcConfig.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package club.config;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.datasource.DriverManagerDataSource;import javax.sql.DataSource;public class JdbcConfig { @Value ("${jdbc.driver}" ) private String driver; @Value ("${jdbc.url}" ) private String url; @Value ("${jdbc.username}" ) private String username; @Value ("${jdbc.password}" ) private String password; @Bean (name = "jdbcTemplate" ) public JdbcTemplate createJdbcTemplate (DataSource dataSource) { return new JdbcTemplate(dataSource); } @Bean (name = "dataSource" ) public DataSource createDataSource () { DriverManagerDataSource ds = new DriverManagerDataSource(); ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(username); ds.setPassword(password); return ds; } }
TransactionConfig.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package club.config;import org.springframework.context.annotation.Bean;import org.springframework.jdbc.datasource.DataSourceTransactionManager;import org.springframework.transaction.PlatformTransactionManager;import javax.sql.DataSource;public class TransactionConfig { @Bean (name = "transactionManager" ) public PlatformTransactionManager createTransactionManager (DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }
SpringConfiguration.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package club.config;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;import org.springframework.context.annotation.PropertySource;import org.springframework.transaction.annotation.EnableTransactionManagement;@Configuration @ComponentScan ("club.guoshizhan" )@Import ({JdbcConfig.class , TransactionConfig .class }) @PropertySource("jdbcConfig.properties") @EnableTransactionManagement public class SpringConfiguration {}
第五步:
在 test
目录下新建测试类,包名根据 package
语句自行创建。 代码如下:
TransactionTest.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package club.guoshizhan.test;import club.config.SpringConfiguration;import club.guoshizhan.service.IAccountService;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith (SpringJUnit4ClassRunner.class ) @ContextConfiguration (classes = SpringConfiguration.class ) public class TransactionTest { @Autowired private IAccountService accountService; @Test public void testTransfer () { accountService.transfer("Lisa" , "Lucy" , 100f ); } }
TIPS:
执行 TransactionTest
测试类中的 testTransfer
方法, 然后去数据库查询结果即可(此时已经配置好了事务)。
编程式事务控制 1 2 3 4 5 + 此部分作为了解内容,以后有时间再写,以此带来的不便,请谅解!!! + 此部分作为了解内容,以后有时间再写,以此带来的不便,请谅解!!! + 此部分作为了解内容,以后有时间再写,以此带来的不便,请谅解!!! + 此部分作为了解内容,以后有时间再写,以此带来的不便,请谅解!!! + 此部分作为了解内容,以后有时间再写,以此带来的不便,请谅解!!!
Spring5 新特性 此部分使用搜索引擎搜索一下就可以了。以后有时间再写。
1 2 3 4 5 + 此部分内容以后有时间再写,以此带来的不便,请谅解!!! + 此部分内容以后有时间再写,以此带来的不便,请谅解!!! + 此部分内容以后有时间再写,以此带来的不便,请谅解!!! + 此部分内容以后有时间再写,以此带来的不便,请谅解!!! + 此部分内容以后有时间再写,以此带来的不便,请谅解!!!
SSM 框架整合 搭建开发环境 第一步:
创建数据库和表结构。 代码如下:
ssm.sql 1 2 3 4 5 6 7 8 9 10 11 create database ssm;use ssm;create table account ( id int primary key auto_increment, name varchar (20 ), money double ); insert into `account` (`id` ,`name` ,`money` ) values (1 ,'Jack' ,1500 ),(2 ,'Lucy' ,1100 ),(3 ,'Lisa' ,900 ),(4 ,'Mary' ,1500 );
第二步:
创建 Maven 项目(记得勾选 maven-archetype-webapp
),完善相应包结构。 然后导入如下依赖:
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 <?xml version="1.0" encoding="UTF-8"?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > club.guoshizhan</groupId > <artifactId > ssm</artifactId > <version > 1.0-SNAPSHOT</version > <packaging > war</packaging > <name > ssm Maven Webapp</name > <url > http://www.example.com</url > <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > <maven.compiler.source > 1.8</maven.compiler.source > <maven.compiler.target > 1.8</maven.compiler.target > <spring.version > 5.0.2.RELEASE</spring.version > <slf4j.version > 1.6.6</slf4j.version > <log4j.version > 1.2.12</log4j.version > <mysql.version > 5.1.6</mysql.version > <mybatis.version > 3.4.5</mybatis.version > </properties > <dependencies > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.6.8</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-aop</artifactId > <version > ${spring.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > ${spring.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-web</artifactId > <version > ${spring.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webmvc</artifactId > <version > ${spring.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-test</artifactId > <version > ${spring.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-tx</artifactId > <version > ${spring.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-jdbc</artifactId > <version > ${spring.version}</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > <scope > compile</scope > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > ${mysql.version}</version > </dependency > <dependency > <groupId > javax.servlet</groupId > <artifactId > servlet-api</artifactId > <version > 2.5</version > <scope > provided</scope > </dependency > <dependency > <groupId > javax.servlet.jsp</groupId > <artifactId > jsp-api</artifactId > <version > 2.0</version > <scope > provided</scope > </dependency > <dependency > <groupId > jstl</groupId > <artifactId > jstl</artifactId > <version > 1.2</version > </dependency > <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > ${log4j.version}</version > </dependency > <dependency > <groupId > org.slf4j</groupId > <artifactId > slf4j-api</artifactId > <version > ${slf4j.version}</version > </dependency > <dependency > <groupId > org.slf4j</groupId > <artifactId > slf4j-log4j12</artifactId > <version > ${slf4j.version}</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > ${mybatis.version}</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis-spring</artifactId > <version > 1.3.0</version > </dependency > <dependency > <groupId > c3p0</groupId > <artifactId > c3p0</artifactId > <version > 0.9.1.2</version > <type > jar</type > <scope > compile</scope > </dependency > </dependencies > <build > <finalName > ssm</finalName > <pluginManagement > <plugins > <plugin > <artifactId > maven-clean-plugin</artifactId > <version > 3.1.0</version > </plugin > <plugin > <artifactId > maven-resources-plugin</artifactId > <version > 3.0.2</version > </plugin > <plugin > <artifactId > maven-compiler-plugin</artifactId > <version > 3.8.0</version > </plugin > <plugin > <artifactId > maven-surefire-plugin</artifactId > <version > 2.22.1</version > </plugin > <plugin > <artifactId > maven-war-plugin</artifactId > <version > 3.2.2</version > </plugin > <plugin > <artifactId > maven-install-plugin</artifactId > <version > 2.5.2</version > </plugin > <plugin > <artifactId > maven-deploy-plugin</artifactId > <version > 2.8.2</version > </plugin > </plugins > </pluginManagement > </build > </project >
第三步:
创建如下各个接口和类,根据 package
语句将类或接口放到对应的包中。 接口和类的代码如下:
Account.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package club.guoshizhan.domain;import java.io.Serializable;public class Account implements Serializable { private Integer id; private String name; private Double money; public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public Double getMoney () { return money; } public void setMoney (Double money) { this .money = money; } @Override public String toString () { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}' ; } }
AccountDao.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package club.guoshizhan.dao;import club.guoshizhan.domain.Account;import java.util.List;public interface AccountDao { public List<Account> findAll () ; public void saveAccount (Account account) ; }
AccountService.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package club.guoshizhan.service;import club.guoshizhan.domain.Account;import java.util.List;public interface AccountService { public List<Account> findAll () ; public void saveAccount (Account account) ; }
AccountServiceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package club.guoshizhan.service.impl;import club.guoshizhan.domain.Account;import club.guoshizhan.service.AccountService;import java.util.List;public class AccountServiceImpl implements AccountService { public List<Account> findAll () { System.out.println("业务层:查询所有账户..." ); return null ; } public void saveAccount (Account account) { System.out.println("业务层:保存帐户..." ); } }
AccountController.java 1 2 3 4 5 6 7 8 package club.guoshizhan.controller;public class AccountController {}
TIPS:
ssm 环境搭建 到此结束!!!
整合 Spring 框架 注意一下:
在 ssm 的整合过程中,它们是有顺序的。必须先整合 Spring 框架,然后再去整合 Spring MVC 和 Mybatis 框架。 如果先整合 Spring MVC 或 Mybatis 框架的话,会导致整合不成功,所以我们 先
来整合 Spring 框架。
第一步:
在 resources
目录下新建 applicationContext.xml
配置文件(这个是 Spring 的配置文件)。 代码如下:
applicationContext.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xmlns:aop ="http://www.springframework.org/schema/aop" xmlns:tx ="http://www.springframework.org/schema/tx" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd" > <context:component-scan base-package ="club.guoshizhan" > <context:exclude-filter type ="annotation" expression ="org.springframework.stereotype.Controller" /> </context:component-scan > </beans >
第二步:
在 resources
目录下新建 log4j.properties
日志文件(可要可不要,加上是方便查看日志信息)。 代码如下:
log4j.properties 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 log4j.rootCategory =info, CONSOLE, LOGFILE log4j.logger.org.apache.axis.enterprise =FATAL, CONSOLE log4j.appender.CONSOLE =org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout =org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern =%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n log4j.appender.LOGFILE =org.apache.log4j.FileAppender log4j.appender.LOGFILE.File =d:\axis.log log4j.appender.LOGFILE.Append =true log4j.appender.LOGFILE.layout =org.apache.log4j.PatternLayout log4j.appender.LOGFILE.layout.ConversionPattern =%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
第三步:
把 AccountServiceImpl
类加入到 Spring 容器中(使用 @Service
注解)。 代码如下:
AccountServiceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package club.guoshizhan.service.impl;import club.guoshizhan.domain.Account;import club.guoshizhan.service.AccountService;import org.springframework.stereotype.Service;import java.util.List;@Service ("accountService" )public class AccountServiceImpl implements AccountService { public List<Account> findAll () { System.out.println("业务层:查询所有账户..." ); return null ; } public void saveAccount (Account account) { System.out.println("业务层:保存帐户..." ); } }
第四步:
编写 SSMTest
测试类,测试 整合 Spring 框架
是否成功。 代码如下:
SSMTest.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package club.guoshizhan.test;import club.guoshizhan.service.AccountService;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class SSMTest { @Test public void springTest () { ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:applicationContext.xml" ); AccountService accountService = (AccountService) ac.getBean("accountService" ); System.out.println(accountService); accountService.findAll(); } }
执行 springTest
方法,然后查看结果。(下列结果是加入了日志信息的) 结果如下:
.java 1 2 3 4 5 6 7 8 9 10 2020 -09 -11 21 :43 :21 ,894 0 [ main] INFO ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@19 b4d0f: startup date [Fri Sep 11 21 :43 :21 CST 2020 ]; root of context hierarchy2020-09-11 21:43:21,961 67 [ main] INFO ry.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [applicationContext.xml] 2020 -09 -11 21 :43 :22 ,459 565 [ main] WARN .mapper.ClassPathMapperScanner - No MyBatis mapper was found in '[cn.itcast.dao]' package . Please check your configuration.2020 -09 -11 21 :43 :22 ,818 924 [ main] INFO com.mchange.v2.log.MLog - MLog clients using log4j logging.2020 -09 -11 21 :43 :23 ,289 1395 [ main] INFO m.mchange.v2.c3p0.C3P0Registry - Initializing c3p0-0.9 .1 .2 [built 21 -May-2007 15 :04 :56 ; debug? true ; trace: 10 ]club.guoshizhan.service.impl.AccountServiceImpl@a 923bb 2020 -09 -11 21 :43 :23 ,576 1682 [ main] INFO l.AbstractPoolBackedDataSource - Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3 , acquireRetryAttempts -> 30 , acquireRetryDelay -> 1000 , autoCommitOnClose -> false , automaticTestTable -> null , breakAfterAcquireFailure -> false , checkoutTimeout -> 0 , connectionCustomizerClassName -> null , connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1 hgeby9acysnco9r43se4|12 b5695, debugUnreturnedConnectionStackTraces -> false , description -> null , driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null , forceIgnoreUnresolvedTransactions -> false , identityToken -> 1 hgeby9acysnco9r43se4|12 b5695, idleConnectionTestPeriod -> 0 , initialPoolSize -> 3 , jdbcUrl -> jdbc:mysql:业务层:查询所有账户... Process finished with exit code 0
TIPS:
如果执行测试方法有如上输出,那么表示 整合 Spring 框架
成功了。
整合 Spring MVC 框架 第一步:
在 resources
目录下新建 springMvc.xml
配置文件(这是 Spring MVC 的配置文件)。 代码如下:
springMvc.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:mvc ="http://www.springframework.org/schema/mvc" xmlns:context ="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <context:component-scan base-package ="club.guoshizhan" > <context:include-filter type ="annotation" expression ="org.springframework.stereotype.Controller" /> </context:component-scan > <bean id ="internalResourceViewResolver" class ="org.springframework.web.servlet.view.InternalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/pages/" /> <property name ="suffix" value =".jsp" /> </bean > <mvc:resources location ="/css/" mapping ="/css/**" /> <mvc:resources location ="/images/" mapping ="/images/**" /> <mvc:resources location ="/js/" mapping ="/js/**" /> <mvc:annotation-driven /> </beans >
第二步:
编写 WEB-INF 目录下的 web.xml
配置文件(配置前端控制器和过滤器)。 代码如下:
web.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app > <display-name > Archetype Created Web Application</display-name > <servlet > <servlet-name > dispatcherServlet</servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class > <init-param > <param-name > contextConfigLocation</param-name > <param-value > classpath:springMvc.xml</param-value > </init-param > <load-on-startup > 1</load-on-startup > </servlet > <servlet-mapping > <servlet-name > dispatcherServlet</servlet-name > <url-pattern > /</url-pattern > </servlet-mapping > <filter > <filter-name > characterEncodingFilter</filter-name > <filter-class > org.springframework.web.filter.CharacterEncodingFilter</filter-class > <init-param > <param-name > encoding</param-name > <param-value > UTF-8</param-value > </init-param > </filter > <filter-mapping > <filter-name > characterEncodingFilter</filter-name > <url-pattern > /*</url-pattern > </filter-mapping > </web-app >
第三步:
修改 webapp 目录下的 index.jsp
页面。 代码如下:
index.jsp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <%-- Created by IntelliJ IDEA. User: guoshizhan Date: 2020 /9 /11 Time: 22 :18 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <a href="account/findAll">测试查询</a> </body> </html>
第四步:
在 WEB-INF 目录下新建 pages 目录,然后在 pages 目录下新建 list.jsp
页面。 代码如下:
list.jsp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <%-- Created by IntelliJ IDEA. User: guoshizhan Date: 2020 /9 /11 Time: 22 :24 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>Title</title> </head> <body> <h3>查询所有的帐户</h3> </body> </html>
第五步:
编写 AccountController
类,实现页面跳转。 代码如下:
.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package club.guoshizhan.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;@Controller @RequestMapping ("/account" )public class AccountController { @RequestMapping ("/findAll" ) public String findAll () { return "list" ; } }
第六步:
配置 tomcat ,然后运行 tomcat 进行测试。 结果如下:
TIPS:
如果看到上述页面且点击有效果,则说明 Spring MVC 能起作用了。 那么接下来才是真正的 Spring 整合 Spring MVC 框架
。
那么问题来了,Spring 如何才算成功整合了 Spring MVC 框架呢?如果 controller 能够成功调用到 service 的方法,那么就算整合成功了。那么要怎么样才能实现 controller 调用 service 呢?由于在 applicationContext.xml
配置文件中已经明确说明了不扫描 Controller , 所以即使在 controller 里面自动注入 service 也是没有用的。因此,我们需要借助 tomcat 把 controller 注入到 Spring 中。 过程如下图:
第一步:
在 web.xml
配置文件中新增如下代码。 新增代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> + <!-- 配置 Spring 的监听器,默认只加载 WEB-INF 目录下的 applicationContext.xml 配置文件 --> + <listener> + <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> + </listener> + <!-- 设置配置文件的路径 --> + <context-param> + <param-name>contextConfigLocation</param-name> + <param-value>classpath:applicationContext.xml</param-value> + </context-param> <!-- 配置前端控制器 --> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 加载 springMvc.xml 配置文件 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springMvc.xml</param-value> </init-param> <!-- 启动服务器,创建该 servlet --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 解决中文乱码的过滤器 --> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
第二步:
编写 AccountController
类。 新增代码如下:
AccountController.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package club.guoshizhan.controller;import club.guoshizhan.domain.Account;import club.guoshizhan.service.AccountService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;import java.util.List;@Controller @RequestMapping ("/account" )public class AccountController { @Autowired private AccountService accountService; @RequestMapping ("/findAll" ) public String findAll (Model model) { System.out.println("表现层:查询所有账户..." ); List<Account> list = accountService.findAll(); model.addAttribute("list" , list); return "list" ; } }
第三步:
启动 tomcat ,然后进入到浏览器,点击测试查询,然后回到控制台查看结果。 结果如下:
1 2 表现层:查询所有账户... 业务层:查询所有账户...
TIPS:
如果能在控制台输出如上结果,那么证明 Spring 整合 Spring MVC 框架
成功了。
整合 Mybatis 框架 第一步:
编写 AccountDao
接口,使用注解进行相应的操作。 代码如下:
AccountDao.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package club.guoshizhan.dao;import club.guoshizhan.domain.Account;import org.apache.ibatis.annotations.Insert;import org.apache.ibatis.annotations.Select;import java.util.List;public interface AccountDao { @Select ("select * from account" ) public List<Account> findAll () ; @Insert ("insert into account (name, money) values (#{name}, #{money})" ) public void saveAccount (Account account) ; }
第二步:
编写 SqlMapConfig.xml
配置文件,进行数据库的相关配置。 代码如下:
SqlMapConfig.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration > <environments default ="mysql" > <environment id ="mysql" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="POOLED" > <property name ="driver" value ="com.mysql.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql://localhost:3306/ssm" /> <property name ="username" value ="root" /> <property name ="password" value ="root" /> </dataSource > </environment > </environments > <mappers > <mapper class ="club.guoshizhan.dao.AccountDao" /> </mappers > </configuration >
第三步:
编写 SSMTest
测试类进行测试,看一下 mybatis 框架能否正常使用。 代码如下:
SSMTest.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package club.guoshizhan.test;import club.guoshizhan.dao.AccountDao;import club.guoshizhan.domain.Account;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.Test;import java.io.InputStream;import java.util.List;public class SSMTest { @Test public void mybatisTest () throws Exception { InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml" ); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); SqlSession session = factory.openSession(); AccountDao dao = session.getMapper(AccountDao.class ) ; List<Account> list = dao.findAll(); for (Account account : list){ System.out.println(account); } session.close(); in.close(); } }
执行测试类中的 mybatisTest
方法,查看返回的结果。 结果如下:
1 2 3 4 5 6 Account{id=1 , name='Jack' , money=1500.0 } Account{id=2 , name='Lucy' , money=1100.0 } Account{id=3 , name='Lisa' , money=900.0 } Account{id=4 , name='Mary' , money=1500.0 } Process finished with exit code 0
TIPS:
如果看到上述结果,则说明 mybatis 起作用了。 那么接下来才是真正的 Spring 整合 mybatis 框架
。
那么要如何去整合 mybatis 呢?如何才算整合成功呢?只要把 dao 对象存到 Spring 容器中,然后 service 能够成功调用 dao 的方法,这样便算整合成功。 那么接下来就按照步骤一步一步地来实现整合的过程。往下看!!!
第一步:
删除 SqlMapConfig.xml 配置文件,然后在 applicationContext.xml
配置文件中新加如下代码:
applicationContext.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 开启注解的扫描,希望处理 service 和 dao 层,controller 层不需要 Spring 框架去处理 --> <context:component-scan base-package="club.guoshizhan" > <!-- 配置哪些注解不扫描,这里配置了 @Controller 注解不扫描 --> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> + <!-- Spring 整合 MyBatis 框架 --> + <!-- 配置 c3p0 连接池 --> + <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> + <property name="driverClass" value="com.mysql.jdbc.Driver"/> + <property name="jdbcUrl" value="jdbc:mysql:///ssm"/> + <property name="user" value="root"/> + <property name="password" value="root"/> + </bean> + + <!-- 配置 SqlSessionFactory 工厂 --> + <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> + <property name="dataSource" ref="dataSource" /> + </bean> + + <!-- 配置 AccountDao 接口所在包 --> + <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> + <property name="basePackage" value="club.guoshizhan.dao"/> + </bean> </beans>
第二步:
把 dao 交给 Spring 容器管理,即给 AccountDao
接口加上 @Repository
注解。 新加代码如下:
AccountDao.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package club.guoshizhan.dao; import club.guoshizhan.domain.Account; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; + import org.springframework.stereotype.Repository; import java.util.List; /** * 帐户 dao 接口 */ + @Repository public interface AccountDao { ……
第三步:
在 AccountServiceImpl
类中注入 dao ,从而实现 mybatis 的整合操作。 新加代码如下:
AccountServiceImpl.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package club.guoshizhan.service.impl; + import club.guoshizhan.dao.AccountDao; import club.guoshizhan.domain.Account; import club.guoshizhan.service.AccountService; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service("accountService") public class AccountServiceImpl implements AccountService { + @Autowired + private AccountDao accountDao; public List<Account> findAll() { System.out.println("业务层:查询所有账户..."); + return accountDao.findAll(); } public void saveAccount(Account account) { System.out.println("业务层:保存帐户..."); + accountDao.saveAccount(account); } }
第四步:
编写 AccountController
类,从而可以在页面中展示数据。 代码如下:
AccountController.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package club.guoshizhan.controller;import club.guoshizhan.domain.Account;import club.guoshizhan.service.AccountService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;import java.util.List;@Controller @RequestMapping ("/account" )public class AccountController { @Autowired private AccountService accountService; @RequestMapping ("/findAll" ) public String findAll (Model model) { System.out.println("表现层:查询所有账户..." ); List<Account> list = accountService.findAll(); model.addAttribute("list" , list); return "list" ; } }
第五步:
编写 list.jsp
页面,把从数据库查到的数据展示出来。 代码如下:
list.jsp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <%-- Created by IntelliJ IDEA. User: guoshizhan Date: 2020 /9 /11 Time: 22 :24 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>Title</title> </head> <body> <h3>查询所有的帐户</h3> <c:forEach items="${list}" var ="account" > ${account.name} ==> ${account.money} <br/> </c:forEach> </body> </html>
运行 tomcat ,然后去浏览器访问,点击测试查询。 结果如下:
如果能够看到和上图类似的结果,那么恭喜你,你已经成功整合了 mybatis 。 但是呢,Spring 和 mybatis 的整合还没完。上述只是实现了从数据库查询,然后在页面展示出来。这个操作是不需要事务支持的。然而,我们的增删改操作是需要事务来支持的。所以,我们还需要实现事务的配置。 那么就接着往下看吧!!!
第一步:
编写 applicationContext.xml
配置文件,新加声明式事务的配置。 新加代码如下:
applicationContext.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 + <!-- 配置 Spring 框架声明式事务管理 --> + <!-- 配置事务管理器 --> + <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> + <property name="dataSource" ref="dataSource" /> + </bean> + + <!-- 配置事务通知 --> + <tx:advice id="txAdvice" transaction-manager="transactionManager"> + <tx:attributes> + <tx:method name="find*" read-only="true"/> + <tx:method name="*" isolation="DEFAULT"/> + </tx:attributes> + </tx:advice> + + <!-- 配置 AOP 增强 --> + <aop:config> + <aop:advisor advice-ref="txAdvice" pointcut="execution(* club.guoshizhan.service.impl.*ServiceImpl.*(..))"/> + </aop:config>
第二步:
在 AccountController
类中新加保存操作代码。 新加代码如下:
AccountController.java 1 2 3 4 5 6 + // 保存操作 + @RequestMapping("/save") + public void save(Account account, HttpServletRequest request, HttpServletResponse response) throws IOException { + accountService.saveAccount(account); + response.sendRedirect(request.getContextPath() + "/account/findAll"); + }
第三步:
编写 index.jsp
页面(加入了表单提交相关的代码)。 代码如下:
index.jsp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <%-- Created by IntelliJ IDEA. User: guoshizhan Date: 2020 /9 /11 Time: 22 :18 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <a href="account/findAll">测试查询</a> <h3>测试保存</h3> <form action="account/save" method="post" > 姓名:<input type="text" name="name" /><br/> 金额:<input type="text" name="money" /><br/> <input type="submit" value="保存" /><br/> </form> </body> </html>
运行 tomcat ,然后去浏览器访问,输入相关数据,点击测试保存。 结果如下:
TIPS:
SSM 框架的整合到此结束!!!感谢阅读完此文,有时间可以看看别的文章。