Bean 名称生成规则
在 Spring 框架中,Bean 的名称决定了我们在容器中如何获取它。理解命名规则是避免冲突的关键。
类注解生成的 Bean 名称
当使用 @Component、@Service 等注解时,Spring 会根据类名自动生成 Bean 名称:
- 情况 1:类名前两个字母都是大写 → Bean 名称直接等于类名本身。
- 例:
class HTTPClient {}→ Bean 名称为HTTPClient
- 例:
- 情况 2:其他情况 → Bean 名称是类名的小驼峰写法(首字母小写)。
- 例:
class UserService {}→ Bean 名称为userService
- 例:
@Bean 注解的 Bean 名称
在配置类中使用 @Bean 标注方法时,Bean 名称直接等于方法名。
@Bean
public UserService userService() {
return new UserService();
}
此时 Bean 名称即为 userService。
如果想自定义 Bean 名称,可以直接在注解中指定:
@Bean(name = "myOrderDao")
public OrderDao orderDao() { return new OrderDao(); }
或者简写为:
@Bean("u1")
public User user1(){
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
这样既可以通过 user1 也可以通过 u1 获取对象。
定义多个同类型对象
同一个类可以定义多个对象,常见于多数据源场景。但要注意,如果通过类型获取,Spring 会报错提示存在多个匹配项。
@Component
public class BeanConfig {
@Bean
public User user1(){
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
@Bean
public User user2(){
User user = new User();
user.setName("lisi");
user.setAge(19);
return user;
}
}
运行后尝试通过类型获取:
ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
User user = context.getBean(User.class); // 这里会抛出异常
报错信息通常提示期望只有一个匹配,结果发现了多个。此时需要根据 Bean 名称来获取:
User user1 = (User) context.getBean("user1");
User user2 = (User) context.getBean("user2");
System.out.println(user1);
System.out.println(user2);
组件扫描路径控制
使用 @Component 等注解声明的 Bean 不一定都会生效,前提是它们必须被 Spring 扫描到。
默认扫描范围
Spring Boot 的 @SpringBootApplication 已包含 @ComponentScan,默认扫描启动类所在的包及其所有子包。因此,只要把 Bean 放在启动类包或子目录下,无需额外配置即可生效。
手动配置扫描
如果遇到以下情况,需要手动添加 @ComponentScan:
- Bean 所在的包不在启动类的包或子包下。
- 想精确控制扫描范围(例如只扫描
service和controller包)。 - 想排除某些不想被扫描的类。
基础用法示例:
@ComponentScan({"com.example.service", "com.example.controller"})
@SpringBootApplication
public class SpringIocDemoApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
}
}
进阶用法支持包含或排除特定类:
@ComponentScan(
basePackages = "com.example",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = UserService.class
)
)
@SpringBootApplication
public class SpringIocDemoApplication {
// ...
}
注意:配置类上的 @Configuration 标记也需要被扫描到,否则其中的 @Bean 方法不会被执行。
依赖注入方式详解
依赖注入(DI)是 IoC 容器创建 Bean 时提供依赖资源的过程。Spring 主要提供三种注入方式。
1. 属性注入(Field Injection)
通过 @Autowired 注解直接作用于字段。
@Controller
public class UserController {
@Autowired
private UserService userService;
public void sayHi(){
userService.sayHi();
}
}
优点简洁,缺点是无法注入 final 修饰的属性,且容易在使用前出现空指针异常。
2. 构造方法注入(Constructor Injection)
在构造方法中实现注入,这是 Spring 4.X 之后官方推荐的方式。
@Controller
public class UserController2 {
private final UserService userService;
@Autowired
public UserController2(UserService userService) {
this.userService = userService;
}
}
如果类只有一个构造方法,@Autowired 可以省略。这种方式能保证依赖对象在使用前完全初始化,且支持 final 属性。
3. Setter 注入(Setter Injection)
通过 Setter 方法注入,需在方法上添加 @Autowired。
@Controller
public class UserController3 {
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
}
适合需要在实例化后重新配置的场景,但注入对象可能被修改。
解决注入冲突
当同一类型存在多个 Bean 时,直接使用 @Autowired 会报错。Spring 提供了以下几种解决方案:
使用 @Primary
指定某个 Bean 为默认实现。
@Component
public class BeanConfig {
@Primary
@Bean("u1")
public User user1(){ /* ... */ }
@Bean
public User user2(){ /* ... */ }
}
使用 @Qualifier
配合 @Autowired 指定具体的 Bean 名称。
@Autowired
@Qualifier("user2")
private User user;
使用 @Resource
JDK 提供的注解,默认按名称注入。
@Resource(name = "user2")
private User user;
@Autowired 与 @Resource 的区别
| 特性 | @Autowired | @Resource |
|---|---|---|
| 来源 | Spring 框架 | JDK (Java EE) |
| 默认注入方式 | 按类型 (byType) | 按名称 (byName) |
| 指定名称 | 需配合 @Qualifier | 直接用 name 属性 |
| 兼容性 | 仅 Spring 容器 | 通用,兼容所有 Java EE 容器 |
在实际开发中,若项目仅使用 Spring 生态,推荐优先用 @Autowired + @Qualifier;若需跨容器兼容,可考虑 @Resource。


