一、从头开始
SpringMvc会通过DispatcherServlet来处理所有的请求,展开这个方法。
下面展开getHandler是怎么做的:
戒急用忍
一、从头开始
SpringMvc会通过DispatcherServlet来处理所有的请求,展开这个方法。
下面展开getHandler是怎么做的:
一、从@AutoWaired开始
在@AutoWaired中有下面的一段代码,其中populateBean负责属性的加载。而initializeBean完成属性属性加载后的自定义操作。
|
|
来看一下initializeBean的实现。
一、解析类
通常如果我们希望通过注解的方式来进行Spring MVC开发,我们都会在*-servlet.xml中加入
通过阅读类注释文档,我们发现这个类主要是用来向工厂中注册了
前两个是HandlerMapping接口的实现类,用来处理请求映射的。其中第一个是处理@RequestMapping注解的。第二个会将controller类的名字映射为请求url。
中间三个是用来处理请求的。具体点说就是确定调用哪个controller的哪个方法来处理当前请求。第一个处理@Controller注解的处理器,支持自定义方法参数和返回值(很酷)。第二个是处理继承HttpRequestHandler的处理器。第三个处理继承自Controller接口的处理器。
后面三个是用来处理异常的解析器。
版权声明:本文为博主原创文章,转载请注明出处:http://blog.jerkybible.com/2015/05/21/Spring MVC 之 @Autowired/
访问原文「Spring MVC 之 @Autowired」
一、准备工作
POJO类Parent
Controller类HomeController
root-context.xml
web.xml略。
二、回忆
在Spring 使用简单Demo进行源码调试(六)这篇文章中介绍了bean参数的赋值,AutoWired也是在这个地方实现自动加载的。首先展开文章中的doCreateBean。这里省略了很多代码。首先看pplyMergedBeanDefinitionPostProcessors这个方法。
断点运行到后去homeController的,后处理为AutowiredAnnotationBeanPostProcessor,如下图。
展开AutowiredAnnotationBeanPostProcessor中的findAutowiringMetadata方法。这一步中的重点是在buildAutowiringMetadata,构建元数据。buildAutowiringMetadata首先去缓存里面找元数据,找不到就遍历bean的和父类的字段域和方法,如果别标记为@Autowired并且不是静态的就添加到InjectionMetadata中,并添加到缓存中。
|
|
三、自动装载
代码运行到populateBean方法。这个方法太长,这里就不贴出来了,直接走到postProcessPropertyValues方法,获得InjectionMetadata对象后便遍历其中的所有InjectionElement对象,调用其中的inject方法。接着对每一个injectedElement进行inject。最后的inject进行真正的属性赋值。
|
|
|
|
看一下resolveDependency。 这个方法其实就是根据类型到bean工厂中查找类型匹配的bean实例,然后就看到了这几个条件分支语句,如果是数组,集合,映射表,自定义类型都执行了差不多的操作,findAutowireCandidate方法。这个方法会去工厂中执行类型匹配的查找,将匹配的结果集返回,不同的是,集合数组类型会通过TypeConverter进行结果的转换。
到此为止,找到了属性的匹配值,然后反射赋值就完成了整个的自动装配过程。可以看出,@Autowired是通过类型来进行自动装配的。
上面是属性的赋值过程也就是InjectionFieldElement的inject方法,InjectionMethodElement的inject方法大致相同只是对每一个方法参数执行一次resolveDependency方法来获取参数值,然后反射执行方法。
到此为止,整个实例化和装配过程也就讲完了,我们总结一下:
1)一切都是从bean工厂的getBean方法开始的,一旦该方法调用总会返回一个bean实例,无论当前是否存在,不存在就实例化一个并装配,否则直接返回。
2)实例化和装配过程中会多次递归调用getBean方法来解决类之间的依赖。
3)Spring几乎考虑了所有可能性,所以方法特别复杂但完整有条理。
4)@Autowired最终是根据类型来查找和装配元素的,但是我们设置了
版权声明:本文为博主原创文章,转载请注明出处:http://blog.jerkybible.com/2015/05/19/Spring MVC 之 contextcomponent-scan/
一、ContextLoader
通过阅读SpringMVC的源码可知,SpringMVC的初始化在ContextLoaderListener类中的contextInitialized方法,跟一下代码发现实际上是在ContextLoader中的initWebApplicationContext方法中进行初始化。初始化的代码如下,首先会创建一个WebApplicationContext对象,然后加载父类,接着使用configureAndRefreshWebApplicationContext初始化这个WebApplicactionContext对象,是在configureAndRefreshWebApplicationContext方法中进行了配置文件的加载和组件的扫描。
展开createWebApplicationContext方法。该方法初始化根WebApplicationContext,这个WebApplicationContext可以使默认的context也尅进行定制。
展开determineContextClass方法。这个方法比较简单,首先查看有没有配置CONTEXT_CLASS_PARAM这个属性,有的话使用这个属性配置的类进行加载,否则使用默认的类。
再看configureAndRefreshWebApplicationContext方法。configureAndRefreshWebApplicationContext最后的refresh方法已经在前面的Spring 使用简单Demo进行源码调试(一)系列文章里进行了简单的分析调试。
二、 <context:component-scan/>
我们在SpringMVC开发项目中,有的用注解和XML配置Bean,这两种都各有自己的优势,数据源配置比较经常用XML配置,控制层依赖的service比较经常用注解等(在部署时比较不会改变的),我们经常比较常用的注解有@Component是通用标注,@Controller标注web控制器,@Service标注Servicec层的服务,@Respository标注DAO层的数据访问。SpringMVC启动时怎么被自动扫描然后解析并注册到Bean工厂中去(放到DefaultListableBeanFactory中的Map
@Controller标注web控制器,@Service标注Service层的服务,@Respository标注DAO层的数据访问。@Component是通用标注,只是定义为一个类为Bean,SpringMVC会把所有添加@Component注解的类作为使用自动扫描注入配置路径下的备选对象。@Controller、@Service\@Respository只是更加的细化,都是被@Component标注,所以我们比较不推荐使用@Component。源代码如下:
都是有标示@Component
我们在配置文件中,标示配置需要扫描哪些包下,也可以配置对某个包下不扫描,代码如下:
说明:
SpringMVC先读取配置文件,然后根据context:component-scan中属性base-package去扫描指定包下的class和jar文件,把标示@Controller标注web控制器,@Service标注Servicec层的服务,@Respository标注DAO层的数据访问等注解的都获取,并注册为Bean类放到Bean工厂。
三、ComponentScanBeanDefinitionParser
通过阅读源码,知道接口BeanDefinitionParser可以实现将自定义的标签转化为 BeanDefinition类。而
展开ComponentScanBeanDefinitionParser中的parse方法。可以看到主要完成以下工作。
(1)获取context:component-scan 配置的属性base-package的值,然后放到数组。
(2)创建扫描对应包下的class和jar文件的对象ClassPathBeanDefinitionScanner ,由这个类来实现扫描包下的class和jar文件并把注解的Bean包装成BeanDefinition。
(3)BeanDefinition注册到Bean工厂。
再展开configureScanner方法。首先通过findCandidateComponents获取候选bean definition。然后注册到registry中。
四、筛选类
展开findCandidateComponents方法。首先获取路径下的资源Resource,然后判断资源是否可读,并且获取可读资源的MetadataReader对象,然后再调用isCandidateComponent(MetadataReader)判段是否是候选组件,如果是,则生成该metadataReader的ScannedGenericBeanDefinition对象。最后判断ScannedGenericBeanDefinition是否为候选的,如果是则添加到工厂中。
展开isCandidateComponent方法。通过变量excludeFilters, includeFilters去匹配传递进来的MetadataReader,如果与excludeFilter匹配成功返回false, 与includeFilter匹配成功返回true。
五、注册bean factory
在上面doScan方法中有
这样一行代码将beanDefinition注册到registry中,这是一个BeanDefinitionRegistry,下面是它的接口定义及继承结构:
|
|
我们可以看到接口中定义了诸多beandefinition的注册,删除,获取等方法,并且Spring为我们提供了三个内部实现,那么运行时,和之前文章的分析一样,使用了DefaultListableBeanFactory。
展开registerBeanDefinition方法。可以看出,所有的beanDefinition都由实例变量beanDefinitionMap来保存管理,他是一个ConcurrentHashMap,beanName作为键,beanDefinition对象作为值。
接上篇日志Spring 使用简单Demo进行源码调试(六)继续。
经过前面几章详(gai)细(kuo)的调试,最后来到了Demo中的第二步,
展开getBean,代码如下。这一步通过getBeanFactory方法获取bean factory,然后使用bean factory获取对应的bean。
接上篇日志Spring 使用简单Demo进行源码调试(五)继续。
接上一篇转日志的第十一步,这一步主要完成单例的初始化。直接进入这一步中的beanFactory.preInstantiateSingletons();这一步的功能主要在对每一个bean进行getBean(beanName);操作。
接上篇日志Spring 使用简单Demo进行源码调试(四)继续。
还记得refresh()函数嘛,记得也模糊了吧,贴出来好了。其中的第二步 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()已经用大篇幅描述了基本流程。接着看以下几步。
接上篇日志Spring 使用简单Demo进行源码调试(三)继续。
第一步
展开loadBeanDefinitions(resources)。这个代码比较简单,是对每一个资源进行加载。
展开loadBeanDefinitions(resource)。这一步为从特定的XML文件中加载bean factory,使用EncodedResource包装传来的resource,然后进一步加载。EncodedResource结合特定编码的资源描述符或特定字符集用于阅读资源,这里的资源描述符和特定字符集都为。
接上篇日志Spring 使用简单Demo进行源码调试(二)继续。
上篇提到了主要的初始化函数refresh,函数源码如下:
先把prepareRefresh的代码贴出来。这段代码主要功能是初始化