什么是 IoC 控制反转
IoC (Inversion of Control:控制反转)是一种设计思想,而不是一个具体的技术实现。
IoC 的思想就是:将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。
注意:IoC 并非 Spring 特有,在其他语言中也有应用。
为什么叫控制反转?
什么是 IoC 容器
在 Spring 中, IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个 Map(key,value),Map 中存放的是各种对象。
工作流程:
main
)需要哪个对象就往 IoC 容器拿就好。简单来说,就是起到了一个中间站的作用。
总结
Spring IoC 是 Spring 框架的核心特性之一。
它通过 IoC 容器来管理对象,实现了控制反转和依赖注入。
在 Spring 中,IoC 容器负责管理对象的创建和依赖关系的维护,应用程序只需要通过 IoC 容器获取所需的对象即可。
在出现 IOC 之前,项目中是如何使用 Bean 的?
在 Spring 框架的出现之前,项目中通常使用【手动创建】和【管理对象】的方式来使用 “bean”,尤其是在 JavaEE(现在称为 Jakarta EE)开发中。下面是在出现 IOC(控制反转)和 Spring 之前,项目中如何使用 “bean” 的一些常见做法:
new
关键字来创建对象,然后自行处理对象的生命周期。(例如 new 对象,结合 get 方法使用)总之,在没有 IOC 和 Spring 框架之前,项目的代码可能会更加繁琐和冗长,需要开发人员手动管理对象的创建、注入和生命周期。Spring 框架的出现极大地简化了这些过程,通过 IOC 容器和依赖注入,让开发人员更专注于业务逻辑,而无需过多关注对象的创建和管理。
Spring 循环依赖:简单说就是自己依赖自己,或者和别的 Bean 相互依赖。
只有单例的 Bean 才存在循环依赖的情况,原型(Prototype)情况下,Spring 会直接抛出异常。原因很简单,AB 循环依赖,A 实例化的时候,发现依赖 B,创建 B 实例,创建 B 的时候发现需要 A,创建 A1 实例······无限套娃,直接把系统干垮。
当循环依赖的实例都采用 setter 方法注入的时候,Spring 可以支持,
都采用构造器注入的时候,不支持,
构造器注入和 setter 注入同时存在的时候,看天。
原因是 Spring 在创建 Bean 时默认会根据自然排序进行创建。比如 A 会先于 B 进行创建。
简单来说,Bean 代指的就是那些被 IoC 容器所管理的对象。
工作流程:
在应用程序运行时
Spring IoC 容器会根据 Bean 的定义和配置,创建对应的 Bean 对象,并对其进行初始化和依赖注入等操作。
一旦 Bean 对象被创建并注入了依赖关系,就可以被其他对象所引用和使用了。
在应用程序结束时
Spring IoC 容器会对 Bean 对象进行销毁操作,释放资源,从而完成 Bean 的生命周期。
@Component
:通用的注解,可标注任意类为 Spring
组件。
如果一个 Bean 不知道属于哪个层,可以使用 @Component
注解标注。(config
配置层常用)
@Repository
: 对应持久层即 Dao
层,主要用于数据库相关操作。
@Service
: 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。
@Controller
: 对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service
层返回数据给前端页面。
使用方式不同
@Component
注解作用于类,
而 @Bean
注解作用于方法。
装配方式不同
@Component
通常是通过类路径扫描来自动侦测以及自动装配到 Spring 容器中(我们可以使用 @ComponentScan
注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。
@Bean
注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean
告诉了 Spring 这是某个类的实例,当我需要用它的时候还给我。
简单来说,就是告诉 Spring,这个方法会返回一个对象,需要被 Spring 管理,当需要使用这个对象的时候,就可以通过注入的方式来获取它。
@Bean
注解比 @Component
注解的自定义性更强
@Bean
注解来注册 bean。比如当我们引用第三方库中的类需要装配到 Spring
容器时,则只能通过 @Bean
来实现。有三种:
@Autowired
@Resource
和 @Inject
@Autowired
是 Spring 提供的注解,@Resource
是 JDK 提供的注解。
Autowired
默认的注入方式为byType
(根据类型进行匹配),@Resource
默认注入方式为 byName
(根据名称进行匹配)。
当一个接口存在多个实现类的情况下,@Autowired
和 @Resource
都需要通过名称才能正确匹配到对应的 Bean。
Autowired
可以通过 @Qualifier
注解来显式指定名称,
@Resource
可以通过 name
属性来显式指定名称。
Bean 的作用域指的是:Bean 实例在容器中存在的范围。
通过配置 Bean 的作用域,可以控制在何种情况下容器会创建新的 Bean 实例,以及何时销毁 Bean 实例。
Spring 框架提供了以下 5 种作用域:
singleton
:单例模式,一个 Bean 在整个应用中只有一个实例(默认模式)。prototype
:原型模式,每次请求获取 Bean 时,都会创建一个新的实例。request
:请求作用域,每个 HTTP 请求都会创建一个新的实例。session
:会话作用域,每个 HTTP 会话都会创建一个新的实例。application/global-session
:全局会话作用域,每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。**注意:**作用域为 request
、session
和 global-session
的 Bean 只有在 Web 应用中才能使用。
配置方式
可以使用 @Scope
注解来指定 Bean 的作用域。
1、注解形式
@Configuration
public class AppConfig {
@Bean(name = "userService")
@Scope("singleton")
public UserService userService() {
return new UserServiceImpl();
}
}
2、xml 形式
<bean id="userService" class="com.example.UserService" scope="singleton">
bean>
原因
单例 Bean 存在线程问题,主要是因为当多个线程操作同一个对象的时候存在资源竞争。
有两种常见的解决方法
ThreadLocal
成员变量,将需要的可变成员变量保存在 ThreadLocal
中(推荐)。无状态的 bean 是线程安全的
不过,大部分 Bean 实际都是无状态(没有实例变量)的(比如 Dao、Service),这种情况下,Bean 是线程安全的。
Bean 的生命周期可以分为以下几个阶段:
Bean 的处理过程可以干扰吗?
可以通过改一些配置信息来进行干扰。
需要注意的点
如果 Bean 实现了 DisposableBean 接口或指定了 destroy-method 方法,容器会自动调用 Bean 的销毁方法。
但如果应用程序是非正常关闭的,如直接关闭 JVM 进程,容器就无法进行正常的销毁操作,这时需要通过注册钩子函数,在 JVM 关闭时手动调用销毁方法。
AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,它通过将程序的横切关注点(如日志、事务、权限控制等)从业务逻辑中剥离出来,并通过切面与业务逻辑进行解耦,从而提高程序的模块化、可维护性和可扩展性。
其中核心是使用动态代理技术,在运行时生成代理对象,并将切面织入到目标对象的方法调用过程中。
实现方式
Spring 框架提供了两种方式来实现 AOP:
JDK
动态代理(对象实现了某个接口的情况下使用)或者 CGLIB
动态代理来实现。AspectJ
框架来实现。AOP 切面编程设计到的 5 个专业术语
AOP 的核心概念是切面(Aspect)、连接点(Join Point)、切点(Pointcut)、通知(Advice)和织入(Weaving)。
优点