• [手写spring](4)实现后置处理器


    目录

    目标

    自定义注解

    自定义接口

    执行bean的初始化方法

    创建后置处理器名称Set

    初始化后置处理器名称集合

    执行后置处理器方法

    执行所有postProcessBeforeInitialization方法

    执行所有postProcessAfterInitialization方法

    调用初始化方法、后置处理器方法

    测试

    总结


    目标

            后置处理器算是实现AOP的前提,我们在这篇文章中就将会实现后置处理器,后置处理器是什么我就不介绍了,不清楚可以参考后置处理器,下面直接开始。


    自定义注解

            我们定义一个注解来标识bean的初始化方法。

    1. @Target(value = {ElementType.METHOD})
    2. @Retention(value = RetentionPolicy.RUNTIME)
    3. public @interface PostConstruct {
    4. }

    自定义接口

            我们定义一个后置处理器的接口,里面的2个方法和原生的spring是一样的。如果一个类实现了这个接口,那么这个类就是后置处理器类。

    1. public interface BeanPostProcessor {
    2. default Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception {
    3. return bean;
    4. }
    5. default Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
    6. return bean;
    7. }
    8. }

    执行bean的初始化方法

            我们创建一个方法,这个方法判断一个类是否有需要进行执行的初始化方法,也就是判断方法上有没有@PostConstruct注解

    1. protected void executeInitMethod(Object o) {
    2. for (Method method : o.getClass().getDeclaredMethods()) {
    3. if (method.isAnnotationPresent(PostConstruct.class)) {
    4. try {
    5. method.invoke(o);
    6. } catch (InvocationTargetException | IllegalAccessException e) {
    7. e.printStackTrace();
    8. }
    9. }
    10. }
    11. }

    创建后置处理器名称Set

            我们创建一个set来将后置处理器的名称存放到里面,方便后续处理

    1. private Set<String> beanPostProcessorNames;
    2. //代码块初始化集合
    3. {
    4. beanPostProcessorNames = new HashSet<>();
    5. }

    初始化后置处理器名称集合

            我们在初始化singletonObjects集合的时候,需要对beanDefinitionMap进行遍历,我们可以在遍历的时候初始化后置处理器名称的集合,只需要判断是否实现BeanPostProcessor接口即可

    1. //将后置处理器的名字存储进set中
    2. if (bean instanceof BeanPostProcessor) {
    3. beanPostProcessorNames.add(name);
    4. }

    执行后置处理器方法

            我们需要遍历后置处理器,并且执行所有的before和after方法,所以我们可以提供2个方法,分别完成执行before和after的功能

    执行所有postProcessBeforeInitialization方法

    1. protected Object processorBeforeMethod(Object o, String beanName) {
    2. for (String postProcessorName : beanPostProcessorNames) {
    3. BeanPostProcessor postProcessor = (BeanPostProcessor) singletonObjects.get(postProcessorName);
    4. Object current = null;
    5. try {
    6. current = postProcessor.postProcessBeforeInitialization(o, beanName);
    7. } catch (Exception e) {
    8. e.printStackTrace();
    9. }
    10. if (current != null) {
    11. o = current;
    12. }
    13. }
    14. }

    执行所有postProcessAfterInitialization方法

    1. protected Object processorAfterMethod(Object o, String beanName) {
    2. for (String postProcessorName : beanPostProcessorNames) {
    3. BeanPostProcessor postProcessor = (BeanPostProcessor) singletonObjects.get(postProcessorName);
    4. Object current = null;
    5. try {
    6. current = postProcessor.postProcessAfterInitialization(o, beanName);
    7. } catch (Exception e) {
    8. e.printStackTrace();
    9. }
    10. if (current != null) {
    11. o = current;
    12. }
    13. }
    14. return o;
    15. }

    调用初始化方法、后置处理器方法

            在上面,我们实现了初始化方法,后置处理器方法,现在我们在对象创建后进行显示调用,相当于要在2个地方进行调用,分别是解决singletonObjects依赖注入之后,如下

    1. //如果是自身就是后置处理器,跳过
    2. if (o instanceof BeanPostProcessor) continue;
    3. //后置处理器
    4. o = processorBeforeMethod(o, beanName);
    5. //调用init方法
    6. executeInitMethod(o);
    7. //后置处理器
    8. o = processorAfterMethod(o, beanName);
    9. //更新单例对象池中的对象
    10. singletonObjects.put(beanName, o);

             然后还需要在createBean方法中进行调用。在返回之前调用即可

    1. //后置处理器
    2. o = processorBeforeMethod(o, beanName);
    3. //调用init方法
    4. executeInitMethod(o);
    5. //后置处理器
    6. o = processorAfterMethod(o, beanName);

    测试

            为了方便,我们在test包下面新建2个子包,分别是bean包和processor包,bean包里面写随便写一个类,提供一个init方法。processor包下写一个类继承BeanPostProcessor接口。

    1. @Component
    2. public class Cat {
    3. @PostConstruct
    4. public void myInit(){
    5. System.out.println("这是Cat的初始化方法");
    6. }
    7. }
    1. @Component
    2. public class MyBeanPostProcessor implements BeanPostProcessor {
    3. @Override
    4. public Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception {
    5. System.out.println(beanName+"的后置处理器--before");
    6. return bean;
    7. }
    8. @Override
    9. public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
    10. System.out.println(beanName+"的后置处理器--after");
    11. return bean;
    12. }
    13. }

            更改我们spring的扫描路径,也就是在更改ComponentScan注解

    1. @ComponentScan(path = "com.ttpfx.use.test")
    2. public class ComponentScanPathConfig {
    3. }

             项目结构如下

            在测试类中只需创建一个容器即可,如下

    1. public class MySpringTest {
    2. public static void main(String[] args) {
    3. ApplicationContext ioc = new ApplicationContext(ComponentScanPathConfig.class);
    4. }
    5. }

             控制台输出如下

             经过测试,可以发现,我们的后置处理器也没有问题。


    总结

            经过这篇文章,我们已经实现了后置处理器,难度总体来说还不算特别大,在下一篇文章中我们将会实现spring的核心机制--AOP,这也是比较难的一点,我自己写的时候也调了很久的bug,做好准备,开始实现AOP吧!!!


     手写spring系列 

    [手写spring](1)构建框架,实现包扫描

    [手写spring](2)初始化BeanDefinitionMap

    [手写spring](3)初始化singletonObjects,实现依赖注入

    [手写spring](4)实现后置处理器

    [手写spring](5)实现AOP机制(完结)  

  • 相关阅读:
    微信小程序多个按钮点击互不影响禁用实现
    gradle学习笔记(六) 官方文档笔记+理解
    【Linux】Http协议
    springboot整合actuator、admin对应用程序进行监控
    汇报一下日常健身和锻炼方面的进展
    王道操作系统___第四章02
    低代码招投标应用:全程不见面,一次都不跑,快速优化招投标流程
    【04】概率图表示之贝叶斯网络
    高阶导数简介
    给使用docker安装的ES和Kibana设置账号密码
  • 原文地址:https://blog.csdn.net/m0_51545690/article/details/125544613