@Autowired 和 @Resource:从简单入手聊聊依赖注入的那些事儿/@Primary @Qualifer

@Autowired 和 @Resource:从简单入手聊聊依赖注入的那些事儿

在 Java 开发中,尤其是用 Spring 框架的时候,依赖注入(Dependency Injection,简称 DI)是个绕不开的话题。Spring 提供了不少工具让我们把对象之间的依赖关系管理得井井有条,其中最常见的就是 @Autowired 和 @Resource 这两个注解。今天咱们就从最简单的用法聊起,一步步看看它们有啥区别,遇到啥问题,再聊聊怎么优化,最后对接上现在主流的方案。


先从最简单的开始:直接用起来

假设我们有个超简单的场景:一个 UserService,里面需要调用 UserRepository 来查用户信息。最朴素的办法是直接 new 一个对象:

 

java

代码解读

复制代码

public class UserService { private UserRepository userRepository = new UserRepository(); public String getUserName(int id) { return userRepository.findNameById(id); } }

这代码看起来没啥毛病,运行也正常。但问题很快就来了:UserRepository 如果是个接口,后面想换成不同的实现(比如从内存查数据改成从数据库查),咋办?直接改代码?那不得累死。而且如果 UserRepository 本身还有别的依赖(比如数据库连接),这手动 new 的方式就更麻烦了,得一层一层自己去创建。

这时候,Spring 的依赖注入就派上场了。咱们把创建对象的事儿交给 Spring,用注解来告诉它:“嘿,这个字段你帮我填一下。”


@Autowired:Spring 家的老朋友

Spring 提供了 @Autowired,用起来特别简单:

 

java

代码解读

复制代码

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private UserRepository userRepository; public String getUserName(int id) { return userRepository.findNameById(id); } }

加上 @Autowired,Spring 就会在启动的时候自动找到 UserRepository 的实例,注入到 UserService 里。简单吧?确实省事儿,默认是按照类型(by type)去找匹配的 bean。

但这朴素的用法有隐患。比如说,如果 Spring 容器里有两个 UserRepository 的实现类(比如 MemoryUserRepository 和 DbUserRepository),Spring 就懵了,会抛个异常:NoUniqueBeanDefinitionException,意思是“哥们儿,我不知道该挑哪个”。

再比如,如果 UserRepository 压根没被 Spring 管理(没加 @Component 或者没配置成 bean),Spring 又会报错:NoSuchBeanDefinitionException,这时候你还得手动去检查配置。


@Resource:J2EE 的通用选手

那 @Resource 呢?它是 Java EE 标准里的东西,不像 @Autowired 是 Spring 专属的。用法也很直白:

 

java

代码解读

复制代码

import javax.annotation.Resource; import org.springframework.stereotype.Service; @Service public class UserService { @Resource private UserRepository userRepository; public String getUserName(int id) { return userRepository.findNameById(id); } }

跟 @Autowired 比,@Resource 默认是按名字(by name)匹配的。如果没指定名字,它会先找字段名(比如 userRepository)对应的 bean,找不到再退回到按类型匹配。这听着是不是更聪明点儿?

但它也有自己的小麻烦。比如你字段名叫 userRepository,但容器里压根没有叫这个名字的 bean,只有个类型匹配的,那它还得 fallback 到类型匹配,这时候又可能撞上多个 bean 的问题,跟 @Autowired 一样翻车。


朴素策略的问题暴露出来了

咱们先停下来想想,这两个注解用最简单的方式上手虽然方便,但问题不少:

  1. 模糊匹配的尴尬:不管是 @Autowired 按类型还是 @Resource 按名字优先,容器里 bean 一多就容易乱套。
  2. 空指针风险:字段注入(field injection)看着简洁,但如果 bean 没注入成功(比如配置漏了),调用的时候就得小心空指针。
  3. 测试不友好:直接在字段上加注解,单元测试时没法轻松 mock 出 UserRepository,得依赖 Spring 容器。

这些不利因素咋解决呢?咱们得往更靠谱的方向优化。


第一步优化:加上限定符

针对“多个 bean 不知道选哪个”的问题,Spring 给 @Autowired 配了个帮手:@Qualifier。比如:

 

java

代码解读

复制代码

@Service public class UserService { @Autowired @Qualifier("dbUserRepository") private UserRepository userRepository; }

这时候 Spring 会去找容器里名字叫 dbUserRepository 的那个 bean,类型还得是 UserRepository,这样就精准多了。

@Resource 也有类似玩法,直接指定 name:

 

java

代码解读

复制代码

@Service public class UserService { @Resource(name = "dbUserRepository") private UserRepository userRepository; }

这步优化已经能解决模糊匹配的问题了,跟现在主流做法也靠得更近了:明确指定依赖,避免容器自己瞎猜。


再进一步:构造器注入

光解决匹配还不够,字段注入的空指针和测试问题咋办?主流方案是改用构造器注入。看看咋写:

 

java

代码解读

复制代码

@Service public class UserService { private final UserRepository userRepository; @Autowired public UserService(UserRepository userRepository) { this.userRepository = userRepository; } public String getUserName(int id) { return userRepository.findNameById(id); } }

为啥这招好?

  • 强制依赖:构造器里要啥,Spring 必须给啥,少一个都不行,启动时就能发现问题,不像字段注入可能运行时才炸。
  • 不可变性:加个 finaluserRepository 就不会被改,稳得一批。
  • 测试方便:单元测试时直接 new 一个 UserService,传个 mock 的 UserRepository 就行,不用 Spring 容器。

其实 Spring 官方文档也推荐构造器注入,@Autowired 可以省掉(Spring 4.3 之后单构造器默认自动注入),代码还能更简洁:

 

java

代码解读

复制代码

@Service public class UserService { private final UserRepository userRepository; public UserService(UserRepository userRepository) { this.userRepository = userRepository; } }

@Resource 没法用在构造器上,所以这块是 @Autowired 的主场。


补充注解:@Primary 和 @Named

有时候你不想每次都写 @Qualifier,可以给某个 bean 加个 @Primary,告诉 Spring:“多个同类型 bean 的时候,默认挑我。”

 

java

代码解读

复制代码

@Component @Primary public class DbUserRepository implements UserRepository { // 实现略 }

这样 @Autowired 直接用,不用额外指定也能挑到 DbUserRepository

如果是 Java EE 环境,@Named 跟 @Qualifier 有点像,用来指定 bean 的名字,常跟 @Inject(JSR-330 标准)搭配。不过 Spring 也支持 @Named,跟 @Resource 的 name 效果差不多。


聊聊优化的方向和主流方案

从最朴素的 new 对象,到字段注入,再到构造器注入,咱们一步步逼近了现代开发的标配。主流方案里,构造器注入 + @Primary + 显式限定符已经是标配了。为啥?这背后其实是面向对象设计里的“依赖倒置”和“单一职责”思想:

  • 解耦:类不自己创建依赖,全交给容器,改实现不改代码。
  • 可测性:构造器注入让测试变得简单,mock 啥都行。
  • 安全性:启动时检查依赖,运行时少踩坑。

再往深了走,像 Spring Boot 这种现代框架,还会结合自动配置(AutoConfiguration)和条件注解(@Conditional),让依赖注入更智能。不过那是另一话题了,咱们今天就先聊到这儿。


总结一下

@Autowired 是 Spring 的原生选手,按类型匹配,灵活但需要 @Qualifier 帮忙精准定位;@Resource 是 J2EE 的通用方案,按名字优先,跨框架用着顺手。简单用都行,但想做得扎实,构造器注入加上显式指定是王道。代码少点坑,开发少点泪,这不就是咱们想要的吗?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值