SpringIoC&DI
文章目录
- SpringIoC&DI
- 总结
什么是spring
Spring是⼀个开源框架, 他让我们的开发更加简单. 他⽀持⼴泛的应⽤场
景, 有着活跃⽽庞⼤的社区, 这也是Spring能够⻓久不衰的原因.
但是这个概念相对来说, 还是⽐较抽象.
我们⽤⼀句更具体的话来概括Spring, 那就是: Spring 是包含了众多⼯具⽅法的 IoC
容器
那问题来了,什么是容器?什么是 IoC
容器?接下来我们⼀起来看
Spring的两大核心思想:Ioc
AOP
什么是容器
容器是⽤来容纳某种物品的(基本)装置。⸺来⾃:百度百科
⽣活中的⽔杯, 垃圾桶, 冰箱等等这些都是容器.
我们想想,之前课程我们接触的容器有哪些?
-
List/Map -> 数据存储容器
-
Tomcat -> Web 容器
-
学校 -> 学生容器
什么是IoC
IoC:控制反转
也就是说 Spring 是⼀个"控制反转"的容器
什么是控制反转呢? 也就是控制权反转. 什么的控制权发⽣了反转? 获得依赖对象的过程被反转了
也就是说, 当需要某个对象时, 传统开发模式中需要⾃⼰通过 new 创建对象, 现在不需要再进⾏创
建, 把创建对象的任务交给容器, 程序中只需要依赖注⼊ (Dependency Injection,DI)就可以了.
这个容器称为:IoC容器. Spring是⼀个IoC容器, 所以有时Spring 也称为Spring 容器
IoC思想
举一个方便理解的例子:造车
传统方式
先设计轮子tire
,设计底盘bottom
,设计车身framework
,设计汽车car
java
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.run();
}
}
java
public class Car {
private Framework framework;
public Car(){
framework = new Framework();
System.out.println("cat init........");
}
public void run() {
System.out.println("car run.........");
}
}
java
public class Framework {
private Bottom bottom;
public Framework() {
bottom = new Bottom();
System.out.println("framework init.....");
}
}
java
public class Bottom {
private Tire tire;
public Bottom() {
tire = new Tire();
System.out.println("bottom init.....");
}
}
java
public class Tire {
private int size = 17;
public Tire() {
System.out.println("tire size = 17");
}
}
运行:
java
tire size = 17
bottom init.....
framework init.....
cat init........
car run.........
但是这样写耦合太高。
一处改 处处改
IoC写法
java
spring的工作
public class Main {
public static void main(String[] args) {
Tire tire = new Tire(17);
Bottom bottom = new Bottom(tire);
Framework framework = new Framework(bottom);
Car car = new Car(framework);
car.run();
}
}
java
public class Car {
private Framework framework;
public Car(Framework framework) {
this.framework = framework;
System.out.println("car init.......");
}
public void run() {
System.out.println("car run........");
}
}
java
public class Framework {
private Bottom bottom;
public Framework(Bottom bottom) {
this.bottom = bottom;
System.out.println("framework init...");
}
}
java
public class Bottom {
private Tire tire;
public Bottom(Tire tire) {
this.tire = tire;
System.out.println("bottom init...");
}
}
java
public class Tire {
private int size;
public Tire(int size) {
System.out.println("tire size = " + size);
}
}
在传统的代码中对象创建顺序是:Car -> Framework -> Bottom -> Tire
改进之后解耦的代码的对象创建顺序是:Tire -> Bottom -> Framework -> Car
我们发现了⼀个规律,通⽤程序的实现代码,类的创建顺序是反的,传统代码是 Car 控制并创建了
Framework
,Framework
创建并创建了 Bottom
,依次往下,⽽改进之后的控制权发⽣的反转,不再
是使⽤⽅对象创建并控制依赖对象了,⽽是把依赖对象注⼊将当前对象中,依赖对象的控制权不再由
当前类控制了.
这样的话, 即使依赖类发⽣任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是 IoC
的
实现思想。
这部分代码, 就是IoC
容器做的⼯作.
从上⾯也可以看出来, IoC
容器具备以下优点:
资源不由使⽤资源的双⽅管理,⽽由不使⽤资源的第三⽅管理,这可以带来很多好处。第⼀,资源集中管理,实现资源的可配置和易管理。第⼆,降低了使⽤资源双⽅的依赖程度,也就是我们说的耦合度。
-
资源集中管理:
IoC
容器会帮我们管理⼀些资源(对象等), 我们需要使⽤时, 只需要从IoC
容器中去取就可以了 -
我们在创建实例的时候不需要了解其中的细节, 降低了使⽤资源双⽅的依赖程度, 也就是耦合度.
Spring
就是⼀种IoC
容器, 帮助我们来做了这些资源管理
DI
DI
: Dependency
Injection
(依赖注⼊)
容器在运⾏期间, 动态的为应⽤程序提供运⾏时所依赖的资源,称之为依赖注⼊。
程序运⾏时需要某个资源,此时容器就为其提供这个资源.
从这点来看, 依赖注⼊(
DI
)和控制反转(IoC
)是从不同的⻆度的描述的同⼀件事情,就是指通过引⼊IoC
容器,利⽤依赖关系注⼊的⽅式,实现对象之间的解耦。
上述代码中, 是通过构造函数的⽅式, 把依赖对象注⼊到需要使⽤的对象中的
IoC
是⼀种思想,也是"⽬标", ⽽思想只是⼀种指导原则,最终还是要有可⾏的落地⽅案,⽽ DI
就属于
具体的实现。所以也可以说, DI
是IoC
的⼀种实现
⽐如说我今天⼼情⽐较好,吃⼀顿好的犒劳犒劳⾃⼰,那么"吃⼀顿好的"是思想和⽬标(是
IoC
),但最后我是吃海底捞还是杨国福?这就是具体的实现,就是DI
。
Spring IoC/DI
对象的管理:
- 存对象
@Component
- 取对象
@AutoWired
IoC详细用法
五大注解:
@Controller
@Service
@Component
@Repository
Configuration
方法注解:
@Bean
获取Bean的方式
-
通过类型获取 Bean:
使用
ApplicationContext
的getBean()
方法根据类型获取 Bean。java// 假设有一个 UserService 接口和其实现类 UserServiceImpl UserService userService = applicationContext.getBean(UserService.class);
-
通过名称获取 Bean:
使用
ApplicationContext
的getBean()
方法根据 Bean 的名称获取。java// 假设有一个名为 "userService" 的 Bean UserService userService = (UserService) applicationContext.getBean("userService");
-
通过类型和名称获取 Bean:
使用
ApplicationContext
的getBean()
方法根据类型和名称获取 Bean。java// 假设有一个名为 "userService" 的 Bean UserService userService = applicationContext.getBean("userService", UserService.class);
-
通过 @Autowired 注解自动注入:
在需要使用的地方使用
@Autowired
注解进行自动注入。java@Autowired private UserService userService;
-
通过构造函数参数注入:
在构造函数中接收 Bean 对象作为参数。
javapublic class UserController { private final UserService userService; public UserController(UserService userService) { this.userService = userService; } }
-
通过@Resource 注解注入:
使用
@Resource
注解进行注入,可以指定 Bean 的名称。java@Resource(name = "userService") private UserService userService;
@Controller(控制器存储)
java
@Controller
public class UserController {
public void sayHi(){
System.out.println("hello, UserController...");
}
}
java
// 根据类型去拿
@SpringBootApplication
public class IocApplication {
public static void main(String[] args) {
// ApplicationContext : Spring 上下文、运行环境
ApplicationContext context = SpringApplication.run(IocApplication.class, args);
UserController bean = context.getBean(UserController.class);
bean.sayHi();
}
}
ApplicationContext
翻译过来就是: Spring 上下⽂因为对象都交给 Spring 管理了,所以获取对象要从 Spring 中获取,那么就得先得到 Spring 的上下⽂
关于上下⽂的概念
上学时, 阅读理解经常会这样问: 根据上下⽂, 说⼀下你对XX的理解
在计算机领域, 上下⽂这个概念, 咱们最早是在学习线程时了解到过, ⽐如我们应⽤进⾏线程切换的时
候,切换前都会把线程的状态信息暂时储存起来,这⾥的上下⽂就包括了当前线程的信息,等下次该
线程⼜得到
CPU
时间的时候, 从上下⽂中拿到线程上次运⾏的信息这个上下⽂, 就是指当前的运⾏环境, 也可以看作是⼀个容器, 容器⾥存了很多内容, 这些内容是当前
运⾏的环境
@Service(服务存储)
java
@Service
public class UserService {
public void syaHi(){
System.out.println("hello UserService...");
}
}
java
// 根据名称去拿
@SpringBootApplication
public class IocApplication {
public static void main(String[] args) {
UserService userService = (UserService) context.getBean("userService");
userService.syaHi();
}
}
@Component(组件存储)
java
// 根据名称("userComponent")和类型(UserComponent.class)来获取的
@SpringBootApplication
public class IocApplication {
public static void main(String[] args) {
UserComponent userComponent = context.getBean("userComponent", UserComponent.class);
userComponent.sayHi();
}
}
java
@Component
public class UserComponent {
public void sayHi(){
System.out.println("hello,UserComponent...");
}
}
@Repository(仓库存储)
java
@Repository
public class UserRepository {
public void sayHi(){
System.out.println("hello, userRepository");
}
}
java
@SpringBootApplication
public class IocApplication {
public static void main(String[] args) {
UserRepository userRepository = (UserRepository) context.getBean("userRepository");
userRepository.sayHi();
}
}
@Configuration(配置存储)
java
@Configuration
public class UserConfig {
public void sayHi(){
System.out.println("hello,userConfig");
}
}
java
@SpringBootApplication
public class IocApplication {
public static void main(String[] args) {
UserConfig userConfig = context.getBean(UserConfig.class);
userConfig.sayHi();
}
}
为什么要有这么多注解?
这和每个省/市都有⾃⼰的⻋牌号是⼀样的.
⻋牌号都是唯⼀的, 标识⼀个⻋辆的. 但是为什么还需要设置不同的⻋牌开头呢.
⽐如陕西的⻋牌号就是:陕X:XXXXXX,北京的⻋牌号:京X:XXXXXX,甚⾄⼀个省不同的县区也
是不同的,⽐如西安就是,陕A:XXXXX,咸阳:陕B:XXXXXX,宝鸡,陕C:XXXXXX,⼀样.
这样做的好处除了可以节约号码之外,更重要的作⽤是可以直观的标识⼀辆⻋的归属地.
-
@
Controller
:控制层, 接收请求, 对请求进⾏处理, 并进⾏响应. -
@
Servie
:业务逻辑层, 处理具体的业务逻辑. -
@
Repository
:数据访问层,也称为持久层. 负责数据访问操作 -
@
Configuration
:配置层. 处理项⽬中的⼀些配置信息
五大注解,从概念上还被赋予了别的含义
@Component
:用于将一个类标识为组件,并将其纳入 Spring 的管理中。它是通用的注解,可以用于任何层次的组件,如控制器、服务、存储库等。@Repository
:用于标识一个数据访问层(DAO)的类。它表示一个持久化操作相关的类,负责与数据库或其他数据存储进行交互。@Service
:用于标识一个服务层(Service)的类。它表示一个业务逻辑相关的类,通常用于封装和处理复杂的业务逻辑。@Controller
:用于标识一个控制器层(Controller)的类。它表示一个处理 HTTP 请求和响应的类,通常用于处理用户的请求并返回相应的视图或数据。除了具备让Spring管理的功能之外,接口的入口,必须为@Controller@Configuration
:用于标识一个配置类,它是 Spring 中定义 bean 的一种方式。通过在类上添加@Configuration
注解,可以将该类声明为一个配置类,其中可以定义 bean 的创建和配置。
这五个注解之间有一定的关系和应用场景:
@Component
是一个通用的注解,用于将一个类标识为组件。它可以用于任何层次的组件,包括控制器、服务、存储库等。其他四个注解@Repository
、@Service
、@Controller
都是特定类型的组件,它们实际上是通过@Component
注解派生而来的,具有更具体的语义。@Repository
注解通常用于标识数据访问层(DAO)的类,表示它是与数据库或其他数据存储进行交互的组件。@Repository
注解还具有将数据库相关的异常转换为 Spring 的数据访问异常的功能。@Service
注解通常用于标识服务层(Service)的类,表示它是应用程序中的一个服务组件。服务层通常封装和处理复杂的业务逻辑,并与其他组件进行交互。@Controller
注解通常用于标识控制器层(Controller)的类,表示它是处理 HTTP 请求和响应的组件。控制器层接收用户的请求,调用适当的服务层组件进行处理,并返回相应的视图或数据。@Configuration
注解用于标识配置类,其中定义了创建和配置 bean 的方法。配置类通常包含@Bean
注解,用于声明和定义 Spring bean。其他组件类可以通过@Autowired
注解注入这些配置类中定义的 bean。
这些注解之间的关系是:@Repository
、@Service
、@Controller
注解都是通过 @Component
注解派生而来的,它们具有更具体的语义,用于在组件层次上提供更明确的分类和区分。而 @Configuration
注解用于标识配置类,提供了声明和定义 bean 的方式。通过使用这些注解,我们可以更好地组织和管理应用程序中的组件,并实现依赖注入和其他特定的功能。
其他四个注解是@Component的衍生类
程序的应用分层,调用流程如下:
方法注解@Bean
类注解是添加到某个类上的, 但是存在两个问题:
-
使⽤外部包⾥的类, 没办法添加类注解
-
⼀个类, 需要多个对象, ⽐如多个数据源
这种场景, 我们就需要使⽤⽅法注解 @Bean
举个例子:
单对象
java
public class BeanConfig {
@Bean
public UserInfo userInfo(){
UserInfo userInfo = new UserInfo();
userInfo.setId(5);
userInfo.setName("zhangsan");
userInfo.setAge(18);
return userInfo;
}
}
java
@SpringBootApplication
public class IocApplication {
public static void main(String[] args) {
UserInfo userInfo = context.getBean(UserInfo.class);
System.out.println(userInfo);
}
}
报错。
原因就是在 Spring 框架的设计中,⽅法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中
java
@Configuration
public class BeanConfig {
@Bean
public UserInfo userInfo(){
UserInfo userInfo = new UserInfo();
userInfo.setId(5);
userInfo.setName("zhangsan");
userInfo.setAge(18);
return userInfo;
}
}
这样既可。
多对象
@Bean
注解定义的对象,默认名称为方法名
java
@Configuration
public class BeanConfig {
@Bean
public UserInfo userInfo(){
UserInfo userInfo = new UserInfo();
userInfo.setId(5);
userInfo.setName("zhangsan");
userInfo.setAge(18);
return userInfo;
}
@Bean
public UserInfo userInfo1(){
UserInfo userInfo1 = new UserInfo();
userInfo1.setId(6);
userInfo1.setName("6666");
userInfo1.setAge(66);
return userInfo1;
}
}
java
@SpringBootApplication
public class IocApplication {
public static void main(String[] args) {
UserInfo userInfo = context.getBean("userInfo",UserInfo.class);
System.out.println(userInfo);
UserInfo userInfo1 = context.getBean("userInfo1",UserInfo.class);
System.out.println(userInfo1);
}
}
重命名
java
@Configuration
public class BeanConfig {
@Bean("u1")
public UserInfo userInfo(){
UserInfo userInfo = new UserInfo();
userInfo.setId(5);
userInfo.setName("zhangsan");
userInfo.setAge(18);
return userInfo;
}
}
java
@SpringBootApplication
public class IocApplication {
public static void main(String[] args) {
UserInfo userInfo = context.getBean("u1",UserInfo.class);
System.out.println(userInfo);
}
}
扫描路径
Q: 使⽤前⾯学习的四个注解声明的bean,⼀定会⽣效吗?
A: 不⼀定(原因:bean想要⽣效,还需要被Spring扫描)
DI详解
依赖注入有三种方式:
- 属性注入
- 构造方法注入
- Setter注入
属性注入
@Autowired
java
@Controller
public class UserController {
// 把 UserService 注入进来
@Autowired
private UserService userService;
public void sayHi(){
System.out.println("hello, UserController...");
userService.syaHi();
}
}
构造方法注入
java
@Controller
public class UserController {
private UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
public void sayHi(){
System.out.println("hello, UserController...");
userService.syaHi();
}
}
java
@Controller
public class UserController {
private UserService userService;
private UserConfig userConfig;
public UserController(UserService userService, UserConfig userConfig) {
this.userService = userService;
this.userConfig = userConfig;
}
public void sayHi(){
System.out.println("hello, UserController...");
userService.syaHi();
userConfig.sayHi();
}
}
当只有一个构造函数的时候,Spring 会知道使用哪个
当有多个构造函数的时候,Spring 会使用默认无参的构造函数,如果没有这个函数,Spring 会报错
java
Caused by: java.lang.NoSuchMethodException: org.haobin.ioc.demo.Controller.UserController.<init>()
at java.lang.Class.getConstructor0(Class.java:3082) ~[na:1.8.0_361]
at java.lang.Class.getDeclaredConstructor(Class.java:2178) ~[na:1.8.0_361]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:78) ~[spring-beans-5.3.23.jar:5.3.23]
... 18 common frames omitted
同样的 我们可以使用@Autowired
指定构造函数
java
@Controller
public class UserController {
private UserService userService;
private UserConfig userConfig;
public UserController(){
}
public UserController(UserService userService) {
this.userService = userService;
}
@Autowired
public UserController(UserService userService, UserConfig userConfig) {
this.userService = userService;
this.userConfig = userConfig;
}
public void sayHi(){
System.out.println("hello, UserController...");
userService.syaHi();
userConfig.sayHi();
}
}
Setter注入
java
@Controller
public class UserController {
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public void sayHi(){
System.out.println("hello, UserController...");
userService.syaHi();
}
}
如果需要注入多个属性,就需要注入多个@Autowired
java
@Controller
public class UserController {
@Autowired
private UserService userService;
@Autowired
private UserComponent userConponent'
public void sayHi(){
System.out.println("hello, UserController...");
userService.syaHi();
userComponent.sayHi();
}
}
三种注入优缺点
-
属性注⼊
- 优点: 简洁,使⽤⽅便;
- 缺点:
- 只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指针异常)
- 不能注⼊⼀个Final修饰的属性
-
构造函数注⼊(Spring 4.X推荐)
-
优点:
-
可以注⼊final修饰的属性
-
final修饰的属性有一个要求,需要满足一下条件
- 声明的时候需要完成初始化
- 在构造函数中进行赋值
-
-
注⼊的对象不会被修改
-
依赖对象在使⽤前⼀定会被完全初始化,因为依赖是在类的构造⽅法中执⾏的,⽽构造⽅法是在类加载阶段就会执⾏的⽅法.
-
通⽤性好, 构造⽅法是JDK⽀持的, 所以更换任何框架,他都是适⽤的
-
-
缺点:
- 注⼊多个对象时, 代码会⽐较繁琐
-
-
Setter注⼊(Spring 3.X推荐)
- 优点: ⽅便在类实例之后, 重新对该对象进⾏配置或者注⼊
- 缺点:
- 不能注⼊⼀个Final修饰的属性
- 注⼊对象可能会被改变, 因为setter⽅法可能会被多次调⽤, 就有被修改的⻛险.
@Autowired存在的问题
java
@Controller
public class UserController2 {
@Autowired
private UserInfo userInfo;
public void sayHi(){
System.out.println("hello, UserController...");
System.out.println(userInfo);
}
}
java
Description:
Field userInfo in org.xxx.ioc.demo.Controller.UserController2 required a single bean, but 2 were found:
- u1: defined by method 'userInfo' in class path resource [org/haobin/ioc/demo/config/BeanConfig.class]
- userInfo1: defined by method 'userInfo1' in class path resource [org/haobin/ioc/demo/config/BeanConfig.class]
当同样的类型存在多个对象的时候,可能会报错
java
@Autowired
private UserInfo userInfo;
先根据名称来获取,如果获取到了,正确响应...
如果没有获取到,就根据类型匹配,此时,如果匹配到多个,则报错。
通常做法:
不使用变量名称来指定获取某个bean,而是通过其他手段来指定bean的名称。
通常我们会认为变量名的修改不影响我们的业务逻辑处理
如何解决上述问题呢?Spring提供了以下⼏种解决⽅案:
- @Primary
- @Qualifier
- @Resource
1. @Primary
@Primary
注解用于标识一个 Bean 为首选项,当存在多个候选 Bean 时,被标记为 @Primary
的 Bean 会被优先选择进行注入。
示例:
java
@Component
@Primary
public class PrimaryDataSource implements DataSource {
// 实现 DataSource 接口的相关逻辑
}
java
@Component
public class SecondaryDataSource implements DataSource {
// 实现 DataSource 接口的相关逻辑
}
在这个示例中,当需要注入 DataSource
类型的 Bean 时,如果没有明确指定使用哪个 Bean,Spring 将会选择使用 PrimaryDataSource
。
2. @Qualifier
@Qualifier
注解用于指定注入的 Bean 名称或者 Bean 的限定符(Qualifier),用于消除歧义,明确指定要注入的是哪个 Bean。
示例:
java
@Component
@Qualifier("primary")
public class PrimaryDataSource implements DataSource {
// 实现 DataSource 接口的相关逻辑
}
java
@Component
@Qualifier("secondary")
public class SecondaryDataSource implements DataSource {
// 实现 DataSource 接口的相关逻辑
}
java
@Component
public class DataSourceService {
@Autowired
@Qualifier("primary") // 指定注入 primary Bean
private DataSource dataSource;
}
在这个示例中,DataSourceService
类中的 dataSource
字段会被注入 PrimaryDataSource
Bean。
3. @Resource
@Resource
注解是 JavaEE 提供的一种依赖注入方式,Spring 也支持该注解。它默认按照名称(即 Bean 的名称)进行自动装配,当无法按照名称匹配时,再按照类型匹配。
示例:
java
@Component
public class PrimaryDataSource implements DataSource {
// 实现 DataSource 接口的相关逻辑
}
java
@Component
public class SecondaryDataSource implements DataSource {
// 实现 DataSource 接口的相关逻辑
}
java
@Component
public class DataSourceService {
@Resource // 默认按照名称匹配,会注入 PrimaryDataSource Bean
private DataSource dataSource;
}
在这个示例中,DataSourceService
类中的 dataSource
字段会被注入 PrimaryDataSource
Bean,因为 PrimaryDataSource
是唯一匹配的 Bean。
总结
告诉Spring管理bean、bean的存储
-
类注解:五大注解
- @Controller
- @Service
- @Repository
- @Configuration
- @Component
-
方法注解:@Bean
@Bean必须和方法注解一起使用
Bean的名称
使用场景:
- 五大注解:自己开发的程序
- @Bean
- 存储第三方的对象(代码不在自己的项目中)
- 一个类型需要创建多个对象的时候
-
五大注解
类名首字母小写,如果前两位字母均为大写,则为原类名
也可以指定Bean的名称 指定方法:
@Controller("beanName")
-
@Bean
默认名称:方法名
也可以指定名称
@Bean("beanName")
获取Bean的方式
获取bean的功能是BeanFeactory提供的
BeanFeactory和ApplicationContext区别
"BeanFactory和ApplicationContext是Spring框架中两个重要的接口,它们在管理和访问bean对象方面有一些区别。
首先,BeanFactory是Spring框架的基础接口,提供了最简单的bean容器功能。它采用延迟加载策略,也就是说,只有在第一次访问bean时才会实例化对象。这样可以节省资源,但也可能导致在第一次访问时稍微延迟。
与之相比,ApplicationContext是BeanFactory的子接口,提供了更多的功能和特性。它在容器启动时会预加载和实例化所有的单例bean,这意味着它能够更早地发现配置错误,并且在启动时进行了校验和初始化。ApplicationContext还提供了对AOP的支持以及国际化资源处理的功能。
另外,BeanFactory对配置文件的处理是按需加载的,只有在需要的时候才会读取和解析配置文件,而ApplicationContext在容器启动时就会读取和解析配置文件。这也是为什么ApplicationContext能够提供更早的错误检测和更快的启动时间的原因之一。
所以,总的来说,BeanFactory适用于资源有限或对延迟加载有特定需求的情况,而ApplicationContext则提供了更多的特性和功能,适用于需要更高级功能的场景。在实际开发中,我们通常会使用ApplicationContext来获得更强大的容器管理和功能支持。"
八股文回答
- 继承关系和功能⽅⾯来说:Spring 容器有两个顶级的接⼝:BeanFactory 和ApplicationContext。其中 BeanFactory 提供了基础的访问容器的能⼒,⽽ApplicationContext 属于 BeanFactory 的⼦类,它除了继承了 BeanFactory 的所有功能之外,它还拥有独特的特性,还添加了对国际化⽀持、资源访问⽀持、以及事件传播等⽅⾯的⽀持.
- 从性能⽅⾯来说:ApplicationContext 是⼀次性加载并初始化所有的 Bean 对象,⽽BeanFactory 是需要那个才去加载那个,因此更加轻量. (空间换时间)
java
ApplicationContext context = SpringApplication.run(IocApplication.class, args);
UserController bean = context.getBean(UserController.class);
bean.sayHi();
UserService userService = (UserService) context.getBean("userService");
userService.syaHi();
UserComponent userComponent = context.getBean("userComponent", UserComponent.class);
userComponent.sayHi();
扫描路径
Spring 默认扫描路径是 启动类所在的路径。
@ComponentScan
可以指定扫描路径