手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】之实现任务阶段 5- bean 后置处理器

前言
手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】的第五篇具体实现了任务阶段 5- bean 后置处理器

个人主页:尘觉主页

个人简介:大家好,我是尘觉,希望我的文章可以帮助到大家,您的满意是我的动力

在csdn获奖荣誉: csdn城市之星2名
⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ Java全栈群星计划top前5
⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣ ⁣⁣⁣⁣  端午大礼包获得者

欢迎大家:这里是CSDN,我总结知识的地方,欢迎来到我的博客,感谢大家的观看
如果文章有什么需要改进的地方还请大佬不吝赐教 先在次感谢啦

文章目录

  • 实现任务阶段 5- bean 后置处理器
    • ● 分析示意图
    • 创建InitializingBean接口
    • 修改MonsterService.java类
    • 修改WyxSpringApplicationContext类
    • 运行完成测试
    • 创建 BeanPostProcessor接口
    • 创建 WyxBeanPostProcessor类
    • 修改WyxSpringApplicationContext类
    • 运行完成测试
    • 总结

实现任务阶段 5- bean 后置处理器

● 分析示意图

手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】之实现任务阶段 5- bean 后置处理器_第1张图片

● 代码实现, 说明,整个实现思路,就是参考 Spring 规范

创建InitializingBean接口

public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}

修改MonsterService.java类

去实现 InitializingBean 接口

\1. afterPropertiesSet就是在bean的setter方法执行完毕后被spring容器调用
2 即就是初始化方法

@Component//(value = "monsterService") //把MonsterService注入我们自己的spring容器中
@Scope(value = "prototype")
public class MonsterService implements InitializingBean {
    //这里我们使用自己的@Autowired来修饰属性
    //表示该属性,是通过容器完成依赖注入
    //说明: 我们实现按照名字来进行组装即可
    @Autowired
    private MonsterDao monsterDao;

    public void m1() {
        monsterDao.hi();
    }

    /**
     * 1. afterPropertiesSet就是在bean的setter方法执行完毕后被spring容器调用
     * 2 即就是初始化方法
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("MonsterService 初始化方法被调用 程序员在这里加入初始化的业务..");
    }
}

修改WyxSpringApplicationContext类

在创建好 Bean 实例后,判断是否需要进行初始化 【容器中常.否实现了某个接口,来判断是否要执行某个业务逻辑, 这里其实就是 java 基础的接口编程实际运用

 //先简单实现实现,后面在完善.
    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        //得到 bean 的类型
        Class clazz = beanDefinition.getClazz();
        try {
            //使用反射得到实例
            Object instance = clazz.getDeclaredConstructor().newInstance();
            //完成依赖注入
            for (Field declaredField : clazz.getDeclaredFields()) {
                if (declaredField.isAnnotationPresent(Autowired.class)) {
                    // 处理@Autowired 注解的属性 required, 很简单,自己完成
                    // Autowired annotation =declaredField.getAnnotation(Autowired.class);
                    // System.out.println(annotation.required());
                    //如果该属性有@Autowired, 就进行组装
                    Object bean = getBean(declaredField.getName());
                    declaredField.setAccessible(true);//因为属性是 private,需要暴破
                    declaredField.set(instance, bean);
                }
            }
            //这里还有其他,比如 Aware 回调. 不写了
            //这里调用初始化,如果 bean 实现了 InitializingBean
            System.out.println("======创建好了====" + instance);
            if (instance instanceof InitializingBean) {
                try {
                    ((InitializingBean) instance).afterPropertiesSet();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        //如果没有创建成功,返回 null
        return null;
    }

运行完成测试

img

创建 BeanPostProcessor接口

该接口可以参考原生 Spring 规范 , 注注意体会切面编程

  1. 参考原生Spring容器定义一个接口BeanPostProcessor

  2. 该接口有两个方法postProcessBeforeInitialization 和 postProcessAfterInitialization

  3. 这两个方法,会对Spring容器的所有Bean生效, 已经是切面编程的概念.

public interface BeanPostProcessor {

    /**
     * 1. postProcessBeforeInitialization在Bean的初始化方法前调用
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    /**
     * 1. postProcessAfterInitialization在Bean的初始化方法后调用
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }
}

创建 WyxBeanPostProcessor类

@Component
public class WyxBeanPostProcessor implements BeanPostProcessor {
    /**
     * 该方法时在 bean 创建好后,进行初始化前调用
     * @param bean : 创建好的 bean 对象
     * @param beanName 创建好的 bean 的名字
     * @return
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        //这里程序员来决定业务逻辑,spring 只是提供处理机制
        System.out.println("postProcessBeforeInitialization 被调用 " + beanName + " bean= " + bean.getClass());
        return bean;
    }
    /**
     * 该方法时在 bean 创建好后,初始化完成后调用
     * @param bean : 创建好的 bean 对象
     * @param beanName : 创建好的 bean 的名字
     * @return
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        //这里程序员来决定业务逻辑,spring 只是提供处理机制
        System.out.println("postProcessAfterInitialization 被调用 " + beanName + " bean= " + bean.getClass());
        return bean;
    }
}

修改WyxSpringApplicationContext类

注意:这里 createBean(String beanName, BeanDefinition beanDefinition) 需要增加入 参 beanName, 就会导致好几个位置错误,需要根据错误提示,对应解决即可.

    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    private void beanDefinitionsByscan(Class configClass) {
        this.configClass = configClass;
        ComponentScan componentScan = (ComponentScan)
                this.configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScan.value();
        System.out.println("扫描路径 = " + path);
        path = path.replace(".", "/");
        URL resource = classLoader.getResource(path);
        File file = new File(resource.getFile());
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                String fileAbsolutePath = f.getAbsolutePath();
                System.out.println("=======================================");
                System.out.println("文件绝对路径 = " + fileAbsolutePath);
                if (fileAbsolutePath.endsWith(".class")) {// 说明是类文件
                    // 通过类加载器获取来类文件的 Clazz 对象
                    // 先 得 到 类 的 完 整 类 路 径 形 式 为
                    com.wyxedu.spring.component.MonsterService
                    String className =
                            fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1,
                                    fileAbsolutePath.indexOf(".class"));
                    String classFullPath = path.replace("/", ".") + "." + className;
                    System.out.println("类名 = " + className);
                    System.out.println("类的全路径 = " + classFullPath);
                    try {
                        // 获取到扫描包下的类的 clazz 对象
                        Class<?> clazz = classLoader.loadClass(classFullPath);
                        if (clazz.isAnnotationPresent(Component.class)) {
                    
                        // 如果这个类有@Commponent, 说明是一个 spring bean
                            System.out.println("是一个 bean = " + clazz);
                        
                        // 1. 增 加 一 个 逻 辑 , 如 果 这 个 clazz 类 型 是 实 现 了BeanPostProcessor 接口, 说明是一个 bean 处理器,特殊处理
                        // 2. 注意不能使用 clazz instanceof BeanPostProcessor 判断因为 clazz 并不是一个实例对象, 而是一个类对象
                        // 3. 这里实现是为了方便获取 bean 处理器对象,所以放在一个 beanPostProcessorList, spring 底层源码,
                        // 还 是 走 的 createBean(),getBean(), 只 是 需 要 在singletonObjects, 增加代码处理, 
                            // 我这里主要讲的是 bean 处理器的工作机制,就不处理了,知道即可
                            if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                // 创建一个实例对象
                                BeanPostProcessor beanPostProcessor =
                                        (BeanPostProcessor) clazz.newInstance();
                                // 放入到 beanPostProcessorList
                                beanPostProcessorList.add(beanPostProcessor);
                                continue;
                            // 1. 因为这里不能直接将 bean 实例放入 singletonObjects
                            // 2. 原因是如果 bean 是 prototype 是需要每次创建新的 bean 对象
                            // 3. 所 以 , Spring 底 层 是 这 样 设 计 的 : 将 bean 信 息 封 装 到
                            BeanDefinition 对象中, 便于 getBean 的操作
                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setClazz(clazz);
                            // 获取 bean 的 name
                            Component componentAnnotation =
                                    clazz.getDeclaredAnnotation(Component.class);
                            String beanName = componentAnnotation.value();
                            // 获取 bean 的 scope
                            if (clazz.isAnnotationPresent(Scope.class)) { // 如果有@Scope
                                Scope scopeAnnotation =
                                        clazz.getDeclaredAnnotation(Scope.class);
                                beanDefinition.setScope(scopeAnnotation.value());
                            } else { // 如果没有@Scope, 默认是 singleton
                                beanDefinition.setScope("singleton");
                            }
                    // 放入到 beanDefinitionMap
                            beanDefinitionMap.put(beanName, beanDefinition);

                        } else {
                    // 如果这个类没有@Commponent, 说明不是一个 spring bean
                            System.out.println("不是一个 bean = " + clazz);
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    }
                    System.out.println("=======================================");
                }
            }
        }
    }

        // 先简单实现实现,后面在完善. private Object createBean(String beanName, BeanDefinition beanDefinition) {
    // 得到 bean 的类型

    Class clazz = beanDefinition.getClazz();
    try
    {
        // 使用反射得到实例
        Object instance = clazz.getDeclaredConstructor().newInstance();
        // 完成依赖注入
        for (Field declaredField : clazz.getDeclaredFields()) {
            if (declaredField.isAnnotationPresent(Autowired.class)) {
            // 处理@Autowired 注解的属性 required, 很简单,自己完成
            // Autowired annotation = declaredField.getAnnotation(Autowired.class);
            // System.out.println(annotation.required());
            // 如果该属性有@Autowired, 就进行组装
                Object bean = getBean(declaredField.getName());
                declaredField.setAccessible(true);// 因为属性是 private,需要暴破
                declaredField.set(instance, bean);
            }
        }
        // 这里还有其他,比如 Aware 回调. 不写了
        // 说明
        // 1. 在 bean 初始化前调用所有 bean 处理器的 postProcessBeforeInitialization
        // 2. 调用时,不能保证顺序
        // 3. 可以通过加入@Order("值"), 来指定 bean 处理器调用顺序,同学们可以自行完成, 不难
        // 4. 如果希望指定对哪些 bean 进行初始化前处理 , 可以在处理器的postProcessBeforeInitialization()
        // 加入相关业务判断即可.比如:
        /**
         * @Override
         * public Object postProcessBeforeInitialization(Object bean, String
        beanName) {
         * if("monsterService".equalsIgnoreCase(beanName)) {
         * //这里程序员来决定业务逻辑,spring 只是提供处理机制
         * System.out.println("postProcessBeforeInitialization 被调用 " * + beanName + " bean= " + bean.getClass());
         * return bean;
         * }else {
         * return bean;
         * }
         * }
         */
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
        // 4. 也会返回一个对象,这个返回的对象是什么,由程序员在编写 bean 处理器决定,可能是原来的 bean, 也可能被改变了

            instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
        }
            // 这里调用初始化,如果 bean 实现了 InitializingBean
            if (instance instanceof InitializingBean) {
            try {
                ((InitializingBean) instance).afterPropertiesSet();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        // 说明
        // 1. 在 bean 初始化后调用所有 bean 处理器的 postProcessAfterInitialization
        // 2. 调用时,不能保证顺序
        // 3. 可以通过加入@Order("值"), 来指定 bean 处理器调用顺序,同学们可以自行完成, 不难
        // 4. 如 果 希 望 指 定 对 哪 些 bean 进 行 初 始 化 后 处 理 , 可 以 在 处 理 器 的postProcessAfterInitialization()
        // 加入相关业务判断即可
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
        // 4. 也会返回一个对象,这个返回的对象是什么,由程序员在编写 bean 处理器决定,可能是原来的 bean, 也可能被改变了
            instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
        }
        return instance;
    } catch(InstantiationException e){
        e.printStackTrace();
    } catch(IllegalAccessException e){
        e.printStackTrace();
    } catch(InvocationTargetException e){
        e.printStackTrace();
    } catch(NoSuchMethodException e){
        e.printStackTrace();
    }
        // 如果没有创建成功,返回 null
        return null;

运行完成测试

手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】之实现任务阶段 5- bean 后置处理器_第2张图片

总结

本篇我们完成了bean 后置处理器是我们的手写进一步完善了手动实现 Spring 底层机制
                                
                                
手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】系列

手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器-准备篇【1】

手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器-准备篇【2】

手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器,实现任务阶段 2- 扫描将 bean 信息封装到 BeanDefinition 对象

手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】实现任务阶段 3- 初始化 bean 单例池 和 实现任务阶段 4- 完成依赖注入
                                
                                
热门专栏推荐
想学习vue的可以看看这个
java基础合集
数据库合集
redis合集
nginx合集
linux合集
等等等还有许多优秀的合集在主页等着大家的光顾感谢大家的支持

欢迎大家加入我的社区 尘觉社区

文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起来评论区一起讨论
希望能和诸佬们一起努力,今后我们一起观看感谢您的阅读
如果帮助到您不妨3连支持一下,创造不易您们的支持是我的动力

你可能感兴趣的:(#,手写机制,spring,java,后端,spring,boot,tomcat,kafka,maven)