小陈的知识图谱
SpringL1 基础核心重点

IoC 与 AOP 原理

IoC 容器、依赖注入、Bean 生命周期、AOP 代理

IoC (Inversion of Control) 控制反转

什么是 IoC?

传统开发中,对象自己创建和管理依赖:

public class UserService {
    // 传统方式:自己 new 依赖
    private UserDao userDao = new UserDao();
    private EmailService emailService = new EmailService();
}

这种方式的痛点:

  • 强耦合:UserService 硬编码依赖具体实现
  • 难以测试:无法替换为 Mock 对象
  • 难以扩展:修改实现需要改代码

IoC 容器思想:将对象的创建和管理交给容器,对象只需声明"我需要什么":

@Component
public class UserService {
    @Autowired  // 由容器注入
    private UserDao userDao;

    @Autowired
    private EmailService emailService;
}

┌─────────────────────────────────────────────────────────┐
│                    IoC Container                        │
│                                                         │
│  ┌──────────┐   ┌──────────┐   ┌──────────┐           │
│  │UserDao   │   │UserService│   │MailService│   ...     │
│  │ (Bean)   │──▶│  (Bean)  │◀──│  (Bean)  │           │
│  └──────────┘   └──────────┘   └──────────┘           │
│       │                                      │         │
│       ▼                                      ▼         │
│  ┌──────────┐                      ┌──────────────┐   │
│  │OrderDao │                      │SmsService    │   │
│  │ (Bean)  │                      │  (Bean)      │   │
│  └──────────┘                      └──────────────┘   │
│                                                         │
│  所有 Bean 由容器管理:创建、装配、销毁                   │
└─────────────────────────────────────────────────────────┘

BeanFactory vs ApplicationContext

特性BeanFactoryApplicationContext
初始化懒加载(需要时才创建)立即加载(启动时创建)
接口基础容器高级容器(继承 BeanFactory)
事件机制不支持支持事件发布/监听
国际化不支持支持 MessageSource
资源访问不支持支持 ResourceLoader
AOP 整合需手动配置自动整合
常用实现XmlBeanFactory(已废弃)AnnotationConfigApplicationContext

// BeanFactory 方式 —— 懒加载
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
UserService userService = factory.getBean(UserService.class);  // 此时才创建

// ApplicationContext 方式 —— 立即加载
ApplicationContext ctx = new AnnotationConfigApplicationContext("com.example");
UserService userService = ctx.getBean(UserService.class);  // 启动时已创建

依赖注入的三种方式

1. 构造器注入(推荐)

@RestController
public class UserController {

    private final UserService userService;
    private final EmailService emailService;

    // Spring 4.3+ 可以省略 @Autowired(只有一个构造器时)
    public UserController(UserService userService, EmailService emailService) {
        this.userService = userService;
        this.emailService = emailService;
    }
}

优点:不可变性(final)、利于测试、不会出现循环依赖(编译期检测)、Spring 官方推荐

2. Setter 注入

@Component
public class OrderService {

    private PaymentService paymentService;

    @Autowired
    public void setPaymentService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}

优点:可选依赖、可以重新注入

3. 字段注入(不推荐)

@Component
public class ProductService {
    @Autowired
    private ProductDao productDao;
}

缺点:无法用 final、隐藏依赖关系、难以测试(需要反射注入)

@Bean 与 @Component 的区别

// @Component —— 标注在类上,自动扫描
@Component
@Slf4j
public class MyComponent {
    public void doSomething() {
        log.info("Doing something...");
    }
}

// @Bean —— 标注在配置类的方法上,手动注册
@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate() {
        // 可以在此做复杂初始化
        RestTemplate template = new RestTemplate();
        template.setConnectTimeout(Duration.ofSeconds(5));
        template.setReadTimeout(Duration.ofSeconds(10));
        template.setMessageConverters(...);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean  // 没有才创建
    public ObjectMapper objectMapper() {
        return new ObjectMapper()
            .registerModule(new JavaTimeModule())
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    }
}

---

Bean 生命周期(面试核心)

完整流程图

┌─────────────────────────┐
                    │  1. 实例化 Instantiation │
                    │  (反射创建原始对象)       │
                    └──────────┬──────────────┘
                               ▼
                    ┌─────────────────────────┐
                    │  2. 属性赋值 Populate    │
                    │  (填充 @Autowired 字段)   │
                    └──────────┬──────────────┘
                               ▼
                    ┌─────────────────────────┐
                    │  3. Aware 接口回调       │
                    └──────────┬──────────────┘
                               ▼
                    ┌─────────────────────────┐
                    │  4. BeanPostProcessor   │
                    │     #postProcessBefore  │
                    │     Initialization      │
                    └──────────┬──────────────┘
                               ▼
                    ┌─────────────────────────┐
                    │  5. 初始化 Initializing │
                    │  ● @PostConstruct       │
                    │  ● InitializingBean     │
                    │     afterPropertiesSet  │
                    │  ● @Bean(initMethod)    │
                    └──────────┬──────────────┘
                               ▼
                    ┌─────────────────────────┐
                    │  6. BeanPostProcessor   │
                    │     #postProcessAfter   │
                    │     Initialization      │
                    └──────────┬──────────────┘
                               ▼
                    ┌─────────────────────────┐
                    │  7. Bean 就绪           │
                    │  (可供应用程序使用)      │
                    └──────────┬──────────────┘
                               ▼
                    ┌─────────────────────────┐
                    │  8. 容器关闭销毁        │
                    │  ● @PreDestroy          │
                    │  ● DisposableBean       │
                    │     destroy()           │
                    │  ● @Bean(destroyMethod) │
                    └─────────────────────────┘

代码示例展示完整生命周期

@Component
public class LifecycleDemoBean implements BeanNameAware, BeanFactoryAware,
        InitializingBean, DisposableBean {

    private String name;

    public LifecycleDemoBean() {
        System.out.println("1. 实例化: LifecycleDemoBean() 构造方法");
    }

    @Autowired
    public void setName(String name) {
        System.out.println("2. 属性赋值: setName(" + name + ")");
        this.name = name;
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("3. Aware: setBeanName(" + name + ")");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("3. Aware: setBeanFactory()");
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("5. @PostConstruct: 初始化回调");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("5. InitializingBean.afterPropertiesSet()");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println("8. @PreDestroy: 销毁前回调");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("8. DisposableBean.destroy()");
    }
}

BeanPostProcessor —— 扩展的关键

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        if (bean instanceof UserService) {
            System.out.println("4. BeforeInit: 对 UserService 做前置处理");
        }
        return bean;  // 可以返回代理对象
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        if (bean instanceof UserService) {
            System.out.println("6. AfterInit: 对 UserService 做后置处理");
            // 常见场景:返回 AOP 代理对象
            return ProxyFactory.getProxy(bean);
        }
        return bean;
    }
}

---

循环依赖与三级缓存(面试高频)

什么是循环依赖?

@Component
public class A {
    @Autowired
    private B b;
}

@Component
public class B {
    @Autowired
    private A a;
}

A ──────依赖──────▶ B
    ▲                    │
    │                    │
    └──────依赖──────────┘

三级缓存机制

public class DefaultSingletonBeanRegistry {

    // 一级缓存:成品对象(完全初始化好的 Bean)
    Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    // 二级缓存:半成品对象(已实例化但未完成属性赋值)
    Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

    // 三级缓存:提前暴露的 Bean 工厂(Lambda,用于生成代理)
    Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
}

三级缓存工作流程

1. A 开始创建,实例化完成 → 放入三级缓存(early reference)
   ┌──────────────┐
   │ 三级缓存:     │
   │ A → Lambda   │  (Lambda 可返回 A 的早期引用,支持 AOP)
   └──────────────┘

2. A 填充属性 b → 发现需要 B → 去容器找 B

3. B 开始创建,实例化完成 → 放入三级缓存
   ┌──────────────┐
   │ 三级缓存:     │
   │ A → Lambda   │
   │ B → Lambda   │
   └──────────────┘

4. B 填充属性 a → 发现需要 A
   → 检查一级缓存:没有
   → 检查二级缓存:没有
   → 检查三级缓存:有!
   → 执行 Lambda 得到 A 的早期引用
   → 放入二级缓存(从三级移除)
   → 注入给 B 的 a 字段

5. B 继续初始化 → 完成 → 放入一级缓存

6. A 拿到 B 的完整引用 → 注入 b 字段
   → A 继续初始化 → 完成 → 放入一级缓存
   → 从二/三级缓存移除

面试常见追问:

  • 为什么需要三级缓存而不是两级? → 需要第三级处理 AOP 代理,如果 Bean 需要 AOP,三级缓存的 Lambda 负责生成代理对象
  • 构造器注入为什么不能解决循环依赖? → 构造器注入在实例化阶段就需要依赖,此时 Bean 还没创建出来
  • @Lazy 如何解决? → 生成一个代理对象注入,真正使用时才去容器获取

---

AOP 面向切面编程

AOP 核心概念

┌─────────────────────────┐
                       │      Aspect(切面)      │
                       │  @Around + @Before +... │
                       └──────────┬──────────────┘
                                  │
          ┌───────────────────────┼───────────────────────┐
          ▼                       ▼                       ▼
   ┌─────────────┐        ┌─────────────┐        ┌─────────────┐
   │ Pointcut    │        │ Join Point  │        │  Advice     │
   │ 切点表达式   │───────▶│ 连接点       │◀───────│  通知       │
   │ execution(* │        │ (方法执行点)  │        │  @Before    │
   │  service..*)│        └─────────────┘        │  @After     │
   └─────────────┘                                └─────────────┘

通知类型详解

@Aspect
@Component
@Slf4j
public class LoggingAspect {

    // @Before:在目标方法执行前运行
    @Before("execution( com.example.service..*(..))")
    public void logBefore(JoinPoint joinPoint) {
        log.info(">> 方法调用: {}.{}()",
            joinPoint.getTarget().getClass().getSimpleName(),
            joinPoint.getSignature().getName());
    }

    // @AfterReturning:目标方法正常返回后运行
    @AfterReturning(value = "execution( com.example.service..*(..))", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        log.info("<< 方法返回: {} = {}", joinPoint.getSignature().getName(), result);
    }

    // @AfterThrowing:目标方法抛出异常后运行
    @AfterThrowing(value = "execution( com.example.service..*(..))", throwing = "ex")
    public void logAfterThrowing(JoinPoint joinPoint, Exception ex) {
        log.error("!! 方法异常: {} - {}", joinPoint.getSignature().getName(), ex.getMessage());
    }

    // @After(finally):无论是否异常都会执行
    @After("execution( com.example.service..*(..))")
    public void logAfter(JoinPoint joinPoint) {
        log.info("== 方法结束: {}", joinPoint.getSignature().getName());
    }

    // @Around:最强大的通知,控制是否执行目标方法
    @Around("@annotation(com.example.annotation.Monitor)")
    public Object measureTime(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            Object result = pjp.proceed();  // 执行目标方法
            return result;
        } finally {
            long duration = System.currentTimeMillis() - start;
            log.info("[监控] {}.{}() 耗时: {}ms",
                pjp.getTarget().getClass().getSimpleName(),
                pjp.getSignature().getName(),
                duration);
            if (duration > 1000) {
                log.warn("[慢调用告警] {}ms > 1000ms", duration);
            }
        }
    }
}

切点表达式详解

@Pointcut("execution(修饰符 返回值 包.类.方法(参数))")

// 常用表达式示例:
execution(public  com.example.service..*(..))          // service 包下所有 public 方法
execution( com.example.service...*(..))                 // service 包及子包所有方法
execution( com.example.service.UserService.(..))        // UserService 所有方法
execution( com.example.service..save*(Long, ..))        // 以 save 开头,第一个参数是 Long
execution( com.example.service..(@com.example.Valid ())) // 带 @Valid 注解的参数

// 注解匹配
@within(org.springframework.stereotype.Service)           // 类上有 @Service
@annotation(org.springframework.cache.annotation.Cacheable) // 方法上有 @Cacheable
@args(com.example.annotation.Sensitive)                   // 参数上有 @Sensitive

// 组合表达式
@Pointcut("execution( com.example.service..*(..))")
public void serviceLayer() {}

@Pointcut("within(com.example..*)")
public void appScope() {}

@Pointcut("serviceLayer() && appScope() && !@annotation(NoLogging)")
public void combinedPointcut() {}

JDK 动态代理 vs CGLIB 代理

┌──────────────────────────────────────────────────────────┐
│               Spring AOP 代理选择策略                      │
│                                                          │
│                 目标类是否实现接口?                        │
│                        │                                 │
│         ┌──────────────┴──────────────┐                  │
│         ▼                             ▼                  │
│    ┌─────────┐                  ┌──────────┐            │
│    │  YES    │                  │   NO     │            │
│    └────┬────┘                  └────┬─────┘            │
│         │                            │                   │
│         ▼                            ▼                   │
│  ┌──────────────┐            ┌──────────────┐           │
│  │ JDK Proxy   │            │ CGLIB Proxy  │           │
│  │ 基于接口     │            │ 基于继承      │           │
│  │ 反射调用     │            │ ASM 生成子类  │           │
│  │ 性能较低     │            │ 性能较高      │           │
│  └──────────────┘            └──────────────┘           │
│                                                          │
│  Spring Boot 2.0+ 默认使用 CGLIB(即使有接口)            │
│  通过 spring.aop.proxy-target-class=false 切回 JDK       │
└──────────────────────────────────────────────────────────┘

JDK 代理示例:

// 目标接口
public interface UserService {
    User findById(Long id);
}

// JDK 动态代理实现
public class JdkProxyFactory {
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            (proxy, method, args) -> {
                System.out.println("JDK Proxy Before...");
                Object result = method.invoke(target, args);
                System.out.println("JDK Proxy After...");
                return result;
            }
        );
    }
}

CGLIB 代理示例:

// CGLIB 代理(不需要接口)
public class CglibProxyFactory implements MethodInterceptor {

    public Object getProxy(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args,
            MethodProxy proxy) throws Throwable {
        System.out.println("CGLIB Before...");
        Object result = proxy.invokeSuper(obj, args);  // 调用父类方法(不走反射)
        System.out.println("CGLIB After...");
        return result;
    }
}

---

实战案例:自定义注解 + AOP 实现日志审计

// 1. 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AuditLog {
    String action();          // 操作类型:CREATE/UPDATE/DELETE/QUERY
    String module();          // 模块名
    String description() default "";
}

// 2. AOP 切面
@Aspect
@Component
@Slf4j
public class AuditLogAspect {

    @Autowired
    private AuditLogRepository auditLogRepository;

    @Around("@annotation(auditLog)")
    public Object around(ProceedingJoinPoint pjp, AuditLog auditLog) throws Throwable {
        // 获取操作前数据
        String methodName = pjp.getSignature().getName();
        Object[] args = pjp.getArgs();
        String params = args.length > 0 ? args[0].toString() : "";

        // 获取当前用户(从 SecurityContext)
        String username = SecurityContextHolder.getContext()
            .getAuthentication().getName();

        Object result = null;
        boolean success = true;
        String errorMsg = null;

        try {
            result = pjp.proceed();
            return result;
        } catch (Exception e) {
            success = false;
            errorMsg = e.getMessage();
            throw e;
        } finally {
            // 异步记录审计日志
            AuditLogEntity logEntity = new AuditLogEntity();
            logEntity.setUsername(username);
            logEntity.setAction(auditLog.action());
            logEntity.setModule(auditLog.module());
            logEntity.setMethod(methodName);
            logEntity.setParams(params);
            logEntity.setSuccess(success);
            logEntity.setErrorMsg(errorMsg);
            logEntity.setDuration(...);
            auditLogRepository.save(logEntity);
        }
    }
}

// 3. 使用
@RestController
@RequestMapping("/api/users")
public class UserController {

    @AuditLog(action = "CREATE", module = "用户管理", description = "创建用户")
    @PostMapping
    public Result<User> createUser(@RequestBody @Valid UserCreateRequest request) {
        return Result.success(userService.create(request));
    }

    @AuditLog(action = "UPDATE", module = "用户管理")
    @PutMapping("/{id}")
    public Result<User> updateUser(@PathVariable Long id, @RequestBody UserUpdateRequest request) {
        return Result.success(userService.update(id, request));
    }

    @AuditLog(action = "DELETE", module = "用户管理")
    @DeleteMapping("/{id}")
    public Result<Void> deleteUser(@PathVariable Long id) {
        userService.delete(id);
        return Result.success();
    }
}

---

面试高频题

Q1: Spring IoC 容器是如何启动的?

A: 以 AnnotationConfigApplicationContext 为例:1. 扫描指定包下的类 2. 解析 @Component、@Service 等注解 3. 通过 BeanDefinitionReader 读取为 BeanDefinition 4. BeanFactoryPostProcessor 处理 BeanDefinition 5. 实例化所有单例 Bean(已完成生命周期)

Q2: @Autowired 和 @Resource 的区别?

A: @Autowired 是 Spring 注解,按类型注入;@Resource 是 JSR-250 注解,默认按名称注入。@Autowired 可以和 @Qualifier 配合实现按名称注入。

Q3: Spring AOP 和 AspectJ 的关系?

A: Spring AOP 是运行时织入(基于代理),只支持方法级别的切面;AspectJ 是编译期织入(通过修改字节码),支持更丰富的切点(构造器、字段等)。Spring 使用 AspectJ 的注解语法(@Aspect/@Before 等),但底层仍用 Spring 的代理机制。

Q4: 如何解决循环依赖?

A: 三级缓存 + 提前暴露。构造器注入无法解决(可加 @Lazy),prototype 作用域无法解决(不在缓存中),多例和单例混合也无法解决。

---

核心要点

  • IoC 容器工作原理
  • Bean 生命周期完整流程
  • 循环依赖与三级缓存
  • JDK 代理 vs CGLIB 代理
  • AOP 通知类型与执行顺序