Spring
1、概述及IOC原理推导
1.1、简介
- Spring:春天—>给软件行业带来的春天
- 2002年首次推出Spring框架的雏形:interface21框架
- 2004.3.24:spring以interface21为基础,重新设计发布了1.0正式版本
- 创始人:Rod Johnson:不仅有计算机学位,还是音乐学博士,在很多行业都有贡献,是Java development community中的杰出人物。
- Spring理念:解决企业应用开发的复杂性,使现有的技术更加容易使用,是一个大杂烩,整合了现有的所有Java框架
1.2、优点
- Spring是一个开源的免费的框架(容器)
- Spring是一个轻量级、非入侵式(引入了Spring不会改变原来的代码,不会产生影响)的框架
- 控制反转(IOC),面向切面编程(AOP)
- 支持事务处理,支持整合框架(基本上所有Java框架都能整合)
总结:Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架
1.3、组成
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。
- Spring Core核心容器:核心容器提供 Spring 框架的基本功能。主要组件是 BeanFactory,实现工厂模式。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
- Spring Context上下文:一个配置文件,向 Spring 框架提供上下文信息。包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
- Spring AOP面向切面编程:通过配置管理特性,直接将面向切面的功能 , 集成到了 Spring 框架中。提供了事务管理服务。可以不用依赖组件,将声明性事务管理集成到应用程序中。
- Spring DAO:并且极大减少了需要编写的异常代码数量(例如打开和关闭连接),提供了JDBC抽象层。
- Spring ORM 对象关系映射:提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。
- Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文,还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
- Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
1.4、扩展
现代化的Java开发
- SpringBoot
- 一个快速开发的脚手架。
- 基于SpringBoot可以快速开发单个微服务
- 约定大于配置!
- SpringCloud
- 基于SpringBoot实现
Spring的弊端
- 发展了太久,框架太多,违背了原来的理念!配置十分繁琐,人称
配置地狱
1.5、IOC原理推导
只需要导入webmvc,基本所有Spring的基础包都导入了
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
分析实现
- 之前的业务实现层
private UserDao userDao = new UserDaoOracleImpl();
public void getUser() {
userDao.getUser();
}
//测试
@Test
public void test(){
UserService service = new UserServiceImpl();
service.getUser();
}
- 现在的业务实现层
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void getUser() {
userDao.getUser();
}
//测试
@Test
public void test(){
UserServiceImpl service = new UserServiceImpl();
service.setUserDao( new UserDaoMySqlImpl() );
service.getUser();
//那我们现在又想用Oracle去实现呢
service.setUserDao( new UserDaoOracleImpl() );
service.getUser();
}
- 之前如果有多个dao(抽象)层,每次需要使用新的抽象层,都需要改变源代码,如果代码量十分大,修改成本也会很昂贵
- 而现在只需要操作者操作的时候使用set方法注入对象就可以操作了,不需要改变源代码
- 也就是之前前所有东西都是由程序去进行控制创建 , 而现在是由我们自行控制创建对象 , 把主动权交给了调用者。 程序不用去管怎么创建,怎么实现,只负责提供接口。
- 这种思想 , 从本质上解决了问题 , 我们程序员不再去管理对象的创建了,更多的去关注业务的实现, 耦合性大大降低,这也就是IOC的原型 !
2、IOC
2.1、本质是控制反转
- 控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入(set方法))是实现IoC的一种方法,控制反转后将对象的创建转移给第三方,控制反转就是:获得依赖对象的方式反转了。
- IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC(自动装配)
- Spring容器在初始化时先读取配置文件,根据配置文件或元数据将对象存入IOC容器中,程序使用时再从Ioc容器中取出需要的对象。
控制反转 :
- 控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的
- 反转 : 程序本身不创建对象 , 而变成被动的接收对象 .
- 依赖注入 : 实现控制反转的一种方式(还可以使用文件、注解等方式),就是利用set方法来进行注入的。
- IOC是一种编程思想,由主动的编程变成被动的接收
- 一句话搞定 : 对象由Spring 来创建 , 管理 , 装配 !
Spring容器通过构造器创建对象
- 默认通过无参构造,可以配置成有参构造
- 在配置文件
bean.xml
被加载的时候,其中管理的所有对象都已经被初始化,需要使用的直接用getBean
获得
2.2、Spring配置
默认applicationContext.xml格式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDaoImpl" class="com.lvboaa.dao.UserDaoImpl"/>
<bean id="userServiceImpl" class="com.lvboaa.service.UserServiceImpl">
<property name="userDao" ref="userDaoImpl"/>
</bean>
</beans>
import
用于团队开发,可以将多个beans.xml合并成一个
3、依赖注入(Dependency Injection)
3.1、构造器注入
3.2、Set方式注入【重点】
依赖注入:set方式
- 依赖:bean对象的创建都依赖于Spring容器
- 注入:bean对象的所有属性,都由Spring容器来注入
- 注入之前会先走无参构造器
3.3、拓展方式注入
- p命名空间:可以直接注入属性的值
- c命名空间:可以直接通过构造器注入
- 注意:使用的时候需要带入约束
3.4、Bean的作用域
- request、session、application只能都用于基于spring的web环境
- 在bean标签的熟悉scope设置作用域,默认是单例模式
<bean id="userDaoImpl" class="com.lvboaa.dao.UserDaoImpl" scope="singleton"/>
- singleton(单例模式):每次通过context获取对象都是同一个对象,单线程使用
- prototype(原型模式):每次通过context获取对象都是新的对象,多线程使用
4、Bean的自动装配
4.1、xml配置
- bean标签的属性autowired,设置
byName
根据id
自动装配,设置byType
根据类型自动装配,不需要设置property
属性,直接自动装配
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User" autowire="byType">
</bean>
4.2、自动装配注解
开启注解
- 导入约束:context
- 开启注解支持:
<context:annotation-config/>
@Autowired
- 按类型自动装配(byType)
- 只要属性被配置在IOC容器中,就可以使用这个注解自动装配
- 可以放在属性上,也可以放在set方法上
@Autowired
private Cat cat;
//如果允许对象为null,设置required = false,默认为true
@Autowired(required = false)
private Cat cat;
Qualifier
- @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
- @Qualifier不能单独使用。
- 可以设置名字
@Qualifier(value = "dog2")
@Resource
- 默认按名字装配,找不到再按类型装配,再找不到就报错
- @Resource=@Autowired+@Qualifier
@Nullable
- 表示可以传入null参数
- @NotNull在类字段中使用,@NonNull在方法或构造函数的参数上使用,都表示该字段不能为空。
4.3、注解开发
beans.xml约束
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns: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="com.kuang.pojo"/>
<!--<context:annotation-config/>-->
</beans>
注解,以下注解功能一样,都是注册bean并交给Spring容器管理
- @Component
- @Controller:web层
- @Service:service层
- @Repository:dao层
- @Scope(“prototype”):设置作用域
小结
XML与注解比较
- XML可以适用任何场景 ,结构清晰,维护方便
- 注解不是自己提供的类使用不了,开发简单方便
xml与注解整合开发 :推荐最佳实践 - xml管理Bean
- 注解完成属性注入
4.4、Java配置类
- 使用java配置类可等价于beans.xml(使用Java的方式配置Spring)
- 方法一:在User类上使用@Component,在配置类上使用@ComponentScan,会自动扫描并注册Bean
@Configuration //代表是配置类,底层也是@Component,相当于beans.xml里面的<beans><</beans>
@ComponentScan("com.lvboaa.pojo") // 扫描包
//可以通过@Import导入多个配置类
public class JavaConfig {
}
- 方法二:在配置类中使用@Bean注解
@Configuration
public class JavaConfig {
@Bean //<bean></bean>标签
public User getUser(){
return new User();
}
}
5、代理模式
5.1、静态代理
角色分析
- 抽象角色 : 一般使用接口或者抽象类来实现
- 真实角色 : 被代理的角色
- 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
- 客户 : 使用代理角色来进行一些操作 .
//抽象角色
public interface Rent {
public void rent();
}
//房东
public class Host implements Rent{
public void rent() {
System.out.println("房东出租房子!!!");
}
}
//中介代理房东
public class Proxy implements Rent{
private Host host;
//这儿 Spring推荐使用set方法
public Proxy(Host host) {
this.host = host;
}
public void rent() {
seeHouse();
host.rent();
hetong();
fare();
}
//看房
public void seeHouse(){
System.out.println("中介带你看房!");
}
//签合同
public void hetong(){
System.out.println("签合同和中介");
}
//收费
public void fare(){
System.out.println("中介收费!");
}
}
//用户
public class Client {
public static void main(String[] args) {
//直接找房东租房子
//Host host = new Host();
//host.rent();
//用户租房只面对中介,不需要面对房东
Host host = new Host();
//代理角色可以有一些附属操作
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
静态代理的好处:
- 使真实角色更加纯粹,不需要关注一些公共的事情 .
- 公共的业务由代理来完成 . 实现了业务的分工 ,
- 公共业务发生扩展时,直接添加业务,容易集中、方便处理
缺点 :
- 一个真实对象就需要有一个代理对象,代码量翻倍,开发效降低
- 解决方式:动态代理(底层是反射实现的)
5.2、动态代理
- 动态代理和静态代理角色一样,动态代理底层都是反射实现
- 代理角色是动态生成的,不是静态的(写好的)
- 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
- 基于接口的动态代理----JDK动态代理(这里讲的)
- 基于类的动态代理–cglib
- Java字节码实现: javasist (jboss服务器下)
JDK的动态代理需要了解两个类
- InvocationHandler:调用处理程序
- Proxy:代理
代码时间
//抽象角色
public interface Rent {
public void rent();
}
//房东
public class Host implements Rent {
public void rent() {
System.out.println("房东出租房子!!!");
}
}
//动态代理接口
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成代理类
public Object getProxy(){
//创建动态代理类的实例和静态方法
//参数:1.获得构造器 2.代理的接口 3.一个InvocationHandler
return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
}
//执行代理类真正执行的方法,执行接口的每个方法真正执行的方法
//proxy:你要代理谁
//method:你要代理这个类里面的什么方法,也可以是接口
//args:method方法的参数
//result:返回实例类
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
//动态代理的本质就是使用反射机制
Object result = method.invoke(rent,args);
fare();
return result;
}
public void seeHouse(){
System.out.println("中介带看房子");
}
public void fare(){
System.out.println("给中介费");
}
}
//客户
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//处理程序得到代理类
ProxyInvocationHandler handler = new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象
handler.setRent(host);
//动态生成代理类
Rent proxy = (Rent) handler.getProxy();
//调用方法会被编码并分配到调用处理程序的invoke方法
proxy.rent();
}
}
//核心:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是【接口】!
通用动态代理类
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
//处理代理实例,返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
//动态代理的本质就是使用反射机制
Object result = method.invoke(target,args);
return result;
}
public void log(String msg){
System.out.println("[Debug] 执行了"+msg+"方法!");
}
}
//测试
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
ProxyInvocationHandler handler = new ProxyInvocationHandler();
handler.setTarget(userService);
UserService proxy = (UserService) handler.getProxy();
proxy.update();
}
}
动态代理的好处
- 静态代理有的它都有,静态代理没有的,它也有!
- 一个动态代理 , 一般代理某一类业务
- 可以代理多个类,不需要实现多个代理类,代理的是接口!
6、AOP面向切面编程
机制
简介
- 通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
6.1、AOP在Spring中的作用
提供声明式事务,允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能,与业务逻辑无关,需要我们关注的部分。如日志 , 安全 , 缓存 , 事务等等(增强的功能)
- 切面(ASPECT):横切关注点被模块化的特殊对象。是一个类,如日志类。
- 通知(Advice):切面必须要完成的工作。日志类中的一个方法。
- 目标(Target):被通知对象,需要代理的接口。
- 代理(Proxy):向目标对象应用通知之后创建的对象,代理类
- 切入点(PointCut):切面通知执行的 “地点”的定义。
- 连接点(JointPoint):与切入点匹配的执行点。切入点和连接点就是在哪执行
AOP的通知(advice)
- AOP就是需要不改变源码的情况下,增加新的功能
6.2、实现AOP
导入依赖
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
</dependencies>
通过 Spring API 实现
- 编写增强类
public class Log implements MethodBeforeAdvice {
//method : 要执行的目标对象的方法
//objects : 被调用的方法的参数
//Object : 目标对象
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被执行了");
}
}
- 配置文件
<?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-->
<bean id="userService" class="com.kuang.service.UserServiceImpl"/>
<bean id="log" class="com.kuang.log.Log"/>
<bean id="afterLog" class="com.kuang.log.AfterLog"/>
<!--aop的配置-->
<aop:config>
<!--切入点 expression:表达式匹配要执行的方法-->
<aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
<!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
自定义类实现AOP
public class DiyPointcut {
public void before(){
System.out.println("----方法执行前-----");
}
public void after(){
System.out.println("----方法执行后----");
}
}
<bean id="diy" class="com.lvboaa.log.DiyPointcut"/>
<aop:config>
<aop:aspect ref="diy">
<aop:pointcut id="diyPonitcut" expression="execution(* com.lvboaa.service.UserServiceImpl.*(..))"/>
<aop:before pointcut-ref="diyPonitcut" method="before"/>
<aop:after pointcut-ref="diyPonitcut" method="after"/>
</aop:aspect>
</aop:config>
使用注解
@Aspect
public class AnnotationPointcut {
@Before("execution(* com.lvboaa.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("----方法执行前-----");
}
@After("execution(* com.lvboaa.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("----方法执行后----");
}
}
<bean id="annotationPointcut" class="com.lvboaa.log.AnnotationPointcut"/>
<!--开启支持注解-->
<aop:aspectj-autoproxy/>
7、Spring整合Mybatis
导入依赖
<!--mybatis相关-->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
</dependency>
<!--mybatis相关-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!--spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
<!--spring的数据源-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.1</version>
</dependency>
<!--aspectJ AOP 织入器-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<!--整合包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
</dependencies>
<!--配置Maven静态资源过滤问题-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
7.1、实现方式一
spring.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
<!--配置数据源DataSource:使用Spring的数据源替换Mybatis的数据源 c3p0 dbcp druid-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--配置sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--关联mybatis-->
<!--mybatis的位置-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--mybatis的配置-->
<property name="mapperLocations" value="classpath:com/lvboaa/mapper/*.xml"/>
</bean>
<!--配置sqlSessionTemplate-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<bean id="userMapper" class="com.lvboaa.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
- 需要Mapper层接口的实现类
public class UserMapperImpl implements UserMapper {
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> getAllUser() {
return sqlSession.getMapper(UserMapper.class).getAllUser();
}
}
- 测试
public class MyTest {
@Test
public void test() throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
UserMapperImpl userMapper = context.getBean("userMapper", UserMapperImpl.class);
for (User user : userMapper.getAllUser()) {
System.out.println(user);
}
}
}
7.2、实现方式二
- 实现类需要继承
SqlSessionDaoSupport
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
public List<User> getAllUser() {
return getSqlSession().getMapper(UserMapper.class).getAllUser();
}
}
- 配置文件注册bean
<bean id="userMapper2" class="com.lvboaa.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
总结
- Spring整合Mybatis可以不需要mybatis.xml文件
- 还可以使用注解来实现整合
8、事务
事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。
事务四个属性ACID
-
原子性(atomicity)
确保动作要么全部完成,要么完全不起作用 -
一致性(consistency)
一旦所有事务动作完成,事务就要被提交,事务执行前后数据保持一致 -
隔离性(isolation)
多个事务会同时处理相同的数据,每个事务都应该与其他事务隔离开来,防止数据损坏 -
持久性(durability)
事务一旦完成,无论系统发生什么错误,结果都不会受到影响。结果会被写到持久化存储器中
8.1、 编程式事务,使用try catch捕获异常执行回滚操作(不建议使用)
- 将事务管理代码嵌到业务方法中来控制事务的提交和回滚
- 缺点:必须在每个事务操作业务逻辑中添加额外的事务管理代码
8.2、 声明式事务,AOP横切实现,不改变原有代码
- 一般情况下比编程式事务好用。
- 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
- 将事务管理作为横切关注点,通过Spring AOP实现声明式事务管理。
接口实现类
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
//估计把delUser的接口写错,用来测试事务
public List<User> getAllUser() {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
mapper.addUser(new User(5,"lisi","12345"));
mapper.delUser(5);
return mapper.getAllUser();
}
public int addUser(User user) {
return getSqlSession().getMapper(UserMapper.class).addUser(user);
}
public int delUser(int id) {
return getSqlSession().getMapper(UserMapper.class).delUser(id);
}
}
spring.xml配置文件(主要是配置事务)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--配置数据源DataSource:使用Spring的数据源替换Mybatis的数据源 c3p0 dbcp druid-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--配置sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--关联mybatis-->
<!--mybatis的位置-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--mybatis的配置-->
<property name="mapperLocations" value="classpath:com/lvboaa/mapper/*.xml"/>
</bean>
<!--配置sqlSessionTemplate-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<bean id="userMapper" class="com.lvboaa.mapper.UserMapperImpl">
<property name="sqlSessionTemplate" ref="sqlSession"/>
</bean>
<!--配制事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--配置什么方法使用什么事务,可配置事务传播特性,Spring默认是REQUIRED(有7种)-->
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="del*"/>
<tx:method name="get*"/>
<!--所有方法配置事务-->
<!--<tx:method name="*"/>-->
</tx:attributes>
</tx:advice>
<!--织入事务-->
<aop:config>
<!--切入点-->
<aop:pointcut id="txPointcut" expression="execution(* com.lvboaa.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
</beans>
测试
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
for (User user : userMapper.getAllUser()) {
System.out.println(user);
}
}
}
- 结果delUser报错事务自动回滚,完成了事务控制,=
- 将sql语句改正确,事务成功
为什么需要事务管理
- 事务在项目开发过程非常重要,主要是保证数据的一致性
- 如果不配置,就需要手动提交控制事务(commit和rollback)