您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

Spring框架的详细介绍

时间:2020-03-03 16:56:05  来源:  作者:

Spring介绍

Spring 是一个开源框架,是一个分层的 JAVAEE 一站式框架。

所谓一站式框架是指 Spring 有 JavaEE 开发的每一层解决方案。

  • WEB层:SpringMVC
  • Service层:Spring的Bean管理,声明式事务
  • DAO层:Spring的JDBC模板,ORM模板

优点:

  • IOC:方便解耦合
  • AOP:对程序进行扩展
  • 轻量级框架
  • 方便与其他框架整合

Spring使用

Spring开发包解压后的目录介绍:

  • docs: Spring 开发规范和API
  • libs: Spring jar 包和源代码
  • schema: Spring 配置文件的约束

DataAccess 用于数据访问,WEB 用于页面显示,核心容器也就是IOC部分。

控制反转(IOC)

控制反转(Inversion of Control)是指将对象的创建权反转(交给)Spring。

使用IOC就需要导入IOC相关的包,也就是上图中核心容器中的几个包:beans,context,core,expression四个包。

实现原理

传统方式创建对象:

UserDAO userDAO=new UserDAO();

进一步面向接口编程,可以多态:

UserDAO userDAO=new UserDAOImpl();

这种方式的缺点是接口和实现类高耦合,切换底层实现类时,需要修改源代码。程序设计应该满足OCP元祖,在尽量不修改程序源代码的基础上对程序进行扩展。此时,可以使用工厂模式:

class BeanFactory{

public static UserDAO getUserDAO(){

return new UserDAOImpl();

}

}

此种方式虽然在接口和实现类之间没有耦合,但是接口和工厂之间存在耦合。

使用工厂+反射+配置文件的方式,实现解耦,这也是 Spring 框架 IOC 的底层实现。

//xml配置文件

//<bean id="userDAO" class="xxx.UserDAOImpl"></bean>

class BeanFactory{

public static Object getBean(String id){

//解析XML

//反射

Class clazz=Class.forName();

return clazz.newInstance();

}

}

IOC XML 开发

在 docs 文件中包含了 xsd-configuration.hmtl 文件。其中定义了 beans schema。

<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

<bean id="userService" class="x.y.UserServiceImpl">

</bean>

</beans>

调用类:

ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");

UserService userService=(UserService)applicationContext.getBean("userService");

userService.save();

IOC 和 DI

DI 指依赖注入,其前提是必须有 IOC 的环境,Spring 管理这个类的时候将类的依赖的属性注入进来。

例如,在UserServiceImpl.java中:

public class UserServiceImpl implements UserService{

private String name;

public void setName(String name){

this.name=name;

}

public void save(){

System.out.println("save "+name);

}

}

在配置文件中:

<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="userService" class="spring.demo1.UserServiceImpl">

<!--配置依赖的属性-->

<property name="name" value="tony"/>

</bean>

</beans>

测试代码:

@Test

public void demo2(){

//创建Spring工厂

ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");

UserService userService=(UserService)applicationContext.getBean("userService");

userService.save();

}

运行结果:

save tony

可以看到,在配置文件中配置的属性,在 Spring 管理该类的时候将其依赖的属性成功进行了设置。如果不使用依赖注入,则无法使用接口,只能使用实现类来进行设置,因为接口中没有该属性。

Spring 的工厂类

  • BeanFactory: 老版本的工厂类,在调用getBean()方法时,才会生成类的实例。
  • ApplicationContext: 在加载配置文件的时候,就会将 Spring 管理的类都实例化。有两个实现类: ClassPathXmlApplicationContext: 加载类路径下的配置文件 FileSystemXmlApplicationContext: 加载磁盘下的配置文件

bean标签配置

  • id: 唯一约束,不能出现特殊字符
  • name: 理论上可以重复,但是开发中最好不要。可以出现特殊字符

生命周期:

  • init-method: bean被初始化的时候执行的方法
  • destroy-method: bean被销毁的时候执行的方法

作用范围:

  • scope: bean的作用范围,有如下几种,常用的是前两种 singleton: 默认使用单例模式创建 prototype: 多例 request: 在web项目中,spring 创建类后,将其存入到 request 范围中 session: 在web项目中,spring 创建类后,将其存入到 session 范围中 globalsession: 在web项目中,必须用在 porlet 环境

属性注入设置

  1. 构造方法方式的属性注入: Car 类在构造方法中有两个属性,分别为 name 和 price。

<bean id="car" class="demo.Car">

<constructor-arg name="name" value="bmw">

<constructor-arg name="price" value="123">

</bean>

2.set 方法属性注入: Employee 类在有两个 set 方法,分别设置普通类型的 name 和引用类型的 Car (使用 ref 指向引用类型的 id 或 name)。

<bean id="employee" class="demo.Employee">

<property name="name" value="xiaoming">

<property name="car" ref="car">

</bean>

3.P名称空间的属性注入: 首先需要引入p名称空间:

<beans xmlns="http://www.springframework.org/schema/beans"

//引入p名称空间

xmlns:p="http://www.springframework.org/schema/p"

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">

</beans>

如果是普通属性:

<bean id="car" class="demo.Car" p:name="bmv" p:price="123">

</bean>

如果是引用类型:

<bean id="employee" class="demo.Employee" p:name="xiaoming" p:car-ref:"car">

</bean>

4.SpEL(Spring Expression Language)属性注入(Spring 3.x以上版本)

<bean id="car" class="demo.Car">

<property name="name" value="#{'xiaoming'}">

<property name="car" ref="#{car}">

</bean>

5.集合类型属性注入:

<bean id="car" class="demo.Car">

<property name="namelist">

<list>

<value>qirui</value>

<value>baoma</value>

<value>benchi</value>

</list>

</property>

</bean>

多模块开发配置

  1. 在加载配置文件的时候,加载多个配置文件
  2. 在一个配置文件中引入多个配置文件,通过实现

IOC 注解开发

示例

  1. 引入jar包: 除了要引入上述的四个包之外,还需要引入aop包。
  2. 创建 applicationContext.xml ,使用注解开发引入 context 约束(xsd-configuration.html

<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">

<!-- bean definitions here -->

</beans>

  1. 组件扫描: 使用IOC注解开发,需要配置组件扫描,也就是哪些包下的类使用IOC的注解。

<context:component-scan base-package="demo1">

  1. 在类上添加注解
  2. 使用注解设置属性的值

属性如果有set方法,将属性注入的注解添加到set方法

属性没有set方法,将注解添加到属性上。

@Component("UserDao")//相当于配置了一个<bean> 其id为UserDao,对应的类为该类

public class UserDAOImpl implements UserDAO {

@Override

public void save() {

// TODO Auto-generated method stub

System.out.println("save");

}

}

注解详解

  1. @Component

组件注解,用于修饰一个类,将这个类交给 Spring 管理。

有三个衍生的注解,功能类似,也用来修饰类。

  • @Controller:修饰 web 层类
  • @Service:修饰 service 层类
  • @Repository:修饰 dao 层类

2.属性注入

  • 普通属性使用 @Value 来设置属性的值
  • 对象属性使用 @Autowired ,这个注解是按照类型来进行属性注入的。如果希望按照 bean 的名称或id进行属性注入,需要用 @Autowired 和 @Qualifier 一起使用
  • 实际开发中,使用 @Resource(name=" ") 来进行按照对象的名称完成属性注入

3.其他注解

  • @PostConstruct 相当于 init-method,用于初始化函数的注解
  • @PreDestroy 相当于 destroy-method,用于销毁函数的注解
  • @Scope 作用范围的注解,常用的是默认单例,还有多例 @Scope("prototype")

IOC 的 XML 和注解开发比较

  • 适用场景:XML 适用于任何场景;注解只适合自己写的类,不是自己提供的类无法添加注解。
  • 可以使用 XML 管理 bean,使用注解来进行属性注入

AOP开发

AOP 是 Aspect Oriented Programming 的缩写,意为面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,是OOP的延续。

AOP 能够对程序进行增强,在不修改源码的情况下,可以进行权限校验,日志记录,性能监控,事务控制等。

也就是说功能分为两大类,一类是核心业务功能,一类是辅助增强功能。两类功能彼此独立进行开发。比如登录功能是核心业务功能,日志功能是辅助增强功能,如果有需要,将日志和登录编制在一起。辅助功能就称为切面,这种能选择性的、低耦合的把切面和核心业务功能结合的编程思想称为切面编程。

底层实现

JDK 动态代理只能对实现了接口的类产生代理。Cglib 动态代理可以对没有实现接口的类产生代理对象,生成的是子类对象。

使用 JDK 动态代理:

public interface UserDao {

public void insert();

public void delete();

public void update();

public void query();

}

实现类:

public class UserDaoImpl implements UserDao { @Override public void insert() { System.out.println("insert"); } @Override public void delete() { System.out.println("delete"); } @Override public void update() { System.out.println("update"); } @Override public void query() { System.out.println("query"); } }

JDK 代理:

public class JDKProxy implements InvocationHandler{

private UserDao userDao;

public JDKProxy(UserDao userDao){

this.userDao=userDao;

}

public UserDao createProxy(){

UserDao userDaoProxy=(UserDao)Proxy.newProxyInstance(userDao.getClass().getClassLoader(),

userDao.getClass().getInterfaces(), this);

return userDaoProxy;

}

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

if("update".equals(method.getName())){

System.out.println("权限校验");

return method.invoke(userDao, args);

}

return method.invoke(userDao, args);

}

}

通过动态代理增强了 update 函数。 测试类:

public class Demo1 {

@Test

public void demo1(){

UserDao userDao=new UserDaoImpl();

UserDao proxy=new JDKProxy(userDao).createProxy();

proxy.insert();

proxy.delete();

proxy.update();

proxy.query();

}

}

运行结果为:

insert

delete

权限校验

update

query

CglibCglib 是第三方开源代码生成类库,可以动态添加类的属性和方法。

与上边JDK代理不同,Cglib的使用方式如下:

public class CglibProxy implements MethodInterceptor{

//传入增强的对象

private UserDao customerDao;

public CglibProxy(UserDao userDao){

this.userDao=userDao;

}

public UserDao createProxy(){

Enhancer enhancer=new Enhancer();

enhancer.setSuperclass(userDao.getClass());

enhancer.setCallback(this);

UserDao proxy=(UserDao)enhancer.create();

return proxy;

}

@Override

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

if("save".equals(method.getName())){

System.out.println("enhance function");

return methodProxy.invokeSuper(proxy, args);

}

return methodProxy.invokeSuper(proxy, args);

}

}

如果实现了接口的类,底层采用JDK代理。如果不是实现了接口的类,底层采用 Cglib代理。

IOC与传统方式的比较

  1. 获取对象方式:传统通过 new 关键字主动创建一个对象。IOC 方式中,将对象的生命周期交给 Spring 管理,直接从 Spring 获取对象。也就是控制反转————将控制权从自己手中交到了 Spring 手中。

Spring 的 AOP 开发(AspectJ 的 XML 方式)

AspectJ 是一个 AOP 的框架,Spring 引入 AspectJ,基于 AspectJ 进行 AOP 的开发。

相关术语

  • Joinpoint: 连接点,可以被拦截到的点。也就是可以被增强的方法都是连接点。
  • Pointcut: 切入点,真正被拦截到的点,也就是真正被增强的方法
  • Advice: 通知,方法层面的增强。对某个方法进行增强的方法,比如对 save 方法进行权限校验,权限校验的方法称为通知。
  • Introduction: 引介,类层面的增强。
  • Target: 目标,被增强的对象(类)。
  • Weaving: 织入,将 advice 应用到 target 的过程。
  • Proxy: 代理对象,被增强的对象。
  • Aspect: 切面,多个通知和多个切入点的组合。

使用方法

  1. 引入相关包
  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: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 definitions here -->

</beans>

  1. 编写目标类并配置:

public class ProductDaoImpl implements ProductDao {

@Override

public void save() {

System.out.println("save");

}

@Override

public void update() {

System.out.println("update");

}

@Override

public void find() {

System.out.println("find");

}

@Override

public void delete() {

System.out.println("delete");

}

}

<bean id="productDao" class="demo1.ProductDaoImpl"></bean>

  1. 编写切面类,假设用于权限验证并配置

public class MyAspectXML {

public void checkPri(){

System.out.println("check auth");

}

}

<bean id="myAspect" class="demo1.MyAspectXML"></bean>

  1. 通过AOP配置完成对目标类的增强

<aop:config>

<aop:pointcut expression="execution(* demo1.ProductDaoImpl.save(..))" id="pointcut1"/>

<aop:aspect ref="myAspect">

<aop:before method="chechPri" pointcut-ref="pointcut1"/>

</aop:aspect>

</aop:config>

通知类型

  1. 前置通知:在目标方法执行前操作,可以获得切入点信息

<aop:before method="chechPri" pointcut-ref="pointcut1"/>

public void checkPri(JoinPoint joinPoint){

System.out.println("check auth "+joinPoint);

}

  1. 后置通知:在目标方法执行后操作,可以获得方法返回值

<aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/>

public void writeLog(Object result){

System.out.println("writeLog "+result);

}

  1. 环绕通知:在目标方法执行前和后操作,可以阻止目标方法执

<aop:around method="around" pointcut-ref="pointcut3"/>

public Object around(ProceedingJoinPoint joinPoint) throws Throwable{

System.out.println("before");

Object result=joinPoint.proceed();

System.out.println("after");

return result;

}

  1. 异常抛出通知:程序出现异常时操作

<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>

public void afterThrowing(Throwable ex){

System.out.println("exception "+ex.getMessage());

}

  1. 最终通知:相当于finally块,无论代码是否有异常,都会执行

<aop:after method="finallyFunc" pointcut-ref="pointcut4"/>

public void finallyFunc(){

System.out.println("finally");

}

  1. 引介通知:不常用

Spring 切入点表达式

基于 execution 函数完成

语法:[访问修饰符] 方法返回值 包名.类名.方法名(参数)

其中任意字段可以使用*代替表示任意值

Spring 的 AOP 基于 AspectJ 注解开发

开发步骤

  1. 引入jar包
  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"

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">

</beans>

  1. 编写配置目标类

<bean id="orderDao" class="demo1.OrderDao"></bean>

public class OrderDao {

public void save(){

System.out.println("save order");

}

public void update(){

System.out.println("update order");

}

public void delete(){

System.out.println("delete order");

}

public void find(){

System.out.println("find order");

}

}

  1. 开启aop注解自动代理

<aop:aspectj-autoproxy/>

  1. 编写切面类并配置

@Aspect

public class MyAspectAnno {

@Before(value="execution(* demo1.OrderDao.save(..))")

public void before(){

System.out.println("before");

}

}

<bean id="myAspect" class="demo1.MyAspectAnno">

注解通知类型

  • @Before: 前置通知
  • @AfterReturning: 后置通知

@AfterReturning(value="execution(* demo1.OrderDao.save(..))",returning="result")

public void after(Object result){

System.out.println("after "+result);

}

  • @Around:环绕通知

@Around(value="execution(* demo1.OrderDao.save(..))")

public Object around(ProceedingJoinPoint joinPoint) throws Throwable{

System.out.println("before");

Object obj=joinPoint.proceed();

System.out.println("after");

return obj;

}

  • @AfterThrowing: 抛出异常

@AfterThrowing(value="execution(* demo1.OrderDao.save(..))",throwing="e")

public void afterThrowing(Throwable e){

System.out.println("exception:"+e.getMessage();

}

  • @After: 最终通知

@After(value="execution(* demo1.OrderDao.save(..))")

public void after(){

System.out.println("finally");

}

  • @PointCut:切入点注解

@PointCut(value="execution(* demo1.OrderDao.save(..))")

private void pointcut1(){}

此时,在上述通知的注解中,value可以替换为该函数名,例如:

@After(value="MyAspect.pointcut1()")

public void after(){

System.out.println("finally");

}

这个注解的好处是,只需要维护切入点即可,不用在修改时修改每个注解。

 

Spring 的 JDBC 模板

Spring 对持久层也提供了解决方案,也就是 ORM 模块和 JDBC 的模板。针对 JDBC ,提供了 org.springframework.jdbc.core.JdbcTemplate 作为模板类。

 

使用 JDBC 模板

  1. 引入jar包,数据库驱动,Spring 的 jdbc 相关包。
  2. 基本使用:

public void demo1(){

//创建连接池

DriverManagerDataSource dataSource=new DriverManagerDataSource();

dataSource.setDriverClassName("com.MySQL.jdbc.Driver");

dataSource.setUrl("jdbc:mysql:///spring4");

dataSource.setUsername("root");

dataSource.setPassword("123456");

//创建JDBC模板

JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource);

jdbcTemplate.update("insert into account values (null,?,?)", "xiaoming",1000d);

}

  1. 将连接池和模板交给 Spring 管理
  • 配置文件:

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource;">

<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>

<property name="url" value="jdbc:mysql:///spring4"></property>

<property name="username" value="root"></property>

<property name="password" value="123456"></property>

</bean>

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate;">

<property name="dataSource" ref="dataSource"></property>

</bean>

  • 测试文件:

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration("classpath:applicationContext.xml")

public class JdbcDemo2 {

@Resource(name="jdbcTemplate")

private JdbcTemplate jdbcTemplate;

@Test

public void demo2(){

jdbcTemplate.update("insert into account values (null,?,?)", "xiaolan",1000d);

}

}

使用开源数据库连接池

  1. 使用 DBCP 的配置:

<bean id="dataSource" class="org.Apache.commons.dbcp.BasicDataSource">

<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>

<property name="url" value="jdbc:mysql://192.168.66.128/spring4"></property>

<property name="username" value="root"></property>

<property name="password" value="123456"></property>

  1. 使用 C3P0 的配置:

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

<property name="driverClass" value="com.mysql.jdbc.Driver"></property>

<property name="jdbcUrl" value="jdbc:mysql://192.168.66.128/spring4"></property>

<property name="user" value="root"></property>

<property name="password" value="123456"></property>

</bean>

  1. 引入外部属性文件

首先建立外部属性文件:

jdbc.driverClass=com.mysql.jdbc.Driver

jdbc.url=jdbc:mysql://192.168.66.128/spring4

jdbc.username=root

jdbc.password=123456

 

然后对属性文件进行配置:

<context:property-placeholder location="classpath:jdbc.properties"/>

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

<property name="driverClass" value="${jdbc.driverClass}"></property>

<property name="jdbcUrl" value="${jdbc.url}"></property>

<property name="user" value="${jdbc.username}"></property>

<property name="password" value="${jdbc.password}"></property>

</bean>

CRUD操作

insert, update, delete 语句都借助模板的 update 方法进行操作。

public void demo(){

jdbcTemplate.update("insert into account values (null,?,?)", "xiaoda",1000d);

jdbcTemplate.update("update account set name=?,money=? where id=?", "xiaoda",1000d,2);

jdbcTemplate.update("delete from account where id=?", 6);

}

查询操作:

public void demo3(){

String name=jdbcTemplate.queryForObject("select name from account where id=?",String.class,5);

long count=jdbcTemplate.queryForObject("select count(*) from account",Long.class);

}

将返回的结果封装成为类:

public void demo4(){

Account account=jdbcTemplate.queryForObject("select * from account where id=?", new MyRowMapper(),5);

}

其中:

class MyRowMapper 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.getDouble("money"));

return account;

}

}

Spring的事务管理

事务

事务是指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败。

具有四个特性:

  • 原子性:事务不可分
  • 一致性:事务执行前后数据完整性保持一致
  • 隔离性:一个事务的执行不应该受到其他事务干扰
  • 持久性:一旦事务结束,数据就持久化到数据库

如果不考虑隔离性会引发安全性问题:

  • 读问题: 脏读:一个事务读到另一个事务未提交的数据 不可重复读:一个事务读到另一个事务已经提交的 update 数据,导致一个事务中多次查询结果不一致 幻读:一个事务读到另一个事务已经提交的 insert 数据,导致一个事务中多次查询结果不一致
  • 写问题: 丢失更新

解决读问题:设置事务隔离级别

  • Read uncommitted: 未提交读,无法解决任何读问题
  • Read committed: 已提交读,解决脏读问题
  • Repeatable read: 重复读,解决脏读和不可重复读问题
  • Serializable:序列化,解决所有读问题

事务管理API

  1. PlatformTransactionManager: 平台事务管理器

这是一个接口,拥有多个不同的实现类,如 DataSourceTransactionManager 底层使用了JDBC 管理事务; HibernateTransactionManager 底层使用了 Hibernate 管理事务。

  1. TransactionDefinition: 事务定义信息

用于定义事务的相关信息,如隔离级别、超时信息、传播行为、是否只读等

  1. TransactionStatus: 事务的状态

用于记录在事务管理过程中,事务的状态的对象。

上述API的关系: Spring 在进行事务管理的时候,首先平台事务管理器根据事务定义信息进行事务管理,在事务管理过程当中,产生各种此状态,将这些状态信息记录到事务状态的对象当中。

事务的传播行为

事务的传播行为主要解决业务层(Service)方法相互调用的问题,也就是不同的业务中存在不同的事务时,如何操作。

Spring 中提供了7种事务的传播行为,分为三类:

  • 保证多个操作在同一个事务中 PROPAGATION_REQUIRED: B方法调用A方法,如果A中有事务,使用A中的事务并将B中的操作包含到该事务中;否则新建一个事务,将A和B中的操作包含进来。(默认) PROPAGATION_SUPPORTS:如果A中有事务,使用A的事务;否则不使用事务 PROPAGATION_MANDATORY:如果A中有事务,使用A的事务;否则抛出异常
  • 保证多个操作不在同一个事务中 PROPAGATION_REQUIRES_NEW:如果A中有事务,将其挂起,创建新事务,只包含自身操作。否则,新建一个事务,只包含自身操作。 PROPAGATION_NOT_SUPPORTED:如果A中有事务,挂起,不使用事务。 PROPAGATION_NEVER:如果A中有事务,抛出异常,也即不能用事务运行。
  • 嵌套事务 PROPAGATION_NESTED:如果A有事务,按照A的事务执行,执行完成后,设置一个保存点,然后执行B的操作。如果出现异常,可以回滚到最初状态或保存点状态。

实例

以转账为例,业务层的DAO层类如下:

public interface AccountDao {

public void outMoney(String from,Double money);

public void inMoney(String to,Double money);

}

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao{

@Override

public void outMoney(String from, Double money) {

this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,from);

}

@Override

public void inMoney(String to, Double money) {

this.getJdbcTemplate().update("update account set money = money + ? where name = ?",money,to);

}

}

public interface AccountService {

public void transfer(String from,String to,Double money);

}

public class AccountServiceImpl implements AccountService {

private AccountDao accountDao;

public void setAccountDao(AccountDao accountDao) {

this.accountDao = accountDao;

}

@Override

public void transfer(String from, String to, Double money) {

accountDao.outMoney(from, money);

accountDao.inMoney(to, money);

}

}

在xml中进行类的配置:

<bean id="accountService" class="tx.demo.AccountServiceImpl">

<property name="accountDao" ref="accountDao"/>

</bean>

<bean id="accountDao" class="tx.demo.AccountDaoImpl">

<property name="dataSource" ref="dataSource"/>

</bean>

事务管理1: 编程式事务管理

  1. 配置平台事务管理器

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<property name="dataSource" ref="dataSource"></property>

</bean>

  1. 配置事务管理模板类

<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">

<property name="transactionManager" ref="transactionManager"></property>

</bean>

  1. 在业务层注入事务管理模板

<bean id="accountService" class="tx.demo1.AccountServiceImpl">

<property name="accountDao" ref="accountDao"/>

<property name="transactionTemplate" ref="transactionTemplate"/>

</bean>

  1. 编码实现事务管理

//ServiceImpl类中:

private TransactionTemplate transactionTemplate;

@Override

public void transfer(String from, String to, Double money) {

transactionTemplate.execute(new TransactionCallbackWithoutResult() {

@Override

protected void doInTransactionWithoutResult(TransactionStatus arg0) {

accountDao.outMoney(from, money);

accountDao.inMoney(to, money);

}

});

}

声明式事务管理(配置实现,基于AOP思想)

  1. XML 方式的声明式事务管理
  • 配置事务管理器

<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="transfer" propagation="REQUIRED"/>

</tx:attributes>

</tx:advice>

  • 配置aop事务

<aop:config>

<aop:pointcut expression="execution(* tx.demo2.AccountServiceImpl.*(..))" id="pointcut1"/>

<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>

</aop:config>

  1. 注解方式
  • 配置事务管理器,和上方一致
  • 开启事务管理的注解:

<tx:annotation-driven transaction-manager="transactionManager"/>

  • 在使用事务的类上添加一个注解@Transactional

引入自作者:supingemail

 

地址:https://blog.csdn.net/supingemail/article/details/85988220



Tags:Spring框架   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
最近呀,有小伙伴提出 自己在学习 Spring 的时候,这个源码环境有些搞不定。 那这怎么能行,不能因为这点小困难就让小伙伴放弃呀。这里咱就不在赘述读Spring源码的好处了吧,想干这...【详细内容】
2021-09-14  Tags: Spring框架  点击:(56)  评论:(0)  加入收藏
第一章:认识SpringSpring认识框架spring全家桶: spring ,springmvc,spring boot,spring cloudspring:出现在2002左右,解决企业开发的难度,减轻对项目模块之间的管理。类和类之间的...【详细内容】
2021-06-24  Tags: Spring框架  点击:(123)  评论:(0)  加入收藏
诞生的背景什么背景下诞生了该技术?不论是哪个框架,不会平白无故诞生,不会平白无故地被人所追捧,了解其背景,追根溯源。让我们把时间拨回到2002年,当时JavaEE和EJB正大行其道。Sp...【详细内容】
2021-05-31  Tags: Spring框架  点击:(180)  评论:(0)  加入收藏
译者:littlebrain4solving来源: https://blog.csdn.net/littlebrain4solving/概要说明在此篇文章中,我们根据使用@Async注解进行探索Spring对异步执行的支持。简单的把@Async注...【详细内容】
2021-03-05  Tags: Spring框架  点击:(379)  评论:(0)  加入收藏
在我们的开发中,会不可避免的遇到Bean之间循环依赖的,所谓循环依赖,就是两个或者两个以上的Bean互相持有对方,这样在程序运行调用中,会出现这种循环依赖的现象,假设是两个Bean,当程...【详细内容】
2020-12-14  Tags: Spring框架  点击:(87)  评论:(0)  加入收藏
前言学习过Spring框架的人一定都会听过Spring的IOC(控制反转) 这个概念,对于初学Spring的人来说,总觉得IOC是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spr...【详细内容】
2020-12-03  Tags: Spring框架  点击:(117)  评论:(0)  加入收藏
1、IOC和DIIOC: 控制反转 即控制权的转移,将我们创建对象的方式反转了,以前对象的创建时由我们开发人员自己维护,包括依赖关系也是自己注入。使用了spring之后,对象的创建以...【详细内容】
2020-11-03  Tags: Spring框架  点击:(58)  评论:(0)  加入收藏
今天学习内容安排如下: 切面配置的核心三步骤。 传统sprin配置和AspectJ配置都学下,xml和注解都使用下。 同时为了测试,补充说明一下spring的junit集成,几天前没来得及学的知识...【详细内容】
2020-08-19  Tags: Spring框架  点击:(64)  评论:(0)  加入收藏
今天是刘小爱自学Java的第123天。感谢你的观看,谢谢你。 学过很多面向XX编程,比如:面向过程编程,面向对象编程,面向接口编程,现在又是面向切面编程。但是不管如何,说来说去最终都是...【详细内容】
2020-08-15  Tags: Spring框架  点击:(97)  评论:(0)  加入收藏
Spring介绍Spring 是一个开源框架,是一个分层的 JavaEE 一站式框架。所谓一站式框架是指 Spring 有 JavaEE 开发的每一层解决方案。 WEB层:SpringMVC Service层:Spring的Bean管...【详细内容】
2020-03-03  Tags: Spring框架  点击:(68)  评论:(0)  加入收藏
▌简易百科推荐
近日只是为了想尽办法为 Flask 实现 Swagger UI 文档功能,基本上要让 Flask 配合 Flasgger, 所以写了篇 Flask 应用集成 Swagger UI 。然而不断的 Google 过程中偶然间发现了...【详细内容】
2021-12-23  Python阿杰    Tags:FastAPI   点击:(6)  评论:(0)  加入收藏
文章目录1、Quartz1.1 引入依赖<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.2</version></dependency>...【详细内容】
2021-12-22  java老人头    Tags:框架   点击:(12)  评论:(0)  加入收藏
今天来梳理下 Spring 的整体脉络啦,为后面的文章做个铺垫~后面几篇文章应该会讲讲这些内容啦 Spring AOP 插件 (了好久都忘了 ) 分享下 4ye 在项目中利用 AOP + MybatisPlus 对...【详细内容】
2021-12-07  Java4ye    Tags:Spring   点击:(14)  评论:(0)  加入收藏
&emsp;前面通过入门案例介绍,我们发现在SpringSecurity中如果我们没有使用自定义的登录界面,那么SpringSecurity会给我们提供一个系统登录界面。但真实项目中我们一般都会使用...【详细内容】
2021-12-06  波哥带你学Java    Tags:SpringSecurity   点击:(18)  评论:(0)  加入收藏
React 简介 React 基本使用<div id="test"></div><script type="text/javascript" src="../js/react.development.js"></script><script type="text/javascript" src="../js...【详细内容】
2021-11-30  清闲的帆船先生    Tags:框架   点击:(19)  评论:(0)  加入收藏
流水线(Pipeline)是把一个重复的过程分解为若干个子过程,使每个子过程与其他子过程并行进行的技术。本文主要介绍了诞生于云原生时代的流水线框架 Argo。 什么是流水线?在计算机...【详细内容】
2021-11-30  叼着猫的鱼    Tags:框架   点击:(21)  评论:(0)  加入收藏
TKinterThinter 是标准的python包,你可以在linx,macos,windows上使用它,你不需要安装它,因为它是python自带的扩展包。 它采用TCL的控制接口,你可以非常方便地写出图形界面,如...【详细内容】
2021-11-30    梦回故里归来  Tags:框架   点击:(27)  评论:(0)  加入收藏
前言项目中的配置文件会有密码的存在,例如数据库的密码、邮箱的密码、FTP的密码等。配置的密码以明文的方式暴露,并不是一种安全的方式,特别是大型项目的生产环境中,因为配置文...【详细内容】
2021-11-17  充满元气的java爱好者  博客园  Tags:SpringBoot   点击:(25)  评论:(0)  加入收藏
一、搭建环境1、创建数据库表和表结构create table account(id INT identity(1,1) primary key,name varchar(20),[money] DECIMAL2、创建maven的工程SSM,在pom.xml文件引入...【详细内容】
2021-11-11  AT小白在线中  搜狐号  Tags:开发框架   点击:(29)  评论:(0)  加入收藏
SpringBoot开发的物联网通信平台系统项目功能模块 功能 说明 MQTT 1.SSL支持 2.集群化部署时暂不支持retain&will类型消 UDP ...【详细内容】
2021-11-05  小程序建站    Tags:SpringBoot   点击:(56)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条