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) {
        /*以前使用对象方式,在哪儿用,在哪儿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.mfbz.cn/a/78744.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring5学习笔记—AOP编程

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; Spring专栏 ✨特色专栏&#xff1a; M…

国企的大数据岗位方向的分析

现如今大数据已无所不在&#xff0c;并且正被越来越广泛的被应用到历史、政治、科学、经济、商业甚至渗透到我们生活的方方面面中&#xff0c;获取的渠道也越来越便利。 今天我们就来聊一聊“大屏应用”&#xff0c;说到大屏就一定要聊到数据可视化&#xff0c;现如今&#xf…

eclipse 导入项目js报错问题

eclipse 导入项目后会出现项目中的js文件报错&#xff08;红叉&#xff09;&#xff0c;如下图所示&#xff0c;有时候报错的文件很多&#xff0c;需要集中处理。 解决办法&#xff1a; 右键项目名称》Properties》MyEclipse》JavaScript》Include Path&#xff0c;在右侧选择“…

linux 文件权限识别及其修改

一、文件权限认识 在 Linux 系统中&#xff0c;一切皆文件&#xff0c;目录也是一种文件形式叫目录文件&#xff0c;它们的属性主要包含&#xff1a;索引节点(inode)&#xff0c;类型、权限属性、链接数、所归属的用户和用户组、最近修改时间等内容。 如下为根目录下目录&…

基于OpenCV的人脸识别和模型训练系统(万字详解)

前言 我们身边的人脸识别有车站检票&#xff0c;监控人脸&#xff0c;无人超市&#xff0c;支付宝人脸支付&#xff0c;上班打卡&#xff0c;人脸解锁手机。 人脸检测是人脸识别系统组成的关键部分之一&#xff0c;其目的是检测出任意给定图片中的包含的一个或多个人脸&#…

网络通信原理IP头部格式(第四十二课)

字段作用解析:1)版本: 指的IP地址的版本 (IPv4 或 IPV6)2)首部长度: 次数据包的首部长度一共是多少,没有加可选项3)优先级与服务类型:表示****数据包是否需要优选传递4)总长度: 表示的是整个数据包的大小,也就****是首部+数据5)标识符、标志、段偏移量:的作用将拆开的…

ts与vue

ts与Vue 如果你已经学习了typeScript,但不知道如何在vue项目中使用&#xff0c;那么这篇文章将会很适合你。参考千峰教育 kerwin视频 1.会自动推导&#xff0c;隐士推导。提示 类型系统。 独立模块。 isolatedModules选项&#xff1a;是否配置为独立的模块。 减少报错 let …

Springboot项目启动后按顺序加载自定义类 (demo)

1. 实现ApplicationRunner接口, 重写run方法 import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.core.annotation.Order; import org.springframewor…

IDEA项目实践——JavaWeb简介以及Servlet编程实战

系列文章目录 IDEA项目实践——创建Java项目以及创建Maven项目案例、使用数据库连接池创建项目简介 IDEWA项目实践——mybatis的一些基本原理以及案例 IDEA项目实践——动态SQL、关系映射、注解开发 IDEA项目实践——Spring框架简介,以及IOC注解 IDEA项目实践——Spring当…

mysql 03.查询(重点)

先准备测试数据&#xff0c;代码如下&#xff1a; -- 创建数据库 DROP DATABASE IF EXISTS mydb; CREATE DATABASE mydb; USE mydb;-- 创建student表 CREATE TABLE student (sid CHAR(6),sname VARCHAR(50),age INT,gender VARCHAR(50) DEFAULT male );-- 向student表插入数据…

好用的安卓手机投屏到mac分享

工具推荐&#xff1a;scrcpy github地址&#xff1a;https://github.com/Genymobile/scrcpy/tree/master mac使用方式 安装环境&#xff0c;打开terminal&#xff0c;执行以下命令&#xff0c;没有brew的先安装brew brew install scrcpy brew install android-platform-too…

运营商三要素 API:构建安全高效的身份验证系统

当今数字化的世界中&#xff0c;身份验证是各行各业中至关重要的一环。为了保护用户的隐私和数据安全&#xff0c;企业需要寻求一种既安全可靠又高效便捷的身份验证方式。运营商三要素 API 应运而生&#xff0c;为构建安全高效的身份验证系统提供了有力的解决方案。 运营商三要…

【卷积神经网络】卷积,池化,全连接

随着计算机硬件的升级与性能的提高&#xff0c;运算量已不再是阻碍深度学习发展的难题。卷积神经网络&#xff08;Convolution Neural Network&#xff0c;CNN&#xff09;是深度学习中一项代表性的工作&#xff0c;CNN 是受人脑对图像的理解过程启发而提出的模型&#xff0c;其…

【C++学习手札】一文带你认识C++虚函数(内层剖析)

食用指南&#xff1a;本文在有C基础的情况下食用更佳 &#x1f340;本文前置知识&#xff1a; C初识继承 ♈️今日夜电波&#xff1a;No title —REOL 1:02 ━━━━━━️&#x1f49f;──────── 4:03 …

微服务中间件--微服务保护

微服务保护 微服务保护a.sentinelb.sentinel限流规则1) 流控模式1.a) 关联模式1.b) 链路模式 2) 流控效果2.a) 预热模式2.b) 排队等待 3) 热点参数限流 c.隔离和降级1) Feign整合Sentinel2) 线程隔离2.a) 线程隔离&#xff08;舱壁模式&#xff09; 3) 熔断降级3.a) 熔断策略-慢…

基于Servlet实现的管理系统(包含服务器源码+数据库)

资料下载链接 介绍 基于Servlet框架的管理系统 简洁版 &#xff1b; 实现 登录 、 注册 、 增 、 删 、 改 、 查 &#xff1b; 可继续完善增加前端、校验、其他功能等&#xff1b; 可作为 Servlet项目 开发练习基础模型&#xff1b; 课程设计 、 毕业设计 开发基础&…

java-垃圾回收与算法

垃圾回收与算法 1. 如何确定垃圾 1. 引用计数法 在 Java 中&#xff0c;引用和对象是有关联的。如果要操作对象则必须用引用进行。因此&#xff0c;很显然一个简单的办法是通过引用计数来判断一个对象是否可以回收。简单说&#xff0c;即一个对象如果没有任何与之关联的引用&…

基于YOLOv5n/s/m不同参数量级模型开发构建茶叶嫩芽检测识别模型,使用pruning剪枝技术来对模型进行轻量化处理,探索不同剪枝水平下模型性能影响【续】

这里主要是前一篇博文的后续内容&#xff0c;简单回顾一下&#xff1a;本文选取了n/s/m三款不同量级的模型来依次构建训练模型&#xff0c;所有的参数保持同样的设置&#xff0c;之后探索在不同剪枝处理操作下的性能影响。 在上一篇博文中保持30的剪枝程度得到的效果还是比较理…

Qt平滑弹出页面

目标功能&#xff1a; (1)按下btn&#xff0c;弹出绿色页面。 (2)按下btn2,绿色页面隐藏。 (3)按下左边余下的区域&#xff0c;绿色页面也隐藏。 (4)平滑地显示和隐藏 效果&#xff1a; form.h #ifndef FORM_H #define FORM_H#include <QWidget>namespace Ui { class…

如何将labelImg打包成exe

最近整理一下数据标注这块的内容&#xff0c;在目标检测和目标分割里面用的最多的标注工具labelimg&#xff0c;labelme labelimg主要用于目标检测领域制作自己的数据集&#xff0c;如&#xff1a;YOLO系列目标检测模型 labelme主要用于图像分割领域制作自己的数据集&#xf…
最新文章