从源码理解Spring原理,并用代码实现简易Spring框架

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

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

  由于实习,时间比较多,也感恩遇到个好老师,教并给我时间看源码,虽然没有做过多少业务,但是感觉比做业务更有意义。慢慢的去跟代码, 对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)解析配置文件的类

    

    import com.springImpl.annotion.Resources;    import org.w3c.dom.Document;    import org.w3c.dom.NamedNodeMap;    import org.w3c.dom.NodeList;    import org.xml.sax.SAXException;

    import javax.xml.parsers.DocumentBuilder;    import javax.xml.parsers.DocumentBuilderFactory;    import javax.xml.parsers.ParserConfigurationException;    import java.io.*;    import java.lang.reflect.Field;    import java.util.HashMap;    import java.util.Iterator;

    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 beanIdName=attrs.item(j).getNodeName();//假装这个节点Id                        String beanId=null;                        if(beanIdName!=null) {                            beanId = attrs.item(j).getNodeValue();                        }                        if(beanId!=null){                            beanMap.put(beanIdName,beanId);                        }

                        String beanClassName=attrs.item(j).getNodeName();//假装这个节点就是class                        String beanClass=null;                        if(beanClassName!=null) {                            beanClass = attrs.item(j).getNodeValue();                        }                        if(beanClass!=null){                            beanMap.put(beanClassName,beanClass);                        }                    }                    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框架完毕。

				
时间: 2024-10-24 08:13:07

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

北京赛车PK10改单软件——已经整套源码的工作原理和代码的编写方式实战分享

北京赛车PK10改单技术分享. 无需账号和密码,业务QQ:博客昵称或者点击联系.只需要提供网址即可做到无痕修改,大家可以放心下载使用,禁止用于非法行业 本北京赛车PK10改单软件都是免费提供使用的 本软件支持各种网盘程序改单只要能读取到数据库成功率百分之百:支持北京赛车PK10改单.时时彩改单,快三改单 六合彩网站改单,各类高频彩改单,各种数据修改 下面我们来介绍下软件操作的详细的操作步骤 <package id="Microsoft.Web.Infrastructure" ve

深入理解ButterKnife源码并掌握原理(一)

前言 话说在android这座大山里,有一座庙(方块公司-square),庙里住着一个神-jake(我是这么叫的嘻嘻). 不要小看这个小jake,这个神可是为android应用开发们提供了强有力的帮助.比如流行的开源库okhttp,eventbus系列 ,retrofit,butterknife 等等都是出于他之手.小弟佩服的不要不要的-,可以说是为android的应用开发效率和耦合性提高了一个台阶啊. 其它的大神我也是佩服的不要不要的-嘻嘻 声明 这一系列的文章是对ButterKnife的源码

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

文章目录 1. EnableAutoConfiguration 帮助我们做了什么 2. 配置参数类 – FreeMarkerProperties 3. 自动配置类 – FreeMarkerAutoConfiguration4. 扩展阅读 3.1. 核心注解 3.2. 注入 Bean 结合<Spring Boot 揭秘与实战 源码分析 - 开箱即用,内藏玄机>一文,我们再来深入的理解 Spring Boot 的工作原理. 在<Spring Boot 揭秘与实战 源码分析 - 开箱即用,内藏

Tomcat7.0源码分析——请求原理分析(上)

前言 谈起Tomcat的诞生,最早可以追溯到1995年.近20年来,Tomcat始终是使用最广泛的Web服务器,由于其使用Java语言开发,所以广为Java程序员所熟悉.很多人早期的J2EE项目,由程序员自己实现Jsp页面或者Servlet接受请求,后来借助Struts1.Struts2.Spring等中间件后,实际也是利用Filter或者Servlet处理请求,大家肯定要问了,这些Servlet处理的请求来自哪里?Tomcat作为Web服务器是怎样将HTTP请求交给Servlet的呢? 本文就

Tomcat7.0源码分析——请求原理分析

Tomcat7.0源码分析--请求原理分析 谈起Tomcat的诞生,最早可以追溯到1995年.近20年来,Tomcat始终是使用最广泛的Web服务器,由于其使用Java语言开发,所以广为Java程序员所熟悉.很多人早期的J2EE项目,由程序员自己实现Jsp页面或者Servlet接受请求,后来借助Struts1.Struts2.spring等中间件后,实际也是利用Filter或者Servlet处理请求,大家肯定要问了,这些Servlet处理的请求来自哪里?Tomcat作为Web服务器是怎样将HTT

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

就目前来说Retrofit2使用的已相当的广泛,那么我们先来了解下两个问题: 1 . 什么是Retrofit? Retrofit是针对于Android/Java的.基于okHttp的.一种轻量级且安全的.并使用注解方式的网络请求框架. 2 . 我们为什么要使用Retrofit,它有哪些优势? 首先,Retrofit使用注解方式,大大简化了我们的URL拼写形式,而且注解含义一目了然,简单易懂: 其次,Retrofit使用简单,结构层次分明,每一步都能清晰的表达出之所以要使用的寓意: 再者,Retr

Tomcat7.0源码分析——请求原理分析(中)

前言 在<TOMCAT7.0源码分析--请求原理分析(上)>一文中已经介绍了关于Tomcat7.0处理请求前作的初始化和准备工作,请读者在阅读本文前确保掌握<TOMCAT7.0源码分析--请求原理分析(上)>一文中的相关知识以及HTTP协议和TCP协议的一些内容.本文重点讲解Tomcat7.0在准备好接受请求后,请求过程的原理分析. 请求处理架构 在正式开始之前,我们先来看看图1中的Tomcat请求处理架构. 图1 Tomcat请求处理架构 图1列出了Tomcat请求处理架构中的主

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

前言 在<TOMCAT源码分析——请求原理分析(上)>一文中已经介绍了关于Tomcat7.0处理请求前作的初始化和准备工作,请读者在阅读本文前确保掌握<TOMCAT源码分析——请求原理分析(上)>一文中的相关知识以及HTTP协议和TCP协议的一些内容.本文重点讲解Tomcat7.0在准备好接受请求后,请求过程的原理分析. 请求处理架构 在正式开始之前,我们先来看看图1中的Tomcat请求处理架构. 图1 Tomcat请求处理架构 图1列出了Tomcat请求处理架构中的主要组件,这里

Tomcat源码分析——请求原理分析(下)

前言 本文继续讲解TOMCAT的请求原理分析,建议朋友们阅读本文时首先阅读过<TOMCAT源码分析——请求原理分析(上)>和<TOMCAT源码分析——请求原理分析(中)>.在<TOMCAT源码分析——请求原理分析(中)>一文我简单讲到了Pipeline,但并未完全展开,本文将从Pipeline开始讲解请求原理的剩余内容. 管道 在Tomcat中管道Pipeline是一个接口,定义了使得一组阀门Valve按照顺序执行的规范,Pipeline中定义的接口如下: getBas