Spring

Spring

鸣谢:黑马程序员

文章目录

  • Spring
    • 一、核心容器
      • 1.`IoC/DI`
        • [1.1 `IoC`](#1.1 IoC)
        • [1.2 `IoC`容器](#1.2 IoC容器)
        • [1.3 `Bean`](#1.3 Bean)
          • [P1 `Bean`的作用范围(`scope`)](#P1 Bean的作用范围(scope))
          • [P2 实例化`Bean`的方式](#P2 实例化Bean的方式)
          • [P3 `Bean`的生命周期及控制](#P3 Bean的生命周期及控制)
          • [P4 `Bean`的销毁时机](#P4 Bean的销毁时机)
        • [1.4 `DI`](#1.4 DI)
          • [P1 概述](#P1 概述)
          • [P2 依赖注入方式](#P2 依赖注入方式)
          • [P3 如何选择依赖注入方式?](#P3 如何选择依赖注入方式?)
          • [P4 依赖自动装配](#P4 依赖自动装配)
          • [P5 集合注入](#P5 集合注入)
          • [P6 加载`properties`文件](#P6 加载properties文件)
      • 2.容器基本操作
        • [2.1 创建容器](#2.1 创建容器)
          • [P1 加载配置文件的两种方式](#P1 加载配置文件的两种方式)
          • [P2 加载多个配置文件](#P2 加载多个配置文件)
        • [2.2 获取`bean`](#2.2 获取bean)
        • [2.3 容器类层次结构](#2.3 容器类层次结构)
        • [2.4 `BeanFactory`](#2.4 BeanFactory)
      • 3.注解开发
        • [3.1 注解开发定义`bean`](#3.1 注解开发定义bean)
          • [P1 步骤](#P1 步骤)
          • [P2 Spring提供的三个衍生注解](#P2 Spring提供的三个衍生注解)
        • [3.2 纯注解开发(`Spring3.0+`)](#3.2 纯注解开发(Spring3.0+))
        • [3.3 控制`Bean`的作用范围与生命周期](#3.3 控制Bean的作用范围与生命周期)
        • [3.4 依赖注入------自动装配](#3.4 依赖注入——自动装配)
          • [P1 使用`@Autowired`注解开启按类型自动装配模式](#P1 使用@Autowired注解开启按类型自动装配模式)
          • [P2 使用`@Qualifier`注解按指定名称装配`bean`](#P2 使用@Qualifier注解按指定名称装配bean)
          • [P3 使用`@Value`注解注入简单类型数据](#P3 使用@Value注解注入简单类型数据)
          • [P4 使用`@PropertySource`注解加载属性文件](#P4 使用@PropertySource注解加载属性文件)
        • [3.5 管理第三方`bean`](#3.5 管理第三方bean)
          • [P1 使用`@Bean`注解来配置第三方`bean`](#P1 使用@Bean注解来配置第三方bean)
          • [P2 为第三方`bean`进行依赖注入](#P2 为第三方bean进行依赖注入)
        • [3.6 `@Component`和`@Bean`的区别](#3.6 @Component@Bean的区别)
          • [P1 概述](#P1 概述)
          • [P2 对比表](#P2 对比表)
    • 二、数据访问与集成
    • 三、`AOP`
      • 1.概述
      • 2.核心概念
      • 3.实现步骤
      • 4.`AOP`工作流程
      • 5.切入点表达式
        • [5.1 标准格式](#5.1 标准格式)
        • [5.2 通配符](#5.2 通配符)
        • [5.3 书写技巧](#5.3 书写技巧)
      • 6.通知类型
        • [6.1 前置通知](#6.1 前置通知)
        • [6.2 后置通知](#6.2 后置通知)
        • [6.3 环绕通知(重点)](#6.3 环绕通知(重点))
        • [6.4 返回后通知(了解)](#6.4 返回后通知(了解))
        • [6.5 抛出异常后通知(了解)](#6.5 抛出异常后通知(了解))
      • 7.从通知中获取数据
        • [7.1 获取参数](#7.1 获取参数)
        • [7.2 获取返回值](#7.2 获取返回值)
        • [7.3 获取异常](#7.3 获取异常)
    • 四、事务

一、核心容器

1.IoC/DI

1.1 IoC
  • IoC(Inversion of Control):控制反转。
  • 核心思想 :将对象的创建控制权由程序内部转移到外部 。也就是使用对象时,在程序中不要主动使用new产生对象,而是转换为由外部提供对象。
1.2 IoC容器
  • Spring提供了一个容器用来充当IoC思想中的外部 ,称为IoC容器。

1.3 Bean

!IMPORTANT

IoC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IoC容器中统称为Bean

P1 Bean的作用范围(scope)
  • 默认是单例

  • 适合交给容器管理的Bean:接口层对象、业务层对象、数据层对象、工具对象;

  • 不适合交给容器管理的Bean:封装实体的域对象。

P2 实例化Bean的方式
  • 常用 :提供无参构造器(private也可)。
  • 使用静态工厂。
  • 使用实例工厂。
  • 重点 :使用FactoryBean。(方式三的变种)
P3 Bean的生命周期及控制
  • Bean的生命周期:

    • Step1初始化容器

      1. 创建对象(内存分配)
      2. 执行构造器
      3. 执行属性注入(调用setter
      4. 执行bean初始化方法
    • Step2使用bean

    • Step3关闭/销毁容器

  • Bean的生命周期控制:

    • 控制方式一:
    • 控制方式二:接口控制

P4 Bean的销毁时机
  • 容器关闭前触发Bean的销毁。
  • 关闭容器的方式
    • 手动关闭:调用ConfigurableApplicationContext接口的close()方法。
    • 注册关闭钩子,先关闭容器再退出JVM:调用ConfigurableApplicationContext接口的registerShutdownHook()方法。

1.4 DI
P1 概述
  • DI(Dependency Injection):依赖注入。

  • 核心思想 :在IoC容器中建立BeanBean之间的依赖关系。

  • 向一个类中传递的数据类型:

    • 简单类型:基本数据类型+String--><value>
    • 引用类型--><ref>
P2 依赖注入方式
  • setter注入
    • 简单类型
    • 引用类型
  • 构造器注入
    • 简单类型
    • 引用类型
P3 如何选择依赖注入方式?
  • 具有强制依赖关系的使用构造器注入,因为使用setter注入有概率没有进行注入,导致null对象出现。
  • 具有可选依赖关系的使用setter注入,灵活性强。
  • Spring框架推荐使用构造器注入,第三方框架内部大多采用构造器注入的形式进行数据初始化,相对严谨。
  • 如有必要可以两者同时使用:
    • 使用构造器注入完成强制依赖关系的注入;
    • 使用setter注入完成可选依赖关系的注入。
  • 实际开发过程中根据实际情况分析,如果受控对象没有提供setter,则必须使用构造器注入。
  • 自己开发的模块推荐 使用setter注入。
P4 依赖自动装配
  • 含义IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中。

  • 自动装配方式

    • 按类型(常用
    • 按名称
    • 按构造器
    • 不启用自动装配
  • 注意事项

    • 自动装配仅用于引用类型的依赖注入,无法对简单类型进行依赖注入。
    • 使用按类型装配(byType)时,必须保证容器中相同类型的bean只有一个,开发中推荐使用这种方式进行自动装配。
    • 使用按名称装配(byName)时,必须保证容器中具有指定名称的bean(即setter方法的名字去掉set后的名称),因为变量名与配置高度耦合,故不推荐使用。
    • 自动装配的优先级低于setter注入和构造器注入,同时出现时自动装配失效。
P5 集合注入
  • BookDaoImpl:
java 复制代码
package com.zsh.dao.impl;

import com.zsh.dao.BookDao;
import java.util.*;

public class BookDaoImpl implements BookDao {

    private int[] zshArray;
    private List<String> zshList;
    private Set<String> zshSet;
    private Map<String,String> zshMap;
    private Properties zshProperties;

    public void setZshArray(int[] zshArray) {
        this.zshArray = zshArray;
    }
    public void setZshList(List<String> zshList) {
        this.zshList = zshList;
    }
    public void setZshSet(Set<String> zshSet) {
        this.zshSet = zshSet;
    }
    public void setZshMap(Map<String, String> zshMap) {
        this.zshMap = zshMap;
    }
    public void setZshProperties(Properties zshProperties) {
        this.zshProperties = zshProperties;
    }

    @Override
    public void save() {
        System.out.println("book dao save ...");
        System.out.println("traverse Array: "+ Arrays.toString(zshArray));
        System.out.println("traverse List: "+ zshList);
        System.out.println("traverse Set: "+ zshSet);
        System.out.println("traverse Map: "+ zshMap);
        System.out.println("traverse Properties: "+ zshProperties);
    }
}
  • applicationContext.xml
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">

    <bean id="bookDao" class="com.zsh.dao.impl.BookDaoImpl">
        <property name="zshArray">
            <array>
                <value>100</value>
                <value>200</value>
                <value>300</value>
            </array>
        </property>

        <property name="zshList">
            <list>
                <value>zsh</value>
                <value>zjl</value>
                <value>zxj</value>
                <value>zxj</value>
            </list>
        </property>

        <property name="zshSet">
            <set>
                <value>apple</value>
                <value>banana</value>
                <value>strawberry</value>
                <value>strawberry</value>
                <!--由于Set元素不可重复,故会自动过滤2个重复的strawberry-->
            </set>
        </property>

        <property name="zshMap">
            <map>
                <entry key="country" value="China"/>
                <entry key="province" value="Guangdong"/>
                <entry key="city" value="Swatow"/>
            </map>
        </property>

        <property name="zshProperties">
            <props>
                <prop key="name">zsh</prop>
                <prop key="sex">male</prop>
                <prop key="height">175</prop>
            </props>
        </property>
    </bean>

</beans>
P6 加载properties文件



2.容器基本操作

2.1 创建容器
P1 加载配置文件的两种方式
  • 通过类路径ApplicationContext context = new ClassPathXmlApplicationContext("???.xml");
  • 通过文件路径ApplicationContext context = new FileSystemXmlApplicationContext("???.xml的绝对路径或本工程下的相对路径");
P2 加载多个配置文件

ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml", "bean2.xml");

2.2 获取bean
  • 使用bean名称获取:BookDao bookDao = (BookDao) context.getBean("bookDao")
  • 使用bean名称获取并指定bean类型:BookDao bookDao = context.getBean("bookDao", BookDao.class)
  • 使用bean类型获取(注意该类型的bean在配置文件中必须是唯一的):BookDao bookDao = context.getBean(BookDao.class)
2.3 容器类层次结构
2.4 BeanFactory

3.注解开发

3.1 注解开发定义bean
P1 步骤

Step1:使用@Component定义bean

java 复制代码
@Component("bookDao")
public class BookDaoImpl implements BookDao {
}

@Component
public class BookServiceImpl implements BookService {
}

Step2:核心配置文件中通过组件扫描加载bean

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
">
    <context:component-scan base-package="com.zsh"/>

</beans>
P2 Spring提供的三个衍生注解

!Tip

功能和@Component一模一样,只是增强了可读性。

  • @Controller:用于接口层bean定义。
  • @Service:用于业务层bean定义。
  • @Repository:用于数据层bean定义。

3.2 纯注解开发(Spring3.0+

创建一个Java类SpringConfig来代替核心配置文件:

java 复制代码
package com.zsh.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration// 代表这是个Spring配置类
@ComponentScan("com.zsh")// 指定扫描路径,此注解只能添加一次,多个数据需用数组形式,如@ComponentScan({"com.zsh.dao", "com.zsh.service"})
public class SpringConfig {
}

重新编写测试类:

java 复制代码
package com.zsh;

import com.zsh.config.SpringConfig;
import com.zsh.dao.BookDao;
import com.zsh.service.BookService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class AppByPureAnnotation {
    public static void main(String[] args) {
        ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig.class);

        BookDao bookDao=(BookDao) context.getBean("bookDao");
        System.out.println(bookDao);
        BookService bookService=context.getBean(BookService.class);
        System.out.println(bookService);
    }
}

3.3 控制Bean的作用范围与生命周期



3.4 依赖注入------自动装配
P1 使用@Autowired注解开启按类型自动装配模式
P2 使用@Qualifier注解按指定名称装配bean
P3 使用@Value注解注入简单类型数据
P4 使用@PropertySource注解加载属性文件

3.5 管理第三方bean
P1 使用@Bean注解来配置第三方bean
  • SpringConfig:

    java 复制代码
    package com.zsh.config;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    
    @Configuration// 代表这是个Spring配置类
    @Import(JdbcConfig.class)// 导入配置
    public class SpringConfig {
    }
  • JdbcConfig:(使用独立的配置类进行解耦合)

    java 复制代码
    package com.zsh.config;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import org.springframework.context.annotation.Bean;
    import javax.sql.DataSource;
    
    public class JdbcConfig {
    
        // 1.定义一个方法来获得要管理的第三方bean
        // 2.添加@Bean表示该方法的返回值是一个bean
        @Bean
        public DataSource getDataSource(){
            DruidDataSource dataSource=new DruidDataSource();
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/spring_db");
            dataSource.setUsername("root");
            dataSource.setPassword("123456");
            return dataSource;
        }
    }
P2 为第三方bean进行依赖注入
  • 简单类型依赖注入:设置成员变量

    java 复制代码
    public class JdbcConfig {
    
        @Value("com.mysql.jdbc.Driver")
        private String driver;
        @Value("jdbc:mysql://localhost:3306/spring_db")
        private String url;
        @Value("root")
        private String username;
        @Value("123456")
        private String password;
    
        @Bean
        public DataSource getDataSource(){
            DruidDataSource dataSource=new DruidDataSource();
            dataSource.setDriverClassName(driver);
            dataSource.setUrl(url);
            dataSource.setUsername(username);
            dataSource.setPassword(password);
            return dataSource;
        }
    }
  • 引用类型依赖注入:只需要为带有@Bean方法设置形参 即可,容器会根据类型自动装配对象。

    !Tip

    BookDaoImpl这个类记得加上@Repository注解,容器才可以自动装配。

    java 复制代码
    public class JdbcConfig {
    
        @Bean
        public DataSource getDataSource(BookDao bookDao){// 容器会自动装配bookDao(byType)
            System.out.println(bookDao);
            DruidDataSource dataSource=new DruidDataSource();
            return dataSource;
        }
    }

3.6 @Component@Bean的区别
P1 概述
  • @Component是类级别注解,表示把这个类交给Spring容器管理。
  • @Bean是方法级别注解,表示把这个方法返回的对象交给Spring容器管理。
  • 两者都能创建Spring容器中的Bean,但用途完全不同。
P2 对比表
对比项 @Component @Bean
注解位置 方法(通常在@Configuration类中)
Bean创建方式 Spring自动扫描并实例化(反射调用无参构造器) 方法返回对象由Spring注册
依赖注入 通过@Autowired@Value等注解进行依赖注入 方法形参自动依赖注入
适用场景 自己编写的类,且可被Spring扫描到 第三方类、需要定制构造/初始化逻辑的Bean
配置方式 需配合@ComponentScan扫描包路径 不需要组件扫描,只要执行配置类即可
灵活性 相对固定,依赖无参构造器 高度灵活,可在方法中写任意 Java 代码控制实例化


二、数据访问与集成

1.Spring整合MyBatis

2.1 mybatis-config.xml
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org/DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--初始化属性数据-->
    <properties resource="jdbc.properties"></properties>

    <!--初始化类型别名-->
    <typeAliases>
        <package name="com.zsh.domain"/>
    </typeAliases>

    <!--初始化数据源-->
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--初始化映射配置:随业务而改变-->
    <mappers>
        <package name="com.zsh.dao"/>
    </mappers>
</configuration>

分析可知,要想让Spring整合MyBatis,则要让Spring管理SqlSessionFactory对象。

2.2 代码重构
  • SpringConfig:

    java 复制代码
    package com.zsh.config;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    import org.springframework.context.annotation.PropertySource;
    
    @Configuration
    @ComponentScan("com.zsh")
    @PropertySource("classpath:jdbc.properties")
    @Import({JdbcConfig.class, MybatisConfig.class})
    public class SpringConfig {
    }
  • JdbcConfig:

    java 复制代码
    package com.zsh.config;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    
    import javax.sql.DataSource;
    
    public class JdbcConfig {
        @Value("${jdbc.driver}")
        private String driver;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
    
        @Bean
        public DataSource dataSource(){
            DruidDataSource dataSource=new DruidDataSource();
            dataSource.setDriverClassName(driver);
            dataSource.setUrl(url);
            dataSource.setUsername(username);
            dataSource.setPassword(password);
            return dataSource;
        }
    }
  • MybatisConfig:

    java 复制代码
    package com.zsh.config;
    
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.mapper.MapperScannerConfigurer;
    import org.springframework.context.annotation.Bean;
    
    import javax.sql.DataSource;
    
    public class MybatisConfig {
    
        @Bean
        public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
            SqlSessionFactoryBean factoryBean=new SqlSessionFactoryBean();
            factoryBean.setTypeAliasesPackage("com.zsh.domain");
            factoryBean.setDataSource(dataSource);
            return factoryBean;
        }
    
        @Bean
        public MapperScannerConfigurer mapperScannerConfigurer(){
            MapperScannerConfigurer configurer=new MapperScannerConfigurer();
            configurer.setBasePackage("com.zsh.dao");
            return configurer;
        }
    }

2.Spring整合JUnit

  • AccountServiceTest:
java 复制代码
package service;

import com.zsh.config.SpringConfig;
import com.zsh.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void testFindById(){
        System.out.println(accountService.findById(2));
    }
}


三、AOP

1.概述

  • AOP(Aspect-Oriented Programming):面向切面编程。是一种编程范式,指导开发者如何组织程序结构。
  • 作用 :在不惊动原有设计 的基础上为其进行功能增强(无侵入式编程)。

2.核心概念

!Important

以下内容参考了Code With Sunil | Code Smarter, not harder在Medium平台发布的文章Mastering Spring AOP (Aspect-Oriented Programming): A Comprehensive Guide with Real-World Examples

  • 连接点(Join Point) :连接点是应用程序执行过程中的任意位置。
    • 在Spring AOP中,连接点可以理解为方法的执行。
  • 切入点(Pointcut) :切入点是一个匹配连接点的表达式,用于决定某个通知应该运行在哪些连接点上,它告诉Spring应该在何处应用你的切面。
    • 在Spring AOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法。
      • 一个具体方法:com.zsh.dao包下的BookDao接口中的public void save()方法。
      • 匹配多个方法:所有save方法,所有getXxx方法,所有xxxDao方法,所有带有一个形参的方法......
    • 注意:切入点范围小于连接点范围,切入点包含于连接点中。
  • 通知(Advice) :通知是你希望在切入点运行的代码,例如在方法运行之前先记录日志。
    • 在Spring AOP中,通知最终以方法的形式呈现。
  • 通知类(Advice Class):专门定义通知的类。
  • 切面(Aspect) :切面是一个包含了多个类通用功能代码的模块,例如日志记录。它描述了通知与切入点的对应关系。
    • 在Spring AOP中,可以使用@Aspect注解或通过XML配置的方式来创建切面。

3.实现步骤

  1. pom.xml中导入AOP相关坐标。注意若已经导入了spring-context的坐标,就不用再导入spring-aop的坐标了,因为后者包含于前者。

    xml 复制代码
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>自己选一个合适的版本号</version>
    </dependency>
  2. 制作连接点方法(原始操作,Dao接口与实现类)。

  3. 定义通知类,在通知类中编写共性功能,即通知。

  4. 定义切入点,切入点依托于一个没有实际意义的方法进行,即无参数、无返回值、方法体无具体逻辑的方法。

    java 复制代码
    public class MyAdvice {
    
        @Pointcut("execution(void com.zsh.dao.BookDao.update())")
        private void pointcutMethod(){}
    
    }
  5. 定义切面,绑定通知与切入点,同时将通知类交由Spring容器进行管理。

    java 复制代码
    @Component
    @Aspect// 用于创建切面
    public class MyAdvice {
    
        @Pointcut("execution(void com.zsh.dao.BookDao.update())")
        private void pointcutMethod(){}
    
        @Before("pointcutMethod()")
        public void before(){
            System.out.println(System.currentTimeMillis());
        }
    }
  6. SpringConfig中开启Spring对AOP注解驱动的支持。

    java 复制代码
    @Configuration
    @ComponentScan("com.zsh")
    @EnableAspectJAutoProxy// 告诉Spring程序中存在用注解开发的AOP
    public class SpringConfig {
    }

4.AOP工作流程

  1. 启动Spring容器。
  2. 读取所有绑定了通知的切入点。
  3. 初始化bean,判定bean对应的类中的方法是否匹配到任意切入点。
    • 若匹配失败,则正常创建bean对象。
    • 若匹配成功,则创建bean对象(称作原始对象或目标对象 )的代理对象
  4. 获取bean
    • 若上一步匹配失败,则正常调用方法并执行,完成操作。
    • 若上一步匹配成功,则根据代理对象的运行模式运行原始方法与增强内容,完成操作。

5.切入点表达式

5.1 标准格式

动作关键字(修饰符 返回值 包名.类/接口名.方法名(方法形参类型) 异常名),其中修饰符、异常名可省略。

example: execution(public User com.zsh.service.UserService.findById(int) RuntimeException)

动作关键字用于描述切入点的行为动作,execution表示执行到指定切入点。

5.2 通配符

使用通配符可以快速描述切入点:

  • *:单个任意符号,可以独立出现,也可以作为前缀或后缀匹配符出现。
    • execution(public * com.zsh.*.UserService.find*(*)):匹配com.zsh包下任意子包中的UserService中,所有以find为开头的,返回值任意且只有一个形参的方法。
  • ..:任意符号重复多次,可以独立出现,常用于简化包名与形参的书写。
    • execution(public User com..UserService.findById(..)):匹配com包下任意子包中的UserService中,所有名称为findById的方法,形参数量不限。
  • +:专门用于匹配子类/实现类类型。
    • execution(* *..*Service+.*(..)):匹配任意包下的以Service为结尾的类/接口及其子类/实现类中的所有方法,即任意业务层接口。
5.3 书写技巧
  • 所有代码按照标准规范开发,否则以下技巧全部失效。
  • 描述切入点通常描述接口,而不描述实现类,避免紧耦合。
  • 针对接口开发,修饰符均采用public描述。
  • 返回值类型对于增、删、改 类使用精准类型加速匹配,对于查询 类使用*通配符快速描述。
  • 包名书写尽量不使用..匹配,效率过低,建议使用*做单个包描述匹配,或精准匹配。
  • 若接口名/类名名称与模块相关,则采用*匹配,例如UserService书写成*Service,绑定业务层接口名。
  • 方法名书写以动词进行精准匹配,名词采用*匹配,例如getById书写成getBy*selectAll书写成selectAll
  • 参数规则较为复杂,根据业务方法灵活调整。
  • 通常不使用异常作为匹配规则。

6.通知类型

6.1 前置通知
6.2 后置通知
6.3 环绕通知(重点)


6.4 返回后通知(了解)
6.5 抛出异常后通知(了解)

7.从通知中获取数据

7.1 获取参数
  • JoinPoint:描述了连接点方法的运行状态,可以获取到原始方法的调用参数(通过getArgs方法),适用于前置、后置、返回后、抛出异常后通知。
  • ProceedingJoinPoint:是JoinPoint的子类,适用于环绕通知。

!Tip

如何通过ProceedingJoinPoint修改用户传入的参数?

java 复制代码
@Around("pct()")
 public Object around(ProceedingJoinPoint pjp) throws Throwable {
     // 获取用户传入的参数
     Object[] args = pjp.getArgs();

     // 修改参数
     args[索引] = ...;// 填入具体修改逻辑

     // 调用ProceedingJoinPoint提供的有参重载方法proceed(Object[] args),
     // 传入修改后的参数
     Object ret=pjp.proceed(args);
     return ret;
 }
7.2 获取返回值
  • 返回后通知:

    java 复制代码
    @AfterReturning(value = "pct()",returning = "ret")// 指定ret用于接收原始方法的返回值
    public void afterReturning(Object ret){
        System.out.println("afterReturning advice ...");
        System.out.println("ret: "+ret);
    }
  • 环绕通知:

    java 复制代码
    @Around("pct()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Object ret=pjp.proceed();
        return ret;
    }
7.3 获取异常
  • 抛出异常后通知:

    java 复制代码
    @AfterThrowing(value = "pct()",throwing = "t")// 指定t用于接收原始方法的异常
    public void afterThrowing(Throwable t){
        System.out.println("afterThrowing advice ...");
        System.out.println("exception: "+t);
    }
  • 环绕通知:

    java 复制代码
    @Around("pct()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Object ret = null;
        try {
            ret = pjp.proceed();
        } catch(Throwable t) {
            t.printStackTrace();
        }
        return ret;
    }


四、事务

1.概述

!Important

版权声明:以下内容摘选自CSDN博主「重生之我在成电转码」的原创文章,遵循CC 4.0 BY-SA版权协议,原文链接:https://blog.csdn.net/m0_69529796/article/details/146242971

事务(Transaction)是数据库操作的最小工作单元,保证多个SQL语句作为一个整体执行,要么全部成功,要么全部失败,确保数据的一致性和完整性。

例如,在银行转账的场景中:第一步:从A账户扣除100元;第二步:向B账户增加100元。

如果第一步执行成功,但第二步失败(比如数据库崩溃),账户A的钱就凭空少了,这会导致数据不一致。事务可以保证这两个操作要么都成功,要么都回滚(撤销),避免数据错误。

2.Spring事务

  • 作用:在数据层或业务层上,保障一系列的数据库操作要么同时成功,要么同时失败。

  • Spring提供了一个接口PlatformTransactionManager

    java 复制代码
    public interface PlatformTransactionManager{
        void commit(TransactionStatus status) throws TransactionException;// 提交事务
        void rollback(TransactionStatus status) throws TransactionException;// 回滚事务
    }
  • 针对JDBC的事物,Spring提供了上述接口的一个实现类DataSourceTransactionManager


3.开启事务的步骤------案例:银行转账

3.1 在业务层接口上添加Spring事务管理

!CAUTION

Spring注解式事务退出添加在业务层接口,而非业务层实现类,以降低耦合。

@Transactional可以添加在接口上,也可以添加在单个方法上。

java 复制代码
@Transactional// 开启事务
public interface AccountService {
    public void transfer(String out, String in, Double money);
}
3.2 设置事务管理器
java 复制代码
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource(){
        DruidDataSource dataSource=new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }

    @Bean
    // 事务管理器,引用类型DataSource通过形参自动注入
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager manager=new DataSourceTransactionManager();
        manager.setDataSource(dataSource);
        return manager;
    }
}
3.3 开启注解式事务驱动
java 复制代码
@Configuration
@ComponentScan("com.zsh")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class, MybatisConfig.class})
@EnableTransactionManagement// 允许开启事务管理器
public class SpringConfig {
}

4.Spring事务角色

5.Spring事务相关配置

属性 作用 示例
readOnly 设置是否为只读事务 readOnly=true(只读事务)
timeout 设置事务超时时间 timeout=-1(永不超时)
rollbackFor 设置事务回滚异常(class) rollbackFor={NullPointException.class}
rollbackForClassName 设置事务回滚异常(String) 同上,但格式为字符串
noRollbackFor 设置事务不回滚异常(class) noRollbackFor={NullPointException.class}
noRollbackForClassName 设置事务不回滚异常(String) 同上,但格式为字符串
propagation 设置事务传播行为 见下
相关推荐
爬山算法2 小时前
Springboot请求和响应相关注解及使用场景
java·spring boot·后端
曹牧2 小时前
Oracle:Replace
数据库·oracle
程序员水自流2 小时前
MySQL InnoDB存储引擎详细介绍之事务
java·数据库·mysql·oracle
Knight_AL2 小时前
MySQL STORED 生成列(Generated Column)详解:让 SQL 变快的秘密武器
数据库·sql·mysql
请为小H留灯2 小时前
Java实际开发@常用注解(附实战场景)
java·后端·个人开发
煎蛋学姐2 小时前
SSM社区疫苗接种预约系统hulgj(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm 框架
老华带你飞2 小时前
在线教育|基于springboot + vue在线教育系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot·后端
路边草随风2 小时前
java操作cosn使用
java·大数据·hadoop
TT哇2 小时前
【项目】玄策五子——匹配模块
java·spring boot·websocket·spring·java-ee·maven