🤟致敬读者
- 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉
📘博主相关
- 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息
文章目录
-
- 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:构造器注入为什么被推荐?
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的区别?
来源 | 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 | 需要额外编译器/类加载器 |
适用场景 | 普通应用 | 高性能、复杂切面需求 |
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);
}
掌握这些核心概念和实战技巧,不仅能应对面试中的深度问题,更能构建高内聚、低耦合的企业级应用。
📜文末寄语
- 🟠关注我,获取更多内容。
- 🟡技术动态、实战教程、问题解决方案等内容持续更新中。
- 🟢《全栈知识库》技术交流和分享社区,集结全栈各领域开发者,期待你的加入。
- 🔵加入开发者的《专属社群》,分享交流,技术之路不再孤独,一起变强。
- 🟣点击下方名片获取更多内容🍭🍭🍭👇
评论前必须登录!
注册