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 控制并创建了
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。// 假设有一个 UserService 接口和其实现类 UserServiceImplUserService userService = applicationContext.getBean(UserService.class);
通过名称获取 Bean:
使用ApplicationContext
的getBean()
方法根据 Bean 的名称获取。// 假设有一个名为 "userService" 的 BeanUserService userService = (UserService) applicationContext.getBean("userService");
通过类型和名称获取 Bean:
使用ApplicationContext
的getBean()
方法根据类型和名称获取 Bean。// 假设有一个名为 "userService" 的 BeanUserService userService = applicationContext.getBean("userService", UserService.class);
通过 @Autowired 注解自动注入:
在需要使用的地方使用@Autowired
注解进行自动注入。@Autowiredprivate UserService userService;
通过构造函数参数注入:
在构造函数中接收 Bean 对象作为参数。public class UserController {private final UserService userService;public UserController(UserService userService) {this.userService = userService;}}
通过@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
:配置层. 处理项⽬中的⼀些配置信息
五大注解,从概念上还被赋予了别的含义
@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
举个例子:
单对象
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详解
依赖注入有三种方式:
- 属性注入
- 构造方法注入
- 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修饰的属性有一个要求,需要满足一下条件
- 声明的时候需要完成初始化
- 在构造函数中进行赋值
注⼊的对象不会被修改
依赖对象在使⽤前⼀定会被完全初始化,因为依赖是在类的构造⽅法中执⾏的,⽽构造⽅法是在类加载阶段就会执⾏的⽅法.
通⽤性好, 构造⽅法是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的存储
类注解:五大注解
- @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 是需要那个才去加载那个,因此更加轻量. (空间换时间)
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
可以指定扫描路径