Spring依赖注入注解详解:@Autowired、@Resource、@Inject

Spring框架提供了多种依赖注入方式,其中@Autowired@Resource@Inject是最常用的三种注解。本文将深入分析它们的实现原理、使用场景和实际案例。

图片[1]_Spring依赖注入注解详解:@Autowired、@Resource、@Inject_知途无界

一、核心注解对比

特性@Autowired@Resource@Inject
来源Spring特有JSR-250 (Java标准)JSR-330 (Java标准)
默认注入方式按类型(type)按名称(name)按类型(type)
是否支持required支持不支持支持
名称指定需配合@Qualifier自带name属性需配合@Named
构造器注入支持不支持支持
适用场景Spring项目内部跨框架通用跨框架通用

二、@Autowired 深度解析

1. 实现原理

@Autowired由Spring的AutowiredAnnotationBeanPostProcessor处理,实现逻辑:

  1. 遍历字段、方法和构造器
  2. 通过BeanFactory按类型查找匹配bean
  3. 解决歧义性(有多个候选bean时)
  4. 通过反射设置依赖

2. 使用案例

字段注入(最常见)

@Service
public class OrderService {
    @Autowired 
    private PaymentService paymentService;
}

构造器注入(推荐方式)

@Service
public class OrderService {
    private final PaymentService paymentService;

    @Autowired // Spring 4.3+可省略
    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}

方法注入

@Configuration
public class AppConfig {
    @Bean
    @Autowired // 可省略
    public DataSource dataSource(Environment env) {
        // 创建并返回DataSource
    }
}

解决歧义性

@Service
public class OrderService {
    @Autowired
    @Qualifier("wechatPayment") 
    private PaymentService paymentService;
}

@Component
@Qualifier("wechatPayment")
public class WechatPayment implements PaymentService {}

@Component
@Qualifier("alipayPayment")
public class Alipay implements PaymentService {}

3. 源码关键逻辑

// AutowiredAnnotationBeanPostProcessor核心方法
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    try {
        metadata.inject(bean, beanName, pvs);
    } catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
    }
    return pvs;
}

三、@Resource 深度解析

1. 实现原理

@ResourceCommonAnnotationBeanPostProcessor处理:

  1. 如果指定name,按名称查找
  2. 未指定name时:
  • 字段/方法:使用属性名作为bean名称
  • 类:使用类名首字母小写作为bean名称
  1. 按名称查找失败则回退到按类型查找

2. 使用案例

按名称注入

@Service
public class UserService {
    @Resource(name = "mysqlUserDao")
    private UserDao userDao;
}

@Repository("mysqlUserDao")
public class MysqlUserDao implements UserDao {}

@Repository("mongoUserDao")
public class MongoUserDao implements UserDao {}

按默认名称注入

@Service
public class UserService {
    @Resource // 相当于@Resource(name="userDaoImpl")
    private UserDao userDaoImpl;
}

@Repository
public class UserDaoImpl implements UserDao {}

3. 源码关键逻辑

// CommonAnnotationBeanPostProcessor处理逻辑
protected Object autowireResource(BeanFactory factory, LookupElement element, String requestingBeanName) {
    if (StringUtils.hasLength(element.mappedName)) {
        return factory.getBean(element.mappedName, element.lookupType);
    }
    if (factory instanceof ListableBeanFactory) {
        // 按类型查找逻辑
    }
    return factory.getBean(element.lookupType);
}

四、@Inject 深度解析

1. 实现原理

@Inject是JSR-330标准注解,需添加依赖:

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

AutowiredAnnotationBeanPostProcessor处理,行为与@Autowired相似但功能更简单。

2. 使用案例

基本注入

@Service
public class ProductService {
    @Inject 
    private InventoryService inventoryService;
}

配合@Named指定名称

@Service
public class ProductService {
    @Inject
    @Named("remoteInventory")
    private InventoryService inventoryService;
}

@Component
@Named("remoteInventory")
public class RemoteInventoryService implements InventoryService {}

3. 与@Autowired的区别

  • 不支持required=false属性
  • 没有@Primary概念
  • 需要额外依赖

五、综合对比与最佳实践

1. 注入方式选择建议

场景推荐注解
Spring项目内部@Autowired
需要按名称注入@Resource
跨框架通用代码@Inject或@Resource
构造器注入(不可变依赖)@Autowired

2. 生产环境最佳实践

推荐做法

// 1. 优先使用构造器注入
@Service
public class OrderService {
    private final PaymentService paymentService;

    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}

// 2. 需要按名称注入时使用@Resource
@Controller
public class UserController {
    @Resource(name = "jdbcTemplate")
    private JdbcTemplate template;
}

// 3. 测试中使用@Inject保持可移植性
@RunWith(SpringRunner.class)
public class ServiceTest {
    @Inject
    private UserService userService;
}

避免的用法

// 反例1:滥用字段注入(不利于测试和不可变性)
@Controller
public class BadExample {
    @Autowired private AService a;
    @Autowired private BService b;
}

// 反例2:混合使用不同注解风格
@Service
public class ConfusingExample {
    @Autowired
    @Resource(name="foo")
    private SomeService service; // 混淆的注入策略
}

六、常见问题解决方案

1. 解决NoSuchBeanDefinitionException

场景:缺少required依赖

// 方案1:标记非必须(仅@Autowired支持)
@Autowired(required = false)
private Optional<SomeService> service;

// 方案2:提供默认实现
@Autowired
private SomeService service = new DefaultSomeService();

2. 解决NoUniqueBeanDefinitionException

场景:存在多个候选bean

// 方案1:使用@Primary标记主候选
@Component
@Primary
public class PrimaryServiceImpl implements SomeService {}

// 方案2:精确指定bean名称
@Autowired
@Qualifier("specificImpl")
private SomeService service;

3. 循环依赖问题

解决方案

  1. 重构代码消除循环依赖(最佳方案)
  2. 使用setter注入代替构造器注入
  3. 使用@Lazy延迟初始化
@Service
public class ServiceA {
    @Lazy
    @Autowired
    private ServiceB serviceB;
}

七、高级应用场景

1. 自定义限定符

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface DatabaseType {
    String value();
}

@Component
@DatabaseType("mysql")
public class MysqlDao implements DataDao {}

@Service
public class ClientService {
    @Autowired
    @DatabaseType("mysql")
    private DataDao dataDao;
}

2. 集合类型注入

@Service
public class Processor {
    // 注入所有实现ProcessorStrategy的bean
    @Autowired
    private List<ProcessorStrategy> strategies;

    // 注入Map(key为bean名称)
    @Autowired
    private Map<String, ProcessorStrategy> strategyMap;
}

3. 方法参数注入

@Configuration
public class Config {
    @Bean
    public DataSource dataSource(
            @Value("${db.url}") String url,
            @Autowired DatabaseConfig config) {
        // 使用注入的参数创建DataSource
    }
}

通过深入理解这些注解的原理和使用方式,可以更灵活地管理Spring应用中的依赖关系,构建更健壮、可测试的应用程序。

© 版权声明
THE END
喜欢就点个赞,支持一下吧!
点赞64 分享
Every dog has its day.
风水轮流转
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容