云计算百科
云计算领域专业知识百科平台

Java 依赖注入、控制反转与面向切面:面试深度解析

🤟致敬读者

  • 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉

📘博主相关

  • 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息

文章目录

    • Java 依赖注入、控制反转与面向切面:面试深度解析
      • 一、控制反转(IoC)与依赖注入(DI)
        • 1. 核心概念解析
        • 2. 高频面试题
      • 二、面向切面编程(AOP)
        • 1. 核心概念解析
        • 2. 高频面试题
      • 三、综合实战与设计模式
        • 1. 典型应用场景
        • 2. 高频面试题
      • 四、最佳实践与避坑指南
      • 五、面试深度问题

📃文章前言

  • 🔷文章均为学习工作中整理的笔记。
  • 🔶如有错误请指正,共同学习进步。

Java 依赖注入、控制反转与面向切面:面试深度解析

在这里插入图片描述

一、控制反转(IoC)与依赖注入(DI)

1. 核心概念解析

#mermaid-svg-aWCuecx1SlpOvfh7 {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-aWCuecx1SlpOvfh7 .error-icon{fill:#552222;}#mermaid-svg-aWCuecx1SlpOvfh7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-aWCuecx1SlpOvfh7 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-aWCuecx1SlpOvfh7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-aWCuecx1SlpOvfh7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-aWCuecx1SlpOvfh7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-aWCuecx1SlpOvfh7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-aWCuecx1SlpOvfh7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-aWCuecx1SlpOvfh7 .marker.cross{stroke:#333333;}#mermaid-svg-aWCuecx1SlpOvfh7 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-aWCuecx1SlpOvfh7 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-aWCuecx1SlpOvfh7 .cluster-label text{fill:#333;}#mermaid-svg-aWCuecx1SlpOvfh7 .cluster-label span{color:#333;}#mermaid-svg-aWCuecx1SlpOvfh7 .label text,#mermaid-svg-aWCuecx1SlpOvfh7 span{fill:#333;color:#333;}#mermaid-svg-aWCuecx1SlpOvfh7 .node rect,#mermaid-svg-aWCuecx1SlpOvfh7 .node circle,#mermaid-svg-aWCuecx1SlpOvfh7 .node ellipse,#mermaid-svg-aWCuecx1SlpOvfh7 .node polygon,#mermaid-svg-aWCuecx1SlpOvfh7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-aWCuecx1SlpOvfh7 .node .label{text-align:center;}#mermaid-svg-aWCuecx1SlpOvfh7 .node.clickable{cursor:pointer;}#mermaid-svg-aWCuecx1SlpOvfh7 .arrowheadPath{fill:#333333;}#mermaid-svg-aWCuecx1SlpOvfh7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-aWCuecx1SlpOvfh7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-aWCuecx1SlpOvfh7 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-aWCuecx1SlpOvfh7 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-aWCuecx1SlpOvfh7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-aWCuecx1SlpOvfh7 .cluster text{fill:#333;}#mermaid-svg-aWCuecx1SlpOvfh7 .cluster span{color:#333;}#mermaid-svg-aWCuecx1SlpOvfh7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-aWCuecx1SlpOvfh7 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

传统编程

对象主动创建依赖

IoC/DI

容器管理依赖

对象被动接收依赖

  • 控制反转(IoC):

    • 设计原则:将对象创建和绑定的控制权从应用程序代码转移到外部容器
    • 实现方式:依赖查找(DL)和依赖注入(DI)
    • 核心思想:“好莱坞原则” – 不要调用我们,我们会调用你
  • 依赖注入(DI):

    • IoC的具体实现方式
    • 三种注入方式:// 1. 构造器注入(推荐)
      public class UserService {
      private final UserRepository repo;

      @Autowired
      public UserService(UserRepository repo) {
      this.repo = repo;
      }
      }

      // 2. Setter注入
      public class OrderService {
      private PaymentGateway gateway;

      @Autowired
      public void setGateway(PaymentGateway gateway) {
      this.gateway = gateway;
      }
      }

      // 3. 字段注入(不推荐)
      public class ProductService {
      @Autowired
      private InventoryService inventory;
      }

2. 高频面试题

Q1:IoC和DI的区别与联系?

  • 区别:
    • IoC是设计原则(思想层面)
    • DI是实现模式(技术层面)
  • 联系:
    • DI是IoC最常用的实现方式
    • Spring框架通过DI实现IoC容器

Q2:构造器注入为什么被推荐?

  • 不可变性:依赖可声明为final
  • 空安全:对象创建时依赖必须就绪
  • 测试友好:无需容器即可测试
  • 循环依赖检测:启动时即可发现循环依赖
  • 线程安全:避免并发修改问题
  • Q3:Spring如何解决循环依赖?

    • 三级缓存机制:
    • 一级缓存:完整Bean(singletonObjects)
    • 二级缓存:早期暴露Bean(earlySingletonObjects)
    • 三级缓存:Bean工厂(singletonFactories)
    • 解决流程:

      #mermaid-svg-SnusREzLVOPr6h4l {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SnusREzLVOPr6h4l .error-icon{fill:#552222;}#mermaid-svg-SnusREzLVOPr6h4l .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SnusREzLVOPr6h4l .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-SnusREzLVOPr6h4l .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SnusREzLVOPr6h4l .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SnusREzLVOPr6h4l .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SnusREzLVOPr6h4l .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SnusREzLVOPr6h4l .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SnusREzLVOPr6h4l .marker.cross{stroke:#333333;}#mermaid-svg-SnusREzLVOPr6h4l svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SnusREzLVOPr6h4l .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-SnusREzLVOPr6h4l text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-SnusREzLVOPr6h4l .actor-line{stroke:grey;}#mermaid-svg-SnusREzLVOPr6h4l .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-SnusREzLVOPr6h4l .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-SnusREzLVOPr6h4l #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-SnusREzLVOPr6h4l .sequenceNumber{fill:white;}#mermaid-svg-SnusREzLVOPr6h4l #sequencenumber{fill:#333;}#mermaid-svg-SnusREzLVOPr6h4l #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-SnusREzLVOPr6h4l .messageText{fill:#333;stroke:#333;}#mermaid-svg-SnusREzLVOPr6h4l .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-SnusREzLVOPr6h4l .labelText,#mermaid-svg-SnusREzLVOPr6h4l .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-SnusREzLVOPr6h4l .loopText,#mermaid-svg-SnusREzLVOPr6h4l .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-SnusREzLVOPr6h4l .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-SnusREzLVOPr6h4l .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-SnusREzLVOPr6h4l .noteText,#mermaid-svg-SnusREzLVOPr6h4l .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-SnusREzLVOPr6h4l .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-SnusREzLVOPr6h4l .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-SnusREzLVOPr6h4l .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-SnusREzLVOPr6h4l .actorPopupMenu{position:absolute;}#mermaid-svg-SnusREzLVOPr6h4l .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-SnusREzLVOPr6h4l .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-SnusREzLVOPr6h4l .actor-man circle,#mermaid-svg-SnusREzLVOPr6h4l line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-SnusREzLVOPr6h4l :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

      BeanA

      容器

      BeanB

      创建A,放入三级缓存

      需要B

      创建B

      需要A(从三级缓存获取早期A)

      完成创建,放入一级缓存

      完成创建,放入一级缓存

      BeanA

      容器

      BeanB

    Q4:@Autowired和@Resource的区别?

    特性@Autowired@Resource
    来源 Spring框架 JSR-250标准
    注入方式 按类型(可配合@Qualifier) 按名称(可指定name属性)
    支持参数 required name, type
    适用场景 Spring专属项目 需要跨框架兼容性

    二、面向切面编程(AOP)

    1. 核心概念解析

    #mermaid-svg-02PQtOi0vcY5Liaf {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-02PQtOi0vcY5Liaf .error-icon{fill:#552222;}#mermaid-svg-02PQtOi0vcY5Liaf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-02PQtOi0vcY5Liaf .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-02PQtOi0vcY5Liaf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-02PQtOi0vcY5Liaf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-02PQtOi0vcY5Liaf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-02PQtOi0vcY5Liaf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-02PQtOi0vcY5Liaf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-02PQtOi0vcY5Liaf .marker.cross{stroke:#333333;}#mermaid-svg-02PQtOi0vcY5Liaf svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-02PQtOi0vcY5Liaf g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-02PQtOi0vcY5Liaf g.classGroup text .title{font-weight:bolder;}#mermaid-svg-02PQtOi0vcY5Liaf .nodeLabel,#mermaid-svg-02PQtOi0vcY5Liaf .edgeLabel{color:#131300;}#mermaid-svg-02PQtOi0vcY5Liaf .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-02PQtOi0vcY5Liaf .label text{fill:#131300;}#mermaid-svg-02PQtOi0vcY5Liaf .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-02PQtOi0vcY5Liaf .classTitle{font-weight:bolder;}#mermaid-svg-02PQtOi0vcY5Liaf .node rect,#mermaid-svg-02PQtOi0vcY5Liaf .node circle,#mermaid-svg-02PQtOi0vcY5Liaf .node ellipse,#mermaid-svg-02PQtOi0vcY5Liaf .node polygon,#mermaid-svg-02PQtOi0vcY5Liaf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-02PQtOi0vcY5Liaf .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-02PQtOi0vcY5Liaf g.clickable{cursor:pointer;}#mermaid-svg-02PQtOi0vcY5Liaf g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-02PQtOi0vcY5Liaf g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-02PQtOi0vcY5Liaf .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-02PQtOi0vcY5Liaf .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-02PQtOi0vcY5Liaf .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-02PQtOi0vcY5Liaf .dashed-line{stroke-dasharray:3;}#mermaid-svg-02PQtOi0vcY5Liaf #compositionStart,#mermaid-svg-02PQtOi0vcY5Liaf .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-02PQtOi0vcY5Liaf #compositionEnd,#mermaid-svg-02PQtOi0vcY5Liaf .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-02PQtOi0vcY5Liaf #dependencyStart,#mermaid-svg-02PQtOi0vcY5Liaf .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-02PQtOi0vcY5Liaf #dependencyStart,#mermaid-svg-02PQtOi0vcY5Liaf .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-02PQtOi0vcY5Liaf #extensionStart,#mermaid-svg-02PQtOi0vcY5Liaf .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-02PQtOi0vcY5Liaf #extensionEnd,#mermaid-svg-02PQtOi0vcY5Liaf .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-02PQtOi0vcY5Liaf #aggregationStart,#mermaid-svg-02PQtOi0vcY5Liaf .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-02PQtOi0vcY5Liaf #aggregationEnd,#mermaid-svg-02PQtOi0vcY5Liaf .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-02PQtOi0vcY5Liaf .edgeTerminals{font-size:11px;}#mermaid-svg-02PQtOi0vcY5Liaf :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    Aspect

    +Pointcut()

    +Advice()

    «interface»

    Advice

    +Before()

    +After()

    +Around()

    • 核心组件:

      • 切面(Aspect):横切关注点的模块化
      • 连接点(Joinpoint):程序执行点(方法调用、异常抛出等)
      • 切点(Pointcut):匹配连接点的表达式
      • 通知(Advice):在切点执行的动作
      • 织入(Weaving):将切面应用到目标对象的过程
    • 通知类型:

      @Aspect
      public class LoggingAspect {
      // 前置通知
      @Before("execution(* com.service.*.*(..))")
      public void logBefore(JoinPoint jp) {
      System.out.println("Method: " + jp.getSignature().getName());
      }

      // 环绕通知(最强大)
      @Around("@annotation(com.audit.Loggable)")
      public Object logAround(ProceedingJoinPoint pjp) throws Throwable {
      long start = System.currentTimeMillis();
      Object result = pjp.proceed();
      long duration = System.currentTimeMillis() start;
      System.out.println("Duration: " + duration + "ms");
      return result;
      }
      }

    2. 高频面试题

    Q1:Spring AOP和AspectJ的区别?

    特性Spring AOPAspectJ
    织入方式 运行时代理 编译时/加载时织入
    性能 有运行时开销 无运行时开销
    连接点支持 仅方法级别 方法、构造器、字段等
    依赖 轻量级,内置于Spring 需要额外编译器/类加载器
    适用场景 普通应用 高性能、复杂切面需求

    Q2:JDK动态代理和CGLIB如何选择?

    • JDK动态代理:
      • 要求目标类实现接口
      • 基于反射,生成接口代理类
      • 示例:Proxy.newProxyInstance()
    • CGLIB代理:
      • 通过继承目标类生成子类
      • 不需要接口
      • 无法代理final类/方法
    • Spring选择策略:

      #mermaid-svg-Q9XA26hVhIYAI8f1 {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Q9XA26hVhIYAI8f1 .error-icon{fill:#552222;}#mermaid-svg-Q9XA26hVhIYAI8f1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Q9XA26hVhIYAI8f1 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-Q9XA26hVhIYAI8f1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Q9XA26hVhIYAI8f1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Q9XA26hVhIYAI8f1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Q9XA26hVhIYAI8f1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Q9XA26hVhIYAI8f1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Q9XA26hVhIYAI8f1 .marker.cross{stroke:#333333;}#mermaid-svg-Q9XA26hVhIYAI8f1 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Q9XA26hVhIYAI8f1 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-Q9XA26hVhIYAI8f1 .cluster-label text{fill:#333;}#mermaid-svg-Q9XA26hVhIYAI8f1 .cluster-label span{color:#333;}#mermaid-svg-Q9XA26hVhIYAI8f1 .label text,#mermaid-svg-Q9XA26hVhIYAI8f1 span{fill:#333;color:#333;}#mermaid-svg-Q9XA26hVhIYAI8f1 .node rect,#mermaid-svg-Q9XA26hVhIYAI8f1 .node circle,#mermaid-svg-Q9XA26hVhIYAI8f1 .node ellipse,#mermaid-svg-Q9XA26hVhIYAI8f1 .node polygon,#mermaid-svg-Q9XA26hVhIYAI8f1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Q9XA26hVhIYAI8f1 .node .label{text-align:center;}#mermaid-svg-Q9XA26hVhIYAI8f1 .node.clickable{cursor:pointer;}#mermaid-svg-Q9XA26hVhIYAI8f1 .arrowheadPath{fill:#333333;}#mermaid-svg-Q9XA26hVhIYAI8f1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Q9XA26hVhIYAI8f1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Q9XA26hVhIYAI8f1 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-Q9XA26hVhIYAI8f1 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-Q9XA26hVhIYAI8f1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Q9XA26hVhIYAI8f1 .cluster text{fill:#333;}#mermaid-svg-Q9XA26hVhIYAI8f1 .cluster span{color:#333;}#mermaid-svg-Q9XA26hVhIYAI8f1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Q9XA26hVhIYAI8f1 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

      Yes

      No

      目标类有接口?

      JDK动态代理

      CGLIB代理

      强制CGLIB

      配置proxyTargetClass=true

    Q3:如何实现自定义注解的切面?

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AuditLog {
    String action();
    }

    @Aspect
    @Component
    public class AuditAspect {
    @AfterReturning(
    value = "@annotation(auditLog)",
    returning = "result"
    )
    public void audit(AuditLog auditLog, Object result) {
    String action = auditLog.action();
    // 保存审计日志到DB
    auditRepository.save(new AuditRecord(action, result));
    }
    }

    // 使用示例
    @Service
    public class OrderService {
    @AuditLog(action = "PLACE_ORDER")
    public Order createOrder(OrderRequest request) {
    // 业务逻辑
    }
    }

    三、综合实战与设计模式

    1. 典型应用场景
    • 事务管理(@Transactional实现原理):

      @Transactional(propagation = Propagation.REQUIRED,
      isolation = Isolation.DEFAULT,
      timeout = 30)
      public void transfer(Account from, Account to, BigDecimal amount) {
      // 使用AOP代理实现:
      // 1. 获取连接
      // 2. 设置隔离级别
      // 3. try{业务逻辑} catch{回滚} finally{关闭连接}
      }

    • 安全审计:

      @Before("within(@org.springframework.stereotype.Controller *)")
      public void logControllerAccess(JoinPoint jp) {
      String user = SecurityContextHolder.getContext().getAuthentication().getName();
      logger.info("User: " + user + " accessed: " + jp.getSignature());
      }

    2. 高频面试题

    Q1:如何选择代理方式?

    • 考虑因素:
    • 目标类是否实现接口 → 是:JDK代理;否:CGLIB
    • 是否需要代理非public方法 → 是:CGLIB
    • 性能要求 → 高:AspectJ编译时织入
    • 应用场景 → Spring Boot默认CGLIB

    Q2:AOP中的设计模式?

  • 代理模式:

    • JDK动态代理:java.lang.reflect.Proxy
    • CGLIB:net.sf.cglib.proxy.Enhancer
  • 责任链模式:

    public interface MethodInterceptor {
    Object invoke(MethodInvocation invocation) throws Throwable;
    }

    // 拦截器链执行
    public class ReflectiveMethodInvocation implements MethodInvocation {
    private final List<MethodInterceptor> interceptors;
    private int currentInterceptorIndex = 1;

    public Object proceed() {
    if (this.currentInterceptorIndex == this.interceptors.size() 1) {
    return invokeJoinpoint();
    }
    MethodInterceptor interceptor =
    interceptors.get(++this.currentInterceptorIndex);
    return interceptor.invoke(this);
    }
    }

  • Q3:如何调试AOP不生效问题?

  • 检查切点表达式:

    // 打印所有被代理的Bean
    @Bean
    public CommandLineRunner aopDebug(ApplicationContext ctx) {
    return args -> {
    String[] beans = ctx.getBeanDefinitionNames();
    Arrays.stream(beans)
    .filter(name -> ctx.getBean(name).getClass().getName().contains("$$"))
    .forEach(System.out::println);
    };
    }

  • 确认代理方式:

    // 检查Bean是否被代理
    if (AopUtils.isAopProxy(bean)) {
    System.out.println(bean + " is proxied");
    }

  • 四、最佳实践与避坑指南

  • DI最佳实践:

    • 优先使用构造器注入
    • 避免字段注入(不利于测试和不变性)
    • 使用@Qualifier解决歧义依赖
    • 对可选依赖使用@Autowired(required=false)
  • AOP避坑指南:

    • 避免在切面中处理业务逻辑
    • 谨慎使用@Around(必须调用proceed())
    • 注意切点表达式的性能影响
    • 避免自调用(this.method())导致的AOP失效

    // 错误示例(自调用导致AOP失效)
    public void process() {
    this.validate(); // 不会被AOP拦截
    }

    @Transactional
    public void validate() { /* … */ }

    // 解决方案:通过代理调用
    @Autowired
    private ApplicationContext context;

    public void process() {
    context.getBean(this.getClass()).validate();
    }

  • 性能优化:

    // 优化切点表达式
    @Pointcut("within(com.service..*) && execution(public * *(..))")
    public void servicePublicMethods() {}

    // 使用条件编译(AspectJ)
    @Aspect
    @ConditionalOnExpression("${aop.enabled:true}")
    public class PerformanceAspect { /* … */ }

  • 五、面试深度问题

    Q1:Spring如何整合IoC和AOP?

    • 核心流程:
    • Bean创建阶段:AbstractAutowireCapableBeanFactory.createBean()
    • 判断是否需要代理:AbstractAutoProxyCreator.postProcessAfterInitialization()
    • 创建代理:ProxyFactory.getProxy()
    • 织入切面:将Advisor应用到目标类

    Q2:如何实现跨切面数据传递?

    // 使用ThreadLocal
    public class RequestContext {
    private static final ThreadLocal<Map<String, Object>> context = new ThreadLocal<>();

    public static void put(String key, Object value) {
    getContextMap().put(key, value);
    }
    }

    // 前置切面
    @Before("@within(org.springframework.web.bind.annotation.RestController)")
    public void initContext() {
    RequestContext.put("startTime", System.currentTimeMillis());
    }

    // 后置切面
    @AfterReturning("@within(org.springframework.web.bind.annotation.RestController)")
    public void logContext() {
    long start = (long) RequestContext.get("startTime");
    System.out.println("Request took: " + (System.currentTimeMillis() start) + "ms");
    }

    Q3:如何扩展Spring AOP?

  • 实现自定义Pointcut:

    public class CustomPointcut extends StaticMethodMatcherPointcut {
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
    return method.isAnnotationPresent(CustomAnnotation.class);
    }
    }

  • 创建自定义Advisor:

    @Bean
    public Advisor customAdvisor() {
    CustomPointcut pointcut = new CustomPointcut();
    Advice advice = new CustomInterceptor();
    return new DefaultPointcutAdvisor(pointcut, advice);
    }

  • 掌握这些核心概念和实战技巧,不仅能应对面试中的深度问题,更能构建高内聚、低耦合的企业级应用。


    📜文末寄语

    • 🟠关注我,获取更多内容。
    • 🟡技术动态、实战教程、问题解决方案等内容持续更新中。
    • 🟢《全栈知识库》技术交流和分享社区,集结全栈各领域开发者,期待你的加入。
    • 🔵​加入开发者的《专属社群》,分享交流,技术之路不再孤独,一起变强。
    • 🟣点击下方名片获取更多内容🍭🍭🍭👇

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » Java 依赖注入、控制反转与面向切面:面试深度解析
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!