🎯《Spring三级缓存完全攻略:从菜鸟到大神的进阶之路》🎯

44 阅读9分钟

"缓存就像生活中的备忘录,让我们记住重要的事情,避免重复劳动!" 📝✨


🚀 引言:为什么需要了解三级缓存?

嘿,小伙伴们!👋 今天我们要聊的是Spring框架中的一个神秘武器——三级缓存。别被这个听起来很高大上的术语吓到,其实它就像我们生活中的"备忘录",帮助我们记住重要的事情,避免重复劳动。

想象一下,你每天早上都要去咖啡店买咖啡。如果每次都重新排队、点单、等待,那岂不是很浪费时间?于是,你决定提前买好一周的咖啡,放在家里需要时直接取用。这种提前准备、随取随用的方式,就是缓存的思想!☕💡


🎭 什么是Spring三级缓存?

在Spring框架中,为了高效地管理Bean(可以理解为程序中的组件),引入了三级缓存机制。简单来说,就是在创建Bean的过程中,Spring会使用三个"储物柜"来存放不同状态的Bean:

🏠 一级缓存(singletonObjects)- 完全准备好的Bean

比喻:就像你家里的冰箱,里面放着已经做好的饭菜,随时可以取出来吃。🍱

// 一级缓存:完全初始化好的单例Bean
Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

特点

  • ✅ 完全初始化完成
  • ✅ 可以直接使用
  • ✅ 性能最好,访问最快

🍚 二级缓存(earlySingletonObjects)- 半成品Bean

比喻:就像你刚刚煮好的米饭,还没炒菜,但已经可以提前尝一口,看看熟了没有。🍚

// 二级缓存:早期暴露的Bean实例
Map<String, Object> earlySingletonObjects = new HashMap<>();

特点

  • ⚠️ 已经实例化但未完全初始化
  • ⚠️ 可以提前暴露给其他Bean使用
  • ⚠️ 主要用于解决循环依赖

🤖 三级缓存(singletonFactories)- Bean制造工厂

比喻:就像你有一个万能的厨师机器人,只要给它指令,它就能做出你想要的菜肴。🤖🍳

// 三级缓存:Bean工厂对象
Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();

特点

  • 🔧 存放创建Bean的工厂对象
  • 🔧 可以生成Bean的早期引用
  • 🔧 支持AOP代理对象的创建

🎪 为什么需要三级缓存?

循环依赖的噩梦 😱

想象一下,你和你的好朋友小明互相送礼物:

  • 你送小明一盒巧克力 🍫
  • 小明也想送你一盒巧克力 🍫
  • 但小明得等你送完才能开始
  • 你也在等小明先送

这就形成了一个"循环依赖"——你们互相等待,谁也动不了!😵

在程序中,如果两个Bean互相依赖,就会出现类似的问题:

@Component
public class UserService {
    @Autowired
    private OrderService orderService;
}

@Component
public class OrderService {
    @Autowired
    private UserService userService;
}

三级缓存的救星 🦸‍♂️

Spring通过三级缓存机制,巧妙地解决了这个死循环:

  1. 提前曝光:当Spring创建Bean时,会先把它的工厂对象放入三级缓存中
  2. 代理对象:如果Bean需要被AOP代理,Spring会在三级缓存中存放代理对象的工厂
  3. 优雅解决:其他Bean需要时,可以通过工厂对象提前拿到,避免互相等待

🎬 三级缓存的工作流程

让我们通过一个小故事来理解三级缓存的工作流程:

场景:创建UserService和OrderService

graph TD
    A[开始创建UserService] --> B[发现需要OrderService]
    B --> C[将UserService工厂放入三级缓存]
    C --> D[开始创建OrderService]
    D --> E[发现需要UserService]
    E --> F[从三级缓存获取UserService早期引用]
    F --> G[完成OrderService创建]
    G --> H[将OrderService放入一级缓存]
    H --> I[完成UserService创建]
    I --> J[将UserService放入一级缓存]
    J --> K[循环依赖解决成功!🎉]

详细步骤解析

第一步:创建UserService 🛠️

// Spring开始创建UserService
UserService userService = new UserService();
// 发现需要OrderService,先去创建OrderService

第二步:放入三级缓存 📦

// 将UserService的工厂对象放入三级缓存
singletonFactories.put("userService", () -> {
    return getEarlyBeanReference("userService", mbd, userService);
});

第三步:创建OrderService 🔄

// 开始创建OrderService
OrderService orderService = new OrderService();
// 发现需要UserService

第四步:从三级缓存获取 🎯

// 从三级缓存获取UserService的早期引用
ObjectFactory<?> singletonFactory = singletonFactories.get("userService");
UserService earlyUserService = (UserService) singletonFactory.getObject();

第五步:完成创建 ✅

// OrderService创建完成,放入一级缓存
singletonObjects.put("orderService", orderService);
// UserService也创建完成,放入一级缓存
singletonObjects.put("userService", userService);

🏠 生活中的完美类比

厨房做菜的比喻 🍳

想象你在家里做饭,需要用到锅、刀和菜板:

  • 一级缓存:就像你手边已经准备好的锅、刀和菜板,随时可以使用 🍽️
  • 二级缓存:就像你正在清洗的锅、刀和菜板,虽然还没完全准备好,但已经可以预见它们马上就能用 🧽
  • 三级缓存:就像你知道厨房里有备用的锅、刀和菜板,虽然还没拿出来,但随时可以取用 🏠

当你需要用刀切菜时:

  1. 如果手边的刀(一级缓存)已经准备好,就直接用 ✅
  2. 如果还在清洗中(二级缓存),你可以稍等片刻 ⏳
  3. 如果都没有,你知道厨房里有备用的刀(三级缓存),可以马上取出来用 🔧

这样,你就不会因为缺少工具而耽误做饭的进度!👨‍🍳


💻 源码分析:深入理解实现原理

DefaultSingletonBeanRegistry类

public class DefaultSingletonBeanRegistry {
    
    // 一级缓存:完全初始化好的单例Bean
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    // 二级缓存:早期暴露的Bean实例
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    
    // 三级缓存:Bean工厂对象
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    
    /**
     * 获取单例Bean的核心方法
     */
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 1. 先从一级缓存获取
        Object singletonObject = this.singletonObjects.get(beanName);
        
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                // 2. 从二级缓存获取
                singletonObject = this.earlySingletonObjects.get(beanName);
                
                if (singletonObject == null && allowEarlyReference) {
                    // 3. 从三级缓存获取工厂对象
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        // 4. 通过工厂对象创建早期Bean
                        singletonObject = singletonFactory.getObject();
                        // 5. 放入二级缓存
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        // 6. 从三级缓存移除
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }
}

关键方法解析

1. getSingleton方法 🔍

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 这是获取Bean的核心方法
    // allowEarlyReference参数决定是否允许早期引用
}

2. addSingletonFactory方法 ➕

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    // 将Bean工厂对象添加到三级缓存
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
        }
    }
}

3. addSingleton方法 ✅

protected void addSingleton(String beanName, Object singletonObject) {
    // 将完全初始化好的Bean添加到一级缓存
    synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
    }
}

🎯 实际应用场景

场景1:用户服务和订单服务循环依赖

@Service
public class UserService {
    @Autowired
    private OrderService orderService;
    
    public void createUser() {
        System.out.println("创建用户");
        orderService.createOrder();
    }
}

@Service
public class OrderService {
    @Autowired
    private UserService userService;
    
    public void createOrder() {
        System.out.println("创建订单");
        userService.createUser();
    }
}

没有三级缓存的问题

  • UserService需要OrderService才能创建
  • OrderService需要UserService才能创建
  • 形成死循环,程序无法启动 😱

三级缓存的解决方案

  1. 创建UserService时,将工厂对象放入三级缓存
  2. 创建OrderService时,从三级缓存获取UserService的早期引用
  3. 两个服务都能成功创建,循环依赖被优雅解决 ✅

场景2:AOP代理对象的处理

@Service
@Transactional
public class UserService {
    @Autowired
    private OrderService orderService;
    
    public void createUser() {
        // 业务逻辑
    }
}

AOP代理的处理

  • 如果UserService需要被事务代理,三级缓存会确保返回代理对象
  • 通过ObjectFactory的getEarlyBeanReference方法创建代理对象
  • 保证最终获取到的是代理后的对象,而不是原始对象

🛠️ 如何开启和配置三级缓存

1. 默认开启状态

Spring Boot默认开启三级缓存,无需额外配置:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

2. 手动配置

如果需要自定义配置,可以通过以下方式:

@Configuration
public class CacheConfig {
    
    @Bean
    public BeanFactoryPostProcessor cacheConfigurer() {
        return beanFactory -> {
            if (beanFactory instanceof DefaultListableBeanFactory) {
                DefaultListableBeanFactory factory = (DefaultListableBeanFactory) beanFactory;
                // 自定义缓存配置
                factory.setAllowCircularReferences(true);
            }
        };
    }
}

3. 禁用循环依赖检测

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.setAllowCircularReferences(false); // 禁用循环依赖
        app.run(args);
    }
}

🎨 性能优化建议

1. 避免过度依赖

// ❌ 不好的设计:过度依赖
@Service
public class UserService {
    @Autowired private OrderService orderService;
    @Autowired private ProductService productService;
    @Autowired private PaymentService paymentService;
    @Autowired private NotificationService notificationService;
    // ... 更多依赖
}

// ✅ 好的设计:职责单一
@Service
public class UserService {
    @Autowired private UserRepository userRepository;
    // 只依赖必要的组件
}

2. 使用接口解耦

// ✅ 使用接口解耦
public interface UserService {
    void createUser();
}

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private OrderService orderService;
    
    @Override
    public void createUser() {
        // 实现逻辑
    }
}

3. 合理使用@Lazy注解

@Service
public class UserService {
    @Autowired
    @Lazy  // 延迟加载,避免循环依赖
    private OrderService orderService;
}

🐛 常见问题和解决方案

问题1:循环依赖异常

错误信息

BeanCurrentlyInCreationException: Error creating bean with name 'userService': 
Requested bean is currently in creation: Is there an unresolvable circular reference?

解决方案

  1. 使用@Lazy注解
  2. 重构代码,消除循环依赖
  3. 使用@PostConstruct方法

问题2:AOP代理问题

问题描述:获取到的Bean不是代理对象

解决方案

@Service
public class UserService {
    @Autowired
    private ApplicationContext applicationContext;
    
    public void someMethod() {
        // 获取代理对象
        UserService proxy = applicationContext.getBean(UserService.class);
        proxy.createUser();
    }
}

问题3:性能问题

问题描述:三级缓存导致性能下降

解决方案

  1. 减少Bean的依赖关系
  2. 使用@Lazy注解延迟加载
  3. 考虑使用原型Bean替代单例Bean

🎓 最佳实践总结

1. 设计原则

  • 单一职责:每个Bean只负责一个功能
  • 依赖倒置:依赖抽象而不是具体实现
  • 接口隔离:使用接口解耦

2. 代码规范

// ✅ 好的实践
@Service
public class UserService {
    private final UserRepository userRepository;
    
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

// ❌ 避免的做法
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private OrderService orderService;
    @Autowired
    private ProductService productService;
    // 太多依赖
}

3. 测试建议

@SpringBootTest
class UserServiceTest {
    
    @Autowired
    private UserService userService;
    
    @Test
    void testCreateUser() {
        // 测试用户创建功能
        assertThat(userService).isNotNull();
    }
}

🎉 总结

通过这篇文章,我们深入了解了Spring三级缓存的工作原理和应用场景。三级缓存就像生活中的备忘录和备用工具,帮助我们高效地管理和使用资源,避免重复劳动。

关键要点回顾:

  1. 一级缓存:完全初始化好的Bean,性能最好 🚀
  2. 二级缓存:早期暴露的Bean,解决循环依赖 🔄
  3. 三级缓存:Bean工厂对象,支持AOP代理 🤖

学习建议:

  1. 理解原理:掌握三级缓存的工作机制
  2. 实践应用:在实际项目中应用这些知识
  3. 持续学习:关注Spring框架的最新发展

希望这篇文章能帮助你更好地理解Spring的三级缓存机制,成为开发路上的一大助力!🚀


📚 参考资料


"编程就像做菜,需要耐心、技巧和一点点创意!" 👨‍🍳💻

祝你在编程的道路上越走越远,越走越顺! 🎉🚀


如果这篇文章对你有帮助,别忘了点赞、收藏、分享哦! 👍💖

Made with ❤️ by AI Assistant