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 设置事务传播行为 见下
相关推荐
zzz_23682 分钟前
【Java基础】链表的七十二变——从LRU缓存到手写浏览器前进后退
java·链表·缓存
番茄去哪了4 分钟前
神领物流面试题(一)
java·大数据·中间件
云烟成雨TD5 分钟前
Agent Scope Java 2.x 系列【9】接入高德 MCP 服务
java·人工智能·agent
吴声子夜歌21 分钟前
SQL经典实例——检索记录
数据库·sql
黄焖鸡能干四碗22 分钟前
软件系统概要设计说明书模版(Word)
大数据·运维·数据库·架构·需求分析
gaohe26AIliuzeyu24 分钟前
Java内部类
java·开发语言
西安邮电大学28 分钟前
有关数组的经典算法题
java·后端·其他·算法·面试
山东点狮信息科技有限公司28 分钟前
点狮HRM-HRM系统安全体系与数据保护方案
后端·安全·spring·spring cloud·微服务·系统安全·资产
dust_and_stars30 分钟前
为什么ubuntu24 snap install code-server 不需要--classic?
网络·数据库
互联网推荐官31 分钟前
上海AI Agent智能体开发公司技术选型实录:六条路径、三类架构与真实落地约束
java·人工智能·ai·架构·开发经验·上海