• 前言(本文为原创,转载请注明出处)

  个人之前对于框架的学习,就停留在配置,使用阶段。说实话过段时间就会忘得荡然无存。也不知道框架的运行逻辑,就是知道添加个注解,就可以用了。

  由于实习,时间比较多,也感恩遇到个好老师,教并给我时间看源码,虽然没有做过多少业务,但是感觉比做业务更有意义。慢慢的去跟代码, 对Spring

  运行流程大致有个解。现分享给大家,不足之处,希望各位补充,相互学习。

  • 从源码看Spring

  可能我们很少在意,ClassPathXmlApplicationContext这个类,其实这个类做了很多的事情,它才是我们了解Spring框架的窗户。 

    ClassPathXmlApplicationContext c=new ClassPathXmlApplicationContext("ApplicationContext.xml");

 当我们执行上面的语句的时候,

  public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
  throws BeansException {
     super(parent);
   setConfigLocations(configLocations);
   if (refresh) {
   refresh();
   }
  }
实际上走的是这个构造函数,这个构造函数做了两个事情,一是setConfigLocations()方法初始化一些配置。一是reFresh()函数,该函数进行了Bean的注册,事件广播等。
refresh()函数十分重要,具体干了什么,请看下面的源码注释:
   public void refresh() throws BeansException, IllegalStateException {
  synchronized (this.startupShutdownMonitor) {
   prepareRefresh();
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  prepareBeanFactory(beanFactory);
  try {
   // Allows post-processing of the bean factory in context subclasses.
   postProcessBeanFactory(beanFactory);
  // 注册Bean.
   invokeBeanFactoryPostProcessors(beanFactory);
  // 登记拦截bean创建的处理器
   registerBeanPostProcessors(beanFactory);
  initMessageSource();
   // 创建了一个广播事件的类
  initApplicationEventMulticaster();
  onRefresh();
  // 注册事件监听者
  registerListeners();
  // 实例化那些被配置成单例的Bean
  finishBeanFactoryInitialization(beanFactory);
  // 结束刷新,实际上就是广播事件等操作
   finishRefresh();
  }
   catch (BeansException ex) {
   // Destroy already created singletons to avoid dangling resources.
  destroyBeans();
  // Reset 'active' flag.
  cancelRefresh(ex);
  // Propagate exception to caller.
  throw ex;
   }
  }
  }
 先对Spring框架的事件机制简单的做个扩展,常规来看事件涉及如下几方:
  1)事件本身(生产者)
  2)消费事件者
  3)事件管理(订阅中心)
 1.Spring事件本身从ApplicationEvent派生,事件消费者为ApplicationListener<T extends ApplicationEvent>,事件管理中心为ApplicationEventMulticaster
  它负责管理监听者等。
 2.Spring当广播一个事件时,它首先去查找该事件的监听者,然后再去遍历监听者调用其onApplicationEvent(Application evnet)接口,将事件传给监听者。
  最后当我们调用getBean()的时候,实际上经过refresh()的bean注册,已经被缓存到map里面,直接出map里面取出实例化即可。
  • 代码简易实现Spring
     

  上面的工程目录结构为com.springImpl.annotion放的spring的注解类。com.springImple.core放得实现Spring框架的核心类。com.springImpl.test放的是测试类。

  1)注解类:

   假设现在我这个框架还是比较搓,就一个注解,

    import java.lang.annotation.*;
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)//为了书写简单 这里只作用于属性 也就是域 成员变量
    public @interface Resources {
    }

  2)事件类:

   现在这个事件就是一个约定,实际啥也没有     

    public class ApplicationEvent {
    }

  3)监听者类  

    public interface ApplicationListener<T extends ApplicationEvent> {
    void onApplicationEvent(T event);
    }

  4)事件订阅中心类

    public interface ApplicationEventMulticaster {
     void publishEvent(ApplicationEvent event);
    }

  5)解析配置文件的类

    public class ConfigResolver extends ApplicationEvent implements ApplicationEventMulticaster{
    private String configXml="spring.xml";
    static HashMap<String ,Object> BeanFactory;//这里就是模仿beanFactory 将所有的bean用beanid与对应实例用map保存起来
    static HashMap<String ,ApplicationListener> RegistryListener;//这里保存那些是监听者的bean
    static {
    BeanFactory=new HashMap<>();
    RegistryListener=new HashMap<>();
    }
    public ConfigResolver(String config){
    configXml=config==null?configXml:config;//默认就是spring.xml
    setConfigLocations(configXml);
    refresh();
    }
     public Object getBean(String beanId){
    return BeanFactory.get(beanId);
     }
     private void setConfigLocations(String configXml){
    //什么都不做 当然可以做一些环境的检查 将配置的提取用一个类去处理等等 我这偷个懒
    }
    private void refresh(){
    //注册bean
    invokeBeanFactoryPostProcessors(BeanFactory);
    //登记监听者
    registerListeners();
    //j结束刷新 表面程序已经启动 可以广播这个刷新完毕事件了 广播事件
    finishRefresh();
    }
    private void finishRefresh(){
    publishEvent(this);
  }      /**
     * 从beanfactory找到那些是监听者类型的bean
     */
    private void registerListeners(){
    Iterator<String> it=BeanFactory.keySet().iterator();
    while(it.hasNext()){
    String key=it.next();
    if(BeanFactory.get(key) instanceof ApplicationListener){
     RegistryListener.put(key,(ApplicationListener)BeanFactory.get(key));
     it.remove();
    }
    }
     }      /**
    * 将配置文件中的bean全部实例化到map里面
    * @param beanFactory
    */
     private void invokeBeanFactoryPostProcessors(HashMap beanFactory){     InputStream in= null;
    try {
     in = ConfigResolver.class.getResourceAsStream(configXml)==null?
              new FileInputStream(configXml):ConfigResolver.class.getResourceAsStream(configXml);//兼容资源路径 与 绝对路径
    } catch (FileNotFoundException e) {
     e.printStackTrace();
     }
    try {
     DocumentBuilder db=DocumentBuilderFactory.newInstance().newDocumentBuilder();
    Document dc=db.parse(in);
    NodeList nl=dc.getElementsByTagName("bean");
    for(int i=0;i<nl.getLength();i++){
    NamedNodeMap attrs= nl.item(i).getAttributes();
     HashMap<String,String> beanMap=new HashMap<>();//对应一个bean标签
    for(int j=0;j<attrs.getLength();j++){
    String beanNodeName=attrs.item(j).getNodeName();
    String beanNodeValue=null;
    if(beanNodeName!=null) {
    beanNodeValue = attrs.item(j).getNodeValue();
    }
    if(beanNodeValue!=null){
     beanMap.put(beanNodeName,beanNodeValue);
     }
    }
     String beanId=beanMap.get("id");
    String beanClass=beanMap.get("class");
    if(beanClass==null||beanId==null){
     continue;
     }
     try {
     Class cls=Class.forName(beanClass);
     Object beanObject=cls.newInstance();
    Field[] fds=beanObject.getClass().getDeclaredFields();
    for(Field fd:fds){
    fd.setAccessible(true);//获取访问私有变量权限
    Resources rs=fd.getAnnotation(Resources.class);
    if(rs!=null){
     fd.set(beanObject,fd.getType().newInstance());//实例化带有Resource注解的成员
    }
    }
    beanFactory.put(beanId,beanObject);//将bean放到map
    } catch (ClassNotFoundException e) {
     e.printStackTrace();
    } catch (IllegalAccessException e) {
     e.printStackTrace();
    } catch (InstantiationException e) {
     e.printStackTrace();
    }
     }
     } catch (ParserConfigurationException e) {
     e.printStackTrace();
     } catch (SAXException e) {
     e.printStackTrace();
    } catch (IOException e) {
     e.printStackTrace();
     }
     }     /**
    * 广播事件
     * @param event
     */
     @Override
     public void publishEvent(ApplicationEvent event) {
    Iterator<String> it=RegistryListener.keySet().iterator();
    while(it.hasNext()){
    RegistryListener.get(it.next()).onApplicationEvent(event);
    }
    }
    }
  6)看一下测试类:
    //监听程序启动的类 监听者
    public class ApplicationStartLister implements ApplicationListener<ApplicationEvent> {
     @Override
     public void onApplicationEvent(ApplicationEvent event) {
     System.out.println("SpringImpl App start");
    }
    }
  
    //假设这里有个眼瞎的人 他用注解注注入了个眼睛Ege类
    public class Blind {
    @Resources
    private Ege ege;
     public Ege getEge(){
    return ege;
     }
    }
    //眼睛Ege类
    public class Ege {
     public String see(){
    return "the world is so beautiful.";
    }
    }     //主程序
     public class DoMain {
        public static void main(String []args){
     ConfigResolver cfg=new ConfigResolver("E:\\__Java\\__idea_proj\\SpringImpl\\src\\resources\\spring.xml");
     Blind b= (Blind) cfg.getBean("mybean");
     System.out.println("tell me how is the world :"+b.getEge().see());
     }
    }
    //配置文件

  7)运行结果

      

  到此完毕,一个简单的模仿spring框架完毕。

												

从源码理解Spring原理,并用代码实现简易Spring框架的更多相关文章

  1. 深入源码理解Spring整合MyBatis原理

    写在前面 聊一聊MyBatis的核心概念.Spring相关的核心内容,主要结合源码理解Spring是如何整合MyBatis的.(结合右侧目录了解吧) MyBatis相关核心概念粗略回顾 SqlSess ...

  2. Spring Boot 揭秘与实战 源码分析 - 工作原理剖析

    文章目录 1. EnableAutoConfiguration 帮助我们做了什么 2. 配置参数类 – FreeMarkerProperties 3. 自动配置类 – FreeMarkerAutoCo ...

  3. Android 网络框架之Retrofit2使用详解及从源码中解析原理

    就目前来说Retrofit2使用的已相当的广泛,那么我们先来了解下两个问题: 1 . 什么是Retrofit? Retrofit是针对于Android/Java的.基于okHttp的.一种轻量级且安全 ...

  4. Tomcat源码分析——请求原理分析(上)

    前言 谈起Tomcat的诞生,最早可以追溯到1995年.近20年来,Tomcat始终是使用最广泛的Web服务器,由于其使用Java语言开发,所以广为Java程序员所熟悉.很多人早期的J2EE项目,由程 ...

  5. MyBatis 源码分析 - 缓存原理

    1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然 ...

  6. 学习laravel源码之中间件原理

    刨析laravel源码之中间件原理 在看了laravel关于中间件的源码和参考了相关的书籍之后,写了一个比较简陋的管道和闭包实现,代码比较简单,但是却不好理解所以还是需要多写多思考才能想明白其中的意义 ...

  7. Tomcat源码分析——请求原理分析(中)

    前言 在<TOMCAT源码分析——请求原理分析(上)>一文中已经介绍了关于Tomcat7.0处理请求前作的初始化和准备工作,请读者在阅读本文前确保掌握<TOMCAT源码分析——请求原 ...

  8. 从微信小程序开发者工具源码看实现原理(一)- - 小程序架构设计

    使用微信小程序开发已经很长时间了,对小程序开发已经相当熟练了:但是作为一名对技术有追求的前端开发,仅仅熟练掌握小程序的开发感觉还是不够的,我们应该更进一步的去理解其背后实现的原理以及对应的考量,这可能 ...

  9. Caffe源码理解2:SyncedMemory CPU和GPU间的数据同步

    目录 写在前面 成员变量的含义及作用 构造与析构 内存同步管理 参考 博客:blog.shinelee.me | 博客园 | CSDN 写在前面 在Caffe源码理解1中介绍了Blob类,其中的数据成 ...

随机推荐

  1. pl_sql develope连接远程数据库的方法

    需要修改你所安装的数据的路径下 tnsnames.ora 文件(我安装路径是F:\app\Aside\product\11.2.0\dbhome_1\NETWORK\ADMIN) tnsnames.o ...

  2. Vijos 1981 跳石头 二分

    描述 一年一度的"跳石头"比赛又要开始了! 这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石.组委会已经选择好了两块岩石作为比赛起点和终点.在起点和终点之间,有 N 块岩 ...

  3. iOS 微信消息拦截插件系列教程-附录(服务端成果展示)

    微信iOS消息拦截插件教程 标签: 越狱开发 背景介绍 本教程所有内容免费 本教程来源于一次知识分享,如果有需要了解更多的 请联系QQ:480071411 iOS逆向高级开发群:375024882 服 ...

  4. PhpStorm配置PHP解释器(wampServer版)

    PHPStorm(以下简称为PS)和wampServer集成环境安装简单,不再赘述. 本人使用PhpStrom版本为2017.1.4版本. PS刚开始使用会使用自带服务器,但是有几率不能自动匹配到PH ...

  5. bash脚本编程---循环

    bash为过程式编程语言 代码执行顺序: 1.顺序执行:逐条执行 2.选择执行:代码有一个分支,条件满足时才会执行                       两个或以上的分支,只会执行其中一个满足条 ...

  6. 如何得到iPhone手机的UUID

    背景 测试ad-hoc打包方式打出来的包,必须在证书里面配置手机的uuid才能安装. 这样就需要获取iPhone手机的uuid来进行证书配置 一般来说iPhone手机可以安装通过Apple发布的软件. ...

  7. 认识cpu、核与线程

    作为一个后台开发人员,我想有必要了解这些基础知识.如果本文有不严谨或者疏忽的地方,请指正. cpu与核心 物理核 物理核数量=cpu数(机子上装的cpu的数量)*每个cpu的核心数 虚拟核 所谓的4核 ...

  8. Java 接口-抽象类解析

    对于面向对象编程,抽象是它的三大特征(抽象.继承.多态)之一.在Java中,可以通过两种形式来体现OOP的抽象:接口和抽象类. 这两者既相似又存异.诸位在初学的时候也会傻傻分不清接口与抽象类的区别,大 ...

  9. 学习笔记TF031:实现VGGNet

    VGGNet,牛津大学计算机视觉组(Visual Geometry Group)和Google DeepMind公司一起研发,深度卷积神经网络.VGGNet反复堆叠3x3小型卷积核和2x2最大池化层, ...

  10. Java设计模式之适配器模式(项目升级案例)

    今天是我学习Java设计模式中的第三个设计模式了,但是天气又开始变得狂热起来,对于我这个凉爽惯了的青藏人来说,又是非常闹心的一件事儿,好了不管怎么样,目标还是目标(争取把23种Java设计模式接触一遍 ...