Spring 中的控制反转(IOC)与依赖注入(DI)
控制反转(IOC, Inversion of Control)
-
核心思想
将对象的创建、依赖管理和生命周期控制权从程序员转移给 Spring 容器,实现解耦和模块化。 -
传统方式的问题
手动通过new关键字创建对象,导致代码耦合度高,难以维护和扩展。
示例:// 传统方式:直接依赖具体实现 public class UserService { private UserRepository userRepository = new UserRepositoryImpl(); } -
IOC 的实现方式
通过 IOC 容器(如ApplicationContext)管理对象(Bean),提供两种配置方式:-
XML 配置:早期方式,显式定义 Bean 及其依赖。
<bean id="userRepository" class="com.example.UserRepositoryImpl"/> <bean id="userService" class="com.example.UserService"> <property name="userRepository" ref="userRepository"/> </bean> -
注解驱动:现代主流方式,使用
@Component、@Service等注解自动扫描和装配。@Service public class UserService { @Autowired private UserRepository userRepository; }
-
-
依赖注入(DI)
-
构造器注入(推荐):通过构造函数传递依赖。
@Service public class UserService { private final UserRepository userRepository; @Autowired public UserService(UserRepository userRepository) { this.userRepository = userRepository; } } -
Setter 方法注入:通过 Setter 方法赋值。
@Service public class UserService { private UserRepository userRepository; @Autowired public void setUserRepository(UserRepository userRepository) { this.userRepository = userRepository; } } -
字段注入:直接通过
@Autowired注解字段(简单但不推荐,易导致循环依赖)。
-
-
IOC 容器的核心功能
-
Bean 生命周期管理:包括初始化(
@PostConstruct)和销毁(@PreDestroy)。 -
作用域控制:支持
singleton(默认单例)、prototype(每次请求新对象)等作用域。 -
延迟加载:通过
@Lazy延迟初始化 Bean。
-
面向切面编程(AOP)
AOP 的核心概念
-
目标问题
解决横切关注点(如日志、事务、安全)在多个模块中重复出现的问题,避免代码冗余和高耦合。 -
核心术语
术语 说明 切面(Aspect) 封装横切逻辑的模块(如日志工具类),通过 @Aspect注解定义。连接点(Join Point) 程序执行过程中的某个点(如方法调用、异常抛出)。 通知(Advice) 切面在连接点执行的具体动作(如方法执行前后),分为 @Before、@After等。切点(Pointcut) 通过表达式匹配需要拦截的连接点(如指定某个包下的所有方法)。 织入(Weaving) 将切面代码插入目标对象的过程,Spring AOP 通过动态代理实现。
Spring AOP 的实现
-
动态代理机制
-
JDK 动态代理:基于接口,要求目标类必须实现接口。
-
CGLIB 代理:基于子类继承,可代理未实现接口的类(需引入
cglib依赖)。
-
-
通知类型
通知类型 执行时机 示例 @Before目标方法执行前 权限校验、日志记录 @AfterReturning目标方法成功返回后 记录返回值 @AfterThrowing目标方法抛出异常后 异常监控、回滚事务 @After目标方法执行后(无论成功或异常) 清理资源 @Around包围目标方法,可自定义执行流程 性能统计、事务管理 -
切点表达式(Pointcut Expression)
使用 AspectJ 表达式语法 定义拦截范围,常见形式:@Pointcut("execution(* com.example.service.*.*(..))") // 拦截 service 包下所有方法 public void serviceMethods() {}
AOP 的实际应用示例
-
日志记录切面
@Aspect @Component public class LoggingAspect { @Before("execution(* com.example.service.*.*(..))") public void logMethodCall(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println("Method " + methodName + " is called."); } } -
事务管理切面
@Aspect @Component public class TransactionAspect { @Around("@annotation(org.springframework.transaction.annotation.Transactional)") public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable { try { System.out.println("Starting transaction..."); Object result = joinPoint.proceed(); System.out.println("Committing transaction..."); return result; } catch (Exception e) { System.out.println("Rolling back transaction..."); throw e; } } }
Spring AOP 的局限性
-
仅支持方法级别的拦截:无法拦截字段访问或构造器调用。
-
仅作用于 Spring 管理的 Bean:非容器管理的对象无法被代理。
-
性能开销:动态代理会引入轻微性能损耗,但对大多数应用影响可忽略。
3192

被折叠的 条评论
为什么被折叠?



