【Spring】一文带你彻底搞懂IOC、AOP

目录

首先简单了解一下什么是spring框架

什么是IOC?

什么是依赖注入(DI)?

控制反转和依赖注入又有什么关系?

AOP是什么?

SpringAOP的实现

说了这么多抽象概念,举个实例方便理解


首先简单了解一下什么是spring框架

Spring是一个轻量级的框架,通过IOC达到松耦合的目的,使用AOP可以分离应用业务逻辑和系统服务进行内聚性的开发(不改变原有逻辑的同时对业务进行增强),不够配置各种组件的时候相对繁琐,因此后面演进除了SpringBoot框架。

使用Spring之后可以让我们不用过多关注底层的技术细节,而是更加关注业务上的逻辑的实现

什么是IOC?

Inverse of Control ------控制反转,是一种思想,这种控制反转的思想主要指的是将对象的创建、组装、管理都从代码中自己实现转移到了外部容器中来帮我们进行实现。在传统的开发方式当中,我们直接手写代码去主动创建和组装对象(将对象所需要的属性注入);在IOC思想中,这个过程被反转了,即由外部容器负责创建和管理对象。

在IOC中,我们将应用程序设计成一个个的组件,每个组件提供一定的功能,并通过接口与其他组件进行交互。通过IOC容器,我们可以把这些组件注册并配置,容器负责根据配置信息创建组件实例,并维护它们之间的依赖关系和生命周期。

什么是依赖注入(DI)?

依赖注入指的是将对象所依赖的其他对象(即依赖)注入到当前对象之中,而不是由对象自己去创建或查找他要依赖的对象

在传统的开发方式当中,对象通过直接创建或查找依赖的对象来获取所需的依赖。

而在DI中,对象不负责创建或查找的过程,而是通过构造函数、工厂方法或属性的方式接收依赖对象。这样,依赖对象的创建和管理由外部容器负责,对象只需要专注于自身的功能实现。

依赖注入可以通过构造函数注入、setter方法注入或接口注入等方式来实现。

控制反转和依赖注入又有什么关系?

在Spring中,IOC是一种设计思想,而DI是IOC的一种具体的实现方式。实际上DI是IOC的实现技术中的一种,它通过依赖注入来实现控制反转。

IOC思想提倡将对象的设计与创建交给外部容器,并通过xml配置文件、注解、或者Java Config等方式来描述和配置对象之间的依赖关系,

Spring框架提供了一个IOC容器,即ApplicationContext,它就实现类DI的功能。在Spring中,我们可以使用XML配置文件、注解、或Java Config等方式来描述和配置对象之间的依赖关系,然后Spring容器会根据配置信息来创建对象并完成注入。

AOP是什么?

AOP即面向切面编程,可以将那些与业务不想关但是很多业务都需要调用的代码提取出来,思想就是不侵入原有代码的同时对功能进行增强。

AOP通过定义一个切面,切面可以横切到应用程序的多个模块中,并添加增强的行为。这样我们就可以将通用的功能逻辑从业务逻辑中解耦出来,提高代码的可维护性和重用性。

切面由切点(Pointcut)和通知(Advice组成)

  1. 切点定义的是在程序中的哪个位置进行拦截,通常使用表达式来指定匹配的连接点(join Point)
  2. 通知定义了要在切点执行的代码,可以在切点之前、切点之后或者周围执行特定的逻辑
  3. 常见的通知类包括前置通知(Before Advice)、后置通知(After Advice)、异常通知(After Throwing Advice)和环绕通知(Around Advice)等。

SpringAOP的实现

SpringAOP是基于动态代理实现的,动态代理有两种,一种是JDK动态代理,另一种是Cglib动态代理

jdk动态代理是利用反射的原理来实现的,需要调用反射包下的Proxy类的newProxyInstance方法来返回代理对象,这个方法中有三个参数,分别是用于加载代理类的类加载器被代理类实现的接口的class数组用于增强方法的InvocatioHandler实现类

cglib动态代理原理是利用asm开源包来实现的,是把被代理类的class文件加载进来,通过修改它的字节码生成子类来处理

jdk动态代理要求代理类必须有实现的接口,生成的动态代理类会和代理类实现同样的接口,cglib则,生成的动态代理类会继承被代理类。Spring默认使用jdk动态代理,当要被代理的类没有实现任何接口的时候采用cglib

说了这么多抽象概念,举个实例方便理解

这是一个实现统计controller路由访问次数的代码

java 复制代码
package com.qcby.springbootdemo.aop;
 
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
 
import java.util.HashMap;
import java.util.Map;
 
@Aspect
@Component
public class MethodCount {
 
    // 声明一个Map类型的对象,用于存储方法调用次数
    private Map<String, Integer> count = new HashMap<>();
 
    // 定义切点,表示拦截com.qcby.springbootdemo.Controller包下的所有方法
    @Pointcut("execution(* com.qcby.springbootdemo.Controller.*.*(..))")
    public void count() {
        System.out.println("切点方法执行");   //声明切点,并不会实际调用
    }
 
    // 环绕通知,在目标方法执行前后进行拦截
    @Around("count()")
    public Object methodExec(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("方法执行前");
 
        // 获取方法签名信息
        Signature signature = pjp.getSignature();
        String name = signature.getName();
        System.out.println(name);   //-----login
        System.out.println(signature.getDeclaringTypeName());  //---com.qcby.springbootdemo.Controller.LoginController
 
        // 获取方法参数
        Object[] args = pjp.getArgs();
        for (Object arg : args) {
            System.out.println("aop arg:" + arg);
        }
 
        Object result = null;
        System.out.println(signature.toLongString());
        System.out.println();
 
        // 统计方法调用次数,使用方法的签名作为key
        String key = signature.toLongString();
        count.put(key, count.getOrDefault(key, 0) + 1);
 
        // 执行目标方法
        result = pjp.proceed();
        System.out.println("方法执行后");
 
        // 输出访问路由次数
        System.out.println(count);
        return result;
    }
}
复制代码
@Aspect,表示这个类为切面类

// 定义切点,表示拦截com.qcby.springbootdemo.Controller包下的所有方法

@Pointcut("execution(* com.qcby.springbootdemo.Controller.*.*(..))")

public void count() {

System.out.println("切点方法执行"); //声明切点,并不会实际调用

}

复制代码
在拦截count()方法这个切点之后,对原方法------------Controller中访问路由的方法,在执行期间进行环绕,在methodExec方法中写明具体的环绕逻辑
// 环绕通知,在目标方法执行前后进行拦截
@Around("count()")
相关推荐
utmhikari37 分钟前
【架构艺术】Go语言微服务monorepo的代码架构设计
后端·微服务·架构·golang·monorepo
蜡笔小新星39 分钟前
Flask项目框架
开发语言·前端·经验分享·后端·python·学习·flask
计算机学姐43 分钟前
基于Asp.net的驾校管理系统
vue.js·后端·mysql·sqlserver·c#·asp.net·.netcore
欢乐少年19043 小时前
SpringBoot集成Sentry日志收集-3 (Spring Boot集成)
spring boot·后端·sentry
夏天的味道٥4 小时前
使用 Java 执行 SQL 语句和存储过程
java·开发语言·sql
冰糖码奇朵5 小时前
大数据表高效导入导出解决方案,mysql数据库LOAD DATA命令和INTO OUTFILE命令详解
java·数据库·sql·mysql
好教员好5 小时前
【Spring】整合【SpringMVC】
java·spring
浪九天6 小时前
Java直通车系列13【Spring MVC】(Spring MVC常用注解)
java·后端·spring
小斌的Debug日记7 小时前
框架基本知识总结 Day16
redis·spring
堕落年代7 小时前
Maven匹配机制和仓库库设置
java·maven