Spring框架

Spring框架

Spring复习

1.了解Spirng

spirng->spring mvc ->spring boot ->spring cloud

48b2da9ced4cd0074a9fdd4f7c0a4318

层层递进–

3a684778fcbcadf529eaad2b4e9ca34d

2.IOC -Di 了解

由分层解耦引出的依赖注入 以及依赖反转

ab5ce66c757c262b95bdbeb4ec1862ad

如图 ->当我们想更改对象 BookDaoImp12 名字时候->时 ->所有业务代码都需要改动改动过大->

Ioc->控制反转
使用对象–由主动new-改为外部提供对象->对象控制器转移到外部

对此-spring提供容器->ioc容器 ->提供了对象的创建和管理

依赖注入

1f0310fa78da12366d47e8d7844a002a

IOC->容器管理Bean->容器管理bookdaoimpl

DI->将具有依赖关系的Bean进行关系绑定->例如上面的bookdao->与bookdaoimpl进行绑定 Spring利用依赖注入机制来处理这些Bean之间的依赖关系,从而实现对象之间的松耦合。

3.加入IOC 容器ed3173cdd0c9bc6ba6d44953a8c38e0a

1.配置Bean

19c1fd155ec41e2182592cffc5262d96

2.获取Bean

506c12b2db4dd7aca712594fa5e279fa

当我们配置bean的时候–容器内就保存了我们的对象地址->单例->谁需要就给予

2.依赖注入

838a65e2572d2c5fafc8c03d3a3ffd44

dace05b9aa02d6ab02fd488a060b202e

eed54b5a29e21c9568ee7b195168d180

set容器执行->最后还是容器将Bean给予了

spring->编译 –自动找set 依赖方法注入

4.Bean配置

2a1a6f9cec7eca02be1d7807228f64ae

316948bfd792a80f844396040f862c94

8f41e07fcd21e2e60e49873e445f8656

spring->默认的Bean是单例的

如果需要多例 就需要修改配置

20bd78b82f85ef5660348295b0f97bf6

为什么bean要单例–
开对象->耗费内存

由于是单例->需要改变的数据就不能加入spring管理->除非开多例

5.Bean实例化

1.ioc过程

2509eb7c62e476183b724c53e7881f29

给接口内->一个构造->通过xml解析出 需要构造的对象->通过反射 ->创建出对象返回->默认单例

通过类名动态加载类的方法

在使用 Class.forName() 方法时,情况会根据加载的类是否已存在而有所不同:

  1. 类已存在
    • 如果使用 Class.forName() 加载的类在类路径中已经存在,则会返回对应的 Class 对象,而不会创建新的对象。这意味着它只是获取已加载类的引用,不会触发类的初始化和实例化。
  2. 类不存在
    • 如果要加载的类在类路径中不存在,则会抛出 ClassNotFoundException 异常,而不会创建新的类对象。在这种情况下,不会创建新的类对象,因为类根本就没有被加载到内存中
2.工厂模式

工厂模式 ->由一个类中的静态方法->返回对象的引用

1
2
3
4
5
6
7
public class MyServiceFactory {
// 静态工厂方法
public static MyService createInstance() {
System.out.println("通过静态工厂方法创建 MyService 实例");
return new MyServiceImpl();
}
}

createInstance() 是静态工厂方法,负责返回 MyService 类型的对象。

在 Spring 配置文件中使用 <bean> 标签指定静态工厂方法:

1
<bean id="myService" class="com.example.MyServiceFactory" factory-method="createInstance"/>

Java 注解配置静态工厂方法

1
2
3
4
5
6
7
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return MyServiceFactory.createInstance();
}
}
3.实例工厂-
1
2
3
4
5
6
7
public class MyServiceFactory {
// 实例工厂方法
public MyService createInstance() {
System.out.println("通过实例工厂方法创建 MyService 实例");
return new MyService();
}
}

Spring 配置文件(applicationContext.xml)中,通过 <bean> 标签配置实例工厂:

1
2
3
4
5
xml复制代码<!-- 配置工厂类的 Bean -->
<bean id="myServiceFactory" class="com.example.MyServiceFactory"/>

<!-- 使用工厂类的实例方法创建 Bean -->
<bean id="myService" factory-bean="myServiceFactory" factory-method="createInstance"/>

获取bean

1
2
3
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
MyService myService = (MyService) context.getBean("myService");
myService.doSomething();

Spring 容器首先会创建 MyServiceFactory 的实例,然后调用其 createInstance 方法来创建 MyService 实例。

Java 注解配置实例工厂方法

//多了一步-先获取到实例工厂bean才能够注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
public class AppConfig {

@Bean
public MyServiceFactory myServiceFactory() {
return new MyServiceFactory();
}

@Bean
public MyService myService() {
return myServiceFactory().createInstance();
}
}

FactoryBean

在 Spring 框架中,FactoryBean 是一个特殊的接口,用于自定义 Bean 的创建逻辑

1
2
3
Object getObject(): 返回要创建的 Bean 实例。这个方法用于定义实际的对象创建逻辑。
Class<?> getObjectType(): 返回创建的 Bean 的类型。
boolean isSingleton(): 指示这个 Bean 是否是单例模式。返回 true 表示单例,false 表示每次请求都会创建一个新的实例。

dd082cfbea4d7e4b13aa31843b6f35c3

指定类-扫描

Spring 容器会创建一个 CarFactoryBean 的实例,并调用它的 getObject() 方法来获取 Car 对象。

1
2
3
4
5
6
7
8
9
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

// 获取由 CarFactoryBean 创建的 UserDao 实例
UserDao car = (Car) context.getBean("UserdAOfACTORBEAN");
System.out.println(car);

// 获取 CarFactoryBean 实例本身
CarFactoryBean factoryBean = (CarFactoryBean) context.getBean("&UserdAOfACTORBEAN");
System.out.println(factoryBean);

6.Bean生命周期

1.生命周期配置

1.配置法

3f411aabb27ce975d2121258bbe3d082

2.接口法

a1a7897db2e6c6a81a71ae18c1629103

0b42710ae7bb291a8a7f65ac715b86bc

2.bean销毁时间

为了体现生命周期->我们提前销毁bean

87ed0bfac3a4d77291ac594173873a77

7.依赖注入

1.了解依赖注入

有Bean关系的类进行自动注入 —向类传递数据方式-set 传递 –构造传参


参数–引用类型和简单类型

2.引用注入

引用注入 是指将一个对象作为另一个对象的属性注入。例如,在一个类中注入另一个类的实例。在 XML 配置或基于注解的方式中,都可以实现引用注入。

引用注入是指将一个 Bean 注入到另一个 Bean 中,这个 Bean 可以通过构造器、Setter 方法、或其他方式注入。引用注入强调的是一种引用关系**。

Person 类需要 Address 类的实例。

1
2
3
4
5
public class Address {
private String city;
private String street;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Person {
private String name;
private Address address; // 引用注入
// getters and setters

public Address getAddress() {
return address;
}

public void setAddress(Address address) {
this.address = address;
}
//构造器
Person(Address address)
{
this.address = address;
}

}
}

Person 类依赖于 Address 类,即 Person 拥有一个 Address 类型的属性。我们可以通过 Spring 配置文件来实现引用注入。

XML 配置引用注入

1
2
3
4
5
6
7
8
9
10
<bean id="address" class="com.example.Address">
<property name="city" value="New York"/>
<property name="street" value="5th Avenue"/>
</bean>

<bean id="person" class="com.example.Person">
<property name="name" value="John Doe"/>//属性注入
<property name="address" ref="address"/> <!-- 引用注入 -->
</bean>
///ref 属性表示引用另一个 Bean。

基于注解

1
2
3
4
5
6
@Component public class Person {    
private String name;
@Autowired // 注解方式引用注入
private Address address;
/
/ getters and setters //... }
3.setter注入

Setter 注入 是一种通过提供 setter 方法来实现依赖注入的方式

Setter 注入通常用于将简单数据类型(如 Stringint)或引用类型注入到 Bean 中。在 XML 配置中使用 <property> 标签来定义:

<property> 标签的 name 属性表示要注入的属性名称,value 属性表示简单数据类型的值,ref 属性表示对其他 Bean 的引用。

1
2
3
4
5
<bean id="person" class="com.example.Person">
<property name="name" value="Jane Doe"/> <!-- 简单类型 -->
<property name="address" ref="address"/> <!-- 引用类型 -->
</bean>

1
2
3
4
5
6
7
8
9
10
11
@Component
public class Person {
private Address address;
public Address getAddress() {
return address;
}
@Autowired // 基于注解的 Setter 注入
public void setAddress(Address address) {
this.address = address;
}
}
4.构造器注入

2ac8de3631e734a2817de72f0e21076e

906d5345ed81c62f2ca79c2c87182043

37aa8dfa1862573eb20ad4710135f7a0

4.依赖注入方式选择

d7f4076b8a85df6389a465186386ba9f

5.依赖自动装配

IOC容器根据bean所依赖的资源在容器中自动查找并注入到bena的过程称为自动装配

1.按类型

2.按名称

3.按构造方法

4.不启用自动装配

d604dff0d944968b0f83ef5e0ed9148f

更改自动装配为按照类型

比如bookServcice依赖BookDao–我们不需要描述其关系 程序会自动根据名字找到对应类的bean

1
2
3
4
@Autowired // 基于注解的 Setter 注入 不需要`<property>来描述引用关系
public void setAddress(BookDao bookDao) {
this.bookDao = bookDao;
}

8.集合注入

1.数组

6956e79faab9804acbd6a47b860b0245

2.list

43ed876a380305fa263d981c7a984c74

3.Set
1
2
3
4
5
<p>
<set>

</set>
</p>
4.Map

ca7bbcd65d882884d7297dd75609d006

5.Properties

2fac8b9f2d57b4c92547d6a8235169a4

9.案例分析-bean注入

我们要拿到druid的连接

1.配置bean -注入参数

00252469c47ede33f309cf222de6d27a

我们就可以进行拿取

Set注入–进行注入传普通参数

10.加载properties文件

.properties 文件的结构和格式

  • 文件扩展名.properties
  • 基本格式:每行包含一个键值对,格式为 key=value

3259185201691e092710f1c19ad74f73

加载模式

e482c7cd8837d44f0b1684ae235838c7

11.容器

创建过程包括多个关键步骤,从加载配置文件、解析 Bean 定义,到创建和初始化 Bean,再到处理 Bean 的生命周期和容器的启动与关闭。

1.初始化容器

加载配置:Spring 容器从配置文件或注解中加载应用程序的配置。这些配置文件可以是 XML 文件、Java 配置类(使用 @Configuration 注解)、或通过其他方式如 YAML 文件等

88afb0e3109da0ceb049ce45b2ea81dd

创建 ApplicationContext 实例

ApplicationContext context = new ClassPathXmlApplicationContext(“applicationContext.xml”);

2.加载 Bean 定义

流程

解析配置:Spring 容器解析配置文件中的 Bean 定义(包括 Bean 的类名、构造器参数、属性值等),或扫描标注有 @Component@Service@Repository@Controller 等注解的类。

注册 Bean 定义:将解析得到的 Bean 定义注册到容器中。这包括 Bean 的名称、类型、作用域(如单例或原型)、依赖关系等信息。

创建 Bean 实例:根据 Bean 定义,Spring 容器创建 Bean 实例。Spring 使用反射机制调用 Bean 的构造函数(若是构造器注入)或默认构造函数。

依赖注入:在 Bean 实例创建后,Spring 容器根据 Bean 定义的依赖关系注入所需的依赖。这包括构造器注入、Setter 注入、字段注入等方式。

执行初始化方法

6441e65ec06a343800d7d118ec915bf7

处理 Bean 的生命周期

Bean 销毁:在容器关闭时,Spring 会销毁 Bean。->bean生命周期方法

容器的启动和关闭

启动容器:Spring 容器在创建完成后会启动并准备好处理应用程序的请求和交互。此时,所有的 Bean 都已经被创建和初始化完毕,可以使用它们提供的功能。

关闭容器:容器可以通过调用 ApplicationContextclose 方法来关闭。在关闭过程中,容器会执行 Bean 的销毁操作,释放资源,完成容器的清理工作。

3.容器接口分析

8d310da378a35e1bc86067389adf5c3b

4.BeanFactory初始化

ccb6cf9576f42e82a4190ea129f29b18

IOC DI Bean总结

aec185bb8dc978e09e302c6e168d677e

18abb534418dab1c205c287cd041975f

高级篇

12.注解

1.@声明为Bean

将一个类声明为Bean

86b345941eab120771d71166dbf2beb3

为了适配MVC->故此还有 @Controller @Service @Repository

2.纯注解配置

547c0427ec597688cc1c0ec42b75f8a2

8e5159d69c7dd85c33f019d20ac3be99

3.依赖注入

ede5cccd6b754d10bd7e5d2dfb4db131

f4d394d6384f8e1211f0b8775862a884

简单类型注入

2cde566b050a6d34fbe85be5502e6c50

4.加载properties文件

73301e328b26e40279005a10ac179d9b

![22a1fbb70d0306860f7d4ce5107158b5](G:\360MoveData\Users\nixg\Documents\Tencent Files\819429207\nt_qq\nt_data\Pic\2024-03\Ori\22a1fbb70d0306860f7d4ce5107158b5.png)

5.第三方bean管理例子

1.注册为bean

b515a9216fdb4b0cf13229af949a5107

2.加入管理-导入式

26e0669596d4e65ef8badc876869a654

扫描式

f1c3b59e56b859a8a04b94fa9cc65876

简单依赖注入

image-20240330164305805

—连接池配置

2f09511581cde24e3c7dfcb783c3dc4c

XML与注解 配置区别

b4f6fbbd9134d7813d17dec19a77cd8c

13.aop

1.Aop基础
1.原理

AOP的原理主要是通过使用代理对象来实现,在Spring中,AOP通过代理对象包装目标对象(被代理的对象),在代理对象中插入切面(aspect),实现对目标对象方法的增强。Spring AOP主要采用动态代理技术来实现AOP,其中包括JDK动态代理和CGLIB动态代理两种方式。

只有那些被 Spring 容器管理的 Bean(例如使用 @Component, @Service, @Repository, 或 @Controller 等注解的类,或者通过 XML 配置的 Bean),Spring 才能为它们创建代理对象。

代理对象的工作原理

  1. 代理对象的生成

    • 当容器启动并扫描到 @EnableAspectJAutoProxy 注解时,Spring AOP 框架会自动创建一个代理对象来包装原始的 UserServiceImpl 对象。
    • 根据 UserService 接口类型,Spring 将使用 JDK 动态代理 创建代理对象。
  2. 切面拦截方法

    • 代理对象接收到方法调用时,它会拦截调用并根据 AOP 配置执行相应的切面逻辑。
    • 在这个例子中,代理对象会在方法执行前和执行后调用 LoggingAspect 中的切面方法 logBeforeMethod()logAfterMethod()
  3. 方法调用的委托

    • 在执行完切面方法后,代理对象会将方法调用委托给实际的目标对象 (UserServiceImpl) 以完成核心业务逻辑4

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      @Configuration
      @EnableAspectJAutoProxy
      public class AppConfig {

      @Bean
      public UserService userService() {
      return new UserServiceImpl();
      }

      @Bean
      public LoggingAspect loggingAspect() {
      return new LoggingAspect();
      }
      }
      ---------------------------------
      @Aspect
      public class LoggingAspect {

      @Pointcut("execution(* com.example.service.UserService.*(..))")
      public void allUserServiceMethods() {}

      @Before("allUserServiceMethods()")
      public void logBeforeMethod() {
      System.out.println("LoggingAspect: Before method execution");
      }

      @After("allUserServiceMethods()")
      public void logAfterMethod() {
      System.out.println("LoggingAspect: After method execution");
      }
      }

再框架中添加了个拦截器->依据正则过滤

2.场景
  1. 日志记录:记录方法的调用、参数、返回值等信息。
  2. 事务管理:管理事务的开始、提交、回滚等操作。
  3. 安全性:实现权限控制、加密解密等安全相关功能。
  4. 性能监控:统计方法的执行时间、次数等性能指标。
3.连接点 切面 切入点 通知

切面 @Aspect

定义一个类为切面类 -通知和切入点

@Aspect注解标识一个类为切面类,Spring会在扫描到这个注解的类时,自动创建代理对象,并将切面逻辑织入到目标方法中。


通知

通知(Advice)是指切面(Aspect)中定义的在程序执行过程中插入的代码片段,它们是在指定的连接点(Join Point)处执行的行为逻辑。通知的主要作用是定义在目标方法执行前后、异常抛出时等特定情况下,应该执行的动作或逻辑。

@Before和@After

1
2
3
4
@Before("execution(* com.example.service.UserService.*(..))")
public void logBeforeMethod() {
System.out.println("Before method execution");
}

切入点

它用于定义通知(Advice)应用的具体位置(方法或类)。切入点决定了通知在哪些连接点(Join Point)上执行。切入点的主要作用是定义通知的应用范围,即通知应该在什么情况下、对哪些方法或类生效。

1
2
execution(* Calculator.divide(..))

-通知+切入点-哪个方法被aop进行管理-再什么时候运行


连接点

连接点是代码执行过程中的具体位置,可以被切面(Aspect)拦截并注入相应的增强逻辑。

当切入点表达式匹配到一个被 AOP 管理的类的方法时,这些方法就被认为是 连接点(Join Points)


@Before(“execution(* Calculator.add(..))”)

通知+切入点

代表了 Calculator类的add方法上执行增强操作

案例代码

1
2
3
4
5
6
7
8
9
10
public class Calculator {
//连接点
public int add(int a, int b) {
return a + b;
}

public int divide(int a, int b) {
return a / b;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Aspect//切面
@Component
public class LoggingAspect {

@Before("execution(* Calculator.add(..))")
///通知 切入点
public void logBeforeAdd(JoinPoint joinPoint) {
System.out.println("Logging before the add method is called");
}

@After("execution(* Calculator.divide(..))")
public void logAfterDivide(JoinPoint joinPoint) {
System.out.println("Logging after the divide method is called");
}
}
1
2
<aop:aspectj-autoproxy/>
<context:component-scan base-package="com.example"/>
2.Aop进阶
1.通知类型
  1. 前置通知(Before Advice):在目标方法执行之前调用通知方法。

    对应注解:@Before

  2. 后置通知(After Returning Advice):在目标方法成功执行之后调用通知方法。

    对应注解:@AfterReturning

  3. 后置异常通知(After Throwing Advice):在目标方法抛出异常后调用通知方法。

    1
    @AfterThrowing
  4. 后置通知(After (finally) Advice):无论目标方法是否成功执行,都会调用通知方法。

    1
    @After
  5. 环绕通知(Around Advice):在目标方法之前和之后执行通知方法,可以控制目标方法的执行过程。

    对应注解:@Around

  6. 引入通知(Introduction Advice):向现有的对象添加新的方法或属性。

  7. 织入通知(AspectJ Advice):在AOP中指定切点和通知的组合。


案例

  1. 前置通知(Before Advice):在用户进行结账操作时,检查用户的权限,确保用户具有足够的权限进行结账操作。

  2. 后置通知(After Returning Advice):在用户提交订单成功后,记录订单信息到日志中,或者发送确认邮件给用户。

  3. 后置异常通知(After Throwing Advice):如果订单处理过程中出现异常,比如库存不足,就发送通知给相关人员进行处理。

  4. 后置最终通知(After (finally) Advice):无论订单处理成功与否,都需要关闭数据库连接或释放其他资源。

  5. 环绕通知(Around Advice):在处理订单之前和之后记录订单处理时间,以及性能监控等功能。

  6. 引入通知(Introduction Advice):向订单类引入一个新的接口,比如可追踪变更历史的接口。

  7. 织入通知(AspectJ Advice):定义一个切面,将上述的各种通知类型织入到订单处理的流程中。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;

@Aspect
public class LoggingAspect {

@Before("execution(* com.example.service.OrderService.processOrder(..))")
public void beforeProcessOrder() {
    System.out.println("Before processing order...");
}

@AfterReturning(pointcut = "execution(* com.example.service.OrderService.processOrder(..))", returning = "result")
public void afterProcessOrder(Object result) {
    System.out.println("After processing order. Result: " + result);
}

@AfterThrowing(pointcut = "execution(* com.example.service.OrderService.processOrder(..))", throwing = "exception")
public void afterThrowingProcessOrder(Exception exception) {
    System.out.println("Exception thrown during order processing: " + exception.getMessage());
}

@After("execution(* com.example.service.OrderService.processOrder(..))")
public void afterProcessOrderCompletion() {
    System.out.println("After processing order completion...");
}

@Around("execution(* com.example.service.OrderService.processOrder(..))")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
    long startTime = System.currentTimeMillis();
    Object result = joinPoint.proceed();
    long endTime = System.currentTimeMillis();
    System.out.println("Order processing time: " + (endTime - startTime) + " milliseconds");
    return result;
}
2.通知顺序

当多个切面匹配到同一个类时,可以按照切入点所在类的字母顺序来决定通知方法的执行顺序

1.类字母

按照匹配到的切入点的类字母决定先后顺讯

1.前置-字母排名靠前的先执行

2.后置-字母排名靠前的后执行

2.@Order

可以使用@Order注解来控制多个通知方法的执行顺序。通过@Order注解可以指定通知方法的执行顺序,数值越小的通知方法优先执行。

具体步骤如下:

  1. 在定义通知方法的类上添加@Component或其他相关注解,使其成为Spring容器中的Bean。
  2. 在通知方法上添加@Order注解,并指定执行顺序的数值,数值越小优先级越高。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class MyAspect {

@Before("execution(* com.example.service.MyService.*(..))")
@Order(1)
public void beforeAdvice() {
// 前置通知的实现
}

@AfterReturning("execution(* com.example.service.MyService.*(..))")
@Order(2)
public void afterReturningAdvice() {
// 后置通知的实现
}
}

目标前-数字小的先执行

目标后-数字小的后执行

3.切入点

1.execution

b0484d9f1bbe10d4207d84882472bf29

e0a84ff1d8ccee34eb3876e12a63ef97

3ce5a2480e7eaf7f880d1624f4013487

2.注解aop开发

代码案例

首先,定义一个自定义注解CustomAnnotation

1
2
3
4
5
6
7
8
@Target(ElementType.METHOD)
//@Target(ElementType.METHOD):表示该注解可以用于方法上。这意味着CustomAnnotation只能用于方法的声明中,而不能用于其他地方,比如类、字段等。
@Retention(RetentionPolicy.RUNTIME)
///@Retention(RetentionPolicy.RUNTIME):表示该注解在运行时可以被反射读取。这意味着在运行时,我们可以通过Java的反射机制来获取并处理带有CustomAnnotation注解的方法。
public @interface CustomAnnotation {
String value();
}
这两个元数据的设定确保了CustomAnnotation注解的使用方式和其在程序运行期间的可见性,使其可以被AOP或其他机制所识别并进行相应的处理。

接着,在一个服务类中使用CustomAnnotation注解:

1
2
3
4
5
6
7
8
@Service
public class MyService {

@CustomAnnotation("Custom Annotation Example")
public void myMethod() {
// 方法实现
}
}

@CustomAnnotation("Custom Annotation Example")这个注解中,双引号里面的参数是注解的属性值。在定义自定义注解时,可以为注解定义属性,并在使用注解时为这些属性赋值。

在这个示例中,自定义注解CustomAnnotation定义了一个名为value的属性,因此在使用这个注解时,需要为value属性赋值。双引号中的内容 "Custom Annotation Example" 就是为value属性赋的具体数值。

当在切面类中通过反射获取带有CustomAnnotation注解的方法时,可以通过访问注解的属性值来获取这里传入的参数值,从而实现根据不同的属性值执行不同的逻辑处理。


然后,在切面类中使用@annotation注解匹配带有CustomAnnotation注解的方法:

1
2
3
4
5
6
7
8
9
10
11
@Aspect
public class MyAspect {

@Pointcut("@annotation(customAnnotation)")
public void annotatedMethod(CustomAnnotation customAnnotation) {}

@Before("annotatedMethod(customAnnotation)")
public void beforeAnnotatedMethod(CustomAnnotation customAnnotation) {
System.out.println("Before advice for method with custom annotation: " + customAnnotation.value());
}
}

这个切入点表达式 @annotation(customAnnotation) 的作用是匹配所有被 @CustomAnnotation 注解标记的方法。

@annotation(customAnnotation) 是 AspectJ 提供的一个语法,用来匹配方法级别的注解。这里,customAnnotation 是一个参数,它表示目标方法上使用的 @CustomAnnotation

同时annotatedMethod f方法的主要用处是可以在通知(Advice)方法中使用一个更清晰、易读的命名标识来引用切入点。


@Before("annotatedMethod(customAnnotation)") 表示在所有匹配 @annotation(customAnnotation) 切入点的方法执行之前,执行 beforeAnnotatedMethod 这个方法。

beforeAnnotatedMethod 方法将会在任何被 @CustomAnnotation 标记的方法执行之前运行。它可以访问这个自定义注解实例 customAnnotation,并获取该注解中的属性(例如 value() 方法)。

4.连接点

c9a1881e71612ce58b920331bae4d304

6ecb0785b0a6a59af4c08f919aeb6b9e

14.事务

0.事务的声明式管理

Spring 事务管理 依赖于 AOP 的思想和机制 来实现声明式事务管理。

在 Spring 中,当你使用 @Transactional 注解声明一个方法或类需要事务管理时,Spring AOP 会在运行时为这些方法或类创建一个代理对象(Proxy)。代理对象负责在方法调用之前开始事务,在方法调用之后提交或回滚事务。

Spring 使用 AOP 机制来织入事务管理代码。@Transactional 注解背后是通过 AOP 来实现的。在方法执行的前后,AOP 拦截器会自动进行事务管理操作(如事务开启、提交和回滚),而这些操作对业务代码是透明的。

这种方式被称为 声明式事务管理,它使用 @Transactional 注解指定哪些方法或类需要事务支持,不需要在业务代码中显式地编写事务处理逻辑。

切面

Spring 框架定义了一个 事务切面(Transaction Aspect),用来拦截被 @Transactional 标记的方法。这个切面是在方法调用前后执行相应的事务操作。

切入点

@Transactional

通知

通知逻辑

在方法执行前,事务切面拦截器会开启一个事务。

如果方法正常执行完成,则事务切面拦截器会提交事务。

如果方法执行过程中抛出了异常,事务切面拦截器会根据配置决定是回滚事务还是提交事务。

1.事务入门 -例子

假设我们有一个简单的用户管理系统,其中包括一个服务类 UserService,负责创建用户。我们希望在执行用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Service
public class UserService {

@Autowired
private UserRepository userRepository;

// 被 @Transactional 注解的方法将由 Spring AOP 管理
@Transactional
public void createUser(String username) {
// 保存用户
userRepository.save(new User(username));

// 模拟一个异常情况,测试事务回滚
if (username == null) {
throw new IllegalArgumentException("Username cannot be null");
}
}
}

----------
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Application.class, args);
UserService userService = context.getBean(UserService.class);

try {
userService.createUser(null); // 将会导致事务回滚
} catch (Exception e) {
System.out.println("Exception caught: " + e.getMessage());
}
}
}

2.事务管理员 事务协调员

例子事务

342dd9a76057ac1992637973417a63c8

9b888313da273ab68bfb514f19274a0b

3.事务配置

b6f65b886518d3736b235a310f1a6754

59d7f8499d23c0a2bcfdb780f1e00ea3

4.事务加入-规则

03f05df2aa222c9fb3abd3efb44ef699

例子

5dde408db43ac7d09f859c76ed257c91

fba20ae7cf0e8f32ff7aa3de0fb42e42

如图-都加入同一事务-一旦有异常-日志也会回滚

9972f6f167883d144abc515733c62536

5f99a409d1ff453639c1d9cc7aa2fcf2

解决

cf8e05d7e9f071a8464d78b45eab2ae5

事务传播行为

fa818038f3cf6f80cf14608cec80c598

0.1 Spring 整合mybatis

11abe4ba3472982e3b1b217edb94c96e

->将该注册为Bean

e038210f851e6d054f2de5a40605d29c

eb16bf7c1655375464b67dcd0194534b

如图–以上配置全部进行修改

241a0676f37e4b12a5eef8cdc8e7fe35

79a9256b4115e3f74adcb4dfc1db5f30

3ba320445dccb955e30224a3bbee6193

收纳

7ff11a9112a3615d298943a68b54e9d6

55cab2a9fb9c8def31581e7f33474ad0

0.2 Spring整合测试

设定类运行器

整合spring运行器

6981732edb98c9b849ff758c5c4053c1


Spring框架
http://example.com/2024/09/10/Spring复习/
作者
John Doe
发布于
2024年9月10日
许可协议