问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Spring单例模式的最佳实践分享

创作时间:
作者:
@小白创作中心

Spring单例模式的最佳实践分享

引用
CSDN
12
来源
1.
https://blog.csdn.net/qq_39319138/article/details/130705746
2.
https://blog.csdn.net/u011305680/article/details/79717238
3.
https://blog.csdn.net/weixin_43172297/article/details/130807084
4.
https://blog.csdn.net/smilehappiness/article/details/119712824
5.
https://m.blog.csdn.net/qq_26893655/article/details/140337231
6.
https://www.cnblogs.com/nickup/p/9800120.html
7.
https://developer.aliyun.com/article/902446
8.
https://www.cnblogs.com/ccx-lly/p/17100137.html
9.
https://www.bilibili.com/read/mobile?id=5835813
10.
https://springdoc.cn/spring-boot-singleton-vs-beans/
11.
https://cloud.tencent.com/developer/article/1860978
12.
https://www.cnblogs.com/xcgShare/p/11951891.html?ivk_sa=1024320u

在Spring框架中,单例模式是一种核心的设计模式,它保证一个类只有一个实例,并提供全局访问点。本文将深入探讨如何在Spring中实现和使用单例模式,以及其在实际开发中的最佳实践。通过学习这些内容,开发者可以更好地管理和维护应用中的资源,提高代码质量和性能。

01

单例模式的实现机制

在Spring中,Bean默认就是单例的。这意味着在整个应用上下文中,每个Bean定义只会有一个实例。当容器启动时(或首次请求时通过@Lazy延迟初始化),Spring会创建这些单例Bean并将其存储在IoC容器中。之后,所有对该Bean的请求都会返回这个已经创建的实例。

要显式指定一个Bean为单例,可以在配置中使用scope="singleton"属性:

<bean id="myBean" class="com.example.MyBean" scope="singleton"/>

或者使用注解方式:

@Service
@Scope("singleton")
public class MyBean {
    // ...
}
02

最佳实践

适用场景

单例模式最适合用于无状态的服务类和工具类。例如,数据库访问对象(DAO)、业务逻辑处理器(Service)、工具类(如日期格式化工具)等。这些类不保存任何可变状态,每次调用都是独立的,因此非常适合做成单例。

线程安全

由于单例Bean被所有线程共享,因此线程安全问题需要特别注意。如果单例Bean包含可变状态,可能会导致数据不一致或竞态条件。解决方法包括:

  • 避免可变状态:尽量设计成无状态的类,不保存任何实例变量。
  • 使用ThreadLocal:如果必须保存状态,可以使用ThreadLocal来为每个线程提供独立的实例。
  • 同步方法:在关键代码段使用synchronized关键字,但这种方式可能会影响性能。

依赖注入

当单例Bean依赖于原型Bean时,需要注意原型Bean的“多例”特性会失效。因为单例Bean只在初始化时注入一次原型Bean,后续始终使用同一个实例。解决方法包括使用ObjectProviderProvider接口延迟获取实例,或者使用AOP代理。

03

实际案例

在Spring MVC应用中,控制器(Controller)和仓库(Repository)通常配置为单例。下面通过一个简单的图书管理系统来说明单例模式的实际应用:

首先,我们创建一个BookRepository来管理Book对象:

@Repository
public class BookRepository {
    private List<Book> books = new ArrayList<>();

    public BookRepository() {
        // 初始化一些图书数据
        books.add(new Book(1L, "Spring in Action"));
        books.add(new Book(2L, "Effective Java"));
    }

    public long count() {
        return books.size();
    }

    public Optional<Book> findById(long id) {
        return books.stream().filter(book -> book.getId() == id).findFirst();
    }
}

然后,创建两个控制器:LibraryController和BookController,它们都依赖于同一个BookRepository:

@RestController
public class LibraryController {
    @Autowired
    private BookRepository repository;

    @GetMapping("/count")
    public Long findCount() {
        System.out.println(repository);
        return repository.count();
    }
}

@RestController
public class BookController {
    @Autowired
    private BookRepository repository;

    @GetMapping("/book/{id}")
    public Book findById(@PathVariable long id) {
        System.out.println(repository);
        return repository.findById(id).get();
    }
}

当我们分别调用这两个控制器的接口时,会发现控制台输出的repository对象ID是相同的:

com.baeldung.spring.patterns.singleton.BookRepository@3ea9524f
com.baeldung.spring.patterns.singleton.BookRepository@3ea9524f

这证明了Spring确实为这两个控制器注入了同一个BookRepository实例。

04

总结

单例模式在Spring应用中非常常见,但使用时需要特别注意以下几点:

  1. 线程安全:确保单例Bean无状态或使用ThreadLocal处理线程安全问题。
  2. 依赖管理:谨慎处理单例依赖原型的情况,必要时使用Provider模式。
  3. 资源管理:对于需要管理资源的Bean(如数据库连接),考虑使用原型作用域或手动管理资源生命周期。

通过遵循这些最佳实践,可以充分发挥单例模式的优势,同时避免潜在的问题。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号