Spring的介绍与使用

1.搭建Spring环境

1.1导入Spring依赖: 最小依赖: Spring-core Spring-beans Spring-context Spring-expression

注意:

spring每个模块的版本要统一

<properties>
        <spring.version>5.2.15.RELEASE</spring.version>
    </properties>
    <dependencies>
        <!--spring的依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>${spring.version}</version>
        </dependency>
    </dependencies>

1.2 配置文件(基本框架)

以xml文件作为配置文件 beans.xml

官方推荐名字: applicationContext.xml

实际开发: beans.xml ,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
        https://www.springframework.org/schema/beans/spring-beans.xsd">



</beans>

 1.3使用Spring方式的好处

  1. 做到类的对象共享

  2. 使用传统方式, 创建这个对象所有权都是使用者类的,

  3. 使用Spring的方式: 使用者对象只有被使用对象的使用权,没有所有权, 被使用对象的所有权是由Spring管理

  4. 使用传统方式, 使用者与被使用者是直接耦合, 耦合高,

  5. 使用Spring的方式: 使用者与被使用者没有直接耦合, 使用者耦合Spring容器对象, 降低使用者与被使用的耦合低

2.Spring两大核心

 1.1 IOC/DI: 控制反转/依赖注入

1.1.1IoC控制反转

(控制反转)就是将程序中原来 new 对象,交给spring创建,从spring工厂获取对象,使用spring来负责控制对象的生命周期和对象间的关系

正转: 对象B使用A对象, 对象B拥有对象A的控制权(所有权)

反转: 对象B使用A对象, 对象B没有对象A的控制权, 对象A的控制权交给Spring

好处: 降低耦合度

1.1.2 DI依赖注入:即由容器动态的将某个依赖关系注入到组件之

IOC: 把对象交给Spring管理, Spring容器保存这个对象, 手动获取这个容器的对象

DI: 对象依赖于Spring, Spring自动把对象需要的资源(对象,数据, 外部资源)注入进来

传统方式:

Spring方式:

IOC: 把StudentDaoImpl 交给Spring管理,

把StudentDaoImpl交个spring管理

要求类的属性必须提供set方法

2.2AOP 面向切面编程

AOP

AOP: Spring的第二大核心: 面向切面编程, 把方法层面的逻辑代码进行分离, 让方法层面逻辑代码解耦, 不改变目标方法前提下, 动态增强目标方法

IOC: Spring的第一大核心: 提供了IOC容器, 提供了BeanFactory, 把项目中的对象交给Spring管理, 降低对象与对象之间的耦合度

AOP: 面向切面编程, 对OOP的延续, 考虑的是方法层面的问题

OOP: 面向对象编程, 考虑对象层面,

作用:

利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

面向切面编程,实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术,

AOP: 横向扩展

OOP: 纵向扩展:

AOP的使用场景

  • 日志记录

  • 权限控制

  • 缓存

  • 性能监控

  • 事务控制

AOP核心概念

连接点: JoinPoint: 目标类的所有的方法都是连接点, 一个连接点对象就是一个方法对象

切入点: Pointcut: 对目标类哪些方法增强, 被增强的方法称为切入点, 是一个字符串表达式,

通知类,增强类: advice, 包含增强方法的类

目标对象: target 被代理的目标对象

Weaving(织rget(目标):入):是指把增强应用到目标对象来创建的代理对象的过程,Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入

Aspect(切面):是切入点和通知(引介)的结合

代理对象: proxy

增强,通知的类型 面试

  • 前置增强, 在目标方法之前执行

  • 后置增强: 在目标方法正常执行之后执行

  • 异常增强: 在目标方法产生异常的时候,执行

  • 最终增强: 不管目标方法是否产生异常,都要执行的

  • 环绕增强:(一个顶上面四个)

AOP代码实现

基于第三方的aop框架: AspectJ( AOP框架), 对通知类没有任何要求

基于xml配置
  1. 导入依赖: spring-aop, spring-aspect依赖

    <!--aop相关依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${spring.version}</version>
    </dependency>
    ​
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${spring.version}</version>
    </dependency>
  2. 编写目标类

SpringAOP底层实现技术:

如果目标类实现接口, 底层基于jdk动态代理,如果目标类没有实现接口, 底层基于cglib的动态代理

  1. 编写通知类/增强类

    package com.fs.aop.advice;
    ​
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.JoinPoint;
    ​
    import java.util.Arrays;
    ​
    /**
     * @author suke
     * @version 1.0
     * @title LogAdvice
     * @description  日志增强类
     *   基于AspectJ框架AOP实现, 通知类/增强类不需要实现任何接口,不需要继承某个类, 方法随便定义
     * @create 2024/4/28 15:49
     */
    @Slf4j
    public class LogAdvice {
    ​
        //前置增强
        //获取目标方法信息 JoinPoint(连接点)
        public void beforeAdvice(JoinPoint jp){
            Object target = jp.getTarget(); //目标对象
            String methodName = jp.getSignature().getName();//得到目标方法名
            //获取调用目标方法传递的参数
            Object[] args = jp.getArgs();
    ​
            //日志打印, 使用log在控制台输出,实际开发, 把日志信息保存到数据库
            log.info(target.getClass().getName()+"类的"+methodName+"方法开始执行,传递的参数:"+ Arrays.asList(args));
            
        }
    }
    ​
  2. 在spring的配置文件中,使用aop的标签进行目标方法与增强方法的织入

 3.spring的Bean的管理

我们可以看一看前面例子的配置文件:

<bean id="dog"① class="org.suke.Dog"② ></bean>
<bean id="cat" class="org.suke.Cat" ></bean>
<bean id="boy" class="org.suke.Boy" >
   <property name="pet"③ ref="dog"④></property>
</bean>

3.1bean的属性注入方式

3.1.1通过set方法给属性注入, 调用set方法

配置使用<property>,bean对象所在类提供对应属性的set方法

 Spring对set方式,提供简写, 使用p命名空间简写

3.1.2通过构造方法给属性注入值, 有参构造,bean所在类提供有参构造

 

 4.spring的纯注解开发

纯注解开发

SpringBoot打基础

不需要spring配置文件:

替换方式: 通过配置类替换配置文件, 要求这个类添加@Configuration注解

扫描注解的配置: 替换 : @ComponentScan 替换

<bean>可以把项目的类的对象交给spring管理, 也可以是第三方的依赖中的类交给Spring

使用注解替换:

  • 如果是自己项目的类, 使用 @Component

    @Repository 用于注册DAO(持久层 )

    @Service 用于注册 Service(业务层)

    @Controller 用于注册 Action (表现层)

  • 第三方的依赖中的类, 提供了@Bean 标记在方法上, 配置类中方法

使用步骤:

  1. 编写配置类

xml的方式: Spring的IOC容器对象: ClassPathXmlApplicationContext

注解方式: Spring的IOC容器对象: AnnotationConfigApplicationContext

  @Test
    public void queryById() {
        //1.创建Spring容器对象
        ApplicationContext  applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        StudentService studentService = applicationContext.getBean(StudentService.class);
        log.info("结果:{}",studentService.queryById(1));
    }
package com.fs.config;
​
import com.fs.dao.StudentDao;
import com.fs.service.StudentService;
import com.fs.service.impl.StudentServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
​
/**
 * @author suke
 * @version 1.0
 * @title MyConfig
 * @description 配置类,  替换配置文件
 * @create 2024/4/25 14:51
 */
@Configuration
//扫描注解
@ComponentScan("com.fs")
@Slf4j
public class MyConfig {
​
    //配置SqlSessionFactoryBuilder的bean对象
    @Bean("sqlSessionFactoryBuilder")  //标记在方法上, 把方法返回的对象保存到Spring容器, bean默认名: 方法名
    //当spring的容器创建的时候,加载配置类,  自动调用该配置类下所有的使用@Bean标记的方法
    public SqlSessionFactoryBuilder createSqlSessionFactoryBuilder(){
        log.info("createSqlSessionFactoryBuilder.....");
        return  new SqlSessionFactoryBuilder();
    }
​
​
    @Bean("studentService")
    public StudentService createStudentService(StudentDao studentDao){
        StudentServiceImpl  studentService = new StudentServiceImpl();
        studentService.setStudentDao(studentDao);
        return studentService;
    }
​
}
​

@PropertySource注解, 加载Properties配置文件

5.spring与web整合

5.1创建maven的web项目

5.2在pom.xml文件添加 与web整合的依赖: spring-web

 <!--spring-web-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>${spring.version}</version>
</dependency>

 5.3在web.xml文件中配置监听器(监听服务器启动), 这个监听器在Spring-web提供了

   
    <!--配置监听器-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!--指定spring的配置文件-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:beans.xml</param-value>
    </context-param>

 相关配置文件

 <?xml version="1.0" encoding="UTF-8"?>
  
  <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
  
    <groupId>org.example</groupId>
    <artifactId>spring-webdemo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
  
    <name>spring-webdemo Maven Webapp</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>
  
    <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <maven.compiler.source>1.8</maven.compiler.source>
      <maven.compiler.target>1.8</maven.compiler.target>
      <spring.version>5.3.21</spring.version>
    </properties>
  
    <dependencies>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
      </dependency>
      <!--spring-web-->
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
      </dependency>
      <!-- servlet-api -->
      <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
      </dependency>
      <!-- jsp-api -->
      <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.1</version>
        <scope>provided</scope>
      </dependency>
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
      </dependency>
  
      <!--log4j-->
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.36</version>
      </dependency>
  
      <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.24</version>
      </dependency>
  
      <dependency>
        <groupId>javax.inject</groupId>
        <artifactId>javax.inject</artifactId>
        <version>1</version>
      </dependency>
    </dependencies>
  
  
    <build>
      <finalName>spring-webdemo</finalName>
      <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
        <plugins>
          <plugin>
            <artifactId>maven-clean-plugin</artifactId>
            <version>3.1.0</version>
          </plugin>
          <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
          <plugin>
            <artifactId>maven-resources-plugin</artifactId>
            <version>3.0.2</version>
          </plugin>
          <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.0</version>
          </plugin>
          <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.22.1</version>
          </plugin>
          <plugin>
            <artifactId>maven-war-plugin</artifactId>
            <version>3.2.2</version>
          </plugin>
          <plugin>
            <artifactId>maven-install-plugin</artifactId>
            <version>2.5.2</version>
          </plugin>
          <plugin>
            <artifactId>maven-deploy-plugin</artifactId>
            <version>2.8.2</version>
          </plugin>
        </plugins>
      </pluginManagement>
    </build>
  </project>

6.动态代理

jdk的实现的动态代理: 要求目标类必须实现接口

核心类: Proxy, InvocationHandler

Proxy类有一个方法: 根据接口的Class 造一个拥有构造方法的Class对象

  1. 得到Proxy类的有参构造:

  2. 通过构造方法对象调用: newInstance(InvacationHandler ) 创建代理对象

    代理对象流程:

为什么这样设计: Proxy得到代理对象, 提供一个InvocationHandler接口

目的: 创建代理对象,与方法的逻辑实现分离, 解耦

proxy类负责创建代理对象, 空壳

InvocationHandler实现方法的逻辑代码

使用工具类封装创建代理对象

public class ProxyUtil {
​
    public static <T> T getProxyObject(Class<T> interfaceClass, InvocationHandler ih) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //调用Proxy的getProxyClass()
        Class<?> proxyClass = Proxy.getProxyClass(interfaceClass.getClassLoader(), interfaceClass);
        Constructor<?> constructor = proxyClass.getDeclaredConstructor(InvocationHandler.class);
        T proxy = (T)(BussinessInterface)constructor.newInstance(ih );
        return proxy;
    }
}

测试代码:

  @Test
    public void  test2() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        InvocationHandler ih = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //做增强,
                return "1";
            }
        };
        BussinessInterface proxyObject = ProxyUtil.getProxyObject(BussinessInterface.class, ih);
​
        proxyObject.execute();
    }
​

为了对目标类的代理, 在代理对象执行方法中,去执行目标方法

Proxy提供产生代理对象:

优化: 把增强代码封装到增强类中,

如果我的增强代码发生变化, 不修改修改invoke() , 修改增强类的方法

动态代理缺点: 但是调用增强类的方法, 是一个硬编码, 如果想增加调用的方法,修改invoke(), 删除增强方法调用, 修改invoke(),

总结:

jdk动态代理缺点:

  1. 要求目标类必须实现接口

  2. 增强类方法调用是硬编码,有耦合

解决目标类必须实现接口问题, 如果目标类没有实现接口,需要代理

解决方案: 第三方jar: cglib

代理类是目标类的子类

7.mybatis和spring整合

mybatis 与 Spring整合

之前mybatis与Spring整合:

需要创建Dao接口实现类, 编写代码, 得到SqlSession对象, 调用方法操作数据, 与Mybatis推荐的Mapper代理模式相违背

mybatis与Spring整合提供的整合包

Mybatis与Spring整合目的

目的:

1. 把Mybatis中核心对象(SqlSessionFactory,Mapper代理对象)交给Spring管理
  1. 单独配置数据源,不使用Mybatis提供的数据源, 为后面的事务控制做准备, Spring提供了声明式事务管理(底层AOP)

方便: 在Service层对象中注入Mapper代理对象

步骤:

  1. 创建一个web项目

  2. 添加相关依赖:

    Spring基本依赖: spring-context spring-aop spring-aspect spring-web spring-test

    mysql驱动: mysql-connector-java

    mybatis依赖:

    日志: slf4j-log4j12

    junit单元测试

    lombok插件

    spring-orm 与orm模型整合, 传递 spring-tx(事务控制)

    mybatis-spring: mybatis整合spring的依赖

 <!--aop相关依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${spring.version}</version>
    </dependency>
​
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>${spring.version}</version>
    </dependency>

如果web项目: servlet-api jsp-api

<!--servlet-api-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>
​
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.1</version>
      <scope>provided</scope>
    </dependency>

连接池的数据源依赖: c3p0

<dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.5.2</version>
    </dependency>

整体项目的依赖:

<properties>
    <spring.version>5.2.15.RELEASE</spring.version>
  </properties>
​
  <dependencies>
    <!--spring的依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
​
​
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
​
​
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.30</version>
    </dependency>
​
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <!--spring-web-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
​
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.36</version>
    </dependency>
​
    <!--DI: depecdency Inject-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.7</version>
    </dependency>
​
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
    </dependency>
​
    <!--servlet-api-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>
​
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.1</version>
      <scope>provided</scope>
    </dependency>
​
​
    <!--aop相关依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${spring.version}</version>
    </dependency>
​
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>${spring.version}</version>
    </dependency>
​
​
    <!--spring-orm: spring与ORM框整合的jar-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${spring.version}</version>
    </dependency>
​
    <!--mybatis与spring整合jar-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.1.0</version>
    </dependency>
​
    <!--连接吃的依赖-->
    <dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.5.2</version>
    </dependency>
​
  </dependencies>
  1. 配置文件

    • spring配置文件

    • mybatis全局配置文件(可以省略)

    • 日志配置文件

    • 数据库参数配置文件

    • sql映射文件

  1. 在spring的配置文件中, 配置Mybtais相关的bean

    • 配置数据源 c3p0

       <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
              <property name="driverClass" value="${jdbc.driver}"/>
              <property name="jdbcUrl" value="${jdbc.url}"/>
              <property name="password" value="${jdbc.password}"/>
              <property name="user" value="${jdbc.username}"/>
      ​
              <!--连接池相关参数-->
              <property name="minPoolSize" value="${jdbc.minPoolSize}"/>
              <property name="maxPoolSize" value="${jdbc.maxPoolSize}"/>
              <property name="initialPoolSize" value="${jdbc.initialPoolSize}"/>
              <property name="acquireIncrement" value="${jdbc.acquireIncrement}"/>
          </bean>
    • SqlSessionFactoryBean 对象

        <!--SqlSessionFactoryBean-->
          <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
              <!--注入数据源-->
              <property name="dataSource"  ref="dataSource"/>
      ​
              <!--某个包下类取别名-->
              <property name="typeAliasesPackage" value="com.fs.entity"/>
      ​
              <!--加载Mybatis全局配置文件: 如果没有这个文件, 该配置省略-->
              <property name="configLocation" value="classpath:mybatis-config.xml"/>
      ​
              <!--加载mapper映射文件  提供通配符: * 任意-->
              <property name="mapperLocations" value="classpath*:mapper/*Mapper.xml"/>
          </bean>
    • Mapper扫描器 MapperScannerConfigurer

      扫描指定包下的所有的Mapper接口, 使用SqlSession的getMapper()方法, 把该Mapper接口的代理对象保存到Spring容器

    <!--配置mapper扫描器--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--注入属性--> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>

     <!--扫描指定mapper接口所在包-->
     <property name="basePackage" value="com.fs.mapper"/>

    </bean>

    ​8.事务的控制

事务控制

  1. 把Mybatis与Spring整合, 把Mapper接口代理对象交给Spring容器管理

好处: 在Service直接注入Mapper对象

  1. Service层对象交给Spring管理

  2. 事务控制

    事务: 同一个事务下的一组sql语句要就全部成功, 要就全部失败 面试

如果一组sql语句执行成功, 提交事务(commit)

如果一组sql语句只有有一条执行失败, 回滚事务(rollback)

一个事务一旦提交或者回滚,表示事务结束, 不能重复提交和回滚

事务四大特征: ACID 面试

  • 原子性

  • 隔离性

  • 一致性

  • 持久性


mapper层类中的方法: 一个方法对应一条sql

业务层类中的方法: 包含多个mapper, 一个业务方法, 调用多个mapper的多个方法, 业务层的类的方法包含多条sql

事务是加在业务层

Spring中事务管理:

面试

spring事务分类:

  • 编程式事务管理, 在代码中,编写事务提交(正常执行)回滚(catch异常)

优点: 精确对方法某些代码加事务,而不需要对整个方法加事务

缺点: 需要编写代码, 可能有重复代码

  • 声明式事务管理: 底层基于AOP, spring提供了spring-tx的包, 事务管理包, 提供事务通知类, 程序员只需要在spring配置文件进行事务配置, 使用注解

    优点: 不需要写代码, 无侵入式, 没有重复代码

    缺点: 对整个方法加事务, 控制范围大, 效率相对于编程式低

spring事务控制的核心类

事务管理器接口: PlatformTransactionManager

//根据TransactionDefinition(事务定义) 得到对应事务状态
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
            throws TransactionException;
​
// 提交事务
    void commit(TransactionStatus status) throws TransactionException;
​
//回滚事务
void rollback(TransactionStatus status) throws TransactionException;

声明式事务管理中, 创建事务管理器对象,交给Spring管理

实现类:

  • DataSourceTransactionManager: 基于数据源的事务管理器, 比如Mybatis

  • HibernateTransactionManager: Hibernate(ORM框架)使用事务管理

  • JpaTransactionManager: Jpa(ORM框架)使用的事务管理

TransactionStatus

事务具体的运行状态,方法介绍如下:

TransactionDefinition

事务定义: 事务配置

public interface TransactionDefinition {
    int PROPAGATION_REQUIRED = 0;
    int PROPAGATION_SUPPORTS = 1;
    int PROPAGATION_MANDATORY = 2;
    int PROPAGATION_REQUIRES_NEW = 3;
    int PROPAGATION_NOT_SUPPORTED = 4;
    int PROPAGATION_NESTED = 6;
    
    
    int ISOLATION_DEFAULT = -1;
    int ISOLATION_READ_UNCOMMITTED = 1;  
    int ISOLATION_READ_COMMITTED = 2;  
    int ISOLATION_REPEATABLE_READ = 4;  
    int ISOLATION_SERIALIZABLE = 8;  
    
    
    int TIMEOUT_DEFAULT = -1; //永不超时
​
​
    //默认事务传播机制
    default int getPropagationBehavior() {
        return PROPAGATION_REQUIRED;
    }
​
    //默认事务隔离级别
    default int getIsolationLevel() {
        return ISOLATION_DEFAULT;
    }
​
    //默认超时时间
    default int getTimeout() {
        return TIMEOUT_DEFAULT;
    }
​
    //默认不只读(查询), 可读可写(修改)
    default boolean isReadOnly() {
        return false;
    }
​
​
    @Nullable
    default String getName() {
        return null;
    }
​
​
    static TransactionDefinition withDefaults() {
        return StaticTransactionDefinition.INSTANCE;
    }
​
}
​

Spring的声明式事务管理的配置:

  • 配置一个事务管理器的bean

  <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入DataSource: idea误报-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
  • 配置事务通知 使用tx标签库

事务隔离级别: 面试题 数据库提供

isolation:事务隔离级别.即当前事务和其他事务的隔离程度,在并发事务处理的情况下需要考虑它的设置,该属性可选的值如下:

  • DEFAULT:默认值,表示使用数据库默认的事务隔离级别

  • READ_UNCOMMITTED:读未提交, 出现脏读, 效率高, 安全性最低

  • READ_COMMITTED:读已提交 oracle 解决脏读, 出现幻读

  • REPEATABLE_READ: 可重复读 mysql 解决幻读

  • SERIALIZABLE: 串行读: 安全性最高, 效率最低

事务传播机制:

propagation:事务传播机制:该属性可选值如下:

  • REQUIRED:默认值,表示如果存在一个事务,则支持当前事务,如果当前没有事务,则开启一个新的事务. 增删改

  • SUPPORTS:表示如果存在一个事务,则支持当前事务,如果当前没有事务,则按非事务方式执行, 查询

  • MANDATORY:表示如果存在一个事务,则支持当前事务,如果当前没有事务,则抛出异常

  • REQUIRES_NEW:表示总是开启一个新的事务,如果当前有一个事务,则将当前事务挂起,开启新事物执行方法.

  • NOT_SUPPORTED:表示总是以非事务方式执行.如果一个事务已经存在,则将这个存在的事务挂起,然后执行方法.

  • NEVER:表示总是以非事务方式执行.如果当前存在一个活动的事务,则抛出异常

  • NESTED:表示如果当前存在一个活动的事务,则创建一个事务作为当前事务的嵌套事务运行,如果没有当前事务,该取值与REQUIRED相同.

实际开发:

REQUIRED: 增删改的方法, 默认值

SUPPORTS: 查询

 <tx:advice id="txAdvice">
        <!--事务定义-->
        <tx:attributes>
            <!--tx:method 针对于切入点匹配的方法, 进行分别定义事务
             name: 方法名, 可以* 作为通配符:  表示任意
              CURD: 增删改查,
              query*: 对方法是以query开头的
​
              isolation: 配置事务隔离级别, 使用默认值
              propagation: 配置事务传播机制
              read-only: 是否只读: 默认值false, 查询中设置为只读
              timeout: 超时时间: 默认 -1  永不超时
              rollback-for: 什么异常下回滚, 默认值:  RuntimeException  增删改
              no-rollback-for: 什么异常不回滚, 默认是空  增删改
             -->
            <tx:method name="query*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="*" />
        </tx:attributes>
    </tx:advice>

  1. aop配置 把txAdvice通知类中方法与目标方法进行织入

    <aop:config>
            <aop:pointcut id="pointcut1" expression="execution(* com.fs.service..*.*(..))"/>
    ​
            <!--织入-->
            <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"></aop:advisor>
        </aop:config>

  • 20
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值