一、Spring 框架的核心原理
Spring 框架的核心思想是控制反转(IoC)和面向切面编程(AOP),通过模块化设计和扩展性支持企业级应用开发。以下是其核心原理的详细分析。
二、控制反转(IoC)和依赖注入(DI)
1. 通俗解释
想象你是一个餐馆老板。传统方式是你自己买菜、炒菜、端菜(代码直接new对象)。而用了Spring后,你只需要告诉服务员“我要一份牛排”(声明需要什么对象),服务员(Spring容器)会把做好的牛排送到你面前。这就是控制反转(你不用自己“控制”对象的创建)。
2. 代码示例
假设有一个“用户服务”(UserService),需要依赖“用户仓库”(UserRepository)操作数据库。
(1) 传统写法(自己控制对象)
public class UserService {
private UserRepository userRepository = new UserRepository(); // 直接 new 对象
public void saveUser() {
userRepository.save();
}
}
(2) Spring 写法(依赖注入)
@Component // 告诉 Spring:这个类需要被管理
public class UserService {
@Autowired // 告诉 Spring:请自动注入一个 UserRepository
private UserRepository userRepository;
public void saveUser() {
userRepository.save();
}
}
@Component // 告诉 Spring:这个类也要被管理
public class UserRepository {
public void save() {
System.out.println("保存用户到数据库");
}
}
(3) 运行代码
public class Main {
public static void main(String[] args) {
// 1. 启动 Spring 容器(自动扫描带 @Component 的类)
ApplicationContext context = new AnnotationConfigApplicationContext("com.example");
// 2. 从容器中获取 UserService(不用自己 new)
UserService userService = context.getBean(UserService.class);
// 3. 调用方法
userService.saveUser(); // 输出:保存用户到数据库
}
}
3. 关键点
(1)Spring 容器会扫描 @Component 注解的类,并自动创建它们的对象(称为 Bean)。
(2)@Autowired 让 Spring 自动把 UserRepository 对象注入到 UserService 中(类似“服务员递给你牛排”)。
三、面向切面编程(AOP)
1. 通俗解释
假设你在写一个游戏,每个关卡开始和结束时需要记录日志。传统方式是在每个关卡代码里写日志逻辑,但这样代码会重复。AOP允许你把这些重复的逻辑(比如日志、事务)抽出来,像“切面”一样插入到需要的地方。
2. 代码示例
(1) 业务代码
@Service
public class GameService {
public void startLevel(int level) {
System.out.println("第 " + level + " 关开始!");
}
public void endLevel(int level) {
System.out.println("第 " + level + " 关结束!");
}
}
(2) 日志切面
@Aspect // 声明这是一个切面
@Component
public class LogAspect {
// 定义切点:拦截所有 GameService 的方法
@Pointcut("execution(* com.example.GameService.*(..))")
public void gameServiceMethods() {}
// 前置通知:在方法执行前执行
@Before("gameServiceMethods()")
public void logBefore(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("方法执行前:" + methodName);
}
// 后置通知:在方法执行后执行
@After("gameServiceMethods()")
public void logAfter(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("方法执行后:" + methodName);
}
}
(3) 运行代码
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext("com.example");
GameService gameService = context.getBean(GameService.class);
gameService.startLevel(1);
gameService.endLevel(1);
}
}
(4) 输出
方法执行前:startLevel
第 1 关开始!
方法执行后:startLevel
方法执行前:endLevel
第 1 关结束!
方法执行后:endLevel
3. 关键点
(1)切面(Aspect):把日志逻辑单独抽出来,不混在业务代码里。
(2)动态代理:Spring 会在运行时为 GameService 生成一个代理对象,在方法调用前后插入日志逻辑。
四、Spring 容器的核心流程
1. 步骤
(1)启动容器:就像打开一个工具箱,Spring会扫描所有带 @Component的类。
(2)创建 Bean:把每个类实例化成对象(单例模式,默认只创建一个)。
(3)依赖注入:检查哪些Bean需要其他Bean(比如 @Autowired),自动“装配”进去。
(4)AOP 处理:如果有切面,生成代理对象,插入额外逻辑。
(5)提供 Bean:当你从容器中获取对象时,它已经是组装好的完整对象。
2. 代码模拟 Spring 的简化版 IoC 容器
public class SimpleContainer {
private Map<String, Object> beans = new HashMap<>();
// 扫描并创建所有 Bean
public void init() {
// 1. 创建 UserRepository
UserRepository userRepository = new UserRepository();
beans.put("userRepository", userRepository);
// 2. 创建 UserService 并注入 UserRepository
UserService userService = new UserService();
userService.setUserRepository(userRepository); // 手动注入依赖
beans.put("userService", userService);
}
// 从容器中获取 Bean
public <T> T getBean(Class<T> clazz) {
return (T) beans.values().stream()
.filter(clazz::isInstance)
.findFirst()
.orElse(null);
}
}
3. 使用方式
public class Main {
public static void main(String[] args) {
SimpleContainer container = new SimpleContainer();
container.init(); // 初始化容器
UserService userService = container.getBean(UserService.class);
userService.saveUser(); // 输出:保存用户到数据库
}
}
五、总结
(1) IoC 容器:帮你管理对象的创建和依赖关系,不用自己new。
(2) 依赖注入:通过 @Autowired 自动装配对象。
(3) AOP:通过动态代理,把日志、事务等逻辑像“切片”一样插入到代码中。
(4) Spring 就像你的私人助手:你只需要告诉它你需要什么(@Component 和 @Autowired),它会自动帮你搞定对象的创建、组装和增强!