侧边栏壁纸
博主头像
BinArTale's Blog 博主等级

行动起来,活在当下

  • 累计撰写 6 篇文章
  • 累计创建 21 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

Spring 中的IoC 和 AOP

binbin
2025-04-04 / 0 评论 / 0 点赞 / 25 阅读 / 0 字

1. IoC(控制反转)

IoC是一种思想,描述的是Java开发中对象的创建和管理

  • 传统方式:如果在类A中要使用B类的对象,需要在A类 new 一个B对象

  • 使 IoC 思 想的开发方式:不通 new 来创建对象,而是通过IoC容器来帮助我们实例化对象,需要哪个对象就去IoC容器中取哪个对象。

优点:

  • 对象之间的耦合度降低

  • 资源管理变得容易

DI(Dependency Injection) IoC是一种具体实现方式 ,IoC 说法太过广泛,所以使DI来代表这种思想的具体实现。

2. AOP(面向切面编程)

AOP(Aspect Oriented Programming)的目的是通过将横切关注点从业务中抽离出来,通过动态代理、字节码操作等技术,实现代码的复用和解耦,提高代码的可维护性和可扩展性。

这里举个例子,点外卖:

一般点外卖的流程如下:

  1. 检查钱包余额

  2. 选菜

  3. 下单

  4. 支付

  5. 发送短信通知

如果有10个下单的方法,就要写诗词检查余额和发送短信通知,代码重复且麻烦

所以就用AOP 把检查余额和发送短信通知抽出来,定义成一个规则

  • 在下单之前自动检查余额

  • 在下单之后自动发送短信

你就只需要写主逻辑,其他交给规则自动处理

AOP中的关键术语:

  1. 横切关注点(cross-cutting concerns):多个类或对象中的公共行为(如日志记录,事物管理,权限控制、接口限流等)

  2. 切面(Aspect):横切关注点的集合,一个切面一个类,切面中可以定义多个通知,用来实现具体的功能,就是在这里例子中,外卖中的检查余额和发送短信是一个切面

  3. 切点(Pointcut):也就是定义 “在哪里” 应用切面的逻辑,在这个例子中就是每次下单之前和每次下单之后

  4. 通知(Advice):通知就是切面在某个连接点要执行的操作,他主要定义“做什么”和什么时候做,通知分为前置通知Before)、后置通知After)、返回通知AfterReturning)、异常通知AfterThrowing)和环绕通知Around)。前四种通知都是在目标方法的前后执行,而环绕通知可以控制目标方法的执行过程。在这个例子中,下单前检查余额是前置通知。

  5. 织入(Weaving):把切面逻辑插入到主逻辑的过程,也就是将通知应用到切点匹配的连接点上。常见的织入类型有两种,分别是编译期织入(Compile-Time Weaving 如:AspectJ)和运行期织入(Runtime Weaving 如:AspectJ、Spring AOP)。在这个例子中就是外卖平台自动在下单流程中加入检查余额和发送短信。

具体应用:

这里以全局异常处理为例,没使用AOP之前,我们要对全局的所有异常进行统一的管理是很困难的,因为很多时候无法预知异常发生的地方,而使用全局异常处理器,我们就可以定义切面,当某种异常发生的时候,就被全局异常处理器捕获,然后做统一的处理,也就是只管跑出异常,不用try-catch,这可以大大减少重复代码的编写,更专注于业务逻辑。

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
 
    @ExceptionHandler(BusinessException.class)
    public BaseResponse<?> businessExceptionHandler(BusinessException e) {
        log.error("businessException: " + e.getMessage(), e);
        return ResultUtils.error(e.getCode(), e.getMessage());
    }
 
    @ExceptionHandler(RuntimeException.class)
    public BaseResponse<?> runtimeExceptionHandler(RuntimeException e) {
        log.error("businessException: " + e.getMessage(), e);
        return ResultUtils.error(ErrorCode.SYSTEM_ERROR, e.getMessage());
    }
}

AOP常见的应用场景:

  • 日志记录:自定义日志记录注解,利用AOP,一行代码即可实现日志记录

  • 性能统计:利用 AOP 在目标方法的执行前后统计方法的执行时间,方便优化和分析。

  • 事物管理:@Transactional 注解可以让 Spring 为我们进行事务管理比如回滚异常操作,免去了重复的事务管理逻辑。@Transactional注解就是基于 AOP 实现的。

  • 权限控制:自定义权限校验注解可以配合AOP,在方法执行前判断用户是否具备所需要的权限

  • 接口限流:利用AOP在目标方法执行前通过限流算法对请求进行限流处理

  • 缓存管理:在方法执行前后进行缓存的读取和更新。

AOP的实现方式:

AOP 是通过动态代理实现的,代理的方式有两种:JDK动态代理和CGLIB代理

  1. JDK动态代理:JDK动态代理是基于接口的,只能代理实现了接口的类。

使用JDK动态代理时,Spring AOP 会创建一个代理对象, 该代理对象实现了目标对象的所有接口,并在方法调用前后插入横切逻辑。他只需要依赖 JDK 自带的 java.lang.reflect类,不需要额外的库,但是只能代理接口,不能代理类本身。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
 
interface Human {
    void eat();
}
 
class Student implements Human {
    @Override
    public void eat() {
        System.out.println("学生吃学生餐");
    }
}
class MyIntercptor implements InvocationHandler {
    private Object target;
 
    public MyIntercptor(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before:" + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("after:" + method.getName());
        return result;
    }
}
public class test {
    public static void main(String[] args) {
        Student student = new Student();
 
        Human proxyInstance = (Human) Proxy.newProxyInstance(
                student.getClass().getClassLoader(),
                student.getClass().getInterfaces(),
                new MyIntercptor(student)
        );
        proxyInstance.eat();
    }
}
  1. CGLIB代理是基于继承实现的,可以代理没有实现接口的类

使用CGLB代理时,Spring AOP 会生成目标类的子类,并在方法调用前后插入横切逻辑。它可以代理没有实现接口的类,灵活性更高,但是需要依赖CGLIB库,创建代理对象的开销相对更大。

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
 
import java.lang.reflect.Method;
 
class Student {
    public void eat() {
        System.out.println("学生吃学生餐");
    }
}
class MyIntercptor implements MethodInterceptor {
 
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("before:" + method.getName());
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("after:" + method.getName());
        return result;
    }
}
 
public class test {
    public static void main(String[] args) {
        // 工具类
        Enhancer enhancer = new Enhancer();
        // 设置父类
        enhancer.setSuperclass(Student.class);
        // 设置回调函数
        enhancer.setCallback(new MyIntercptor());
        // 创建子类对象代理
        Student student = (Student) enhancer.create();
        student.eat();
    }
}

0

评论区