文章目录
IOC控制反转
- 传统模式下,对象主动创建依赖。自己去new()对象。
- IOC 模式,容器管理依赖生命周期
什么是 IoC(控制反转)
IoC 是一种设计原则,核心思想是:把对象的创建与依赖管理的控制权,从对象本身反转给外部容器或框架。
IoC(Inversion of Control,控制反转)容器是依赖注入(DI)的实现框架,负责管理对象的创建、配置和生命周期。
三大核心作用:
- 对象创建,不再写 new,容器读取配置(注解/XML/代码)决定创建哪个实现类。
- 依赖组装,容器自动分析依赖关系,按正确顺序创建所有对象。
- 生命周期管理
依赖注入(DI)
依赖注入是 IoC 最常见的实现方式,容器负责把依赖"注入"到对象中。
三种注入方式:
构造器注入
java
public class OrderService {
private final UserRepository repo;
public OrderService(UserRepository repo) { // 容器自动传入
this.repo = repo;
}
}
Setter 注入
java
public void setRepo(UserRepository repo) { this.repo = repo; }
字段注入
java
@Autowired
private UserRepository repo;
AOP
AOP是什么?
Aspect-Oriented Programming 面向切面编程。AOP采用的是横向切面的方式,注入与主业务流程无关的功能,例如事务管理和日志管理。
AOP核心概念
Aspect 切面 = Pointcut + Advice (在哪里做 + 做什么)
Pointcut 切入点 = 拦截哪些方法,连接点的集合
Advice 通知 = 在切面中(类)的某个连接点(方法)采取的动作。一般有几种不同的通知方式:1)around; 2)before; 3)after; 4)exception; 5)return(返回通知)
JoinPoint 连接点 = 方法执行的具体时机
Weaving 织入 = 把切面应用到目标对象的过程。可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring在运行时完成织入。
底层原理
Spring AOP本质上是动态代理。Spring AOP的实现方式是动态织入,通过动态代理技术在运行时动态将要增强的代码织入到目标类中;
- JDK动态代理(目标类有接口时用,通过反射实现)
- CGLIB代理(目标类没有接口时用,通过继承实现)
示例
1)实体类
java
public class User {
private int id;
private String name;
private String role;
public User(int id, String name, String role) {
this.id = id;
this.name = name;
this.role = role;
}
// getter setter
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getRole() {
return role;
}
@Override
public String toString() {
return "User{id=" + id + ", name=" + name + ", role=" + role + "}";
}
}
2)注解
java
public @interface MyLog {
String value() default "";
}
- service层
UserService
java
public interface UserService {
User findUser(int id);
}
UserServiceImpl
java
@Service
public class UserServiceImpl implements UserService{
@MyLog("查询用户")
@Override
public User findUser(int id) {
System.out.println(" >>> [业务] 查询用户 id=" + id);
return new User(id, "张三", "admin");
}
@MyLog("新增用户")
@Override
public void addUser(User user) {
System.out.println(" >>> [业务] 新增用户: " + user);
}
@MyLog("删除用户")
@Override
public void deleteUser(int id) {
System.out.println(" >>> [业务] 删除用户 id=" + id);
}
}
4)切面类
java
@Aspect
@Component
public class LogAspect {
// 切点1:拦截service包下所有方法
@Pointcut("execution(* AOP.service.*.*(..))")
public void serviceMethod() {}
// 切点2:拦截有@MyLog注解的方法
@Pointcut("@annotation(AOP.annotation.MyLog)")
public void myLogMethod() {}
// 5. 环绕通知 + 读取@MyLog注解内容
@Around("myLogMethod()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// 获取注解上的描述
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
MyLog myLog = method.getAnnotation(MyLog.class);
String operation = myLog.value();
long startTime = System.currentTimeMillis();
System.out.println("【Around-前】操作: 【" + operation + "】开始执行");
Object result;
try {
result = pjp.proceed(); // 执行真实方法
} catch (Throwable e) {
System.out.println("【Around-异常】操作: 【" + operation + "】发生异常");
throw e; // 继续抛出,让AfterThrowing也能捕获
}
long endTime = System.currentTimeMillis();
System.out.println("【Around-后】操作: 【" + operation
+ "】执行成功,耗时: " + (endTime - startTime) + "ms");
return result;
}
}
5)测试
java
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext ctx =
SpringApplication.run(App.class, args);
UserService userService = ctx.getBean(UserService.class);
System.out.println("\n========== 测试1: 查询用户 ==========");
userService.findUser(1);
ctx.close();
}
}
java
========== 测试1: 查询用户 ==========
【Around-前】操作: 【查询用户】开始执行
>>> [业务] 查询用户 id=1
【Around-后】操作: 【查询用户】执行成功,耗时: 1ms