手写Spring框架第四天

本文介绍了如何在Spring框架中自定义Bean后处理器,使用自定义注解@MyPostConstruct,并详细解释了其在Bean对象生命周期中的作用,包括前方法和后方法,以及如何在ApplicationContext中集成和执行这些处理器。

目录

前言

Bean后处理器的作用

自定义注解@MyPostConstruct

自定义BeanPostProcessor后处理器接口

自定义Bean后处理器

修改ApplicationContext类

测试

完整代码


前言

 今天实现Bean后处理器也就是上图中右侧的部分。

Bean后处理器的作用

Bean后处理器的作用在Spring中是对Bean对象做功能增强的,Bean后处理器中有两个方法,暂时把他们叫为前方法与后方法。

  • 前方法是在依赖注入结束之后执行,主要作用是对bean对象属性赋值。
  • 然后执行bean对象的初始化方法。
  • 其次再执行后方法。主要作用是对bean对象的方法增强。

自定义注解@MyPostConstruct

在Spring中,注解@PostConstruct用于初始化方法上。执行时机为依赖注入完成后,被标注的方法只会被执行一次。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyPostConstruct {
}

自定义BeanPostProcessor后处理器接口

在Spring框架中,也可以自己自定义Bean后处理器添加到Spring启动流程中。只需要实现Spring框架中的BeanPostProcessor接口即可。

现在我们自定义BeanPostProcessor接口,来模仿Spring的实现方式。

/**
 * 实现该接口的被当作Bean后处理器
 */
public interface MyBeanPostProcessor {
    default Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    default Object postProcessAfterInitialization(Object bean, String beanName){
        return bean;
    }
}

自定义Bean后处理器

接下来创建出一个类来实现Bean后处理器接口

@MyComponent
public class MyBeanPostProcess implements MyBeanPostProcessor{
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("执行Bean后处理器前方法:"+beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("执行Bean后处理器后方法:"+beanName);
        return bean;
    }
}

修改ApplicationContext类

首先我们要清楚,Bean后处理器可以有多个并且具有执行顺序的,并且执行时机为Bean自动装配之后,也就是在实例化BeanDefinitionMap之后且实例化SingletonObjects之前执行。从Bean后处理器执行时机我们知道Bean后处理器是不会被加载到单例池的。那么我们定义一个List列表来存储Bean后处理对象。

    //用来存储Bean后处理器对象
    private List<MyBeanPostProcessor> beanPostProcesses;
    //初始化代码块
      {
        beanPostProcesses = new ArrayList<>();
      }

Bean后处理器是在加入单例池之前执行,因此添加一个新的initBeanPostProcess()方法

    private void initBeanPostProcess() {
        Set<Map.Entry<String, BeanDefinition>> entries = this.beanDefinitionMap.entrySet();
        for (Map.Entry<String, BeanDefinition> entry : entries) {
            if (MyBeanPostProcessor.class.isAssignableFrom(entry.getValue().getClazz())){
                try {
                    String beanName = entry.getKey();
                    Object o = entry.getValue().getClazz().newInstance();
                    beanPostProcessors.add((MyBeanPostProcessor) o);
                    singletonObjects.put(beanName,o);
                } catch (InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

接下来需要修改加载bean对象到单例池中方法,在此之前需要先执行bean后处理器方法,但需要注意的是,我们需要对bean处理器本身过滤

private void createBean(Map.Entry<String, BeanDefinition> entry) {
        //获取到类
        Object o = null;
        String beanName = entry.getKey();
        if (cacheSingleton.containsKey(beanName)) {
            o = cacheSingleton.get(beanName);
        } else {
            Class<?> clazz = entry.getValue().getClazz();
            try {
                o = clazz.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        //获取该类中的字段
        Field[] declaredFields = o.getClass().getDeclaredFields();
        for (Field declaredField : declaredFields) {
            //判断字段上是否存在@MyResource注解
            if (declaredField.isAnnotationPresent(MyResource.class)) {
                MyResource annotation = declaredField.getAnnotation(MyResource.class);
                //需要被注入的名称
                String setBeanName = annotation.value();
                if ("".equals(setBeanName)) {
                    //如果MyResource没有指定,那么以字段名为注入名
                    setBeanName = declaredField.getName();
                }
                if (!beanDefinitionMap.containsKey(setBeanName)) {
                    throw new RuntimeException("不存在该bean名称:" + setBeanName);
                }
                //开放权限
                declaredField.setAccessible(true);
                BeanDefinition beanDefinition = beanDefinitionMap.get(setBeanName);
                try {
                    if (beanDefinition.getType().equals("Singleton")) {
                        //如果是单例bean
                        if (!cacheSingleton.containsKey(setBeanName)) {
                            //如果缓存单例池不存在,那么去单例池寻找
                            if (singletonObjects.containsKey(setBeanName)) {
                                //单例池存在的情况
                                Object o1 = singletonObjects.get(setBeanName);
                                declaredField.set(o, o1);
                            } else {
                                //单例池中也不存在
                                Object o1 = beanDefinition.getClazz().newInstance();
                                //存放在缓存单例池中
                                cacheSingleton.put(setBeanName, o1);
                                declaredField.set(o, o1);
                            }
                        } else {
                            //如果已经创建过了,但未完成创建
                            Object o1 = cacheSingleton.get(setBeanName);
                            declaredField.set(o, o1);
                        }
                    } else {
                        Object o1 = beanDefinition.getClazz().newInstance();
                        declaredField.set(o, o1);
                    }
                } catch (IllegalAccessException | InstantiationException e) {
                    e.printStackTrace();
                }
            }
        }
        //到这里执行结束,清除缓存单例池信息
        //去执行Bean后处理器后再添加到单例池
        executeBeanPostBefore(o, beanName);
        executeBeanInit(o);
        executeBeanPostAfter(o, beanName);

        cacheSingleton.remove(beanName);
        if (entry.getValue().getType().equals("Singleton")) {
            singletonObjects.put(beanName, o);
        }
    }

首先将未经过Bean后处理加工过的单例bean加载到单例池中(注:Spring中不会这样做,Spring中有三级缓存分步加载到单例池)如果遍历到Bean后处理器对象,那么就将其加入List列表。

接下来我们要调用执行Bean后处理器的方法,以及bean对象的初始化方法,这个三个方法实现如下

    /**
     * Bean后处理器前方法执行
     * @param o        需要被被处理的bean
     * @param beanName 该bean的名称
     */
    private void executeBeanPostAfter(Object o, String beanName) {
        for (MyBeanPostProcessor beanPostProcessor : beanPostProcessors) {
            if (o instanceof MyBeanPostProcessor) {
                break;
            }
            beanPostProcessor.postProcessAfterInitialization(o, beanName);
        }
    }
    /**
     * Bean后处理器后方法执行
     *
     * @param o        需要被被处理的bean
     * @param beanName 该bean的名称
     */
    private void executeBeanPostBefore(Object o, String beanName) {
        for (MyBeanPostProcessor beanPostProcessor : beanPostProcessors) {
            if (o instanceof MyBeanPostProcessor) {
                break;
            }
            beanPostProcessor.postProcessBeforeInitialization(o, beanName);
        }
    }
    /**
     * Bean初始化
     * 需要编写一个判断Bean是否存在初始化方法
     */
    private void executeBeanInit(Object o) {
        for (Method method : o.getClass().getDeclaredMethods()) {
            //获取method方法,并判断是否存在注解
            if (method.isAnnotationPresent(MyPostConstruct.class)) {
                try {
                    //即使是private修饰的方法也可以执行
                    method.setAccessible(true);
                    method.invoke(o);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    System.out.println("bean初始化失败");
                    e.printStackTrace();
                }
            }
        }
    }

在依赖注入后依次调用这三个方法,对加工后的Bean放入单例池。

测试

@MyComponent
public class People {
    private String name;

    @MyPostConstruct
    public void init(){
        this.name = "张三";
        System.out.println("people名字叫"+name);
    }
}

可以看到,每创建一个Bean就执行一次Bean 后处理器,同时也能执行bean的初始化方法。

完整代码

这次的改动比较大,修改过后的ApplicationContext的完整代码如下

public class SpringApplication3 {
    //配置类属性
    private Class<?> configClass;
    //单例池
    private ConcurrentHashMap<String, Object> singletonObjects;
    //BeanDefinitionMap
    private Map<String, BeanDefinition> beanDefinitionMap;

    private Map<String, Object> cacheSingleton;

    private List<MyBeanPostProcessor> beanPostProcessors;

    //每创建一个容器都会有各自的单例池
    {
        singletonObjects = new ConcurrentHashMap<>(256);
        beanDefinitionMap = new ConcurrentHashMap<>(256);
        cacheSingleton = new ConcurrentHashMap<>(256);
        beanPostProcessors = new ArrayList<>();
    }

    public SpringApplication3(Class<?> configClass) throws Exception {
        this.configClass = configClass;
        this.init();
        this.initBeanPostProcess();
        this.initSingletonObjects();
    }

    private void init() throws Exception {
        //判断该类是否是配置类
        MyConfiguration configuration = configClass.getAnnotation(MyConfiguration.class);
        if (configuration == null) {
            //说明不是配置类
            throw new RuntimeException("该类不是配置类,目前只支持传入配置类");
        }
        //如果是配置类,那么获取需要扫描的类路径
        MyComponentScan scan = configClass.getAnnotation(MyComponentScan.class);
        if (scan == null) {
            throw new RuntimeException("不存在扫描注解");
        }
        String[] paths = scan.value();
        //遍历需要扫描的路径
        for (String path : paths) {
            path = URLDecoder.decode(path, "UTF-8");
            //path这里还有作用,因此不建议将path接收replace的结果
            // com/zmt
            String newPath = path.replace(".", "/");
            //获取根路径,并去除最前面的/
            String classPath = configClass.getResource("/").getPath().substring(1);
            classPath = URLDecoder.decode(classPath, "UTF-8");
            System.out.println("classPath:" + classPath);

            //获取被扫描包的绝对路径
            String absolutePath = configClass.getResource("/" + newPath).getPath();
            absolutePath = URLDecoder.decode(absolutePath, "UTF-8");
            System.out.println(absolutePath);

            //遍历绝对路径的子包,寻找.class结尾的文件
            File rootFile = new File(absolutePath);
            if (rootFile.isFile()) {
                throw new RuntimeException("不支持扫描具体类");
            }
            //说明指定路径是一个文件夹
            //获取所有的类名称
            List<String> listClass = dirFile(rootFile);
            for (String absoluteClassPath : listClass) {
                //完整类名,通过Class.forName()创建出该类
                String fullClassName = absoluteClassPath.replace(classPath, "").replace("/", ".");
                //加载出该类
                ClassLoader classLoader = configClass.getClassLoader();
                Class<?> aClass = classLoader.loadClass(fullClassName);
                MyComponent myComponent = aClass.getAnnotation(MyComponent.class);
                if (myComponent == null) {
                    //该类不应该被加载
                    continue;
                }

                //获取Bean名称
                String beanName = getBeanName(fullClassName, aClass, myComponent);

                //判断是否添加了@MyScope注解,通知判断该注解的值
                BeanDefinition beanDefinition = new BeanDefinition();
                beanDefinition.setType("Singleton");
                if (aClass.isAnnotationPresent(MyScope.class)) {
                    String value = aClass.getAnnotation(MyScope.class).value();
                    if (!"Singleton".equals(value) && "Prototype".equals(value)) {
                        //说明是不是非单例的对象
                        beanDefinition.setType("Prototype");
                    }
                }
                beanDefinition.setClazz(aClass);
                beanDefinitionMap.put(beanName, beanDefinition);

            }
        }
    }

    /**
     * 初始化Bean后处理器
     */
    private void initBeanPostProcess() {
        Set<Map.Entry<String, BeanDefinition>> entries = this.beanDefinitionMap.entrySet();
        for (Map.Entry<String, BeanDefinition> entry : entries) {
            if (MyBeanPostProcessor.class.isAssignableFrom(entry.getValue().getClazz())) {
                try {
                    String beanName = entry.getKey();
                    Object o = entry.getValue().getClazz().newInstance();
                    beanPostProcessors.add((MyBeanPostProcessor) o);
                    singletonObjects.put(beanName, o);
                } catch (InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void initSingletonObjects() {
        //从BeanDefinitionMap当中获取信息并加载单例对象到单例池中
        Set<Map.Entry<String, BeanDefinition>> entries = this.beanDefinitionMap.entrySet();
        for (Map.Entry<String, BeanDefinition> entry : entries) {
            createBean(entry);
        }
    }

    /**
     * 实现依赖注入
     *
     * @param entry
     */
    private void createBean(Map.Entry<String, BeanDefinition> entry) {
        //获取到类
        Object o = null;
        String beanName = entry.getKey();
        if (cacheSingleton.containsKey(beanName)) {
            o = cacheSingleton.get(beanName);
        } else {
            Class<?> clazz = entry.getValue().getClazz();
            try {
                o = clazz.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        //获取该类中的字段
        Field[] declaredFields = o.getClass().getDeclaredFields();
        for (Field declaredField : declaredFields) {
            //判断字段上是否存在@MyResource注解
            if (declaredField.isAnnotationPresent(MyResource.class)) {
                MyResource annotation = declaredField.getAnnotation(MyResource.class);
                //需要被注入的名称
                String setBeanName = annotation.value();
                if ("".equals(setBeanName)) {
                    //如果MyResource没有指定,那么以字段名为注入名
                    setBeanName = declaredField.getName();
                }
                if (!beanDefinitionMap.containsKey(setBeanName)) {
                    throw new RuntimeException("不存在该bean名称:" + setBeanName);
                }
                //开放权限
                declaredField.setAccessible(true);
                BeanDefinition beanDefinition = beanDefinitionMap.get(setBeanName);
                try {
                    if (beanDefinition.getType().equals("Singleton")) {
                        //如果是单例bean
                        if (!cacheSingleton.containsKey(setBeanName)) {
                            //如果缓存单例池不存在,那么去单例池寻找
                            if (singletonObjects.containsKey(setBeanName)) {
                                //单例池存在的情况
                                Object o1 = singletonObjects.get(setBeanName);
                                declaredField.set(o, o1);
                            } else {
                                //单例池中也不存在
                                Object o1 = beanDefinition.getClazz().newInstance();
                                //存放在缓存单例池中
                                cacheSingleton.put(setBeanName, o1);
                                declaredField.set(o, o1);
                            }
                        } else {
                            //如果已经创建过了,但未完成创建
                            Object o1 = cacheSingleton.get(setBeanName);
                            declaredField.set(o, o1);
                        }
                    } else {
                        Object o1 = beanDefinition.getClazz().newInstance();
                        declaredField.set(o, o1);
                    }
                } catch (IllegalAccessException | InstantiationException e) {
                    e.printStackTrace();
                }
            }
        }
        //到这里执行结束,清除缓存单例池信息
        //去执行Bean后处理器后再添加到单例池
        executeBeanPostBefore(o, beanName);
        executeBeanInit(o);
        executeBeanPostAfter(o, beanName);

        cacheSingleton.remove(beanName);
        if (entry.getValue().getType().equals("Singleton")) {
            singletonObjects.put(beanName, o);
        }
    }

    private void executeBeanPostAfter(Object o, String beanName) {
        for (MyBeanPostProcessor beanPostProcessor : beanPostProcessors) {
            if (o instanceof MyBeanPostProcessor) {
                break;
            }
            beanPostProcessor.postProcessAfterInitialization(o, beanName);
        }
    }

    private void executeBeanInit(Object o) {
        for (Method method : o.getClass().getDeclaredMethods()) {
            //获取method方法,并判断是否存在注解
            if (method.isAnnotationPresent(MyPostConstruct.class)) {
                try {
                    //即使是private修饰的方法也可以执行
                    method.setAccessible(true);
                    method.invoke(o);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    System.out.println("bean初始化失败");
                    e.printStackTrace();
                }
            }
        }
    }

    private void executeBeanPostBefore(Object o, String beanName) {
        for (MyBeanPostProcessor beanPostProcessor : beanPostProcessors) {
            if (o instanceof MyBeanPostProcessor) {
                break;
            }
            beanPostProcessor.postProcessBeforeInitialization(o, beanName);
        }
    }

    private String getBeanName(String fullClassName, Class<?> aClass, MyComponent myComponent) {
        //如果注解中没有没有给定值,那么采用类名首字母小写的方式
        String beanName = myComponent.value();
        if ("".equals(beanName)) {
            System.out.println(aClass.getName() + "没有指定bean名称");
            String className = fullClassName.substring(fullClassName.lastIndexOf(".") + 1);
            beanName = className.substring(0, 1).toLowerCase() + className.substring(1);
        }
        return beanName;
    }


    /**
     * 遍历文件夹,获取所有类绝对路径集合
     *
     * @param rootFile
     * @return
     */
    private List<String> dirFile(File rootFile) {
        List<String> fileList = new ArrayList<>();
        if (rootFile.isDirectory()) {
            File[] files = rootFile.listFiles();
            for (File file : files) {
                fileList.addAll(dirFile(file));
            }
        } else {
            //走到这说明已经不是文件夹了
            String filePath = rootFile.getPath();
            if (filePath.endsWith(".class")) {
                //类的绝对路径
                String classPath = filePath.replace("\\", "/").replace(".class", "");
                fileList.add(classPath);
            }
        }
        return fileList;
    }

    public Object getBean(String beanName) {
        Object o = this.singletonObjects.get(beanName);
        if (o == null) {
            throw new RuntimeException("不存在bean为:" + beanName);
        }
        return o;
    }

}
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zmbwcx2003

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值