Spring原理篇(2)--BeanPostProcessor or BeanDefinition or Aware or InitializingBean

@TOC# Spring系列
记录在程序走的每一步___auth:huf


拨开云雾见天日 守得云开见月明

Spring关健实现 简化版

以下实现均为简化版本;有助于帮助记忆; 该版本代码有部分缺陷 例如:相互依赖…本章节不会记录相互依赖是怎么解决的; 之后会有专门的章节讲解其细节;主要描述Spring大致是怎么实现的 BeanDefinition 与 BeanPostProcessor 又是什么; 之后会一步一步引导读者理解Spring原理.并且记住.
以下代码有详细注释: 关健代码已经全部展示; (源码下载): 该篇章极为重要;如果想很好的理解后续Spring底层原理 一定要理解透彻该篇章;

package huf_spring;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 模拟SPring启动主类:
 *   ps: Spring中使用了大量反射; 进行类注解 属性 进行 功能性的区分;
 * 1:主类主要做了以下几件事
 *  1): 获取到配置文件;从配置文件中;获取到扫描路径(类的扫描路径);然后得到文件; 然后对其目录进行挨个扫描 得到相对应的class;
 *  2): 在初始化每个Bean的时候 会执行Aware回调 进行属性的注入;
 *  3): 在Aware回调之后 进入 BeanPostProcesscor List. 开始循环调用 该实现类的前置方法:postProcessBeforeInitialization
 *  4): 实例化对象的创建; 这时候是真实对象; 但是一旦进入了postProcessAfterInitialization 方法;
 *     (控制方案 可以有 事务 等等注解进行控制) 如果有 就会转变为代理对象; 如果没有 就返回创建的真实对象;
 *  5): InitializingBean 是初始化接口 使用该接口 把该接口放到容器中去; 不能保证它的执行顺序就是排在第一位;注意;
 *  
 * auth:huf
 */
public class HufApplicationContext {
    
    

    //beanDefinitionMap 存放的是所有扫描到的Bean 的class信息
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

    //singletonObjects  单例池 存放着所有扫描到的 <单例> 对象 且是非懒加载对象 也就是@Lazy 标注的对象;如果使用配置文件可以在配置文件中设置
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>();

    //beanPostProcessorList 存放着所有处理器的List  该处理器 会在实例化前后进行循环调用. 从而达到初始化前 初始化 初始化后的效果;
    //也是AOP切面的主要实现途径;
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<BeanPostProcessor>();

    //test main方法 new出该对象 注入Class文件  第一个大步骤
    public HufApplicationContext(Class configClazz) {
    
    
         //扫描 传入为AppConfig.class;
         scam(configClazz);
        /**
         * 实例化非懒加载单例bean:
         *  1. 实例化
         *  2. 属性填充
         *  3. Aware回调
         *  4. 初始化
         *  5. 添加到单例池
         */
        instanceSingletonBean();
    }

    //一
    private void scam(Class configClazz) {
    
    
        //获取到扫描类上的注解 获取到要扫描的类 目录路径
        ComponentScan componentScanAnnotation = (ComponentScan) configClazz.getAnnotation(ComponentScan.class);
        //获取到 "com.huf.service"
        String compomentScanPath = componentScanAnnotation.value();
         //第一个大步骤 2)
        List<Class> classList = getBeanClasses(compomentScanPath);
        if(null!=classList && classList.size()>0){
    
    
            for (Class clazz : classList) {
    
    
                //判断是否被注解; 注解也有很多; 这里仅模仿流程
                if (clazz.isAnnotationPresent(Service.class)) {
    
    
                    //被扫描注解注视到 或者是配置文件配置到. 那么 就开始封装 BeanDefinition;  自行到BeanDefinition 查看该类用处;
                    BeanDefinition beanDefinition = new BeanDefinition();
                    //保存Class
                    beanDefinition.setBeanClass(clazz);
                    Service service = (Service) clazz.getAnnotation(Service.class);
                    //获取该Service 名字 实际上 Spring有自动扫描 自动生成名字的一套方案 在以前XML文件定义的形式 有id 可以定义Bean的名字.
                    String beanName  = service.value();
                    //判断类是否实现了BeanPostProcessor接口 假设实现;
                    if(BeanPostProcessor.class.isAssignableFrom(clazz)){
    
    
                        try {
    
    
                            //获取该实现类BeanPostProcessor
                            BeanPostProcessor instance = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
                            //在初始化过程中 它仅仅是把相关实例保存到内存List中 在此处并无调用;请注意;
                            beanPostProcessorList.add(instance);
                        } catch (Exception e) {
    
    
                            e.printStackTrace();
                        }
                    }
                    // 解析scope
                    if (clazz.isAnnotationPresent(Scope.class)) {
    
    
                        Scope scope = (Scope) clazz.getAnnotation(Scope.class);
                        String scopeValue = scope.value();
                        if (ScopeEnum.singleton.name().equals(scopeValue)) {
    
    
                            beanDefinition.setScope(ScopeEnum.singleton);
                        } else {
    
    
                            beanDefinition.setScope(ScopeEnum.prototype);
                        }
                    } else {
    
    
                        beanDefinition.setScope(ScopeEnum.singleton);
                    }
                    //保存BeanDefinition 使用的Map集合 后续用于创建对象;
                    beanDefinitionMap.put(beanName,beanDefinition);
                }
            }
            System.out.println("");
        }
    }

    //二 获取该目录下面的所有Class 该步骤执行完后 返回主流程 开始执行instanceSingletonBean
    private List<Class> getBeanClasses(String compomentScanPath) {
    
    
        //创建List 用于返回
        List<Class> beanClasses = new ArrayList<Class>();
        //拿到主类的ClassLoader 用于获取URL路径
        ClassLoader classLoader = HufApplicationContext.class.getClassLoader();
        //"com.huf.service" 替换为 "com/huf/service"
        compomentScanPath = compomentScanPath.replace(".","/");
        //获得URL 用于创建File
        URL resource = classLoader.getResource(compomentScanPath);
        //利用URL 创建File 文件
        File file = new File(resource.getFile());
        //是否是目录
        if (file.isDirectory()) {
    
    
            //获取目录下面所有Files
            File[] files = file.listFiles();
            if(files.length>0){
    
    
                //获取到File后; 遍历
                for (File f : files) {
    
    
                    //获取 Class的 路径 :E:\Users\Administrator\hufSpring\target\classes\com\huf\service\StudentService.class
                    String fileName = f.getAbsolutePath();
                    if (fileName.endsWith(".class")) {
    
    
                        //截取名字;
                        String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                        className = className.replace("\\", ".");
                        try {
    
    
                            //通过名字拿到相对应的Clazz;
                            Class<?> clazz = classLoader.loadClass(className);
                            //加入集合 尾部返回;
                            beanClasses.add(clazz);
                        } catch (ClassNotFoundException e) {
    
    
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return beanClasses;
    }

    //三
    private void instanceSingletonBean() {
    
    
        //遍历beanDefinitionMap 得到beanDefinition集合;
        for (String beanName : beanDefinitionMap.keySet()) {
    
    
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.getScope().equals(ScopeEnum.singleton)) {
    
    
                //如果该类是单例类 就开始创建;
                Object bean = doCreateBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);
            }
        }
    }

    private Object doCreateBean(String beanName, BeanDefinition beanDefinition) {
    
    
        //通过BeanDefinition获取Class;
        Class beanClass = beanDefinition.getBeanClass();
        try {
    
    
            // 获取构建对象;
            Constructor declaredConstructor  = beanClass.getDeclaredConstructor();
            //得到实例化对象
            Object instance = declaredConstructor.newInstance();

            // 拿到当前对象的每个属性;
            //  这里举个例子 Autowired 简化;
            Field[] fields = beanClass.getDeclaredFields();
            for (Field field : fields) {
    
    
                if (field.isAnnotationPresent(Autowired.class)) {
    
    
                    String fieldName = field.getName();
                    Object bean = getBean(fieldName);
                    field.setAccessible(true);
                    field.set(instance, bean);
                }
            }
            // Aware回调
            if (instance instanceof BeanNameAware) {
    
    
                ((BeanNameAware)instance).setBeanName(beanName);
            }
            //beanPostProcessorList 提取到所有 处理器 这是 在初始化前  调用该方法;  重要
            for (BeanPostProcessor beanPostProcessor: beanPostProcessorList) {
    
    
                beanPostProcessor.postProcessBeforeInitialization(beanName, instance);
            }
            // 初始化
            if (instance instanceof InitializingBean) {
    
    
                ((InitializingBean)instance).afterPropertiesSet();
            }

            //beanPostProcessorList 提取到所有 处理器 这是 在初始化后  调用该方法;  重要
            for (BeanPostProcessor beanPostProcessor: beanPostProcessorList) {
    
    
                //这里很关键 代理对象就是在这里创建的. 我们有一个StudentPostProcessor 类;
                instance = beanPostProcessor.postProcessAfterInitialization(beanName, instance);
            }
            return instance;

        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        return null;
    }


    public Object getBean(String beanName) {
    
    
        if (singletonObjects.containsKey(beanName)) {
    
    
            return singletonObjects.get(beanName);
        } else {
    
    
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            return doCreateBean(beanName, beanDefinition);
        }
    }


}

package huf_spring;
/**
 * 该类是主要 目的是为了保存类的相关信息;
 * ps : 是类的相关信息; 它是 Spring定义类的基石; 非常非常重要!
 * 在实际情况下 当spring容器启动的时候会去调用ConfigurationClassPostProcessor这个bean工厂的后置处理器完成扫描;
 * 当扫描到的Class文件 里面有很多信息需要保存 例如 class中的 scope、lazy,等等信息; 它的信息不止以下几个属性;
 * BeanDefinition 是 Spring 设计出来保存这些信息的;
 *
 * auth:huf
 */
public class BeanDefinition {
    
    
    private Class beanClass;
    private ScopeEnum scope;
    public Class getBeanClass() {
    
    
        return beanClass;
    }
    public void setBeanClass(Class beanClass) {
    
    
        this.beanClass = beanClass;
    }
    public ScopeEnum getScope() {
    
    
        return scope;
    }
    public void setScope(ScopeEnum scope) {
    
    
        this.scope = scope;
    }
}

package huf_spring;
/**
 * 后置处理器; 它主要作用于在Bean的初始化前 初始化后
 * 该接口用于Bean的初始化
 * 是Spring IOC容器给我们提供的一个扩展接口
 * auth:huf
 */
public interface BeanPostProcessor {
    
    
     //Bean初始化前
     Object postProcessBeforeInitialization(String beanName, Object bean);

     //Bean初始化后;
     Object postProcessAfterInitialization(String beanName, Object bean);
}

package com.huf.service;

import huf_spring.BeanPostProcessor;
import huf_spring.Service;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * StudentService 实现 BeanPostProcessor 类
 * 在一开始就Bean初始化的时候就已经实例化 加载进入了 List<BeanPostProcessor> 中等待调用
 * 这个地方仅仅使用类扫描后 使用的方法是 BeanPostProcessor.class.isAssignableFrom(clazz) 就得到了这个类的实例;
 * 具体请看 hufApplicationContext
 * auth:huf
 */
@Service("hufPostProcessor")
public class HufPostProcessor implements BeanPostProcessor {
    
    
    public Object postProcessBeforeInitialization(String beanName, Object bean) {
    
    
        System.out.println("前置方法执行:"+beanName);
        return bean;
    }
    public Object postProcessAfterInitialization(String beanName, final Object bean1) {
    
    
        System.out.println("后置方法执行:"+beanName);
        //开始创建动态代理 这里为了方便直接使用JDK代理; 因为JDK代理 是使用接口代理的;
        Object proxyInstance = null;
        if(beanName.equals("teacherService")){
    
    
             proxyInstance = Proxy.newProxyInstance(HufPostProcessor.class.getClassLoader(), bean1.getClass().getInterfaces(), new InvocationHandler() {
    
    
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
                    System.out.println("切入点:test方法");
                    method.invoke(bean1,args);
                    System.out.println("切入点:test之后");
                    return null;
                }
            });
            return proxyInstance;
        }
        return proxyInstance!=null?proxyInstance:bean1;
    }
}

以下为运行结果集:

在这里插入图片描述

总结

至此; BeanPostProcessor or BeanDefinition or Aware or InitializingBean 以及Bean的详细创建过程 已经全部模拟完毕; 感兴趣的小伙伴 可以 (源码下载):环境只需要一个JDK1.8 其他任何包都不用加;

猜你喜欢

转载自blog.csdn.net/wenaicoo/article/details/120091547