Spring 是 Java 企业级开发的支柱,其成功很大程度归功于两个核心机制:IOC(控制反转) 和 AOP(面向切面编程)。掌握这两个机制,不仅能写出更优雅的业务代码,还能理解 Spring 的设计哲学。
✅ 一、IOC(Inversion of Control)控制反转
1.1 什么是 IOC?
控制反转是一种设计思想,把对象的创建权从程序员转移到 Spring 容器,程序员只需要声明依赖,而无需关心对象是如何创建和管理的。
本质上就是“我不去
new
了,你容器帮我创建并注入”。
1.2 IOC 的两种主要实现方式
Spring 提供两种依赖注入方式:
1.3 IOC 示例
1)定义接口和实现类
public interface UserService {
void register(String username);
}
@Service
public class UserServiceImpl implements UserService {
@Override
public void register(String username) {
System.out.println("注册用户:" + username);
}
}
2)Controller 中注入依赖
@RestController
@RequestMapping("/user")
public class UserController {
private final UserService userService;
// 构造函数注入
public UserController(UserService userService) {
this.userService = userService;
}
@PostMapping("/register")
public String register(@RequestParam String username) {
userService.register(username);
return "注册成功:" + username;
}
}
Spring 容器会在项目启动时扫描带有
@Service
的类,并自动将其实例注入到UserController
中。
1.4 IOC 的底层原理简析(源码角度)
IOC 的核心类是:ApplicationContext
。
启动时,Spring 通过
AnnotationConfigApplicationContext
加载配置类。使用 反射 + 工厂模式 创建 Bean。
将 Bean 存放在单例池(
singletonObjects
)中。通过
@Autowired
或构造器将依赖注入。
简化流程如下:
@Configuration
@ComponentScan
--> ApplicationContext 启动
--> 扫描 @Component、@Service、@Controller
--> 创建 BeanDefinition
--> 实例化(反射)
--> 填充依赖
🔁 二、AOP(面向切面编程)
2.1 为什么需要 AOP?
在大型项目中,经常有一些横切关注点,比如:
日志打印
权限检查
缓存处理
异常监控
性能统计
这些逻辑往往在多个业务方法中重复出现,难以维护。AOP 就是解决这类问题的理想方式。
2.2 AOP 核心概念
2.3 AOP 示例:记录方法调用日志
1)引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2)定义切面类
@Aspect
@Component
public class LoggingAspect {
// 切点:匹配 com.example.service 包下的所有方法
@Pointcut("execution(* com.example.service..*(..))")
public void logPointcut() {}
// 前置通知
@Before("logPointcut()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("[前置通知] 调用方法:" + joinPoint.getSignature());
}
// 后置通知
@After("logPointcut()")
public void logAfter(JoinPoint joinPoint) {
System.out.println("[后置通知] 方法结束:" + joinPoint.getSignature());
}
// 返回后通知
@AfterReturning(value = "logPointcut()", returning = "result")
public void logReturn(Object result) {
System.out.println("[返回通知] 返回值:" + result);
}
// 异常通知
@AfterThrowing(value = "logPointcut()", throwing = "ex")
public void logException(Throwable ex) {
System.out.println("[异常通知] 异常信息:" + ex.getMessage());
}
// 环绕通知(最强大)
@Around("logPointcut()")
public Object logAround(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed(); // 执行目标方法
long end = System.currentTimeMillis();
System.out.println("[环绕通知] 耗时:" + (end - start) + "ms");
return result;
}
}
2.4 AOP 的底层原理简析
Spring AOP 是基于 代理模式 实现的:
原理流程如下:
容器扫描带
@Aspect
的切面类;通过
ProxyFactoryBean
创建代理;方法调用时,代理对象拦截方法并执行增强逻辑;
执行目标方法(
proceed()
);方法返回或异常时继续执行对应通知。
🧩 三、IOC 与 AOP 协同工作的过程
Spring 容器加载所有 Bean;
检查是否有与 AOP 匹配的切点;
如果匹配,就生成代理类(使用 JDK/CGLIB);
注入到其它 Bean 中的是代理类而非原始类;
调用方法时,先进入代理类,再执行切面逻辑。
🛠 四、完整目录结构示意
src/main/java
└── com.example
├── aspect
│ └── LoggingAspect.java
├── controller
│ └── UserController.java
├── service
│ ├── UserService.java
│ └── UserServiceImpl.java
└── Application.java
🚀 五、实战建议
开发中,推荐使用构造函数注入(便于单元测试);
AOP 中环绕通知最灵活,但应谨慎使用;
建议将切面逻辑模块化,比如:日志切面、事务切面、安全切面分开写;
自定义注解结合 AOP 可实现更细粒度的控制。
📘 六、总结
Spring 的精髓就在于这两个强大的机制,通过 IOC 解耦依赖,通过 AOP 解耦横切关注点,让我们专注于业务本身!