SpringIoC&DI

文章目录

  • SpringIoC&DI
    • 什么是spring
    • 什么是容器
    • 什么是IoC
    • IoC思想
      • 传统方式
      • IoC写法
    • DI
      • Spring IoC/DI
    • IoC详细用法
      • 获取Bean的方式
      • @Controller(控制器存储)
      • @Service(服务存储)
      • @Component(组件存储)
      • @Repository(仓库存储)
      • @Configuration(配置存储)
      • 为什么要有这么多注解?
      • 方法注解@Bean
        • 单对象
        • 多对象
        • 重命名
    • 扫描路径
    • DI详解
      • 属性注入
      • 构造方法注入
      • Setter注入
      • 三种注入优缺点
      • @Autowired存在的问题
        • 1. @Primary
        • 2. @Qualifier
        • 3. @Resource
  • 总结
    • 告诉Spring管理bean、bean的存储
    • Bean的名称
    • 获取Bean的方式
      • BeanFeactory和ApplicationContext区别
        • 八股文回答
    • 扫描路径

什么是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

public class Main {public static void main(String[] args) {Car car = new Car();car.run();}}
public class Car {private Framework framework;public Car(){framework = new Framework();System.out.println("cat init........");}public void run() {System.out.println("car run.........");}}
public class Framework {private Bottom bottom;public Framework() {bottom = new Bottom();System.out.println("framework init.....");}}
public class Bottom {private Tire tire;public Bottom() {tire = new Tire();System.out.println("bottom init.....");}}
public class Tire {private int size = 17;public Tire() {System.out.println("tire size = 17");}}

运行:

tire size = 17bottom init.....framework init.....cat init........car run.........

但是这样写耦合太高。

一处改 处处改

IoC写法

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();}}
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........");}}
public class Framework {private Bottom bottom;public Framework(Bottom bottom) {this.bottom = bottom;System.out.println("framework init...");}}
public class Bottom {private Tire tire;public Bottom(Tire tire) {this.tire = tire;System.out.println("bottom init...");}}
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 控制并创建了

FrameworkFramework 创建并创建了 Bottom,依次往下,⽽改进之后的控制权发⽣的反转,不再

是使⽤⽅对象创建并控制依赖对象了,⽽是把依赖对象注⼊将当前对象中,依赖对象的控制权不再由

当前类控制了.

这样的话, 即使依赖类发⽣任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是 IoC

实现思想。

这部分代码, 就是IoC容器做的⼯作.

从上⾯也可以看出来, IoC容器具备以下优点:

资源不由使⽤资源的双⽅管理,⽽由不使⽤资源的第三⽅管理,这可以带来很多好处。第⼀,资源集中管理,实现资源的可配置和易管理。第⼆,降低了使⽤资源双⽅的依赖程度,也就是我们说的耦合度。

  1. 资源集中管理: IoC容器会帮我们管理⼀些资源(对象等), 我们需要使⽤时, 只需要从IoC容器中去取就可以了

  2. 我们在创建实例的时候不需要了解其中的细节, 降低了使⽤资源双⽅的依赖程度, 也就是耦合度.

Spring 就是⼀种IoC容器, 帮助我们来做了这些资源管理


DI

DI: Dependency Injection(依赖注⼊)

容器在运⾏期间, 动态的为应⽤程序提供运⾏时所依赖的资源,称之为依赖注⼊。

程序运⾏时需要某个资源,此时容器就为其提供这个资源.

从这点来看, 依赖注⼊(DI)和控制反转(IoC)是从不同的⻆度的描述的同⼀件事情,就是指通过引⼊ IoC 容器,利⽤依赖关系注⼊的⽅式,实现对象之间的解耦。

上述代码中, 是通过构造函数的⽅式, 把依赖对象注⼊到需要使⽤的对象中的

IoC 是⼀种思想,也是”⽬标”, ⽽思想只是⼀种指导原则,最终还是要有可⾏的落地⽅案,⽽ DI 就属于

具体的实现。所以也可以说, DIIoC的⼀种实现

⽐如说我今天⼼情⽐较好,吃⼀顿好的犒劳犒劳⾃⼰,那么”吃⼀顿好的”是思想和⽬标(是IoC),但最后我是吃海底捞还是杨国福?这就是具体的实现,就是 DI

Spring IoC/DI

对象的管理:

  1. 存对象 @Component
  2. 取对象@AutoWired

IoC详细用法

五大注解:

  • @Controller
  • @Service
  • @Component
  • @Repository
  • Configuration

方法注解:

  • @Bean

获取Bean的方式

  1. 通过类型获取 Bean:
    使用 ApplicationContextgetBean() 方法根据类型获取 Bean。

    // 假设有一个 UserService 接口和其实现类 UserServiceImplUserService userService = applicationContext.getBean(UserService.class);
  2. 通过名称获取 Bean:
    使用 ApplicationContextgetBean() 方法根据 Bean 的名称获取。

    // 假设有一个名为 "userService" 的 BeanUserService userService = (UserService) applicationContext.getBean("userService");
  3. 通过类型和名称获取 Bean:
    使用 ApplicationContextgetBean() 方法根据类型和名称获取 Bean。

    // 假设有一个名为 "userService" 的 BeanUserService userService = applicationContext.getBean("userService", UserService.class);
  4. 通过 @Autowired 注解自动注入:
    在需要使用的地方使用 @Autowired 注解进行自动注入。

    @Autowiredprivate UserService userService;
  5. 通过构造函数参数注入:
    在构造函数中接收 Bean 对象作为参数。

    public class UserController {private final UserService userService;public UserController(UserService userService) {this.userService = userService;}}
  6. 通过@Resource 注解注入:
    使用 @Resource 注解进行注入,可以指定 Bean 的名称。

    @Resource(name = "userService")private UserService userService;

@Controller(控制器存储)

@Controllerpublic class UserController {public void sayHi(){System.out.println("hello, UserController...");}}
// 根据类型去拿@SpringBootApplicationpublic 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(服务存储)

@Servicepublic class UserService {public void syaHi(){System.out.println("hello UserService...");}}
// 根据名称去拿@SpringBootApplicationpublic class IocApplication {public static void main(String[] args) {UserService userService = (UserService) context.getBean("userService");userService.syaHi();}}

@Component(组件存储)

// 根据名称("userComponent")和类型(UserComponent.class)来获取的@SpringBootApplicationpublic class IocApplication {public static void main(String[] args) {UserComponent userComponent = context.getBean("userComponent", UserComponent.class);userComponent.sayHi();}}
@Componentpublic class UserComponent {public void sayHi(){System.out.println("hello,UserComponent...");}}

@Repository(仓库存储)

@Repositorypublic class UserRepository {public void sayHi(){System.out.println("hello, userRepository");}}
@SpringBootApplicationpublic class IocApplication {public static void main(String[] args) {UserRepository userRepository = (UserRepository) context.getBean("userRepository");userRepository.sayHi();}}

@Configuration(配置存储)

@Configurationpublic class UserConfig {public void sayHi(){System.out.println("hello,userConfig");}}
@SpringBootApplicationpublic 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:配置层. 处理项⽬中的⼀些配置信息

五大注解,从概念上还被赋予了别的含义

  1. @Component:用于将一个类标识为组件,并将其纳入 Spring 的管理中。它是通用的注解,可以用于任何层次的组件,如控制器、服务、存储库等。
  2. @Repository:用于标识一个数据访问层(DAO)的类。它表示一个持久化操作相关的类,负责与数据库或其他数据存储进行交互。
  3. @Service:用于标识一个服务层(Service)的类。它表示一个业务逻辑相关的类,通常用于封装和处理复杂的业务逻辑。
  4. @Controller:用于标识一个控制器层(Controller)的类。它表示一个处理 HTTP 请求和响应的类,通常用于处理用户的请求并返回相应的视图或数据。除了具备让Spring管理的功能之外,接口的入口,必须为@Controller
  5. @Configuration:用于标识一个配置类,它是 Spring 中定义 bean 的一种方式。通过在类上添加 @Configuration 注解,可以将该类声明为一个配置类,其中可以定义 bean 的创建和配置。

这五个注解之间有一定的关系和应用场景:

  1. @Component 是一个通用的注解,用于将一个类标识为组件。它可以用于任何层次的组件,包括控制器、服务、存储库等。其他四个注解 @Repository@Service@Controller 都是特定类型的组件,它们实际上是通过 @Component 注解派生而来的,具有更具体的语义。
  2. @Repository 注解通常用于标识数据访问层(DAO)的类,表示它是与数据库或其他数据存储进行交互的组件。@Repository 注解还具有将数据库相关的异常转换为 Spring 的数据访问异常的功能。
  3. @Service 注解通常用于标识服务层(Service)的类,表示它是应用程序中的一个服务组件。服务层通常封装和处理复杂的业务逻辑,并与其他组件进行交互。
  4. @Controller 注解通常用于标识控制器层(Controller)的类,表示它是处理 HTTP 请求和响应的组件。控制器层接收用户的请求,调用适当的服务层组件进行处理,并返回相应的视图或数据。
  5. @Configuration 注解用于标识配置类,其中定义了创建和配置 bean 的方法。配置类通常包含 @Bean 注解,用于声明和定义 Spring bean。其他组件类可以通过 @Autowired 注解注入这些配置类中定义的 bean。

这些注解之间的关系是:@Repository@Service@Controller 注解都是通过 @Component 注解派生而来的,它们具有更具体的语义,用于在组件层次上提供更明确的分类和区分。而 @Configuration 注解用于标识配置类,提供了声明和定义 bean 的方式。通过使用这些注解,我们可以更好地组织和管理应用程序中的组件,并实现依赖注入和其他特定的功能。

其他四个注解是@Component的衍生类

程序的应用分层,调用流程如下:

方法注解@Bean

类注解是添加到某个类上的, 但是存在两个问题:

  1. 使⽤外部包⾥的类, 没办法添加类注解

  2. ⼀个类, 需要多个对象, ⽐如多个数据源

这种场景, 我们就需要使⽤⽅法注解 @Bean

举个例子:

单对象
public class BeanConfig {@Beanpublic UserInfo userInfo(){UserInfo userInfo = new UserInfo();userInfo.setId(5);userInfo.setName("zhangsan");userInfo.setAge(18);return userInfo;}}
@SpringBootApplicationpublic class IocApplication {public static void main(String[] args) {UserInfo userInfo = context.getBean(UserInfo.class);System.out.println(userInfo);}}

报错。

原因就是在 Spring 框架的设计中,⽅法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中

@Configurationpublic class BeanConfig {@Beanpublic UserInfo userInfo(){UserInfo userInfo = new UserInfo();userInfo.setId(5);userInfo.setName("zhangsan");userInfo.setAge(18);return userInfo;}}

这样既可。

多对象

@Bean注解定义的对象,默认名称为方法名

@Configurationpublic class BeanConfig {@Beanpublic UserInfo userInfo(){UserInfo userInfo = new UserInfo();userInfo.setId(5);userInfo.setName("zhangsan");userInfo.setAge(18);return userInfo;}@Beanpublic UserInfo userInfo1(){UserInfo userInfo1 = new UserInfo();userInfo1.setId(6);userInfo1.setName("6666");userInfo1.setAge(66);return userInfo1;}}
@SpringBootApplicationpublic 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);}}
重命名
@Configurationpublic class BeanConfig {@Bean("u1")public UserInfo userInfo(){UserInfo userInfo = new UserInfo();userInfo.setId(5);userInfo.setName("zhangsan");userInfo.setAge(18);return userInfo;}}
@SpringBootApplicationpublic 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详解

依赖注入有三种方式:

  1. 属性注入
  2. 构造方法注入
  3. Setter注入

属性注入

@Autowired

@Controllerpublic class UserController {// 把 UserService 注入进来@Autowiredprivate UserService userService;public void sayHi(){System.out.println("hello, UserController...");userService.syaHi();}}

构造方法注入

@Controllerpublic class UserController {private UserService userService;public UserController(UserService userService) {this.userService = userService;}public void sayHi(){System.out.println("hello, UserController...");userService.syaHi();}}
@Controllerpublic 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 会报错

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指定构造函数

@Controllerpublic class UserController {private UserService userService;private UserConfig userConfig;public UserController(){}public UserController(UserService userService) {this.userService = userService;}@Autowiredpublic UserController(UserService userService, UserConfig userConfig) {this.userService = userService;this.userConfig = userConfig;}public void sayHi(){System.out.println("hello, UserController...");userService.syaHi();userConfig.sayHi();}}

Setter注入

@Controllerpublic class UserController {private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}public void sayHi(){System.out.println("hello, UserController...");userService.syaHi();}}

如果需要注入多个属性,就需要注入多个@Autowired

@Controllerpublic class UserController {@Autowiredprivate UserService userService;@Autowiredprivate UserComponent userConponent'public void sayHi(){System.out.println("hello, UserController...");userService.syaHi();userComponent.sayHi();}}

三种注入优缺点

  • 属性注⼊

    • 优点: 简洁,使⽤⽅便;
    • 缺点:
      • 只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指针异常)
      • 不能注⼊⼀个Final修饰的属性
  • 构造函数注⼊(Spring 4.X推荐)

    • 优点:

      • 可以注⼊final修饰的属性

        • final修饰的属性有一个要求,需要满足一下条件

          1. 声明的时候需要完成初始化
          2. 在构造函数中进行赋值
      • 注⼊的对象不会被修改

      • 依赖对象在使⽤前⼀定会被完全初始化,因为依赖是在类的构造⽅法中执⾏的,⽽构造⽅法是在类加载阶段就会执⾏的⽅法.

      • 通⽤性好, 构造⽅法是JDK⽀持的, 所以更换任何框架,他都是适⽤的

    • 缺点:

      • 注⼊多个对象时, 代码会⽐较繁琐
  • Setter注⼊(Spring 3.X推荐)

    • 优点: ⽅便在类实例之后, 重新对该对象进⾏配置或者注⼊
    • 缺点:
      • 不能注⼊⼀个Final修饰的属性
      • 注⼊对象可能会被改变, 因为setter⽅法可能会被多次调⽤, 就有被修改的⻛险.

@Autowired存在的问题

@Controllerpublic class UserController2 {@Autowiredprivate UserInfo userInfo;public void sayHi(){System.out.println("hello, UserController...");System.out.println(userInfo);}}
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]

当同样的类型存在多个对象的时候,可能会报错

@Autowiredprivate UserInfo userInfo;

先根据名称来获取,如果获取到了,正确响应…

如果没有获取到,就根据类型匹配,此时,如果匹配到多个,则报错。

通常做法:

不使用变量名称来指定获取某个bean,而是通过其他手段来指定bean的名称。

通常我们会认为变量名的修改不影响我们的业务逻辑处理


如何解决上述问题呢?Spring提供了以下⼏种解决⽅案:

  • @Primary
  • @Qualifier
  • @Resource
1. @Primary

@Primary 注解用于标识一个 Bean 为首选项,当存在多个候选 Bean 时,被标记为 @Primary 的 Bean 会被优先选择进行注入。

示例:

@Component@Primarypublic class PrimaryDataSource implements DataSource {// 实现 DataSource 接口的相关逻辑}
@Componentpublic class SecondaryDataSource implements DataSource {// 实现 DataSource 接口的相关逻辑}

在这个示例中,当需要注入 DataSource 类型的 Bean 时,如果没有明确指定使用哪个 Bean,Spring 将会选择使用 PrimaryDataSource

2. @Qualifier

@Qualifier 注解用于指定注入的 Bean 名称或者 Bean 的限定符(Qualifier),用于消除歧义,明确指定要注入的是哪个 Bean。

示例:

@Component@Qualifier("primary")public class PrimaryDataSource implements DataSource {// 实现 DataSource 接口的相关逻辑}
@Component@Qualifier("secondary")public class SecondaryDataSource implements DataSource {// 实现 DataSource 接口的相关逻辑}
@Componentpublic class DataSourceService {@Autowired@Qualifier("primary") // 指定注入 primary Beanprivate DataSource dataSource;}

在这个示例中,DataSourceService 类中的 dataSource 字段会被注入 PrimaryDataSource Bean。

3. @Resource

@Resource 注解是 JavaEE 提供的一种依赖注入方式,Spring 也支持该注解。它默认按照名称(即 Bean 的名称)进行自动装配,当无法按照名称匹配时,再按照类型匹配。

示例:

@Componentpublic class PrimaryDataSource implements DataSource {// 实现 DataSource 接口的相关逻辑}
@Componentpublic class SecondaryDataSource implements DataSource {// 实现 DataSource 接口的相关逻辑}
@Componentpublic class DataSourceService {@Resource // 默认按照名称匹配,会注入 PrimaryDataSource Beanprivate DataSource dataSource;}

在这个示例中,DataSourceService 类中的 dataSource 字段会被注入 PrimaryDataSource Bean,因为 PrimaryDataSource 是唯一匹配的 Bean。

总结

告诉Spring管理bean、bean的存储

  1. 类注解:五大注解

    • @Controller
    • @Service
    • @Repository
    • @Configuration
    • @Component
  2. 方法注解:@Bean

    @Bean必须和方法注解一起使用

Bean的名称

使用场景:

  1. 五大注解:自己开发的程序
  2. @Bean
    1. 存储第三方的对象(代码不在自己的项目中)
    2. 一个类型需要创建多个对象的时候
  1. 五大注解

    类名首字母小写,如果前两位字母均为大写,则为原类名

    也可以指定Bean的名称 指定方法:@Controller("beanName")

  2. @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 是需要那个才去加载那个,因此更加轻量. (空间换时间)
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可以指定扫描路径