乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 Spring概述与IoC容器

    • Spring概述与IoC容器
    • Spring Framework 概述
    • IoC 与 DI 核心概念
    • @Configuration 详解
    • @Component 详解
    • @ComponentScan 详解
    • @Import 详解
    • @Profile 详解
    • @PropertySource 详解
    • @Service 详解
    • @Repository 详解
  • 第2章 Bean的定义与依赖注入

    • Bean的定义与依赖注入
    • @Bean 详解
    • @Autowired 详解
    • @Qualifier 详解
    • @Primary 详解
    • @Resource 详解
    • @Inject 详解
    • @Named 详解
    • @Value 详解
    • @Scope 详解
    • @Lazy 详解
  • 第3章 Bean生命周期与作用域

    • Bean生命周期与作用域
    • Bean生命周期概述
    • @PostConstruct
    • @PreDestroy
    • InitializingBean
    • DisposableBean
    • BeanPostProcessor
    • BeanFactoryPostProcessor
  • 第4章 AOP面向切面编程

    • AOP面向切面编程
    • AOP核心概念
    • @EnableAspectJAutoProxy
    • @Aspect
    • @Pointcut
    • @Before
    • @After
    • @AfterReturning
    • @AfterThrowing
    • @Around
  • 第5章 数据访问与事务管理

    • 数据访问与事务管理
    • 数据访问概述
    • @EnableTransactionManagement
    • @Transactional
    • @Transactional 的传播行为
    • @Transactional 的隔离级别
    • @Transactional 的回滚规则
    • @Transactional 的超时与只读属性
    • @TransactionalEventListener
  • 第6章 Spring Boot自动配置基础

    • Spring Boot自动配置基础
    • @SpringBootApplication 注解
    • @EnableAutoConfiguration 注解
    • @ConfigurationProperties 注解
    • @ConditionalOnClass 注解
    • @ConditionalOnMissingBean 注解
    • @ConditionalOnProperty 注解
  • 第7章 从容器到Web: Spring MVC导引

    • Spring MVC 导引
  • 第8章 扩展阅读

    • 扩展阅读
    • Spring 事件机制 — ApplicationEvent / ApplicationListener
    • @EventListener
    • SpEL — Spring 表达式语言
    • 校验 Validation — JSR-303 / JSR-380 Bean Validation
    • 类型转换与数据绑定 — Converter / DataBinder
  • 附录

    • Spring Framework 专业术语
    • Spring 核心知识点
    • Spring 面试高频考点
    • Spring 核心注解速查表

@Named 详解

定义与作用

@Named 是 JSR-330(javax.inject)标准定义的注解,在 Spring 中承担双重角色:

  1. 作为组件标识:等价于 Spring 的 @Component,用于将类标记为受容器管理的 Bean
  2. 作为限定符:等价于 Spring 的 @Qualifier,用于在 @Inject 注入时精确指定 Bean 名称

在飞翔科技的学生管理系统中,架构师白歌在推行 JSR-330 标准注解以降低框架锁定时,@Named 是替换 @Component 和 @Qualifier 的核心工具。小崔在编写需要跨容器兼容的基础模块时,使用 @Named 替代 Spring 专有注解,使代码更符合 Jakarta EE 规范方向。

适用位置与常用属性

属性类型说明
valueString指定 Bean 的名称。作为组件标识时,等价于 @Component("name");作为限定符时,等价于 @Qualifier("name")

适用位置:

  • 类级别:标记类为 Bean,并指定其名称
  • 字段/参数级别:配合 @Inject 限定注入的 Bean 名称

核心原理

@Named 在 Spring 中的处理由两个不同的处理器负责:

  1. 类级别的 @Named:由 ClassPathBeanDefinitionScanner 在组件扫描阶段识别,功能与 @Component 完全相同。扫描到 @Named 后,会生成 BeanDefinition 并注册到容器,Bean 名称取自 @Named("value") 或类名的首字母小写形式。

  2. 注入点级别的 @Named:由 AutowiredAnnotationBeanPostProcessor 在依赖解析阶段识别,功能与 @Qualifier 完全相同。当 @Inject 遇到多个同类型候选时,@Named("value") 会按名称筛选出唯一的 Bean。

完整示例

场景简述

飞翔科技的学生管理系统中,StudentDao 接口有两个实现:JdbcStudentDao(稳定版)和 JpaStudentDao(新版)。白歌要求新模块全部使用 JSR-330 注解编写,小崔负责用 @Named 标记实现类,并在消费端通过 @Named 限定注入。

操作前:混用 Spring 专有注解

@Component("jdbcStudentDao")
public class JdbcStudentDao implements StudentDao { }

@Component("jpaStudentDao")
public class JpaStudentDao implements StudentDao { }

@Service
public class LegacyService {
    @Autowired
    @Qualifier("jdbcStudentDao")
    private StudentDao studentDao; // 使用了 Spring 专有注解
}

痛点分析:

  • 代码中同时出现 @Component、@Qualifier、@Service,全部是 Spring 特有注解
  • 如果未来迁移到 CDI(Contexts and Dependency Injection)容器,这些注解需要全部替换

使用该注解的完整代码

接口定义:

package com.feixiang.student.dao;

import com.feixiang.student.entity.Student;

import java.util.List;

public interface StudentDao {
    Student findById(Long id);
    List<Student> findAll();
}

实现类(使用 @Named 标记):

package com.feixiang.student.dao;

import com.feixiang.student.entity.Student;

import javax.inject.Named;
import java.util.ArrayList;
import java.util.List;

@Named("jdbcStudentDao")
public class JdbcStudentDao implements StudentDao {

    @Override
    public Student findById(Long id) {
        return new Student(id, "小崔-JDBC", 20, "计算机科学");
    }

    @Override
    public List<Student> findAll() {
        List<Student> list = new ArrayList<>();
        list.add(new Student(1L, "小崔", 20, "计算机科学"));
        return list;
    }
}
package com.feixiang.student.dao;

import com.feixiang.student.entity.Student;

import javax.inject.Named;
import java.util.ArrayList;
import java.util.List;

@Named("jpaStudentDao")
public class JpaStudentDao implements StudentDao {

    @Override
    public Student findById(Long id) {
        return new Student(id, "小崔-JPA", 20, "计算机科学");
    }

    @Override
    public List<Student> findAll() {
        List<Student> list = new ArrayList<>();
        list.add(new Student(1L, "小崔", 20, "计算机科学"));
        list.add(new Student(2L, "黄俪", 21, "软件工程"));
        return list;
    }
}

消费端(使用 @Inject + @Named):

package com.feixiang.student.service;

import com.feixiang.student.dao.StudentDao;
import com.feixiang.student.entity.Student;

import javax.inject.Inject;
import javax.inject.Named;
import java.util.List;

@Named // 等价于 @Component,将类标记为 Bean
public class StudentService {

    private final StudentDao studentDao;

    // 构造器注入:@Inject 配合 @Named 精确指定实现
    @Inject
    public StudentService(@Named("jpaStudentDao") StudentDao studentDao) {
        this.studentDao = studentDao;
    }

    public List<Student> listStudents() {
        return studentDao.findAll();
    }
}
package com.feixiang.student.legacy;

import com.feixiang.student.dao.StudentDao;
import com.feixiang.student.entity.Student;

import javax.inject.Inject;
import javax.inject.Named;

@Named
public class LegacyReportService {

    private final StudentDao studentDao;

    // 旧模块继续使用 JDBC 实现
    @Inject
    public LegacyReportService(@Named("jdbcStudentDao") StudentDao studentDao) {
        this.studentDao = studentDao;
    }

    public Student getStudent(Long id) {
        return studentDao.findById(id);
    }
}

配置类:

package com.feixiang.student.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = "com.feixiang.student")
public class AppConfig {
}

启动类:

package com.feixiang.student;

import com.feixiang.student.config.AppConfig;
import com.feixiang.student.legacy.LegacyReportService;
import com.feixiang.student.service.StudentService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class StudentApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx =
                new AnnotationConfigApplicationContext(AppConfig.class);

        StudentService studentService = ctx.getBean(StudentService.class);
        LegacyReportService legacyService = ctx.getBean(LegacyReportService.class);

        System.out.println("新模块查询结果数:" + studentService.listStudents().size());
        System.out.println("旧模块查询结果:" + legacyService.getStudent(1L).getName());

        ctx.close();
    }
}

操作后运行结果及分析

新模块查询结果数:2
旧模块查询结果:小崔-JDBC

变化分析:

  • JdbcStudentDao 和 JpaStudentDao 使用 @Named 标记,被组件扫描自动注册为 Bean,名称分别为 jdbcStudentDao 和 jpaStudentDao
  • StudentService 通过 @Inject + @Named("jpaStudentDao") 注入新版实现,查询返回 2 条数据
  • LegacyReportService 通过 @Inject + @Named("jdbcStudentDao") 注入旧版实现,保证旧模块行为稳定
  • 整个代码中未出现任何 org.springframework 包的注解(除了 @Configuration 和 @ComponentScan,这是启动配置所必需),业务代码与 Spring 解耦

@Named 与 @Component、@Qualifier 的映射关系

Spring 注解JSR-330 等价注解作用
@Component@Named标记类为 Spring 管理的 Bean
@Qualifier("xxx")@Named("xxx")按名称限定注入的 Bean
@Autowired@Inject按类型自动装配依赖

注意:@Named 作为组件标识时,Spring 的 ClassPathBeanDefinitionScanner 默认只识别 @Component 及其派生注解(@Service、@Repository、@Controller)。要让扫描器识别 @Named,需要确保 javax.inject 包在类路径中,Spring 会自动注册 Jsr330ScopeMetadataResolver 和 Jsr330Provider 来支持 JSR-330 注解扫描。

易错场景与面试考点

易错场景一:组件扫描未识别 @Named

@Configuration
@ComponentScan(basePackages = "com.feixiang.student")
public class AppConfig { }
@Named("studentDao")
public class JdbcStudentDao implements StudentDao { }

后果:如果项目中没有引入 javax.inject 依赖,Spring 的 ClassPathBeanDefinitionScanner 不会将 @Named 识别为组件注解,JdbcStudentDao 不会被注册到容器,启动时抛出 NoSuchBeanDefinitionException。

正确做法:确保 pom.xml 或 build.gradle 中包含 javax.inject 依赖:

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

易错场景二:@Named 未指定 value 导致名称冲突

@Named // 未指定 value,Bean 名称为 "jdbcStudentDao"
public class JdbcStudentDao implements StudentDao { }

@Named // 未指定 value,Bean 名称为 "jpaStudentDao"
public class JpaStudentDao implements StudentDao { }

@Named
public class StudentService {
    @Inject
    @Named // 错误!未指定 value,推导名称为 "studentDao"
    private StudentDao studentDao;
}

后果:@Named 在注入点未指定 value 时,Spring 会尝试按字段名 studentDao 查找 Bean,但容器中只有 jdbcStudentDao 和 jpaStudentDao,找不到匹配,回退按类型查找又遇到多个候选,最终报错。

正确做法:注入点必须显式指定 @Named("jdbcStudentDao") 或 @Named("jpaStudentDao")。

易错场景三:Spring Boot 3.x 使用 javax.inject

Spring Boot 3.x / Spring Framework 6.x 使用 Jakarta EE 9 命名空间,JSR-330 的包名从 javax.inject 变为 jakarta.inject。如果继续使用 javax.inject.Named,Spring 的扫描器不会识别。

正确做法:

  • Spring Boot 2.x:import javax.inject.Named;
  • Spring Boot 3.x:import jakarta.inject.Named;

面试考点

Q:@Named 和 @Component 有什么区别?

功能上完全等价,都是将类标记为 Spring 容器管理的 Bean。区别在于 @Named 是 JSR-330 标准注解,代码不绑定 Spring API;@Component 是 Spring 专有注解。在组件扫描时,Spring 对两者一视同仁(只要 javax.inject 在类路径中)。

Q:@Named 作为限定符时,和 @Qualifier 有什么区别?

功能等价。@Named("xxx") 在注入点的作用与 @Qualifier("xxx") 完全相同,都是在类型匹配后按名称筛选。@Named 的优势是标准规范,@Qualifier 的优势是可以作为元注解定义自定义限定符(如 @Master、@Slave),而 @Named 不支持元注解扩展。

Q:为什么 @Named 可以同时在类上和注入点上使用?

这是 JSR-330 规范的设计决策,用一个注解统一"命名 Bean"和"按名引用"两个语义,减少注解数量。Spring 的 @Component 和 @Qualifier 职责分离,意图更明确,但增加了学习成本。两者都是合理的设计选择。

Q:@Named 能否配合 @Autowired 使用?

可以,但不推荐混用。@Autowired + @Named("xxx") 在 Spring 中确实能工作(Spring 将 @Named 识别为 @Qualifier 的等价物),但语义上不一致:@Autowired 是 Spring 注解,@Named 是 JSR-330 注解。推荐保持风格统一:@Autowired 配 @Qualifier,@Inject 配 @Named。

上一页
@Inject 详解
下一页
@Value 详解