Spring源码阅读笔记

来源:互联网 发布:mac 终端翻墙 编辑:程序博客网 时间:2024/05/22 15:43

前言

  作为一个Java开发者,工作了几年后,越发觉力有点不从心了,技术的世界实在是太过于辽阔了,接触的东西越多,越感到前所未有的恐慌。

每天捣鼓这个捣鼓那个,结果回过头来,才发现这个也不通,那个也不精。就连我吃饭的家伙Java,现在想想,其实我根本就不了解。

  可是每当编写简历的时候,总想把工作经验、工作年限写的长一点,半年写成一年,一年写成两年。可是每当有人问我技术原理的时候,又会想,

我的工作时间要是短一点的话,答不上来是不是就不会这么丢脸。

  还记得刚工作不久,就在项目中使用过Spring了,但是那个时候,只是照着别人写的代码,照葫芦画瓢似得写着自己的代码,画瓢花久了,写的熟练了,心中就开始窃喜,

竟然也就敢厚着脸皮说,我也会Spring了,简历上就毫不犹豫的写上:了解Spring。(真是臭不要脸啊)

  Spring真的是很流行,做过了好多项目,基本上都或多或少的使用了Spring框架或者集成了Spring框架,于是乎,无论进入了哪个项目中,竟然也都得心应手。

然后就有点飘飘然,领导还给安排带几个新人,开发时,还得意的给新人们讲解,这个注解这样使用,那个配置那样配置。但是,其实如何使用这个东西,只要是程序员,短短几天也都会了。

几天后,新人就能与你干同样的活了,你这个老人与新人的区别在哪里呢?其实这时,我有点慌了。

  平时游荡于各个技术交流群,发现群里面其实有好多还是在校生,就开始钻研各种技术难题。回头再想想,我当时还在学校的时候在干嘛呢:打游戏、看小说、睡懒觉、泡校内网、约妹子、耗时间…

人家还在学生时期,就开始阅读Spring源码了。而我呢,竟然都用了好几年了,都从来没有提起过阅读源码的念头。

  后来有一次面试,人家问:看你也有几年工作经验了,说一说Spring的原理吧、IOC、DI、AOP…再说一说,从一个请求开始,一直到得到响应,Spring都干了些什么?

我的个亲娘啊,你到底在说什么,我怎么听不懂。我忽然想起了一首歌:

我想了很久,我开始慌了,是不是我又做错了什么,你哭着对我说,童话里都是骗人的,你不可能是我要的程序猿

岔题了…

亡羊补牢,痛定思痛,总而言之,就读一下Spring源码(3.1版本)看看吧。

读读读读读…

对于没有任何源码阅读经验的人,而且大局观整体概念很差的人来说,源码真的是太难读了,可能还是人笨吧。所以我只能采取老办法,所谓书读百遍,其义自现,读源码应该也是同样的道理。

到开始写本篇笔记开始,前前后后已经花了整整3周时间,期间各种debug,打了上百个断点,不厌其烦的一遍又一遍跟踪跟进,从服务器启动开始,

从HttpServletBean的init()进入,再到initWebApplicationContext(),再到refresh(),再到refreshBeanFactory(),再到finishRefresh(),直到服务器启动成功。不知道读了多少遍,

但是源码的东西实在的太多了,想要完全读懂,完全理清头绪,还差很远啊。所以我只重点关注了两块内容,就是bean的定位加载解析注册、bean的实例化两大块内容,其他地方稍作了解,没有太过深入。

整个容器的启动流程,都在AbstractApplicationContext的refresh()的模板方法中了。

public void refresh() throws BeansException, IllegalStateException {        synchronized (this.startupShutdownMonitor) {            // Prepare this context for refreshing.            prepareRefresh();            // Tell the subclass to refresh the internal bean factory.            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();            // Prepare the bean factory for use in this context.            prepareBeanFactory(beanFactory);            try {                // Allows post-processing of the bean factory in context subclasses.                postProcessBeanFactory(beanFactory);                // Invoke factory processors registered as beans in the context.                invokeBeanFactoryPostProcessors(beanFactory);                // Register bean processors that intercept bean creation.                registerBeanPostProcessors(beanFactory);                // Initialize message source for this context.                initMessageSource();                // Initialize event multicaster for this context.                initApplicationEventMulticaster();                // Initialize other special beans in specific context subclasses.                onRefresh();                // Check for listener beans and register them.                registerListeners();                // Instantiate all remaining (non-lazy-init) singletons.                finishBeanFactoryInitialization(beanFactory);                // Last step: publish corresponding event.                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技术内幕:深入解析SPRING架构与设计原理》为主,仅仅才看了前两章节,关于Spring Framework的核心实现,AOP什么,事务控制什么的都还没看。

看博客:看源码过程中,有些不理解的,就上网查博客,然后再回头读源码。觉得写得还可以的博客文章我记录了下来,供以后参考(希望管理员不要因为有外部链接,就把我踢掉了)。

  • Spring的IOC原理

  • web.xml 中的listener、 filter、servlet 加载顺序及其详解 - JesseV - 博客园

正如上文所说,因为水平有限,本篇随笔并非是解析源码的文章,因为源码很多看不懂的地方并不敢妄下断言, 网上各种解析源码,画UML图,时序图(我不会画图%>_<%)的文章也有很多,

我是抱着解决疑问的态度来分析读源码的,另外在大神眼中,有些问题看起来可能问的很愚蠢,但是谁让我是菜鸟呢。

以上是我的心路历程,不看也罢。

那么,列出有疑问的地方(目前主要针对IOC相关部分、问题将不断补充)

(补充:我用来阅读并且debug的demo是基于注解配置的,另外以下说明都纯属个人谬论,如果错漏,请予以批评指正。)

问题1:我为什么要使用Spring?

问题2:Spring怎么这么聪明,知道哪些bean需要被实例化?

问题3:Spring中Bean是什么时候被实例化的?

问题4:依赖的bean是何时被注入的?

问题5:bean的单例模式?原型模式是什么东西?分别是何时被实例化的?

问题6:采用注解注入时,为什么只声明接口就可以将其实现类注入?

问题7:采用注解注入时,接口与实现类为何都能注入成功?区别?

问题8:加上了autowired的属性是什么时候实例化的?

问题9:网络上经常说的“第一次使用bean的时候实例化该bean”是什么意思?

问题10:发送一个请求,是怎么定位到具体的Controller的某一个方法的?

问题11:HandlerMapping、HandlerAdater与Controller到底是什么关系?

问题1:我为什么要使用Spring?

  这个问题其实比较尴尬,因为其实我入行比较晚, 这个时候,已经有一大波比较成熟的框架体系了,早期的什么繁琐的臃肿的框架基本上没用过几个,再加上项目经验比较单一,用来用去就那么几个框架,用起来都大差不差,

用的熟练了,感觉都差不多。所以说,完全没有能力来横向比较,谁谁谁架构合理,谁谁谁扩展性强,只能勉强说这个框架的那种写法挺方便,比起单纯的Servlet写法要方便之类的….其他的,我都呵呵就好了。

如果你要强行问我为什么使用Spring,那我只能回答你:因为大家都在用啊,呵呵…

问题2:Spring怎么这么聪明,知道哪些bean需要被实例化?

    什么控制反转、依赖注入,说到底就是程序在需要使用一个bean的时候,Spring框架确保该bean已经被实例化了,可以直接拿来使用。那么这时候,我想要知道,Spring是如何知道哪些bean需要实例化?

其实程序总归是程序,程序是很笨的,它并不知道要实例化哪些东西,除非你告诉它。

首先需要配置DispatcherServlet,这是web应用的总入口,也可以说是中央处理器,就是通过它,一步一步的启动Spring容器的。

在web.xml中

<servlet>        <servlet-name>springmvc</servlet-name>        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>        <init-param>            <param-name>contextConfigLocation</param-name>            <param-value>classpath:/spring/readspring-servlet.xml</param-value>        </init-param>        <load-on-startup>1</load-on-startup>    </servlet>    <servlet-mapping>        <servlet-name>springmvc</servlet-name>        <url-pattern>/</url-pattern>    </servlet-mapping>

另外还需要配置另外一个文件:readspring-servlet.xml,这个东西叫做上下文,主要是告诉Spring,在启动的时候干些什么事情,启动哪些功能,实例化哪些Bean。

上一个最简单的配置:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"    xmlns:mvc="http://www.springframework.org/schema/mvc"    xsi:schemaLocation="    http://www.springframework.org/schema/tx     http://www.springframework.org/schema/tx/spring-tx.xsd    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd                           http://www.springframework.org/schema/beans                           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd                           http://www.springframework.org/schema/context                           http://www.springframework.org/schema/context/spring-context-3.1.xsd">    <!-- 扫描指定目录 -->    <context:component-scan base-package="com.readspring" />    <!-- 不拦截静态资源 -->    <mvc:default-servlet-handler />    <!-- 启动注解支持 -->    <mvc:annotation-driven />    <!-- 配置视图解析器 -->    <bean id="internalResourceViewResolver"        class="org.springframework.web.servlet.view.InternalResourceViewResolver">        <property name="prefix" value="/WEB-INF/jsp/" />        <property name="suffix" value=".jsp" />    </bean></beans>

我不逐一解读每句话的意思,还是针对上面的问题,Spring如何知道哪些bean要被实例化?

或者说的具体一点,Spring是如何知道要实例化我们写的Controller、Service、Dao的。

是这段配置告诉它的。

<!-- 扫描指定目录 -->
<context:component-scan base-package="com.readspring" />

从字面意思可以看出,component-scan就是组件扫描的意思,然后有一个base-package,告诉它需要扫描的文件路径。  

 组件自动扫描是在Spring2.5加上的,在一个稍大的项目中通常会有上百个组件,如果都使用XML的bean定义来配置组件的话,显然会增加配置文件的体积,

 查找及维护也不方便,而Spring2.5就为我们引入了组件自动扫描机制,它可以在classpath下寻找标注了@Service、@Repository、@Controller、@Component注解的类

 并把这些类纳入Spring容器中管理,它的作用和在XML中使用bean节点配置组件是一样的。

另外补充说明,既然是component扫描,为什么标注了@Service等也会被扫描到,可以看一下源码。

Service注解本身也被标注了@Component注解,所以说可以被扫描到,相当于继承自@Component。

这里写图片描述

下面,我大概的描述一下,容器启动的过程中,如何根据配置,来扫描指定类的。

容器启动过程中,首先调用DispatcherSerlvet的init方法,init方法内部根据web.xml的配置,读取配置的上下文readspring-servlet.xml,然后逐句解析该上下文,

当它读取到context:component-scan标签时,就启动对应的解析器,也可以叫做扫描器,对应的Class为:ComponentScanBeanDefinitionParser

有这么一个对应关系的Class,可以让Spring知道,为什么读取到这个标签,就实例化ComponentScanBeanDefinitionParser这个类。

public class ContextNamespaceHandler extends NamespaceHandlerSupport {    public void init() {        registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());        registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());        registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());        registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());        registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());        registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());        registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());    }}

转载于http://www.cnblogs.com/notDog/p/5420727.html

原创粉丝点击