从源码理解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)解析配置文件的类
    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框架的更多相关文章
- 深入源码理解Spring整合MyBatis原理
		
写在前面 聊一聊MyBatis的核心概念.Spring相关的核心内容,主要结合源码理解Spring是如何整合MyBatis的.(结合右侧目录了解吧) MyBatis相关核心概念粗略回顾 SqlSess ...
 - Spring Boot 揭秘与实战 源码分析 - 工作原理剖析
		
文章目录 1. EnableAutoConfiguration 帮助我们做了什么 2. 配置参数类 – FreeMarkerProperties 3. 自动配置类 – FreeMarkerAutoCo ...
 - Android 网络框架之Retrofit2使用详解及从源码中解析原理
		
就目前来说Retrofit2使用的已相当的广泛,那么我们先来了解下两个问题: 1 . 什么是Retrofit? Retrofit是针对于Android/Java的.基于okHttp的.一种轻量级且安全 ...
 - Tomcat源码分析——请求原理分析(上)
		
前言 谈起Tomcat的诞生,最早可以追溯到1995年.近20年来,Tomcat始终是使用最广泛的Web服务器,由于其使用Java语言开发,所以广为Java程序员所熟悉.很多人早期的J2EE项目,由程 ...
 - MyBatis 源码分析 - 缓存原理
		
1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然 ...
 - 学习laravel源码之中间件原理
		
刨析laravel源码之中间件原理 在看了laravel关于中间件的源码和参考了相关的书籍之后,写了一个比较简陋的管道和闭包实现,代码比较简单,但是却不好理解所以还是需要多写多思考才能想明白其中的意义 ...
 - Tomcat源码分析——请求原理分析(中)
		
前言 在<TOMCAT源码分析——请求原理分析(上)>一文中已经介绍了关于Tomcat7.0处理请求前作的初始化和准备工作,请读者在阅读本文前确保掌握<TOMCAT源码分析——请求原 ...
 - 从微信小程序开发者工具源码看实现原理(一)- - 小程序架构设计
		
使用微信小程序开发已经很长时间了,对小程序开发已经相当熟练了:但是作为一名对技术有追求的前端开发,仅仅熟练掌握小程序的开发感觉还是不够的,我们应该更进一步的去理解其背后实现的原理以及对应的考量,这可能 ...
 - Caffe源码理解2:SyncedMemory CPU和GPU间的数据同步
		
目录 写在前面 成员变量的含义及作用 构造与析构 内存同步管理 参考 博客:blog.shinelee.me | 博客园 | CSDN 写在前面 在Caffe源码理解1中介绍了Blob类,其中的数据成 ...
 
随机推荐
- h5  新增的invalid事件,貌似有很大bug
			
<!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8" ...
 - Java编程思想总结笔记Chapter 2
			
本章介绍Java程序的基本组成部分,体会到Java中几乎一切都是对象. 第二章 一切都是对象 目录: 2.1 用引用操纵对象 2.2 必须由你创建所有对象 2.3 永远不需要销毁对象 2.4 创建 ...
 - CJOJ 1087 【NOIP2010】乌龟棋 / Luogu 1541 乌龟棋(动态规划)
			
CJOJ 1087 [NOIP2010]乌龟棋 / Luogu 1541 乌龟棋(动态规划) Description 小明过生日的时候,爸爸送给他一副乌龟棋当作礼物. 乌龟棋的棋盘是一行N个格子,每个 ...
 - 关于Actionbar的那些事
			
咱们来说说有趣的Actionbar吧 Actionbar这个控件是3.0以后添加的,Google在13年的时候把它加入了v7包,现在第三方的Actionbarserlock基本没什么用了, 要兼容2. ...
 - socket 异步通信的一些问题
			
socket通信在使用时被封装很简单,像操作文件一样简单,正是因为简单里面好多细节需要深入研究一下. windows下通信有select和iocp方式,select是传统方式,在socket里使用re ...
 - hdu_1506:Largest Rectangle in a Histogram 【单调栈】
			
题目链接 对栈的一种灵活运用吧算是,希望我的注释写的足够清晰.. #include<bits/stdc++.h> using namespace std; typedef long lon ...
 - 工程经验总结之吹水"管理大境界"
			
1.个人认为项目管理最核心的能力是预见风险和快速解决风险的能力. 从实践来看,没有百分百的完美计划,任何计划都有出现偏差的可能,或者说计划总是不会按照最初的设定去完美执行的. 项目经理存在的主要价值就 ...
 - Python面向对象编程(一)
			
1.什么是面向对象 面向对象(oop)是一种抽象的方法来理解这个世界,世间万物都可以抽象成一个对象,一切事物都是由对象构成的.应用在编程中,是一种开发程序的方法,它将对象作为程序的基本单元. 2.面向 ...
 - iBatis一些非见用法(相当实用)
			
兼顾效率,iBatis一些非见用法(10条) 2009-09-18 10:33:03 标签:iBatis 休闲 职场 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声 ...
 - 自动生成proto Js语句
			
在与后端的WebSocket通信时,前端要带一个proto文件是一个累赘的事情.首先是明显的曝光了协议实体对象,再一个浏览器客户端很容易会缓存该文件,新的协议更新可能导致客户端不能使用,另外在cdn服 ...
 
			
		