当前位置: 首页 > news >正文

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

🤟致敬读者

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

📘博主相关

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

文章目录

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


📃文章前言

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

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

在这里插入图片描述

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

1. 核心概念解析
传统编程
对象主动创建依赖
IoC/DI
容器管理依赖
对象被动接收依赖
  • 控制反转(IoC)

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

    • IoC的具体实现方式
    • 三种注入方式:
      // 1. 构造器注入(推荐)
      public class UserService {private final UserRepository repo;@Autowiredpublic UserService(UserRepository repo) {this.repo = repo;}
      }// 2. Setter注入
      public class OrderService {private PaymentGateway gateway;@Autowiredpublic void setGateway(PaymentGateway gateway) {this.gateway = gateway;}
      }// 3. 字段注入(不推荐)
      public class ProductService {@Autowiredprivate InventoryService inventory;
      }
      
2. 高频面试题

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

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

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

  1. 不可变性:依赖可声明为final
  2. 空安全:对象创建时依赖必须就绪
  3. 测试友好:无需容器即可测试
  4. 循环依赖检测:启动时即可发现循环依赖
  5. 线程安全:避免并发修改问题

Q3:Spring如何解决循环依赖?

  • 三级缓存机制
    1. 一级缓存:完整Bean(singletonObjects
    2. 二级缓存:早期暴露Bean(earlySingletonObjects
    3. 三级缓存:Bean工厂(singletonFactories
  • 解决流程
    BeanA 容器 BeanB 创建A,放入三级缓存 需要B 创建B 需要A(从三级缓存获取早期A) 完成创建,放入一级缓存 完成创建,放入一级缓存 BeanA 容器 BeanB

Q4:@Autowired和@Resource的区别?

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

二、面向切面编程(AOP)

1. 核心概念解析
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选择策略
    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();// 保存审计日志到DBauditRepository.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:如何选择代理方式?

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

Q2:AOP中的设计模式?

  1. 代理模式

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

    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不生效问题?

  1. 检查切点表达式:

    // 打印所有被代理的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);};
    }
    
  2. 确认代理方式:

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

四、最佳实践与避坑指南

  1. DI最佳实践

    • 优先使用构造器注入
    • 避免字段注入(不利于测试和不变性)
    • 使用@Qualifier解决歧义依赖
    • 对可选依赖使用@Autowired(required=false)
  2. 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();
    }
    
  3. 性能优化

    // 优化切点表达式
    @Pointcut("within(com.service..*) && execution(public * *(..))")
    public void servicePublicMethods() {}// 使用条件编译(AspectJ)
    @Aspect
    @ConditionalOnExpression("${aop.enabled:true}")
    public class PerformanceAspect { /* ... */ }
    

五、面试深度问题

Q1:Spring如何整合IoC和AOP?

  • 核心流程
    1. Bean创建阶段:AbstractAutowireCapableBeanFactory.createBean()
    2. 判断是否需要代理:AbstractAutoProxyCreator.postProcessAfterInitialization()
    3. 创建代理:ProxyFactory.getProxy()
    4. 织入切面:将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?

  1. 实现自定义Pointcut

    public class CustomPointcut extends StaticMethodMatcherPointcut {@Overridepublic boolean matches(Method method, Class<?> targetClass) {return method.isAnnotationPresent(CustomAnnotation.class);}
    }
    
  2. 创建自定义Advisor

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

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


📜文末寄语

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

相关文章:

  • 【PmHub面试篇】PmHub集成Redission分布式锁保障流程状态更新面试专题解析
  • ubuntu24.04 使用apt指令只下载不安装软件
  • Ubuntu中常用的网络命令指南
  • Unity3D中Newtonsoft.Json序列化优化策略
  • C++11实现TCP网络通讯服务端处理逻辑简化版
  • Vue2数组数字字段求和技巧 数字求和方法
  • 20250605在荣品的PRO-RK3566开发板的Android13下让PMIC芯片RK809在长按下开机
  • 设置存储器若干单元为相同的字节
  • CMake指令:add_definitions
  • CentOS 7 如何pip3安装pyaudio?
  • Windows下安装MySQL8.X
  • 【JVM】三色标记法原理
  • GPU加速与非加速的深度学习张量计算对比Demo,使用PyTorch展示关键差异
  • Python[数据结构及算法 --- 栈]
  • tcp/udp
  • 6月5日day45
  • Python实现markdown文件转word
  • 六、数据库的安全性
  • 书籍将正方形矩阵顺时针转动90°(8)0605
  • RADIUS-如何获取凭证
  • 南昌网站建设开发团队/如何创造一个自己的网站
  • 广告设计要学哪些软件/东莞百度推广排名优化
  • 温州市城乡建设建档案馆网站/晋江友情链接是什么意思
  • 网站模板 招聘/黑马程序员培训机构在哪
  • 网站用哪些系统做的好处/网站建设开发公司
  • 济源网站制作/大连seo外包平台