品牌型号:联想ThinkPad X1
系统:Windows10家庭版
软件版本:Spring 5.3.7
在日常开发中我们几乎每天都在用Spring,但有些问题用久了反而容易忽略,比如大家有没有想过注入进来的对象,到底是单例的还是多例的?注入的是接口还是实现类?这两个问题看起来虽然很基础,但真正能说清楚的人并不多。很多同学凭着“感觉”在写代码,项目跑起来没问题就过了,可是一旦遇到并发场景或者需要替换实现的时候,才发现自己对底层逻辑的理解其实是模糊的。下面就给大家介绍一下Spring注入的是单例还是多例,Spring注入的是接口还是实现类的相关内容。
一、Spring注入的是单例还是多例
Spring默认注入的是单例对象,很多人在听到这个结论的时候会有点懵,会好奇如果是单例,那多个地方同时用同一个对象,不会出问题吗?这个疑虑是正常的,但要搞清楚这件事,得先理解Spring管理Bean的方式。Spring容器启动的时候,会把标注了@Component、@Service这类注解的类实例化,然后统一放到容器里,后面不管哪个地方注入,拿到的都是同一个实例,这就是默认的单例作用域,也叫singleton。

那多例呢?Spring当然也支持,用@Scope("prototype")就可以声明一个多例Bean。每次从容器里获取这个Bean,都会新建一个实例,互不干扰。不过在实际项目里,多例的使用场景相对少见,大多数业务Bean用单例就够了,需要注意的是,单例Bean如果持有了有状态的成员变量,在并发场景下是会出问题的。这不是Spring的锅,而是代码设计上需要注意的地方。

既然默认是单例,为什么大家平时写的Service、Dao用起来没有出现并发问题呢?原因在于,这些类本身通常是无状态的,方法里的局部变量在每个线程的栈上独立存在,不共享。真正容易踩坑的是在Bean里定义了实例变量,并且多个线程会同时修改它。这种情况下,哪怕用的是Spring注入,也照样会有线程安全问题。

二、Spring注入的是接口还是实现类
这个问题在实际开发中其实很容易被忽视,有很多同学为了图方便,直接@Autowired一个具体的实现类,功能上没什么问题,项目也能跑。但这样写其实埋了一个隐患,一旦后续需要替换实现,或者同一个接口有多个实现类的时候,代码改动就会比较麻烦。相反,如果注入的是接口,调用方根本不需要关心底层用的是哪个实现,只管调接口方法就行,扩展起来干净很多。

从Spring的角度来看,它本身也更偏向接口注入,当我们用@Autowired注入一个接口时,Spring会去容器里找这个接口对应的实现Bean,自动完成装配。如果容器里只有一个实现,直接注入没有任何问题,如果有多个实现,再配合@Qualifier或者@Primary指定一下就好,整个过程框架都帮我们处理了,我们只需要面向接口编程就行。

当然,也有人会问项目小、实现类就一个,注入实现类有什么问题?短期来看确实没问题,但这是一个习惯问题。面向接口编程是Java里一个很基本的设计原则,养成这个习惯,写出来的代码在可维护性和可扩展性上会好很多。
以上就是Spring注入的是单例还是多例,Spring注入的是接口还是实现类的全部内容了。Spring默认注入的是单例,主要是为了减少对象频繁创建销毁带来的开销,理解单例后,我们再结合自己的业务场景判断要不要改成多例就行了。Spring注入接口还是实现类,其实都可以,但是推荐注入接口,调用的时候不需要关心底层用的哪个实现,只管调用就行了。