spring详解
spring是于2003年兴起的一款轻量级的,非侵入式的IOC和AOP的一站式的java开发框架,为简化企业级应用开发而生。
轻量级的:指的是spring核心功能的jar包不大。
非侵入式的:业务代码不需要继承或实现spring中任何的类或接口
IOC:控制反转(Inverse of Control),以前项目都是在哪儿用到对象,在哪儿new,把生成对象的权利反转给spring框架(让spring把对象管理起来,在哪用在哪注入)
AOP:面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。
一站式框架:
- 提供核心功能,主要是IOC,创建管理对象;
- 提供面向切面编程,增强程序扩展;
- 对数据访问层进行了封装(重点在于事务管理)
- 对web层进行封装,使得请求更加便捷
Spring Hello World搭建
1.Maven 导入 spring 核心基础 jar
<!-- spring-context -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.2.RELEASE</version>
</dependency>
2.编写一个Admin实体类
package com.ffyc.ssm.model;public class Admin {private Integer id;private String account;public Admin() {System.out.println("admin无参构造");}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getAccount() {return account;}public void setAccount(String account) {this.account = account;}
}
3.编写 spring 配置文件,在resources/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" xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"><!--配置需要让spring进行管理的类--><bean id="admin" class="com.ffyc.ssm.model.Admin"></bean>
</beans>
4.测试spring
读取spring配置文件,并对文件中配置的对象进行创建
package com.ffyc.ssm.test;public class Test1 {public static void main(String[] args) {/*以前使用对象方式,在哪儿用,在哪儿newnew Admin();spring思想是,由框架统一对项目中的类进行管理(创建对象,后期增强一些功能),在需要的地方注入即可*///Map(admin,new Admin)//ClassPathXmlApplicationContext就是spring中的实际实现者ClassPathXmlApplicationContext app=new ClassPathXmlApplicationContext("spring.xml");//Object admin=app.getBean("admin");//不知道返回的类型,从Map中拿出new Admin对象Admin admin=app.getBean("admin",Admin.class);//明确类型System.out.println(admin);}
}
IOC(控制反转)
正控:若要使用某个对象,需要自己去负责对象的创建
反控:若要使用某个对象,只需要从 Spring 容器中获取需要使用的对象,不关心对象的创建过程,也就是把创建对象的控制权反转给了 Spring 框架
**底层实现方式:**解析 xml/扫描注解标签 + 工厂模式 + 反射机制
Spring Bean 管理
1、基于 xml 配置方式
配置需要让spring进行管理的类
id=“唯一的标识”
class=“让spring管理的类名”
bean对象?由spring框架创建并管理的对象
- Scope=“singleton 默认值”,单例的,在整个应用程序中只创建一个对象,在spring框架启动时就创建好了
- scope=“prototype”,原型的,每次获取时创建一个对象,可以创建多个
<?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"><!--配置需要让spring进行管理的类--><bean id="admin" class="com.ffyc.ssm.model.Admin" scope="prototype"></bean>
</beans>
public class Test1 {public static void main(String[] args) {//读取spring配置文件,并对文件中配置的对象进行创建//ClassPathXmlApplicationContext就是spring中的实际实现者ClassPathXmlApplicationContext app=new ClassPathXmlApplicationContext("spring.xml");Admin admin=app.getBean("admin",Admin.class);Admin admin1=app.getBean("admin",Admin.class);System.out.println(admin);System.out.println(admin1);}
}
/*
admin无参构造
admin无参构造
com.ffyc.ssm.model.Admin@685cb137
com.ffyc.ssm.model.Admin@6a41eaa2
*/
spring框架创建对象(控制反转)时,为对象的属性进行赋值操作(这个赋值操作称为依赖注入)
1.属性赋值 getXXX、setXXX方法
<bean id="admin" class="com.ffyc.ssm.model.Admin" scope="prototype"><property name="id" value="10"></property><property name="account" value="张三"></property>
</bean>
public class Test1 {public static void main(String[] args) {ClassPathXmlApplicationContext app=new ClassPathXmlApplicationContext("spring.xml");Admin admin=app.getBean("admin",Admin.class);System.out.println(admin);}
}
/*
admin无参构造
Admin{id=10, account='张三'}
*/
2.构造方法赋值
<bean id="admin" class="com.ffyc.ssm.model.Admin" scope="prototype"><constructor-arg name="id" value="1"></constructor-arg><constructor-arg name="account" value="1"></constructor-arg>
</bean>
public class Test1 {public static void main(String[] args) {ClassPathXmlApplicationContext app=new ClassPathXmlApplicationContext("spring.xml");Admin admin=app.getBean("admin",Admin.class);System.out.println(admin);}
}
//Admin{id=1, account='1'}
在一个方法中注入另一个方法
<bean id="admin" class="com.ffyc.ssm.model.Admin" scope="prototype"><constructor-arg name="id" value="1"></constructor-arg><constructor-arg name="account" value="1"></constructor-arg>
</bean>
<bean id="adminDao" class="com.ffyc.ssm.dao.AdminDao"></bean>
<bean id="adminService" class="com.ffyc.ssm.service.AdminService"><property name="adminDao" ref="adminDao"></property> <!--在adminService中注入adminDao-->
</bean>
public class Test2 {public static void main(String[] args) {ClassPathXmlApplicationContext app=new ClassPathXmlApplicationContext("spring.xml");Admin admin=app.getBean("admin",Admin.class);AdminService adminService=app.getBean("adminService",AdminService.class);adminService.save(admin);}
}
2、注解方式实现(建议)
1、注解需要的jar包
注解功能封装在AOP包中,导入 Spring aop jar 包即可
<!-- spring-context -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.2.RELEASE</version>
</dependency>
2、开启注解扫描
<?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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--开启注解扫描,包名范围尽可能大一些--><context:component-scan base-package="com.ffyc.ssm"> </context:component-scan>
</beans>
3、注解创建对象
有注解标签才是spring管理的对象,并创建对象
1.@Component(value = “admin”) 一般用于模型类,等同于
2.@Scope(value=“prototype”) 原型(多次)
@Scope(value=“singleton”) 单例(一次)
@Component(value = "admin")
@Scope(value = "singleton")
3.@Repository(value = “adminDao”),用于dao层
@Repository(value = "adminDao")
public class AdminDao {public void saveAdmin(Admin admin){System.out.println("保存管理员"+admin);}
}
4.@Service(value = “adminService”)
5.@Autowired,spring框架提供的一种自动注入的注解标签,可以写在字段和 setter 方法上。如果写在字段上,那么就不需要再写 setter 方法。默认情况下它要求依赖对象必须存在,如果允许 null 值,可以设置它的 required 属性为 false。
有两种方式去查找对象:
- byType,去spring容器中根据当前类型搜索,@Autowired
- byName,通过名称查找,需要结合 @Qualifier(value=“adminDao”),value值和@Repository(value = “adminDao”)里面的value对应
6.@Resource,是jdk中提供的一个注解标签 - byType,去spring器中根据当前类型搜索,@Resource
- byName,通过名称查找,@Resource(name = “adminDao”)
Autowired(required=true),注入时,对象值不能为空,为空报错
@Service(value = "adminService")
public class AdminService {/*@Autowired@Qualifier(value = "adminDao")AdminDao adminDao;//依赖注入*///@Resource@Resource(name = "adminDao")AdminDao adminDao;public void save(Admin admin){adminDao.saveAdmin(admin);}
}
public class Test3 {public static void main(String[] args) {ClassPathXmlApplicationContext app=new ClassPathXmlApplicationContext("spring.xml");Admin admin=app.getBean("admin",Admin.class);AdminService adminService=app.getBean("adminService",AdminService.class);adminService.save(admin);}
}
3、注解与 XML 的对比
注解优点: 方便、直观、高效(代码少,没有配置文件的书写那么复杂)。
**注解缺点:**以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的(通过maven把项目重新编译compile一下)。
xml 优点是: 配置和代码是分离的,在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。
**xml 缺点是:**编写麻烦,效率低,大型项目过于复杂。
Spring JDBC
spring框架中的JDBC功能
- jdbc封装: JdbcTemplate(了解即可 后面常用mybatis),事务管理(重点)
- ioc实际的应用
Spring 是个一站式框架:Spring 自身也提供了控制层的 SpringMVC 和 持久层的 Spring JdbcTemplate。
1.下载 Spring JdbcTemplate 的 jar 包
导入jdbc模块依赖
<!-- spring-jdbc -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.2.RELEASE</version>
</dependency>
导入mysql
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.16</version>
</dependency>
导入阿里巴巴提供的数据源管理组件,数据源组件封装了连接数据库,还有数据库连接池功能
druid(德鲁伊),常用的数据库链接池组件:dbcp、c3p0
<!-- 阿里的数据源 -->
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.10</version>
</dependency>
2.config.properties
classDriverName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/ssmdb?serverTimezone=Asia/Shanghai
uname=root
pwd=123456
3.db.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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--配置jdbc功能--><!--负责加载config.properties文件--><context:property-placeholder location="classpath:config.properties"></context:property-placeholder><!--spring统一管理数据库链接对象,创建对象并赋值--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${classDriverName}"></property><property name="url" value="${url}"></property><property name="username" value="${uname}"></property><property name="password" value="${pwd}"></property><property name="initialSize" value="5"></property><property name="maxActive" value="10"></property></bean><!--创建spring框架封装的jdbc功能的实现类--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property> <!--注入数据源对象--></bean><!--以后引入mybatis,需要数据源对象,直接注入即可-->
</beans>
4.spring.xml注解db.xml
<import resource="classpath:db.xml"></import> <!--指的是target/classes下的-->
增删改用update
@Repository(value = "adminDao")
public class AdminDao {@AutowiredJdbcTemplate jdbcTemplate;public void saveAdmin(Admin admin){//jdbcTemplate.update("insert into admin(account,password)value(?,?)","aaa","111");jdbcTemplate.update("delete from admin where id =?","5");}
}
查询,返回单个结果
public void saveAdmin(Admin admin){Admin admin2=jdbcTemplate.queryForObject("select * from admin where id=?",new RowMapper<Admin>() {@Overridepublic Admin mapRow(ResultSet resultSet, int i) throws SQLException {Admin admin1=new Admin();admin1.setId(resultSet.getInt("id"));admin1.setAccount(resultSet.getString("account"));return admin1;}},1);System.out.println(admin2);
}
查询,返回多个结果
public void saveAdmin(Admin admin){List<Admin> adminList=jdbcTemplate.query("select * from admin",new RowMapper<Admin>() {@Overridepublic Admin mapRow(ResultSet resultSet, int i) throws SQLException {Admin admin1=new Admin();admin1.setId(resultSet.getInt("id"));admin1.setAccount(resultSet.getString("account"));return admin1;}});System.out.println(adminList);
}
AOP
不使用 AOP 的开发方式
案例:
先定义好接口与一个实现类,该实现类中除了要实现接口中的方法外,还要写两个非业务方法。非业务方法也称为交叉业务逻辑:
- doTransaction():用于事务处理
- doLog():用于日志处理
然后,再使接口方法调用它们。接口方法也称为主业务逻辑.
在方法中想要实现额外功能时,需要在业务代码中显示调用.
即使抽取一个工具类,将这些方法封装起来,依然还是需要显示调用.
AOP概述
AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,AOP 可以**对业务逻辑和非业务逻辑进行隔离,从而使得各部分之间的耦合度降低,提高程序的可重用性,**同时提高了开发的效率。将程序中的一些非业务代码进行提取,在不需要修改原来代码的情况下,为程序添加额外的功能。
AOP思想不是spring框架特有的,只是spring框架引入使用了这一思想。
面向切面编程的好处就是: 减少重复,专注业务;
注意:面向切面编程只是面向对象编程的一种补充。
核心原理: 使用动态代理的方式在执行方法前后或者出现异常的时候做加入相关的逻辑.
非业务代码使用案例:
- 事务处理:开启事务,关闭事务,出现异常后回滚事务
- 验证权限:在执行方法前,判断是否具有权限
- 打印日志:在执行前进行日志处
- 统一异常处理
是怎么做到的?底层实现:使用的是动态代理模式。是通过一个代理对象来实现对非业务代码进行调用的。告诉代理对象,调用哪个方法时,让代理对象去帮助我们调用哪个方法
代理对象(4s 中介 手机店)
AOP的基本概念
**连接点(Joinpoint):**类中可以被增强的方法
**切入点(pointcut):**类中有很多方法可以被增强,但实际中只有 add 和 update被增了,那么 add 和 update 方法就被称为切入点(类中实际被增强的方法)
通知(Advice): 指提取的非业务的功能。通知分为:前置通知、后置通知、异常通知、最终通知、环绕通知
**切面(Aspect):**把通知添加到切入点的整个过程
目标(Target): 代理的目标对象(连接点,切入点所在类AdminDao)
代理(Proxy): 向目标对象应用通知时创建的代理对象
springAOP实现
有两种实现方式:1.xml配置方式 2.注解方式
1、xml配置方式
对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向切面编程。
AspectJ 是一个基于 Java 语言的 AOP 框架,它提供了强大的 AOP 功能,且其实现方式更为简捷,使用更为方便, 而且还支持注解式开发。所以,spring中引入了一个AspectJ的aop框架.
下载 AOP 相关 jar
<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.2.2.RELEASE</version>
</dependency>
<!--spring.xml中-->
<import resource="classpath:aopDemo.xml"></import>
基于 aspectj 的 xml 配置方式实现。
先把类交给spring管理,这样spring生成的代理对象才可以调用
<!--aopDemo.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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!--把非业务代码(通知) 交给spring框架管理,以后要用到这个类的方法,就把myutil注入--><bean id="myutil" class="com.ffyc.ssm.util.MyUtil"></bean><aop:config><!--配置切入点--><aop:pointcut id="saveAdmin" expression="execution(* com.ffyc.ssm.dao.AdminDao.saveAdmin(..))"/><!--将通知与切入点进行配置,生成的代理对象就知道如何调用--><aop:aspect ref="myutil"><!--前置通知:先输出通知在执行方法--><aop:before method="printLog" pointcut-ref="saveAdmin"></aop:before></aop:aspect></aop:config>
</beans>
package com.ffyc.ssm.util;/* 提取的非业务的功能称为通知通知又分为5种: 前置通知、后置通知、异常通知、最终通知、环绕通知*/
public class MyUtil {public void printLog(){System.out.println("打印日志");}public void commit(){System.out.println("提取事务");}public void exceptionAdvice(Throwable e){System.out.println("异常通知"+e.getMessage());}
}
package com.ffyc.ssm.dao;@Repository(value = "adminDao")
//目标类
public class AdminDao {@AutowiredJdbcTemplate jdbcTemplate;/*连接点:类中可以被增强的方法,称为连接点切入点: 类中实际被增强的方法,横切面切入的方法*/public void saveAdmin(Admin admin){System.out.println("保存管理员");}public void updateAdmin(Admin admin){System.out.println("修改管理员");}
}
public class Test3 {public static void main(String[] args) {ClassPathXmlApplicationContext app=new ClassPathXmlApplicationContext("spring.xml");Admin admin=app.getBean("admin",Admin.class);AdminService adminService=app.getBean("adminService",AdminService.class);adminService.save(admin);}
}
通知又分为5种:
前置通知:业务方法执行前调用
后置通知(after-returning):业务方法执行后调用,当方法出现异常不执行
异常通知 (after-throwing):业务方法出现异常时调用
最终通知(after):业务方法执行后调用,当方法出现异常也会执行
环绕通知,环绕通知包含前四种通知
package com.ffyc.ssm.util;public class MyUtil {/*ProceedingJoinPoint需要调用的方法*/public void aroundAdvice(ProceedingJoinPoint point){try {System.out.println("前置通知");point.proceed();//调用我们自己的业务方法System.out.println("后置通知");} catch (Throwable throwable) {throwable.printStackTrace();System.out.println("异常通知"+throwable.getMessage());}System.out.println("最终通知");}
}
<aop:config><!--配置切入点--><aop:pointcut id="saveAdmin" expression="execution(* com.ffyc.ssm.dao.AdminDao.saveAdmin(..))"/><!--将通知与切入点进行配置,生成的代理对象就知道如何调用--><aop:aspect ref="myutil"><aop:around method="aroundAdvice" pointcut-ref="saveAdmin"></aop:around></aop:aspect>
</aop:config>
2、注解方式的实现
1.开启自动代理
<!--spring.xml中开启自动代理-->
<aop:aspectj-autoproxy />
2.@Component //让spring管理生成对象
@Aspect //表明装有通知的类/切面
package com.ffyc.ssm.util;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Component //让spring管理生成对象
@Aspect //表明装有通知的类/切面
public class MyUtil {//@Before("execution(* com.ffyc.ssm.dao.AdminDao.saveAdmin(..))")//@AfterReturning("execution(* com.ffyc.ssm.dao.AdminDao.saveAdmin(..))")//@After("execution(* com.ffyc.ssm.dao.AdminDao.saveAdmin(..))")public void printLog(){System.out.println("打印日志");}public void commit(){System.out.println("提取事务");}//@AfterThrowing(value = "execution(* com.ffyc.ssm.dao.AdminDao.saveAdmin(..))",throwing = "e")public void exceptionAdvice(Throwable e){System.out.println("异常通知"+e.getMessage());}/*ProceedingJoinPoint需要调用的方法*/@Around("execution(* com.ffyc.ssm.dao.AdminDao.saveAdmin(..))")public void aroundAdvice(ProceedingJoinPoint point){try {System.out.println("前置通知");point.proceed();//调用我们自己的业务方法System.out.println("后置通知");} catch (Throwable throwable) {throwable.printStackTrace();System.out.println("异常通知"+throwable.getMessage());}System.out.println("最终通知");}
}
spring事务管理
jdbc自动事务提交,mybatis里面,事务默认不自动提交,需要我们在程序中手动提交 sqlsession.commit();spring框架把提交事务的功能帮助我们管理起来了,封装好了。
事物可以看做是由对数据库若干操作组成的一个单元。案例:转账
关系型数据库事务基本特征:
1.原子性:保障一个事务中的多条sql,要么都执行,要么都不执行
2.隔离性
3.持久性
4.一致性
我们在开发企业应用时,对于业务人员的一个操作实际是对数据读写的多步操作的结合。由于数据操作在顺序执行的过程中,任何一步操作都有可能发生异常, 异常会导致后续操作无法完成,此时由于业务逻辑并未正确的完成,之前成功操作数据的并不可靠,需要在这种情况下进行回退。
事务的作用就是为了保证用户的每一个操作都是可靠的,事务中的每一步操作都必须成功执行,只要有发生异常就回退到事务开始未进行操作的状态,这些操作要么都完成,要么都取消,从而保证数据满足一致性的要求
Spring 中的事务管理分两种:
1.编程式事务管理:在代码中写,实际开发中使用的少。这种方式需要注入一个事务管理对象TransactionTemplate,然后在我们代码中需要提交事务或回滚事务时自己写代码实现。
public class AdminDao {@AutowiredJdbcTemplate jdbcTemplate;@AutowiredTransactionTemplate transactionTemplate;public void saveAdmin(Admin admin){System.out.println("保存管理员");transactionTemplate.execute(new TransactionCallback<Object>() {@Overridepublic Object doInTransaction(TransactionStatus transactionStatus) {jdbcTemplate.update("insert into admin(account,password)value(?,?)","aa","aa");int i=10/0;jdbcTemplate.update("insert into admin(account,password)value(?,?)","bb","bb");return null;}});}
}
2.声明式事务管理:使用注解标签标实现,底层实现原理就是AOP的思想,本质是对方法前后进行拦截,是方法级别的,在执行某个方法时,为方法添加额外的事务管理功能
Spring 声明式事物管理方式有两种:
- 基于 xml 配置
- 基于注解实现
Spring 针对不同的 dao 框架,提供了不同的实现类,Jdbc,mybatis事物管理实现类是 DataSourceTransactionManager.
(1)jar包
<!-- spring-jdbc -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.2.RELEASE</version>
</dependency>
(2)配置事物管理器、注解方式
<!-- 配置spring事务管理类, 并注入数据源。db.xml -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"></property>
</bean><!--开启注解事务管理。db.xml-->
<tx:annotation-driven transaction-manager="transactionManager"/>
(3)@Transactional 注解标签
public class AdminDao {
@Autowired
JdbcTemplate jdbcTemplate;@Transactional
public void saveAdmin(Admin admin){System.out.println("保存管理员");jdbcTemplate.update("insert into admin(account,password)value(?,?)","aa","aa");int i=10/0;jdbcTemplate.update("insert into admin(account,password)value(?,?)","bb","bb");
}
@Transactional标签的用法:
- 一般把事务管理的注解标签在service的方法中来进行控制。因为dao层是接口,一个接口对应一个sql,在一个业务逻辑中,可能要执行多个dao层的sql,需要把这多个sql,放在同一个事务管理中进行
- @Transactional可以添加在service层中类上,类中所有的方法都会添加事务管理功能
- @Transactional如果只添加在某个方法上,那么表示此方法在事务管理中进行
案例:转钱
这个案例中如果service层没有@Transactional,而dao有@Transactional,可能会出现问题。加减是独立的的事务,不是同一个事务
package com.ffyc.ssm.service;@Service(value = "adminService")
public class AdminService {
//对于service层来讲,转账是一个操作
@Transactionalpublic void zhuanzhang(){int id1=1;int id2=7;int money=500;//调用不同的层的方法adminDao.jian(id1,money);System.out.println(10/0);adminDao.jia(id2,money);}
}
package com.ffyc.ssm.dao;@Repository(value = "adminDao")
//目标类
public class AdminDao {@AutowiredJdbcTemplate jdbcTemplate;//减钱public void jian(int id1,int m){jdbcTemplate.update("update admin set money=money-"+m+" where id=?",id1);}//加钱public void jia(int id2,int m){jdbcTemplate.update("update admin set money=money+"+m+" where id=?",id2);}
}
package com.ffyc.ssm.test;public class Test3 {public static void main(String[] args) {ClassPathXmlApplicationContext app=new ClassPathXmlApplicationContext("spring.xml");Admin admin=app.getBean("admin",Admin.class);AdminService adminService=app.getBean("adminService",AdminService.class);adminService.zhuanzhang();}
}
@Transactional 声明式注解事务管理,在以下情况会失效:(重点)
1.修饰非public的方法,底层权限只针对public修饰的方法
2.方法中的异常被catch捕获处理了
@Transactional
public void zhuanzhang(){int id1=1;int id2=7;int money=500;//调用不同的层的方法adminDao.jian(id1,money);try{System.out.println(10/0);}catch (Exception e){System.out.println("算术异常");}adminDao.jia(id2,money);
}
//方法中出现异常,但是却正常转账了,是不正常的
3.出现编译期异常,事务不生效
默认情况下,事务只对运行期异常进行生效@Transactional(rollbackFor = RuntimeException.class)
我们可以把其修改为**@Transactional(rollbackFor = Exception.class),这样就可以处理任意的异常**
@Transactional(rollbackFor = Exception.class)//出现编译期异常,事务也会生效
public void zhuanzhang() throws UnsupportedEncodingException {int id1=1;int id2=7;int money=500;adminDao.jian(id1,money);"abc".getBytes("utf--8");adminDao.jia(id2,money);
}
4.@Transactional事务传播行为设置错误
5.数据库引擎不支持事务。数据库引擎是mysql底层具体的一种数据处理实现的机制
常用的两个引擎:innodb(支持事务功能),myisam(不支持事务)
6.同一个类中,使用非代理对象调用一个有事务的方法
在一个非事务方法中使用this(原始的对象==自己new出来的对象)
public class AdminService {//this表示自己没有被spring进行任何增强的 最原始的new出来的对象//如果是spring生成的对象,spring会根据这个方法上所添加的注解标签来做一些额外的增强,会返回一个代理对象@Transactional(rollbackFor = Exception.class)//加上这个就好了public void test() throws UnsupportedEncodingException {this.zhuanzhang();}@Transactional(rollbackFor = Exception.class)public void zhuanzhang() throws UnsupportedEncodingException {int id1=1;int id2=7;int money=500;//调用不同的层的方法adminDao.jian(id1,money);"abc".getBytes("utf--8");adminDao.jia(id2,money);}
}
public class Test3 {public static void main(String[] args) throws UnsupportedEncodingException {ClassPathXmlApplicationContext app=new ClassPathXmlApplicationContext("spring.xml");Admin admin=app.getBean("admin",Admin.class);AdminService adminService=app.getBean("adminService",AdminService.class);adminService.test();}
}
事务传播行为
事务功能本质上属于mysql数据库
spring事务管理:指的是spring框架可以帮助我们进行事务开启、提交、回滚
什么叫事务传播行为?
即然是传播,那么至少有两个方法才可以发生传播,单体不存在传播这个行为。
事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。事务传播行为是 Spring 框架独有的事务增强特性,不属于事务实际提供者mysql.
例如:A事务方法调用B事务方法时,B事务是一个独立的事务呢(独立的,表示B出现了问题,不影响A)?还是B事务合并到A事务中呢(B影响A,A影响B)?这就是由B的事务传播行为决定的
要么多个事务嵌套在一个中,要么多个事务相互独立运行。
Spring 定义了七种传播行为:
事务传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果A有事务, B加入到A事务中;如果A没有事务,B就新建一个事务,与A没有关系。(常见) |
PROPAGATION_REQUIRES_NEW | 无论A是否有事务,B都会创建新的事务(独立的) |
PROPAGATION_SUPPORTS | 支持A事务;如果A没有事务,B就以非事务方式执行 |
PROPAGATION_NEVER | B以非事务方式执行;如果A存在事务,则抛出异常。 |
PROPAGATION_MANDATORY | 使用A事务,如果A没有事务,就抛出异常。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务, 则执行与 PROPAGATION_REQUIRED 类似的操作。 |
注意:事务传播行为不能是同一个类中的方法相互调用,必须是一个类中的方法调用另一个类中的方法
案例:保存管理员时,需要向日志表里插入一条记录
1、PROPAGATION_REQUIRED
A事务方法调用B方法,传播行为REQUIRED。如果A方法有事务,B加入到A方法事务中,任意一方出现异常都会回滚;A方法没有事务,B方法会自己创建一个事务,与A没有关系。
AdminService中save的调用LogService中的saveLog方法,两个方法都有事务,调用saveLog时就加入到save中(合二为一),如果出现异常两者SQL都不会执行
@Service(value = "adminService")
public class AdminService {@AutowiredAdminDao adminDao;@AutowiredLogService logService;//AdminService调用LogService@Transactional(propagation = Propagation.REQUIRED)public void save(Admin admin){adminDao.saveAdmin(admin);logService.saveLog();//save调用saveLog方法System.out.println(10/0);}
}
@Repository(value = "adminDao")
public class AdminDao {@AutowiredJdbcTemplate jdbcTemplate;public void saveAdmin(Admin admin){System.out.println("保存管理员");jdbcTemplate.update("insert into admin(account,password)value(?,?)","ff","ff");}
}
@Service(value = "logService")
public class LogService {@AutowiredLogDao logDao;@Transactional(propagation = Propagation.REQUIRED)public void saveLog(){logDao.saveLog();}
}
@Repository(value = "logDao")
public class LogDao {@AutowiredJdbcTemplate jdbcTemplate;public void saveLog(){jdbcTemplate.update("insert into log(oper_time)value(now())");}
}
public class Test3 {public static void main(String[] args) throws UnsupportedEncodingException {ClassPathXmlApplicationContext app=new ClassPathXmlApplicationContext("spring.xml");Admin admin=app.getBean("admin",Admin.class);AdminService adminService=app.getBean("adminService",AdminService.class);adminService.save(admin);}
}
save没有事务,saveLog有事务,此时saveLog会新建一个事务,与save没有关系。保存管理员正常执行。结果:保存管理员也可以打印日志
@Service(value = "adminService")
public class AdminService {@AutowiredAdminDao adminDao;@AutowiredLogService logService;//AdminService调用LogServicepublic void save(Admin admin){adminDao.saveAdmin(admin);//正常执行logService.saveLog();//save调用saveLog方法,新建一个事务System.out.println(10/0);}
}
@Service(value = "logService")
public class LogService {@AutowiredLogDao logDao;@Transactional(propagation = Propagation.REQUIRED)public void saveLog(){logDao.saveLog();}
}
save没有事务,saveLog有事务,此时saveLog会新建一个事务,异常在saveLog中,保存管理员正常执行,而保存日志有异常而不会执行
@Service(value = "adminService")
public class AdminService {@AutowiredAdminDao adminDao;@AutowiredLogService logService;//AdminService调用LogServicepublic void save(Admin admin){adminDao.saveAdmin(admin);logService.saveLog();//save调用saveLog方法}
}
@Service(value = "logService")
public class LogService {@AutowiredLogDao logDao;@Transactional(propagation = Propagation.REQUIRED)public void saveLog(){logDao.saveLog();System.out.println(10/0);}
}
2、PROPAGATION_REQUIRES_NEW
无论A是否有事务,B都会创建新的事务(独立的)
两者是独立的事务,异常在save中,保存管理员不执行,保存日志执行
@AutowiredAdminDao adminDao;@AutowiredLogService logService;//AdminService调用LogServicepublic void save(Admin admin){adminDao.saveAdmin(admin);logService.saveLog();System.out.println(10/0);}
}
@Service(value = "logService")
public class LogService {@AutowiredLogDao logDao;@Transactional(propagation = Propagation.REQUIRES_NEW)public void saveLog(){logDao.saveLog();}
}
3、PROPAGATION_NEVER
B以非事务方式执行;如果A存在事务,则抛出异常。
saveLog在非事务的方法中执行,如果save中有事务,则会抛出异常
public class AdminService {@AutowiredAdminDao adminDao;@AutowiredLogService logService;//AdminService调用LogService@Transactionalpublic void save(Admin admin){adminDao.saveAdmin(admin);logService.saveLog();}
}
@Service(value = "logService")
public class LogService {@AutowiredLogDao logDao;@Transactional(propagation = Propagation.NEVER)public void saveLog(){logDao.saveLog();}
}
相关文章:

spring详解
spring是于2003年兴起的一款轻量级的,非侵入式的IOC和AOP的一站式的java开发框架,为简化企业级应用开发而生。 轻量级的:指的是spring核心功能的jar包不大。 非侵入式的:业务代码不需要继承或实现spring中任何的类或接口 IOC&…...

香港服务器备案会通过吗?
对于企业或个人来说,合规备案是网络运营的基本要求,也是保护自身权益的重要举措。以下内容围绕备案展开话题,希望为您解开疑惑。 香港服务器备案会通过吗? 目前,香港服务器无法备案,这是由于国内管理规定的限制…...

乐鑫推出 ESP ZeroCode 控制台
乐鑫科技 ESP ZeroCode 控制台是一个网页应用,用户只需点击鼠标,描述想要创建的产品类型、功能及其硬件配置,即可按照自身需求,快速生成符合 Matter 认证的固件,并在硬件上进行试用。试用过程中,如有任何不…...

从NLP到聊天机器人
一、说明 今天,当打电话给银行或其他公司时,听到电话另一端的机器人向你打招呼是很常见的:“你好,我是你的数字助理。请问你的问题。是的,机器人现在不仅可以说人类语言,还可以用人类语言与用户互动。这是由…...

相关搜索引擎常用搜索语法(Google hacking语法和FOFA语法)
一:Google Hack语法 Google Hacking原指利用Google搜索引擎搜索信息来进行入侵的技术和行为,现指利用各种搜索引擎并使用一些高级的搜索语法来搜索信息。既利用搜索引擎强大的搜索功能,在在浩瀚的互联网中搜索到我们需要的信息。 ࿰…...

Mysql查询
第三章:select 语句 SELECT employees.employee_id,employees.department_id FROM employees WHERE employees.employee_id176; DESC departments;SELECT * FROM departments;第四章:运算符使用 SELECT employees.last_name,employees.salary FROM em…...

解决http下navigator.clipboard为undefined问题
开发环境下使用navigator.clipboard进行复制操作,打包部署到服务器上后,发现该功能显示为undefined;查相关资料后,发现clipboard只有在安全域名下才可以访问(https、localhost),在http域名下只能得到undefined…...

mysql之host is blocked问题
程序上线一段时间之后,更新程序总是遇到这个问题 每次都是重启几次程序,或者执行 flush hosts; 毕竟指标不治本,抽出时间决定分析一下问题,查阅了几篇博客。(感谢这几位大佬) https://blog.51cto.com/u_…...

每日一题:2337 移动片段得到字符串
给你两个字符串 start 和 target ,长度均为 n 。每个字符串 仅 由字符 L、R 和 _ 组成,其中: 字符 L 和 R 表示片段,其中片段 L 只有在其左侧直接存在一个 空位 时才能向 左 移动,而片段 R 只有在其右侧直接存在一个 …...

嵌入式设备的 Json 库基本使用
大家好,今天给介绍一款基于 C 语言的轻量级的 Json 库 – cJson。可用于资源受限的嵌入式设备中。 cJSON 是一个超轻巧,携带方便,单文件,简单的可以作为 ANSI-C 标准的 JSON 解析器。 cJSON 是一个开源项目,github 下…...

GEEMAP 中如何拉伸图像
图像拉伸是最基础的图像增强显示处理方法,主要用来改善图像显示的对比度,地物提取流程中往往首先要对图像进行拉伸处理。图像拉伸主要有三种方式:线性拉伸、直方图均衡化拉伸和直方图归一化拉伸。 GEE 中使用 .sldStyle() 的方法来进行图像的…...

软件测试学术顶会——ISSTA 2023 论文(网络安全方向)清单、摘要与总结
总结 本次会议涵盖的安全研究主题广泛,包括源代码分析、二进制代码分析、恶意软件检测、漏洞检测、模糊测试、程序验证等。一些热门的研究方向包括:基于机器学习的漏洞检测、大型语言模型在软件安全中的应用、区块链智能合约安全分析。这些方向都在最近几年持续发展。一些较冷门…...

基于YOLOv8模型和PCB电子线路板缺陷目标检测系统(PyTorch+Pyside6+YOLOv8模型)
摘要:基于YOLOv8模型PCB电子线路板缺陷目标检测系统可用于日常生活中检测与定位PCB线路板瑕疵,利用深度学习算法可实现图片、视频、摄像头等方式的目标检测,另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检…...

centos安装mysql8
检查是否有mariadb rpm -qa|grep mariadb rpm -e --nodeps mariadb-server安装mysql-8.0.31-el7-x86_64.tar.gz包 安装mysql-8.0.31-el7-x86_64.tar.gz包 cd datatar -xvf mysql-8.0.31-el7-x86_64.tar.gzmv mysql-8.0.31-el7-x86_64 mysql 依赖安装 报错解决,安…...

【Apollo】阿波罗自动驾驶技术:引领汽车行业革新
前言 Apollo (阿波罗)是一个开放的、完整的、安全的平台,将帮助汽车行业及自动驾驶领域的合作伙伴结合车辆和硬件系统,快速搭建一套属于自己的自动驾驶系统。 开放能力、共享资源、加速创新、持续共赢是 Apollo 开放平台的口号。百度把自己所拥有的强大、…...

一文看懂!数据管道和数据流在数据分析中的作用
当我们谈论数据分析时,我们通常会想到一系列的步骤,包括数据收集、数据清洗、数据分析和数据可视化等。然而,在这些步骤中,有两个非常重要的概念:数据管道和数据流。这两个概念在数据分析过程中起着至关重要的作用。本…...

Linux系统下检验Tensorflow 2.xx版本和1.xx版本是否安装成功
目录 版本问题Tensorflow 1.xx的测试代码:Tensorflow 2.xx的测试代码:Tensorflow 2.6版本实际的测验结果 总结 版本问题 查询资料发现,多数检验Tensorflow是否安装成功的方法,多数方法都是1.xx版本的,直接使用1.xx版本…...

暑期高铁站大量遗失物品,FindMy帮助寻找
近日,一女子在上海坐高铁时,将户口本、房产证遗落安检处的新闻引起网友的关注。然后业内人士表示:常事,车站什么都能捡到。 据中国铁路透露,暑运期间,上海虹桥站客流增加,日均发送旅客20多万人…...

通过安全日志读取WFP防火墙放行日志
前言 之前的文档中,描写了如何对WFP防火墙进行操作以及如何在防火墙日志中读取被防火墙拦截网络通讯的日志。这边文档,着重描述如何读取操作系统中所有被放行的网络通信行为。 读取系统中放行的网络通信行为日志,在win10之后的操作系统上&am…...

JDK、JRE、Java SE、Java EE和Java ME有什么区别?
Java现在已不仅仅是一种语言,从广义上说,它代表了一个技术体系。该体系根据应用方向的不同主要分为Java SE、Java EE和Java ME的3个部分。 1.SE(JavaSE),standard edition,标准版,是我们通常用的一个版本,从…...

Neo4j之unwind基础
UNWIND 语句在 Cypher 查询中用于将列表(数组)中的元素逐个“展开”,从而将每个元素视为单独的行进行处理。它通常与 MATCH、CREATE、MERGE 等语句结合使用,用于对列表中的每个元素执行相应的操作。以下是一些常用的示例和解释&am…...

回归预测 | MATLAB实现SSA-SVM麻雀搜索算法优化支持向量机多输入单输出回归预测(多指标,多图)
回归预测 | MATLAB实现SSA-SVM麻雀搜索算法优化支持向量机多输入单输出回归预测(多指标,多图) 目录 回归预测 | MATLAB实现SSA-SVM麻雀搜索算法优化支持向量机多输入单输出回归预测(多指标,多图)效果一览基…...

深入探索代理技术:Socks5、IP代理与网络安全
在当今高度互联的世界中,代理技术在网络安全和爬虫等领域发挥着重要作用。本文将着重介绍Socks5代理、IP代理以及它们在网络安全与爬虫开发中的应用,旨在帮助读者深入理解这些技术,从而更好地应用于实际情境。 1. Socks5代理的特点与用途 S…...

Matlab 频谱图中如何设置频率刻度
Matlab 频谱图中如何设置频率刻度(横坐标) 1、概述 时域信号经FFT 变换后得到了频谱,在作图时还必须设置正确的频率刻度,这样才能从图中得到正确的结果。下面来介绍如何设置正确的频率刻度。 2、案例分析 有一个余弦信号&#…...

在线转换器有哪些优势?在线Word转PDF操作分享
我们如果想要将两者不同格式文件进行格式转换,就需要下载安装转换器。如果出门带的设备没有安装转换软件客户端,就无法使用,会比较麻烦。现在有了在线转换工具,只需要打开相应的网页就可使用,那么在线Word转PDF的操作是…...

2023国赛数学建模A题思路模型代码汇总 高教社杯
本次比赛我们将会全程更新思路模型及代码,大家查看文末名片获取 之前国赛相关的资料和助攻可以查看 2022数学建模国赛C题思路分析_2022国赛c题matlab_UST数模社_的博客-CSDN博客 2022国赛数学建模A题B题C题D题资料思路汇总 高教社杯_2022国赛c题matlab_UST数模社…...

vue3如何批量设置动态ref
示例如下: <template v-for"item in selectList"><el-select v-model"item.value" :ref"el > setRef(el, item)"><el-optionv-for"v in item.options":key"v.value":label"v.label"…...

Android Studio run app 设置 release 模式
背景 为验证我们的 SDK 集成在客户应用上的质量,需要我们的测试环境尽量的与客户应用保持一致。客户普遍都会打 release 包并混淆,然后进行上线应用,因此我们在测试过程中也需要使用 release 包进行验证。对于 Android Studio 运行项目&…...

【SA8295P 源码分析】41 - SA8295所有镜像位置、拷贝脚本、生成QFIL包
【SA8295P 源码分析】41 - SA8295所有镜像位置、拷贝脚本、生成QFIL包 一、SA8295 各镜像位置二、SA8295 QNX 侧镜像拷贝脚本三、SA8295 Android 侧镜像拷贝脚本四、使用QFIL 下载整包五、Fastboot 下载命令整理系列文章汇总见:《【SA8295P 源码分析】00 - 系列文章链接汇总》…...

【Redis】Redisson分布式锁原理与使用
【Redis】Redisson分布式锁原理与使用 什么是Redisson? Redisson - 是一个高级的分布式协调Redis客服端,能帮助用户在分布式环境中轻松实现一些Java的对象,Redisson、Jedis、Lettuce 是三个不同的操作 Redis 的客户端,Jedis、Le…...