SpringIoC容器通过@Controller、@Service、@Repository、@Component、@Configuration五大类注解以及@Bean方法注解来注册和管理Bean对象,默认采用单例模式。依赖注入支持属性注入、构造方法注入和Setter注入三种方式,其中构造方法注入被推荐。当存在多个同类型Bean时,可通过@Primary、@Q
先聊聊Bean的存储。上一节我们提到,要把某个对象交给IoC容器管理,直接在类上添加一个@Component注解就行。但Spring为了适配Web开发,还提供了更细分的注解,让不同层次的代码各司其职。
类注解:@Controller、@Service、@Repository、@Component、@Configuration
长期稳定更新的攒劲资源: >>>点此立即查看<<<
方法注解:@Bean
用@Controller来存储Bean,操作起来非常直观。

接下来,从Spring容器中把这个对象取出来看看。

上面这段代码是根据类型来查找对象的。但问题来了——如果同一个类型在Spring容器中有多个Bean,该怎么办?(关于Bean的命名,上一节已经聊过,这里就不重复了。)
1.根据bean名称获取bean
Object getBean(String var1)throws BeansException;
2.根据bean名称和类型获取bean
T getBean(String var1,Class var2)throws BeansException; 3.根据类型获取bean
T getBean(Class var1)throws BeansException; 4. 按bean名称和构造函数动态创建bean,只适用于具有原型(prototype)作用域的bean
Object getBean(String var1,Class
var2)throws BeansException; 5.按bean类型和构造函数动态创建bean,只适用于具有原型(prototype)作用域的bean
T getBean(String var1,Class var2)throws BeansException;
实际开发中,前三种方法的出镜率最高,后面几个注解也一样,咱们就重点展示这三种。

检查一下,拿到的这几个对象是不是同一个。

从地址来看,完全一致,说明获取的是同一个Bean对象。Spring容器默认是单例模式,这点很关键。
用@Service来存储Bean,用法和@Controller几乎一样。

读取Bean也是同样的套路。

@Repository,顾名思义,用于数据访问层。

读取Bean的方式,依然是一脉相承。

@Component是通用组件注解,它像是一个基础款,其他注解都是它的衍生品。

读取Bean,步骤不变。

@Configuration用于处理项目的配置信息,比如数据源配置。

读取Bean,没毛病。

@Controller、@Service、@Repository、@Configuration 都是@Component的衍生注解。除了@Controller不完全等同于@ResponseBody,其余衍生注解主要做分层语义区分,底层功能其实一致——都是把类交给Spring IoC容器管理。

打个比方:杯子有喝水的杯子,也有刷牙的杯子。虽然都是杯子,但咱们日常还是倾向于用刷牙的杯子刷牙,用喝水的杯子喝水。这些注解的角色划分,本质上也是为了让代码结构更清晰。
上面介绍了五大类注解,但实际开发中会遇到两个棘手的问题:
1. 外部包里的类,没法添加类注解
2. 一个类需要多个对象,比如配置多个数据源
这两个场景,就需要请出方法注解@Bean了。注意,@Bean注解必须搭配五大类注解才能使用。


在Spring的设计中,@Bean方法注解必须配合类注解,才能把对象正常存储到容器中。
如果把@Component注掉试试看?





问题来了:用五大注解声明的Bean,一定会生效吗?
答案是不一定。 原因很简单:Bean想要生效,还得被Spring扫描到才行。
下面,我们通过调整项目工程的目录结构,来测试一下Bean是否生效。

然后运行下面的代码:
@SpringBootApplication
public class SpringIocDemoApplication {
public static void main(String[] args) {
// 获取 Spring 上下文对象
ApplicationContext context =
SpringApplication.run(SpringIocDemoApplication.class, args);
// 从 Spring 上下文中获取对象
User u1 = (User) context.getBean("u1");
// 使用对象
System.out.println(u1);
}
}
运行结果:

报错了: 找不到名称为“u1”的Bean。
为什么找不到?
使用五大注解声明的Bean,要想生效,还得配置扫描路径,让Spring把这些注解扫进来。这个任务就交给@ComponentScan。
@ComponentScan({"com.example.demo"})
@SpringBootApplication
public class SpringIocDemoApplication {
public static void main(String[] args) {
// 获取 Spring 上下文对象
ApplicationContext context =
SpringApplication.run(SpringIocDemoApplication.class, args);
// 从 Spring 上下文中获取对象
User u1 = (User) context.getBean("u1");
// 使用对象
System.out.println(u1);
}
}
{} 里可以配置多个包路径,比如:@ComponentScan({"com.example.demo", "com.example.service"})。
注意: 这种做法了解一下即可,并不推荐在生产环境使用。
那为什么前面没有显式配置@ComponentScan也能跑起来呢?因为@ComponentScan其实已经包含在启动类上的@SpringBootApplication注解里了。默认情况下,它会扫描启动类所在包及其所有子包。
推荐做法: 把启动类放在我们希望扫描的根包路径下,这样所有定义的Bean都能被自动扫描到。
@ComponentScan注解可以指定要扫描的包路径。合理配置扫描路径,是确保Spring能够正确发现和管理所有Bean组件的基础,也是IoC容器正常工作的前提。
上面聊的都是控制反转(IoC)的细节,接下来看看依赖注入(DI)。
依赖注入这个过程, 简单说,就是IoC容器在创建Bean时,顺手把运行时需要的资源(也就是其他对象)给塞进去。
Spring提供了三种依赖注入的方式:
代码实现如下:

运行效果:

代码实现:

运行效果:

如果只有一个构造方法,@Autowired可以省略。
如果有多个构造方法,默认采用无参构造方法。
可以通过@Autowired来指定使用哪个构造方法。
代码实现:

运行效果:

Setter注入和属性的Setter方法实现类似,区别在于set方法上需要加上@Autowired注解。
Spring提供了三种依赖注入方式,各有各的适用场景。了解它们之间的差异,能在实际开发中做出更合适的选择。
优点:
@Autowired就完事。缺点:
final修饰的属性。优点:
final修饰的属性,保证依赖不可变。缺点:
注意事项:如果类只有一个构造方法,@Autowired可以省略;如果有多个构造方法,需要加@Autowired来指定用哪个。
优点:
缺点:
final修饰的属性。| 特性 | 属性注入 | 构造方法注入 | Setter 注入 |
|---|---|---|---|
| 代码简洁性 | ★★★★★ | ★★★☆☆ | ★★★★☆ |
| 不可变性 | ★☆☆☆☆ | ★★★★★ | ★★☆☆☆ |
| 测试友好性 | ★★☆☆☆ | ★★★★★ | ★★★★☆ |
| 框架通用性 | ★☆☆☆☆ | ★★★★★ | ★★★★☆ |
| 循环依赖处理 | ★★★★☆ | ★☆☆☆☆ | ★★★★☆ |
| Spring 官方推荐 | Spring 4.x 之前 | Spring 4.x 之后 | Spring 3.x 推荐 |
在实际开发中,Spring官方从4.x版本开始推荐构造方法注入,因为它能保证依赖的不可变性和完全初始化,同时提高代码的可测试性。当然,具体选哪种,还得看项目需求和团队规范。
当同一个类型存在多个Bean时,@Autowired就会出问题。

Spring提供了三种解决方案:
用@Primary注解:当存在多个相同类型的Bean时,加上@Primary,指定一个默认实现。

用@Qualifier注解:通过value属性指定要注入的Bean名称。注意,@Qualifier不能单独使用,必须配合@Autowired。

用@Resource注解:按Bean的名称进行注入,通过name属性指定要注入的Bean名称。

感觉这几天有点昼夜颠倒,好困好困好困好累好累,怀疑是不是日常没什么运动量导致身体虚虚的。想报个游泳班,但是都好贵啊,最后还是决定不报了,等后面有机会了再报吧。明天吃完火锅后,就开始减肥控糖,一定要瘦瘦瘦。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述