总文档 :文章目录
Github : github.com/black-ant
一 . 前言
还是来看这个图 , 来源于 @ topjava.cn/article/139…
前文说了前两步 :
这一篇来说说后面一步 InitializeBean 的相关流程 , 此篇主要包含 :
- initializeBean 主要流程
- 四种初始化方式的前世今生
- initializeBean 后的参数总结
二 . InitializeBean 方法详解
2.1 BeanInitializeBean 方法 + 初始化扩展功能
上述 Bean 创建主要完成了 Bean 创建 , 属性注入,依赖处理 , 从 AbstractAutowireCapableBeanFactory # doCreateBean 发起创建流程 , 该流程后 , 实体就已经创建完成了
1 | java复制代码 |
2.1.1 首先是 InitializingBean 的加载流程
1 | java复制代码C173- AbstractAutowireCapableBeanFactory |
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 | java复制代码- BeanNameAware:对该 bean 对象定义的 beanName 设置到当前对象实例中 |
在整体结构中有一个操作就是 : 检查 , 激活 Aware , 这个过程的前因后果是什么样的呢?
Aware 接口的体系非常庞大 , 我们仅以 BeanNameAware 为例
单纯的从结构上说 , 它只是一个接口
1 | java复制代码public interface BeanNameAware extends Aware { |
那么 ,BeanNameAware 他干了什么 ?
BeanNameAware 是一个接口 , 由希望知道自己在bean工厂中的bean名称的bean实现 , 对应 Bean 依赖该接口获取其 BeanName
aware的英文意思:意识到,察觉到,发觉,发现
也就是说 , 实现了对应的 Aware 接口的类 , 才能去做相应的事情 . 也就是说 Bean 实现了 BeanNameAware , 那么就可以感知到系统为其生成的 BeanName
这也就是为什么要求 aware 中方法应该是 void 的原因 , 这里是为了回调拿到设置的值.
从代码底层看 , Aware 运行的入口
1 | java复制代码C- AbstractAutowireCapableBeanFactory |
也就是说 , 在处理完成各种属性后 , aware 是在处理玩相关方法后 ,通过回调的方式来完善部分功能 , 例如 invokeAwareMethods 中就处理了3件事 :
- BeanNameAware 设置Bean 名称
- BeanClassLoaderAware 设置 BeanClassLoader
- BeanFactoryAware 设置 Bean 工厂
Aware 的实现方式
1 | java复制代码public class CommonService implements BeanNameAware { |
2.4 applyBeanPostProcessorsBeforeInitialization
这里很好理解, 执行 BeanPostProcessors 相关方法
1 | java复制代码@Override |
2.5 invokeInitMethods 主流程
此时属性已经设置完成 , 检查bean是否实现了InitializingBean或定义了一个定制的init方法,如果实现了,则调用必要的回调
1 | java复制代码// PS:M173_51_01 详情处理 |
补充一 : InitializingBean 方法
1 | java复制代码> InitializingBean 是一个接口 , |
补充二 : invokeCustomInitMethod
通常这种方式是通过 @Bean(initMethod = “initMethod”) 通过注解指定 进行处理的
- 获取去配置的 initMethod
- 通过反射获取方法对象
- 方法反射的方式执行 method
1 | java复制代码protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd) |
2.5 初始化方法的调用方式
在应用启动时就初始化 Bean 的方式有以下几种 :
- 实现 InitializingBean 接口方法 afterPropertiesSet
- 实现 ApplicationRunner 接口方法 run(ApplicationArguments args)
- 方法标注注解 @PostConstruct
- @Bean(initMethod = “initMethod”) 通过注解指定
1 | java复制代码// 直观的从 log 上面看 , 顺序为 |
@PostConstruct 的加载流程
@PostConstruct 归属于 javax.annotation , 是 Java 原生的注解之一 , 用于需要在依赖注入完成后执行任何初始化的方法上
在类投入服务之前必须调用此方法。所有支持依赖注入的类都必须支持这个注释。即使类不请求注入任何资源,也必须调用带有PostConstruct注释的方法。
1 | java复制代码 |
@Bean(initMethod = “initMethod”)
入上文说诉 , 最终会在 initializeBean -> invokeInitMethods 中执行 , 最后通过反射的方式执行
1 | java复制代码String initMethodName = mbd.getInitMethodName(); |
afterPropertiesSet
afterPropertiesSet 同样是在 invokeInitMethods 中执行
1 | java复制代码 |
ApplicationRunner # run 的启动流程
run 方法的执行就比较简单了, ApplicationRunner 是 SpringBoot 的专属方法 , 当 SpringApplication调用 run 方法时 , 即会执行
1 | java复制代码public ConfigurableApplicationContext run(String... args) { |
总结
这一篇比较简单 , 但是整体效果比上一篇好 , 感觉每一篇篇幅不能太长 , 涉及的点太多就不容易捋清楚 , 也有可能导致一晚上也写不完.
附录 :
原始对象 :
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
initializeBean 进入前的 Object
实际上这里属性注入 ,autowired 都已经处理好了
处理完的就不发了 , 因为没有进行 aware 和 postProcess 操作 , 实际上是一样的
本文转载自: 掘金