spring详解

news2024/11/24 1:52:14

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) {
        /*以前使用对象方式,在哪儿用,在哪儿new
        new 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/beans
        https://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.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功能

  1. jdbc封装: JdbcTemplate(了解即可 后面常用mybatis),事务管理(重点)
  2. 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/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://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 {
    @Autowired
    JdbcTemplate 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>() {
                @Override
                public 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>() {
                @Override
                public 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/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://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 {
    @Autowired
    JdbcTemplate 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 {
    @Autowired
    JdbcTemplate jdbcTemplate;

    @Autowired
    TransactionTemplate transactionTemplate;
    
    public void saveAdmin(Admin admin){
        System.out.println("保存管理员");
        transactionTemplate.execute(new TransactionCallback<Object>() {
            @Override
            public 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层来讲,转账是一个操作
@Transactional
    public 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 {
    @Autowired
    JdbcTemplate 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_NEVERB以非事务方式执行;如果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 {
    @Autowired
    AdminDao adminDao;

    @Autowired
    LogService 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 {
    @Autowired
    JdbcTemplate 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 {
    @Autowired
    LogDao logDao;
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void saveLog(){
        logDao.saveLog();
    }
}
@Repository(value = "logDao")
public class LogDao {
    @Autowired
    JdbcTemplate 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 {
    @Autowired
    AdminDao adminDao;

    @Autowired
    LogService logService;//AdminService调用LogService
    
    public void save(Admin admin){
        adminDao.saveAdmin(admin);//正常执行
        logService.saveLog();//save调用saveLog方法,新建一个事务
        System.out.println(10/0);
    }
}
@Service(value = "logService")
public class LogService {
    @Autowired
    LogDao logDao;
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void saveLog(){
        logDao.saveLog();
    }
}

save没有事务,saveLog有事务,此时saveLog会新建一个事务,异常在saveLog中,保存管理员正常执行,而保存日志有异常而不会执行

@Service(value = "adminService")
public class AdminService {
    @Autowired
    AdminDao adminDao;

    @Autowired
    LogService logService;//AdminService调用LogService
    
    public void save(Admin admin){
        adminDao.saveAdmin(admin);
        logService.saveLog();//save调用saveLog方法
    }
}
@Service(value = "logService")
public class LogService {
    @Autowired
    LogDao logDao;
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void saveLog(){
        logDao.saveLog();
        System.out.println(10/0);
    }
}

2、PROPAGATION_REQUIRES_NEW
无论A是否有事务,B都会创建新的事务(独立的)
在这里插入图片描述
两者是独立的事务,异常在save中,保存管理员不执行,保存日志执行

    @Autowired
    AdminDao adminDao;

    @Autowired
    LogService logService;//AdminService调用LogService

    public void save(Admin admin){
        adminDao.saveAdmin(admin);
        logService.saveLog();
        System.out.println(10/0);
    }
}
@Service(value = "logService")
public class LogService {
    @Autowired
    LogDao logDao;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveLog(){
        logDao.saveLog();
    }
}

3、PROPAGATION_NEVER
B以非事务方式执行;如果A存在事务,则抛出异常。
saveLog在非事务的方法中执行,如果save中有事务,则会抛出异常

public class AdminService {
    @Autowired
    AdminDao adminDao;

    @Autowired
    LogService logService;//AdminService调用LogService
	@Transactional
    public void save(Admin admin){
        adminDao.saveAdmin(admin);
        logService.saveLog();
    }
}
@Service(value = "logService")
public class LogService {
    @Autowired
    LogDao logDao;

    @Transactional(propagation = Propagation.NEVER)
    public void saveLog(){
        logDao.saveLog();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/892602.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

jeecg导出excel文件时候是id,展示名称的处理方式

一.问题描述: 在jeecg3.5.3版本中,创建了一个基础表,并配置菜单,输入数据以后,如果需要导出数据,而且数据类型的展示页面类型是他表字段,这个情况下,直接使用jeecg默认功能,导出的excel并不会和页面一样,默认显示出来被选字段的字段名称。 例如:页面显示如下: 实…

Spring讲解和ioc用途及Web容器的整合

目录 一、Spring概述 ( 1 ) 背景 ( 2 ) 是什么 ( 3 ) 核心特性 二、Spring中的ioc 2.1 讲解 2.2 主要功能 2.3 实例演示 三、注入方式 3.1 set注入 3.2 构造注入 3.3 接口注入 四、Web容器整合 4.1 思考 4.2 实操 对我们的收获 一、Spring概述 ( 1 ) 背景 Spr…

春秋云镜 CVE-2020-17530

春秋云镜 CVE-2020-17530 S2-061 靶标介绍 对CVE-2019-0230的绕过&#xff0c;Struts2官方对CVE-2019-0230的修复方式是加强OGNL表达式沙盒&#xff0c;而CVE-2020-17530绕过了该沙盒。当对标签属性中的原始用户输入进行评估时&#xff0c;强制 OGNL 评估可能会导致远程代码执…

C语言实例_获取文件MD5值

一、MD5介绍 MD5&#xff08;Message Digest Algorithm 5&#xff09;是一种常用的哈希函数算法。将任意长度的数据作为输入&#xff0c;并生成一个唯一的、固定长度&#xff08;通常是128位&#xff09;的哈希值&#xff0c;称为MD5值。MD5算法以其高度可靠性和广泛应用而闻名…

15-生命周期

Vue生命周期 和 生命周期的四个阶段 Vue生命周期总结: 四个阶段,八个钩子 -> 三个常用 created,mounted,beforeDestroy 生命周期的钩子函数 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"…

智能报警系统:利用人工智能保障安全和及时应对危险

引言&#xff1a;随着人工智能的快速发展&#xff0c;智能报警系统成为了一种高效、及时应对危险和保障安全的重要工具。通过分析监控视频中的图像、声音以及其他传感器数据&#xff0c;人工智能可以自动检测和识别火灾、破坏、烟雾、异常温度等情况&#xff0c;并及时触发报警…

STM32单片机SPI通信实战:示例代码详解与应用案例

引言&#xff1a; 单片机SPI&#xff08;串行外设接口&#xff09;通信是一种常用的串行同步通信协议&#xff0c;用于单片机与外设之间的高速数据传输。SPI通信具有简单、高效、可靠等特点&#xff0c;在各种嵌入式系统中被广泛应用。本文将介绍单片机SPI通信的原理、配置和性…

WebRTC | 网络传输协议RTP与RTCP

目录 一、UDP与TCP 1. TCP 2. UDP 二、RTP 1. RTP协议头 &#xff08;1&#xff09;V&#xff08;Version&#xff09;字段 &#xff08;2&#xff09;P&#xff08;Padding&#xff09;字段 &#xff08;3&#xff09;X&#xff08;eXtension&#xff09;字段 &#x…

嵌入式笔试面试刷题(day11)

文章目录 前言一、字节流&#xff0c;数据报&#xff0c;报文二、makefile怎么引入库和模块三、多次free一块内存空间会怎么样四、字符操作函数越界会发生什么五、QT中一个信号可以连接多个槽函数吗六、QT中一个槽函数可以对应多个信号吗总结 前言 本篇文章继续刷题。 一、字…

系统架构设计师---计算机基础知识之存储管理

存储管理的主要任务&#xff1a;提高主存的利用率、扩充主存以及对主存信息实现有效保护。 存储管理的对象&#xff1a;主存储器(简称主存或内存)。 逻辑地址和物理地址&#xff1a;用户编程所用的地址称为逻辑地址(虚地址)&#xff0c;而实际的内存地址 则称为物理地址(实地…

【组合数学】CF1622 D

Problem - 1622D - Codeforces 题意&#xff1a; 思路&#xff1a; 一开始的思路&#xff1a; 事实上&#xff0c;观察样例可知&#xff0c;如果一个小区间被包含在大区间里面&#xff0c;那么我们需要计算的是大区间的贡献 所以只需要找出大区间即可 一开始想的是找出所有…

C语言案例 判断是否为回文数-06.1

题目&#xff1a;随机输入一个5位数&#xff0c;判断它是不是回文数 步骤一&#xff1a;定义程序的目标 编写C程序&#xff0c;随机输入一个5位数&#xff0c;判断它是不是回文数 步骤二&#xff1a;程序设计 原理&#xff1a;即12321是回文数&#xff0c;个位与万位相同&#…

剑指Offer68-II.二叉树的最近公共祖先 C++

1、题目描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个结点 p、q&#xff0c;最近公共祖先表示为一个结点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以…

前端开发,怎么解决浏览器兼容性问题? - 易智编译EaseEditing

解决浏览器兼容性问题是前端开发中常见的挑战之一。不同的浏览器可能对网页元素的渲染和功能支持有所不同&#xff0c;因此需要采取一些策略来确保您的网页在不同浏览器上都能正常运行和呈现。以下是一些解决浏览器兼容性问题的方法和策略&#xff1a; 使用CSS Reset&#xff…

(三)行为模式:2、命令模式(Command Pattern)(C++示例)

目录 1、命令模式&#xff08;Command Pattern&#xff09;含义 2、命令模式的UML图学习 3、命令模式的应用场景 4、命令模式的优缺点 5、C实现命令模式的实例 1、命令模式&#xff08;Command Pattern&#xff09;含义 命令模式&#xff08;Command&#xff09;&#xff…

土地利用变化碳排放效应的遥感监测、生态系统碳库的遥感估算、收支的遥感模拟、能源消耗碳排放空间格局模拟

以全球变暖为主要特征的气候变化已成为全球性环境问题&#xff0c;对全球可持续发展带来严峻挑战。2015年多国在《巴黎协定》上明确提出缔约方应尽快实现碳达峰和碳中和目标。2019年第49届 IPCC全会明确增加了基于卫星遥感的排放清单校验方法。随着碳中和目标以及全球碳盘点的现…

UI界面设置

文章目录 1. 修改 share.html 内容如下&#xff1a;2. 修改 html 文件格式为 utf-83.保存&#xff0c;运行程序4. 访问页面 1. 修改 share.html 内容如下&#xff1a; <!DOCTYPE html><html> <head><meta charset"utf-8"><title>1v1屏…

创建和使用角色

创建和使用角色 根据下列要求&#xff0c;在 /home/curtis/ansible/roles 中创建名为 apache 的角色&#xff1a; httpd 软件包已安装&#xff0c;设为在系统启动时启用并启动 防火墙已启用并正在运行&#xff0c;并使用允许访问 Web 服务器的规则 模板文件 index.html.j2 已存…

【ROS】服务通信--从理论介绍到模型实现

一、概念介绍 服务通信也是ROS中一种极其常用的通信模式&#xff0c;服务通信是基于请求响应模式的&#xff0c;是一种应答机制。也即: 一个节点A向另一个节点B发送请求&#xff0c;B接收处理请求并产生响应结果返回给A。 一个节点需要向相机节点发送拍照请求&#xff0c;相机…

关于STM32Cube的HAL库使用RTOS设置且给芯片下载的注意事项

学了标准库和RTOS之后&#xff0c;发现HAL库开发比较快&#xff0c;于是尝试使用HAL库开发ROTS程序&#xff0c; 第一次下载程序到芯片发现程序是运行了&#xff0c;但是不能再次下载程序&#xff08;也就是不能检测到ST-link或者DIP等接口&#xff09; 于是根据BOOT0和BOOT1…