day13
一、Spring Bean 生命周期是怎样的?
详细过程分为以下几个步骤:
① 初始化 Bean
容器通过获取 BeanDefinition 中的信息进行实例化,这一步仅仅是简单的实例化,并没有进行依赖注入。
实例化的对象被包装在 BeanWrapper 对象中,BeanWrapper 提供了设置对象属性的接口,从而避免了使用反射机制来注入属性。
② 设置对象属性(依赖注入)
实例化后的 Bean 仍是一个原生的状态,然后 Spring 根据 BeanDefinition 中的信息进行依赖注入,并且通过 BeanWrapper 提供的设置属性的接口完成依赖注入。
③ 注入 Aware 接口
Spring 会检测该对象是否实现了 xxxAware 接口,并将相关的 xxxAware 实例注入给 bean。
BeanNameAware
:通过 Bean 的引用来获取 Bean 的 ID,一般业务中很少使用;BeanFactoryAware
:获取 Spring BeanFactory 容器,如发布事件等;ApplicationContextAwaer
:获取 Spring ApplicationContext 容器;
④ BeanPostProcessor 前置处理
如果 Spring 实现了
BeanPostProcessor
接口,pring 将调用它们的postProcessBeforeInitialization(Object bean, String beanName)
(预初始化)方法,作用是在 Bean 实例创建成功后对进行增强处理,如对 Bean 进行修改,增加某个功能。
⑤ InitializingBean 接口
InitializingBean
接口只有一个函数:afterPropertiesSet()
,作用是在 bean 正式构造完成前增加我们自己自定义的逻辑,与前置处理不同,该函数的入参没有 bean 对象, 因为无法处理 bean 对象本身,只能增加一些额外的逻辑。
⑥ init-method 声明
作用同 InitializingBean 接口。
⑦ BeanPostProcessor 后置处理
如果 Spring 实现了
BeanPostProcessor
接口,Spring 将调用它们的postProcessAfterInitialization(Object bean, String beanName)
(预初始化)方法,作用与 4 的一样,只不过初始化方法声明是在 Bean 初始化前执行的,而这个是在 Bean 初始化后执行的,时机不同。
⑧ Bean 初始化完成
经过以上的工作后,Bean 将一直驻留在应用上下文中给应用使用,直到应用上下文被销毁。
⑨ DispostbleBean 接口
DispostbleBean
接口只有一个函数:destroy()
在 bean 被销毁前执行此函数里的逻辑。
⑩ destroy-method 声明
作用同 DispostbleBean 接口。
简单描述分为以下几个步骤:
-
实例化:通过反射去推断构造函数进行实例化,主要使用的是
doCreateBean()
方法- 一般有静态工厂、实例工厂的方式进行实例化
-
属性赋值:解析自动装配(byName、byType、Constructor、@Autowired)
- 是 DI 的体现,将依赖的对象/属性值注入到需要创建的对象中
-
初始化:
-
调用 xxxAware 回调方法,这个过程是一个渐进过程,只有实现了 Aware 接口才会去调用,依此如下
- 调用 BeanNameAware 的
setBeanName()
方法 - 调用 BeanFactoryAware 的
setBeanFactory()
方法 - 调用 ApplicationContextAware 的
setApplicationContext()
方法
- 调用 BeanNameAware 的
-
Aware 接口实现之后,调用 BeanPostProcessor 的预初始化方法。调用 InitializingBean 的
afterPropertiesSet()
方法,调用定制的初始化方法,调用 BeanPostProcessor 的后初始化方法。(调用初始化生命周期回调,有三种方式,此处是其一)初始化生命周期回调另外两种方式:① XML 文件中指定<init-method>
;② 用注解 @PostConstructor 实现初始化生命周期回调 -
如果 Bean 实现了 AOP,会在这一步创建动态代理
-
-
销毁
-
Spring 容器关闭的时候进行调用
-
调用销毁生命周期回调(三种方式)
- 实现
Disposable
接口的destroy()
方法 - XML 配置文件中配置
<destroy-method>
- 使用注解
@PreDestory
创造销毁前置方法
- 实现
-
二、Spring Boot 的自动装配原理是什么?
启动类上标注的 @SpringBootApplication
注解,实际上内部会有一个 @EnableAutoConfiguation
注解。
Spring Boot 通过 @EnableAutoConfiguration
开启自动装配,通过 SpringFactoriesLoader
最终加载 META-INF/spring.factories
中的自动配置类实现自动装配,自动配置类其实就是通过 @Conditional
按需加载的配置类,想要其生效必须引入 spring-boot-starter-xxx
包实现起步依赖。
三、BeanDefinition 中保存的是什么?
BeanDefinition
包含了对 Bean 的所有描述信息,是 Spring IoC 容器保存 Bean 的基本数据结构。同时对外提供了获取/修改 Bean 描述的各种方法。BeanDefinition
包装了需要让 IoC 容器管理的 Bean 对象的数据信息:依赖关系,创建方式,加载方式等。
通常项目启动时,IoC 容器启动扫描路径,根据配置将需要容器管理的 Bean 信息装配成 BeanDefinition
对象。在 getBean
时通过反射将 BeanDefinition
中的 beanClass 创建、初始化赋值、依赖注入等操作,通过这种方式让 IoC 容器控制 Bean 的创建、初始化、销毁。
用来存储 Bean 的定义信息,用来决定 Bean 的生产方式。
定义信息包括:Bean 的类别、父类名称、BeanClass 名称、Scope 、是否为懒加载、依赖对象、初始化/销毁方法名称;是否为单例、多例、抽象等。通常定义的 Bean 中,只有 singleton、非 abstract、非 lazy 的 Bean 才会在 IoC 容器被创建的时候加载。
BeanDefinition 的部分源代码如下:
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
// 单例、原型标识符
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
// 标识 Bean 的类别,分别对应 1.用户定义的 Bean 2.来源于配置文件的 Bean 3.Spring 内部的 Bean
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;
void setParentName(@Nullable String parentName);
void setBeanClassName(@Nullable String beanClassName);
void setScope(@Nullable String scope);
void setLazyInit(boolean lazyInit);
boolean isLazyInit();
void setDependsOn(@Nullable String... dependsOn);
void setAutowireCandidate(boolean autowireCandidate);
boolean isAutowireCandidate();
void setPrimary(boolean primary);
boolean isPrimary();
void setFactoryBeanName(@Nullable String factoryBeanName);
void setFactoryMethodName(@Nullable String factoryMethodName);
ConstructorArgumentValues getConstructorArgumentValues();
default boolean hasConstructorArgumentValues() {
return !getConstructorArgumentValues().isEmpty();
}
MutablePropertyValues getPropertyValues();
default boolean hasPropertyValues() {
return !getPropertyValues().isEmpty();
}
void setInitMethodName(@Nullable String initMethodName);
void setDestroyMethodName(@Nullable String destroyMethodName);
void setRole(int role);
void setDescription(@Nullable String description);
ResolvableType getResolvableType();
boolean isSingleton();
boolean isPrototype();
boolean isAbstract();
// 其他的一些方法和属性...
}
四、BeanName 的生成规则?
Bean 的名称一般是通过使用 BeanNameGenerator
接口来实现,其下面存在两个实现类,包括 AnnotationBeanNameGenerator
和 DefaultBeanNameGenerator
。
Bean 将自己的全路径类名作为自己的 Bean 名字。如果没有类名,那就看是否有父 Bean;如果有,假设父 Bean 名字为 hehe
,那么就用 hehe$child
作为此子 Bean 的名字;如果没有父 Bean,那就看 Bean 的工厂 Bean 的名字。如果有,假设工厂 Bean 名字为 haha
,那么 Bean 的名字就是 haha$created
;如果没有工厂,那就报错:既没有自己的类名、也没有父 Bean 类名、也没有工厂 Bean 类名。
Caused by: org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unnamed bean definition specifies neither ‘class’ nor ‘parent’ nor ‘factory-bean’ - can’t generate beanName.
这种情况,通常是存在没有定义的 Bean,多见于配置文件的 Bean 中。
不管最终用的是哪一个的名字,需要对这个名字进行唯一性检查。如果已经有这个名字存在了,那就在名字后面加上 #1
(#num
)格式的字符,这样每个 Bean 的名字就是唯一的了。#
后面的数字,使用一个 counter 来维护,保持数字在相同的前缀名称下不会发生重复。
Reference
https://blog.csdn.net/dgh112233/article/details/102919658
五、JDK 动态代理和 CGLib 动态代理的实现?
JDK 动态代理使用 Java 反射包中的类和接口实现动态代理的功能,主要涉及三个类:InvocationHandler
、Method
、proxy
。
- JDK 动态代理使用 Java 反射技术进行操作,在生成类上更高效。
- JDK 动态代理只能对接口进行代理,这是因为 JDK 动态代理生成的代理类,其父类是
Proxy
,且 Java 不支持类的多继承。
CGLIB Code Generation Library
通过动态生成一个需要被代理类的子类,即被代理类作为父类。该子类重写被代理类的所有不是 final 修饰的方法,并在子类中采用方法拦截的技术拦截父类所有的方法调用,进而织入横切逻辑。在底层实现上,CGLIB 使用字节码处理框架 ASM,该框架用于转换字节码并生成新的类。之后将新的类字节码文件通过类加载器加载到内存中,通过调用目标类实现动态代理。
- CGLIB 能够代理接口和普通的类,但是被代理的类不能被
final
修饰,且接口中的方法不能使用final
修饰。 - CGLIB 使用 ASM 框架直接对字节码进行修改,使用了
FastClass
的特性。在某些情况下,类的方法执行会比较高效。