盘点 SpringIOC Bean 创建之 Initia

总文档 :文章目录

Github : github.com/black-ant

一 . 前言

还是来看这个图 , 来源于 @ topjava.cn/article/139…

beanSelf.jpg

前文说了前两步 :

这一篇来说说后面一步 InitializeBean 的相关流程 , 此篇主要包含 :

  • initializeBean 主要流程
  • 四种初始化方式的前世今生
  • initializeBean 后的参数总结

二 . InitializeBean 方法详解

2.1 BeanInitializeBean 方法 + 初始化扩展功能

上述 Bean 创建主要完成了 Bean 创建 , 属性注入,依赖处理 , 从 AbstractAutowireCapableBeanFactory # doCreateBean 发起创建流程 , 该流程后 , 实体就已经创建完成了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
java复制代码
/**
* 发起流程 , 创建主流程
**/
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
//............
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
} catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
} else {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
//............
}

2.1.1 首先是 InitializingBean 的加载流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
java复制代码C173- AbstractAutowireCapableBeanFactory
M173_50- initializeBean
- invokeAwareMethods(beanName, bean) :激活 Aware 方法,对特殊的 bean 处理 -> PS:M173_50_02
- applyBeanPostProcessorsBeforeInitialization : 后处理器,before
- invokeInitMethods : 激活用户自定义的 init 方法
- applyBeanPostProcessorsAfterInitialization :后处理器,after
M173_51- invokeAwareMethods -> PS:M173_51_01
?- 根据 Aware的具体类型分别设置 BeanName / BeanClassLoaderAware / BeanFactoryAware
M173_52- applyBeanPostProcessorsBeforeInitialization
?- getBeanPostProcessor 获取所有的 BeanPostProcessor 并且进行FOR 循环调用 postProcessBeforeInitialization
?- 注意 , 如果没有 Processor , 则直接返回原本的 Object bean , 存在则返回处理过的
FOR- getBeanPostProcessor
- processor.postProcessBeforeInitialization(result, beanName)
M173_53- invokeInitMethods
- 如果包含 afterPropertiesSet , 则调用 ((InitializingBean) bean).afterPropertiesSet()

// M173_50 代码
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
// 激活 Aware 方法 , 此处会根据权限不同分别处理
// Step 1 : 获取系统安全接口 , 如果已经为当前应用程序建立了安全管理器,则返回该安全管理器
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}else {
invokeAwareMethods(beanName, bean);
}

Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}

try {
invokeInitMethods(beanName, wrappedBean, mbd);
}catch (Throwable ex) {
.....
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}

PS:M173_50_02 许可权限控制
AccessController.doPrivileged : 使用指定的AccessControlContext启用和限制的权限执行指定的PrivilegedAction。

PrivilegedAction : 此处使用的 Action , 该 Action 支持在启用特权的情况下执行逻辑

getAccessControlContext : 将访问控制上下文的创建委托给SecurityContextProvider

原因 : 这么做的主要原因是因为JVM 保护域的机制 , 当使用了 SecurityManager 时 , 并不能随意访问 . 此时类java.security.AccessController提供了一个默认的安全策略执行机制,它使用栈检查来决定潜在不安全的操作是否被允许 , 而使用 doPrivileged 的代码主体享有特权

initializeBean 详情

大概可以看到 , 其中重要的几个调用

  • invokeAwareMethods
  • applyBeanPostProcessorsBeforeInitialization
  • invokeInitMethods
  • applyBeanPostProcessorsAfterInitialization

我们在后面详细看看这几个方法 :

2.2 invokeAwareMethods 及 Aware 的处理

这里是为了执行 Aware 方法 , 顺便把 Aware 流程说一下

Aware 接口为 Spring 容器的核心接口,是一个具有标识作用的超级接口,实现了该接口的 bean 是具有被 Spring 容器通知的能力

用法 : 指示一个bean有资格通过一个回调样式的方法由Spring容器通知一个特定的框架对象

结构 : 通常应该只包含一个接受单个参数的返回void的方法

作用 : 被 Spring 容器通知 , 获取其中的隐藏属性

常见的 BeanAware 类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
java复制代码- BeanNameAware:对该 bean 对象定义的 beanName 设置到当前对象实例中
- BeanClassLoaderAware:将当前 bean 对象相应的 ClassLoader 注入到当前对象实例中
- BeanFactoryAware:BeanFactory 容器会将自身注入到当前对象实例中,这样当前对象就会拥有一个 BeanFactory 容器的引用。

// 当然,Spring 不仅仅只是提供了上面三个 Aware 接口,而是一系列:
- LoadTimeWeaverAware:加载Spring Bean时织入第三方模块,如AspectJ
- BootstrapContextAware:资源适配器BootstrapContext,如JCA,CCI
- ResourceLoaderAware:底层访问资源的加载器
- PortletConfigAware:PortletConfig
- PortletContextAware:PortletContext
- ServletConfigAware:ServletConfig
- ServletContextAware:ServletContext
- MessageSourceAware:国际化
- ApplicationEventPublisherAware:应用事件
- NotificationPublisherAware:JMX通知

在整体结构中有一个操作就是 : 检查 , 激活 Aware , 这个过程的前因后果是什么样的呢?

Aware 接口的体系非常庞大 , 我们仅以 BeanNameAware 为例

单纯的从结构上说 , 它只是一个接口

1
2
3
4
java复制代码public interface BeanNameAware extends Aware {
// 安装上文说的 , 接受单参数 ,返回 void
void setBeanName(String name);
}

那么 ,BeanNameAware 他干了什么 ?

BeanNameAware 是一个接口 , 由希望知道自己在bean工厂中的bean名称的bean实现 , 对应 Bean 依赖该接口获取其 BeanName

aware的英文意思:意识到,察觉到,发觉,发现

也就是说 , 实现了对应的 Aware 接口的类 , 才能去做相应的事情 . 也就是说 Bean 实现了 BeanNameAware , 那么就可以感知到系统为其生成的 BeanName

这也就是为什么要求 aware 中方法应该是 void 的原因 , 这里是为了回调拿到设置的值.

从代码底层看 , Aware 运行的入口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
java复制代码C- AbstractAutowireCapableBeanFactory
M- invokeAwareMethods(final String beanName, final Object bean)
?- 该方法是在 initializeBean 中被调用

private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {

// 根据Aware 类型的不同调用对应的方法
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}

也就是说 , 在处理完成各种属性后 , aware 是在处理玩相关方法后 ,通过回调的方式来完善部分功能 , 例如 invokeAwareMethods 中就处理了3件事 :

  • BeanNameAware 设置Bean 名称
  • BeanClassLoaderAware 设置 BeanClassLoader
  • BeanFactoryAware 设置 Bean 工厂

Aware 的实现方式

1
2
3
4
5
6
7
8
9
10
11
java复制代码public class CommonService implements BeanNameAware {

private String beanName;

// Spring 创建过程中 , 在生成name 后 , 会回调该接口 , 将 BeanName 注入进来 ,让对象可感知
@Override
public void setBeanName(String name) {
this.beanName = name;
}

}

2.4 applyBeanPostProcessorsBeforeInitialization

这里很好理解, 执行 BeanPostProcessors 相关方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java复制代码@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {

Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}


// applyBeanPostProcessorsAfterInitialization 类似 , 其区别就是调用 postProcessAfterInitialization

2.5 invokeInitMethods 主流程

此时属性已经设置完成 , 检查bean是否实现了InitializingBean或定义了一个定制的init方法,如果实现了,则调用必要的回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
java复制代码// PS:M173_51_01  详情处理
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
// 同理 , 对存在安全域 SecurityManager 的方法 , 通过 AccessController 进行授权调用
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
((InitializingBean) bean).afterPropertiesSet();
}
}

/
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
// 取反 , 避免反复调用
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
// 在给定bean上调用指定的自定义init方法
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}

补充一 : InitializingBean 方法

1
2
3
4
5
6
7
8
java复制代码> InitializingBean 是一个接口 , 
M- afterPropertiesSet() :
|- 在 bean 的初始化进程中会判断当前 bean 是否实现了 InitializingBean
|- 如果实现了则用 #afterPropertiesSet() 方法,进行初始化工作
|- 属性初始化的处理
|- 然后再检查是否也指定了 init-method
|- 如果指定了则通过反射机制调用指定的 init-method 方法
|- 利用反射机制执行, 激活用户自定义的初始化方法

补充二 : invokeCustomInitMethod

通常这种方式是通过 @Bean(initMethod = “initMethod”) 通过注解指定 进行处理的

  • 获取去配置的 initMethod
  • 通过反射获取方法对象
  • 方法反射的方式执行 method
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
java复制代码protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {

// 获取配置的 initMethod
String initMethodName = mbd.getInitMethodName();

// 通过反射获取方法对象
Method initMethod = (mbd.isNonPublicAccessAllowed() ?
BeanUtils.findMethod(bean.getClass(), initMethodName) :
ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName));

// 如果方法不存在 , 抛出异常或返回
if (initMethod == null) {
// 指示配置的init方法是否为默认方法
if (mbd.isEnforceInitMethod()) {
throw new BeanDefinitionValidationException(.....);
}
else {
return;
}
}

// 如果可能,为给定的方法句柄确定相应的接口方法
Method methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(initMethod);

// 方法反射的方式执行 method , 同理 , 会获取权限管理
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
ReflectionUtils.makeAccessible(methodToInvoke);
return null;
});
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () ->
methodToInvoke.invoke(bean), getAccessControlContext());
}
catch (PrivilegedActionException pae) {
InvocationTargetException ex = (InvocationTargetException) pae.getException();
throw ex.getTargetException();
}
}
else {
try {
// 获取许可后反射获取
ReflectionUtils.makeAccessible(methodToInvoke);
methodToInvoke.invoke(bean);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}

2.5 初始化方法的调用方式

在应用启动时就初始化 Bean 的方式有以下几种 :

  • 实现 InitializingBean 接口方法 afterPropertiesSet
  • 实现 ApplicationRunner 接口方法 run(ApplicationArguments args)
  • 方法标注注解 @PostConstruct
  • @Bean(initMethod = “initMethod”) 通过注解指定
1
2
3
4
5
java复制代码// 直观的从 log 上面看 , 顺序为 
- @PostConstruct
- InitializingBean
- @Bean(initMethod = "initMethod")
- ApplicationRunner

@PostConstruct 的加载流程

@PostConstruct 归属于 javax.annotation , 是 Java 原生的注解之一 , 用于需要在依赖注入完成后执行任何初始化的方法上
在类投入服务之前必须调用此方法。所有支持依赖注入的类都必须支持这个注释。即使类不请求注入任何资源,也必须调用带有PostConstruct注释的方法。

1
2
3
4
java复制代码    
C- DefaultInstanceManager # newInstance
C- DefaultInstanceManager # populateAnnotationsCache
C- DefaultInstanceManager # findPostConstruct

@Bean(initMethod = “initMethod”)

入上文说诉 , 最终会在 initializeBean -> invokeInitMethods 中执行 , 最后通过反射的方式执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
java复制代码String initMethodName = mbd.getInitMethodName();

C- AbstractBeanDefinition
F- private String initMethodName;

// 对应的初始化流程
C- ConfigurationClassBeanDefinitionReader
M- loadBeanDefinitionsForBeanMethod

// 这里获取 @Bean 中的属性 initMethod
// @Bean(initMethod = "initMethod", autowire = Autowire.BY_TYPE)
String initMethodName = bean.getString("initMethod");
if (StringUtils.hasText(initMethodName)) {
beanDef.setInitMethodName(initMethodName);
}

// 最后在 AbstractAutowireCapableBeanFactory 中调用
C- AbstractAutowireCapableBeanFactory
M- initializeBean
M- invokeInitMethods

afterPropertiesSet

afterPropertiesSet 同样是在 invokeInitMethods 中执行

1
2
3
4
5
6
7
8
9
java复制代码
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}else {
invokeAwareMethods(beanName, bean);
}

ApplicationRunner # run 的启动流程

run 方法的执行就比较简单了, ApplicationRunner 是 SpringBoot 的专属方法 , 当 SpringApplication调用 run 方法时 , 即会执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
java复制代码public ConfigurableApplicationContext run(String... args) {
//..............
try {
//..............
// 执行 run 方法
callRunners(context, applicationArguments);
} catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
//..............
return context;
}


private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
// 添加 ApplicationRunner 类
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// 添加 CommandLineRunner 类
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
// 对所有的 runner 进行处理
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}

// 最后通过 (runner).run(args); 调用

总结

这一篇比较简单 , 但是整体效果比上一篇好 , 感觉每一篇篇幅不能太长 , 涉及的点太多就不容易捋清楚 , 也有可能导致一晚上也写不完.

附录 :

原始对象 :
image.png

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd)

initializeBean 进入前的 Object

实际上这里属性注入 ,autowired 都已经处理好了

image.png

处理完的就不发了 , 因为没有进行 aware 和 postProcess 操作 , 实际上是一样的

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%