0%

SprintBoot - IOC|AOP

Spring boot-IOC|AOP

IOC(控制反转)

IOC 是一种设计原则,指的是控制权的反转。它的核心思想是将对象的创建和管理的控制权从程序中转移给外部容器或框架。这意味着,传统上程序中类之间的依赖关系是由程序代码手动创建和管理的,而在使用IOC时,框架或容器负责这些对象的创建和管理。通过IOC,程序的控制流程被反转,程序员不再显式地控制对象的创建和生命周期,而是交由容器(如Spring容器)来管理。

DI(依赖注入)

DI 是实现IOC的一种方式,是一种将依赖关系(即对象之间的依赖)注入到对象中的设计模式。DI 通过将一个对象的依赖(例如,它需要使用的服务、数据库连接等)传递给它的构造函数、字段或方法,来实现对象间的解耦。DI 是IOC的具体实现手段之一。

过程

1、调用程序:不指定对象如何生成,通过标准(接口)使用。
2、配置文件:配置文件中需要注明哪些bean被注入,以及引用关系。
3、程序执行:由Spring容器通过配置文件生成所有的bean对象,从容器中取出对象进行执行。

举个例子:

  • 在传统的编程方式中,一个类A需要依赖类B,这时类A会自己创建类B的实例:

    javaCopy codepublic class A {
    private B b = new B(); // 类A自己创建类B的实例
    }
  • 而使用IOC和DI的方式,类A不会自己创建类B的实例,而是由外部容器(比如Spring)来将B的实例注入到类A中:

    javaCopy codepublic class A {
    private B b;

    // 通过构造函数注入B
    public A(B b) {
    this.b = b;
    }
    }

    在Spring中,Spring容器会创建类A和类B的实例,并负责将B的实例注入到类A中,完成依赖注入。这样,类A不再直接依赖于类B的具体实现,而只依赖于接口或抽象类,从而实现了更松散的耦合。

AOP(面向切面编程)

其编程思想是把散布于不同业务但功能相同的代码从业务逻辑中抽取出来,封装成独立的模块,这些独立的模块被称为切面,切面的具体功能方法被称为切入点。
在业务逻辑执行过程中,AOP会把分离出来的切面和切入点动态切入到业务流程中,这样做的好处是提高了功能代码的重用性和可维护性。

核心概念:

  1. 切面(Aspect):横切关注点的模块化,它包含了代码的增强逻辑,比如日志、事务等。一个切面通常会定义在特定的点(切入点)插入逻辑。
  2. 连接点(Join Point):程序执行过程中可以插入切面的位置,通常是方法的调用、方法的执行、构造函数的调用等。
  3. 通知(Advice):切面所执行的具体操作,表示在连接点执行的代码。通知可以分为以下几种类型:
    • 前置通知(Before):在目标方法执行前执行。
    • 后置通知(After):在目标方法执行后执行(不管目标方法是否抛出异常)。
    • 返回通知(AfterReturning):目标方法执行成功后执行。
    • 异常通知(AfterThrowing):目标方法抛出异常后执行。
    • 环绕通知(Around):包围目标方法的执行,允许在方法前后做增强,并且可以决定是否执行目标方法。
  4. 切入点(Pointcut):定义在哪些连接点应用通知。通常是通过方法签名、注解等方式来指定的。
  5. 目标对象(Target Object):被增强的对象,也叫做被代理的对象。
  6. 代理(Proxy):AOP 会为目标对象创建一个代理对象,代理对象会在目标对象方法执行之前或之后插入增强的代码。

使用方法

1.注解定义切面类

添加切面和通知的注解
@Aspect – 定义切面类的注解
通知类型(注解的参数是切入点的表达式)
@Before – 前置通知
@AfterReturning – 后置通知
@Around – 环绕通知
@After – 最终通知
@AfterThrowing – 异常抛出通知

@Component
@Aspect
public class MyAspectXml {
// 定义通知
@Before("execution(public * com.zhong.aopdemo.CustomDaoImpl.save(..))")
public void log() {
System.out.println("记录日志...");
}
}

2. 修改配置文件pom.xml,加入扫描生成相关的bean

示例代码

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

<context:component-scan base-package="com.zhong.aopdemo" />
<!-- 在配置文件中开启自动代理 -->
<aop:aspectj-autoproxy />
</beans>

常用依赖项说明

<aop:aspectj-autoproxy>

它会扫描容器中的 bean,检查是否有与切面相关的注解或配置,如果有,则会自动为这些 bean 创建代理对象,并将切面逻辑织入到代理对象中。

spring-context

spring-context是Spring框架的核心模块之一,提供了与Spring IoC容器(控制反转容器)相关的功能。它包括了创建、管理、注入bean(对象)的功能。Spring容器用于管理应用程序的生命周期,提供了依赖注入(DI)等功能,是开发Java应用的基础模块。

作用
  • 提供Spring核心的功能,如ApplicationContextBeanFactory、事件发布、环境配置等。
  • 支持XML和注解配置方式。
  • 提供了对组件的生命周期管理(例如,单例bean、原型bean等)。
依赖示例
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.28</version>
</dependency>
spring-beans

spring-beans模块提供了Spring的基础Bean管理功能,允许我们在Spring容器中创建、初始化、管理和销毁Java对象。它是Spring框架中最基础的模块,几乎所有的Spring应用都需要这个模块。

作用
  • 处理bean的创建、依赖注入和生命周期管理。
  • 负责bean的配置和管理,通常与spring-context模块一起使用。
依赖示例
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.28</version>
</dependency>
spring-aop

spring-aop模块是Spring框架的面向切面编程(AOP)模块,它为Spring应用提供了AOP支持,允许通过声明性事务管理、日志记录等功能来增强应用的功能。

作用
  • 提供面向切面编程(AOP)的支持,使得可以在不修改源代码的情况下,向现有代码中添加功能(如日志记录、事务管理等)。
  • 允许通过切面(Aspect)定义横切关注点(如日志、性能监控等),然后应用于目标方法。
依赖示例
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.28</version>
</dependency>
spring-aspects

spring-aspects模块包含了Spring AOP的相关功能,并且在实际使用时需要spring-aop模块。它包含了Spring框架所需的切面功能的具体实现,如切面(Aspect)定义、增强(Advice)等。

作用
  • 这是一个专门用于与AOP相关的库,通常用于处理切面(Aspect)相关的功能,允许开发者将事务管理、日志等功能应用到Spring beans中。
  • 通过@Aspect注解或配置文件配置切面和通知(Advice),以控制程序的某些行为。
依赖示例
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.28</version>
</dependency>
junit

junit是一个流行的Java单元测试框架,广泛用于测试Java应用的功能。JUnit提供了一种规范化的方式来编写和运行测试用例,确保代码的正确性和稳定性。JUnit用于编写测试类,测试项目中的功能模块。

作用
  • 用于编写自动化的单元测试,帮助开发者验证代码功能。
  • 提供了断言(assertions)功能来验证方法的行为是否符合预期。
  • 常与其他测试框架结合使用,如Mockito。
依赖示例
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope> <!-- 说明这个依赖只在测试环境中需要 -->
</dependency>

常用的4种创建Bean的注解:

1. @Component
  • 作用:通用的组件注解,用于标识当前类是一个 Spring 的组件。
  • 功能:Spring 会将标注了 @Component 的类识别为一个 Bean,并将其实例化并加入到 Spring 的容器中。
  • 适用场景:当某个类不属于特殊的功能分类(如控制器、服务层、数据访问层)时,可以使用 @Component

示例

@Component
public class MyComponent {
public void doSomething() {
System.out.println("执行组件逻辑...");
}
}

2. @Controller
  • 作用:用于标注控制层组件,专门处理用户请求。
  • 功能
    • 在 MVC 架构中,@Controller 是用来定义控制器的,负责处理 HTTP 请求。
    • 配合 @RequestMapping 注解定义 URL 映射。
  • 适用场景:适用于处理前端发来的请求(如 Web 应用开发中的 RESTful API)。

示例

@Controller
public class MyController {
@RequestMapping("/hello")
public String sayHello() {
return "Hello, Spring!";
}
}

3. @Service
  • 作用:用于标注服务层组件,表示该类主要负责业务逻辑。

  • 功能

    • 通常与业务逻辑相关的类会使用 @Service 注解。
    • @Component 功能相同,但语义化更强,表明这是一个“服务层”组件。
  • 适用场景:适用于包含核心业务逻辑的类。

示例

@Service
public class MyService {
public String getGreeting() {
return "欢迎来到服务层!";
}
}

4. @Repository
  • 作用:用于标注数据访问层(DAO)的组件,专门处理数据库操作。
  • 功能
    • 表示当前类是用于与数据库交互的类。
    • 可以将数据访问层的异常转换为 Spring 的统一异常(DataAccessException)。
  • 适用场景:适用于与数据库直接交互的类,如 DAO(Data Access Object)类。

示例

@Repository
public class MyRepository {
public void save() {
System.out.println("保存数据到数据库...");
}
}

总结
注解 作用 典型使用场景
@Component 通用组件注解 非特定功能分类的组件类
@Controller 控制器,处理用户请求 Web 层,负责处理 HTTP 请求
@Service 服务层,处理业务逻辑 服务层,包含核心业务逻辑
@Repository 数据访问层,处理数据库操作 数据层,与数据库交互的类

💡**@Bean注解**是用在配置类(@Configuration)中声明一个Bean的,通常用在配置类中的方法上,将方法的返回值对象注入到Spring的ioc容器中。通过bean注解,我们可以创建和配置Bean的初始化过程,包括Bean的名称、作用域、依赖关系等。

JoinPoint 类

JoinPoint 是 AOP 提供的一个接口,用于表示连接点(Join Point)。通过它可以获取以下信息:

  • 代理对象:即调用切面逻辑的对象。
  • 被代理对象:即实际被拦截的目标对象。
  • 方法签名:即被拦截方法的详细信息(如方法名、参数类型、返回类型等)。
  • 方法参数:目标方法所接收到的参数。
主要方法
  1. Object getTarget()

    • 返回被代理的目标对象。
    • 例如:通过此方法可以访问原始对象以执行额外逻辑。
  2. Object[] getArgs()

    • 返回目标方法的参数列表(作为一个 Object 数组)。
    • 可以通过此方法获取调用方法时传递的具体参数。
  3. Signature getSignature()

    • 返回当前执行方法的签名(Signature 是一个接口,常用实现类是 MethodSignature)。

    • 通过签名可以获取方法的详细信息,例如方法名、参数类型、返回类型等。

    • 示例:

      javaCopy codeSignature signature = joinPoint.getSignature();
      System.out.println("方法名: " + signature.getName());
  4. String getKind()

    • 返回当前连接点的类型,例如是方法执行、异常抛出等。
  5. Object getThis()

    • 返回当前代理对象本身。
适用范围

JoinPoint 通常用于以下通知:

  • 前置通知@Before):在目标方法执行之前运行。
  • 后置通知@After):在目标方法执行之后运行。
  • 异常通知@AfterThrowing):在目标方法抛出异常后运行。
  • 最终通知@AfterReturning):在目标方法正常返回后运行。

ProceedingJoinPoint 类

ProceedingJoinPointJoinPoint 的子接口,它在 JoinPoint 的基础上增加了一个非常重要的方法:proceed()。它仅在环绕通知@Around)中使用。

核心方法
  1. Object proceed()

    • 作用:启动目标方法的执行。
    • 环绕通知逻辑中通常会调用此方法来执行被拦截的目标方法。
    • 可以将其看作是 AOP 的核心,用于控制目标方法的实际调用。
  2. Object proceed(Object[] args)

    • 作用:与 proceed() 类似,但允许传递新的参数来改变目标方法的行为。

    • 通过修改参数列表,可以改变目标方法所接收到的参数。

    • 示例:

      java


      Copy code
      Object result = proceedingJoinPoint.proceed(newArgs);
环绕通知的使用

环绕通知包含前置、目标方法执行、后置通知的组合,通过 proceed() 方法控制目标方法的执行。以下是环绕通知的典型实现:

javaCopy code@Around("execution(* com.example.service.*.*(..))")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("前置通知:方法执行前");

// 获取方法名
String methodName = pjp.getSignature().getName();
System.out.println("方法名:" + methodName);

// 执行目标方法
Object result = pjp.proceed();

System.out.println("后置通知:方法执行后");

return result;
}