轻松吃透 Spring 核心:IoC 与 AOP 极简入门
在学习 Java 后端开发时Spring 框架是绕不开的核心技术栈。Spring 最核心的两个基石是IoC控制反转)与AOP面向切面编程——不仅是框架的设计哲学也是理解后续 Spring Boot、Spring Cloud 等技术的前提。本文抛开复杂的企业级分层架构以数据库配置DataConfig和计算器Calculator两个最小化示例直接呈现 IoC 与 AOP 要解决的核心问题及其使用方式。一、IoC 与 AOP 解决了什么问题1.1 控制反转IoC在传统开发模式下对象的创建由开发者手动控制DataConfigconfignewDataConfig();config.setUrl(jdbc:mysql://localhost:3306/mydb);config.setDriverName(com.mysql.cj.jdbc.Driver);// ...当项目规模扩大这种手动创建方式会导致两个问题耦合严重调用方必须知道被调用方的构造细节维护困难依赖关系变更时需要逐处修改代码IoC 的本质是将对象创建与依赖关系管理的控制权从程序代码转移给外部容器。Spring 容器负责实例化对象、注入依赖开发者只需声明我需要什么而无需关心它怎么来。1.2 依赖注入DIDI 是实现 IoC 的具体技术手段。Spring 容器在运行时自动将依赖对象或属性值注入到目标组件中。注入方式主要有两种构造器注入通过构造函数传入依赖设值注入通过 setter 方法或字段反射注入属性值1.3 面向切面编程AOP在业务开发中日志记录、权限校验、性能监控等横切关注点往往散落在多个业务方法中。传统做法是在每个方法内重复编写这些辅助代码导致业务逻辑与非功能性代码混杂代码冗余度高修改成本大AOP 允许将这些横切关注点抽取为独立的切面Aspect通过声明式配置在指定连接点动态织入实现业务代码与通用逻辑的解耦。二、IoC 实践基于 XML 配置XML 配置虽非当下主流但能最直观地展示 Spring 容器的属性注入机制。2.1 定义配置类packagecom.southwind.ioc;importlombok.Data;DatapublicclassDataConfig{privateStringurl;privateStringdriverName;privateStringusername;privateStringpassword;}Data注解由 Lombok 提供自动生成 getter、setter 及toString方法。2.2 编写 spring.xml在resources目录下创建配置文件?xml version1.0 encodingUTF-8?beansxmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdbeaniddataConfigclasscom.southwind.ioc.DataConfigpropertynameurlvaluejdbc:mysql://localhost:3306/mydb/propertynamedriverNamevaluecom.mysql.cj.jdbc.Driver/propertynameusernamevalueroot/propertynamepasswordvalue123456//bean/beansbean标签声明受容器管理的对象property标签完成属性注入。Spring 通过反射调用对应 setter 方法完成赋值。2.3 启动容器并获取 Beanpackagecom.southwind.ioc;importorg.springframework.context.ApplicationContext;importorg.springframework.context.support.ClassPathXmlApplicationContext;publicclassTestXml{publicstaticvoidmain(String[]args){ApplicationContextcontextnewClassPathXmlApplicationContext(spring.xml);DataConfigconfigcontext.getBean(dataConfig,DataConfig.class);System.out.println(config);}}ClassPathXmlApplicationContext从类路径加载 XML 配置初始化容器后通过getBean获取已装配完成的实例。三、IoC 实践基于注解配置两种方式注解配置摆脱了 XML 文件是现代 Spring 开发的主流方式。基于注解的实现有两条路径方式核心注解特点配置类 BeanConfiguration、Bean手动声明 Bean与 XMLbean逻辑等价适合第三方库类的注册扫包 类注解ComponentScan、Component自动扫描并注册适合自定义业务类方式一配置类 Bean显式定义在配置类中通过方法返回 Bean 实例Spring 自动将返回值注册为容器中的 Bean。这种方式与 XML 配置在逻辑上等价只是形式不同。packagecom.southwind.ioc;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;Configuration// 声明该类为配置类publicclassBeanConfig{Bean// 手动声明一个名为 dataConfig 的 BeanpublicDataConfigdataConfig(){DataConfigconfignewDataConfig();config.setUrl(jdbc:mysql://localhost:3306/mydb);config.setDriverName(com.mysql.cj.jdbc.Driver);config.setUsername(root);config.setPassword(123456);returnconfig;}}启动容器publicclassTestBeanConfig{publicstaticvoidmain(String[]args){AnnotationConfigApplicationContextcontextnewAnnotationConfigApplicationContext(BeanConfig.class);DataConfigconfigcontext.getBean(DataConfig.class);System.out.println(config);}}这种方式下DataConfig类本身不需要添加Component注解。Bean 的创建完全由配置类掌控适合注册第三方库中的类如数据源DataSource、模板引擎等无法修改源码的类。方式二扫包 Component自动扫描核心是让 Spring 自动扫描指定包下的注解类并完成注册。改造 DataConfig 类packagecom.southwind.ioc;importlombok.Data;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.stereotype.Component;DataComponent// 声明该类受 Spring 容器管理publicclassDataConfig{Value(jdbc:mysql://localhost:3306/mydb)privateStringurl;Value(com.mysql.cj.jdbc.Driver)privateStringdriverName;Value(root)privateStringusername;Value(123456)privateStringpassword;}配置类开启扫描packagecom.southwind.ioc;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;importorg.springframework.context.annotation.ComponentScan;importorg.springframework.context.annotation.Configuration;ConfigurationComponentScan(com.southwind.ioc)// 自动扫描当前包及子包下的 ComponentpublicclassAppConfig{publicstaticvoidmain(String[]args){AnnotationConfigApplicationContextcontextnewAnnotationConfigApplicationContext(AppConfig.class);DataConfigconfigcontext.getBean(DataConfig.class);System.out.println(config);}}这种方式下每个需要被管理的类自身要打上Component或其派生注解Service、Repository、ControllerSpring 通过扫包自动发现并注册。适合项目中的自定义业务类。两种方式怎么选场景推荐方式原因自定义业务类Service、DAO 等扫包 Component类是自己写的可以直接贴注解代码更简洁第三方库的类DataSource、SqlSessionFactory 等配置类 Bean无法修改第三方源码只能在配置类中手动声明需要精细控制 Bean 的创建过程配置类 Bean可以在方法中编写创建逻辑如条件判断、多步初始化四、AOP 实践日志切面4.1 目标业务类packagecom.southwind.ioc;importorg.springframework.stereotype.Component;ComponentpublicclassCalculator{publicintadd(inta,intb){System.out.println(--- 正在执行核心计算逻辑 ---);returnab;}}4.2 编写切面类packagecom.southwind.ioc;importorg.aspectj.lang.annotation.After;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Before;importorg.springframework.stereotype.Component;AspectComponentpublicclassLogAspect{Before(execution(* com.southwind.ioc.Calculator.*(..)))publicvoidbefore(){System.out.println([日志通知] 计算开始准备执行...);}After(execution(* com.southwind.ioc.Calculator.*(..)))publicvoidafter(){System.out.println([日志通知] 计算结束);}}关键点解析Aspect标识该类为切面需配合aspectjweaver依赖使用Before/After分别声明前置通知与后置通知execution(* com.southwind.ioc.Calculator.*(..))切点表达式匹配Calculator类下所有方法4.3 启用 AOP 自动代理在配置类上添加EnableAspectJAutoProxyConfigurationComponentScan(com.southwind.ioc)EnableAspectJAutoProxypublicclassAppConfig{}该注解指示 Spring 为匹配切点表达式的目标 Bean 生成动态代理对象。默认使用 JDK 动态代理目标类实现接口时或 CGLIB 子类代理。4.4 运行结果当调用calculator.add(1, 2)时实际执行流程为[日志通知] 计算开始准备执行... --- 正在执行核心计算逻辑 --- [日志通知] 计算结束 Result: 3日志逻辑与计算逻辑完全分离后续若需调整日志格式或移除日志仅需修改LogAspect无需触碰Calculator源码。五、IoC 与 AOP 的关系维度IoCAOP核心职责对象生命周期管理与依赖装配横切关注点的模块化与动态织入解决的问题对象创建耦合代码重复与逻辑混杂技术实现反射 工厂模式动态代理JDK / CGLIB典型注解Component,Value,AutowiredAspect,Before,After,Around两者共同支撑 Spring低耦合、高内聚的设计目标IoC 管理对象怎么来AOP 管理功能怎么加。六、总结IoC/DI通过容器接管对象创建与依赖注入消除了组件间的硬编码依赖。XML 配置适合理解底层注入原理注解配置更适合生产环境开发。基于注解又有两种实现路径Bean显式声明适合第三方类注册扫包 Component适合自定义业务类。AOP将横切关注点抽离为独立切面在不侵入业务代码的前提下实现通用逻辑的复用。Spring AOP 基于动态代理实现适用于方法级的拦截场景。掌握这两个机制后再去理解 Spring 的事务管理Transactional、声明式缓存、安全控制等功能会发现它们本质上都是 IoC 与 AOP 的组合应用。