先来看看传统项目中,springMVC如何搭建。
1.web项目中,总有一个web.xml。如图所示,需要指定最终处理请求的Servlet,以及一些初始化的参数,指定配置文件在哪里。那么如何载入容器的相关信息。

2.我们先来看看一些结构。关于DisPatcherServlet类的。

上图就可以解释,如何将web.xml中Servlet相关信息载入容器的。通过抽象类GenericServlet中的init(ServletConfigc onfig)方法。我们来看看这个方法的实现。如下图所示。

看到init(ServletConfig config)方法的注释,这个config包含了Servlet的相关配置信息,此外这个方法的实现将这个config保存起来,方便后期使用。init(ServletConfig config)中除了将config保存起来,下一步便是初始化了。在这个类中,init方法是空方法,如下图所示。

看一下注解,这个方法的话,直接覆写就可以了,就用不着去调用super.init(config),想了一下这是一种很聪明的做法,各个不同的servlet实现类只需要覆写init()方法即可。然后通过父类去调用init(Config)方法即可,而不用去关心各个子类的init方法的具体实现。
下面进入重点,那么DispatcherServlet究竟对init()方法做了什么实现呢,在源码中,DispatcherServlet并没有实现对应的方法,随后发现对应的父类FrameworkServlet也没有,最终FrameworkServlet的父类HttpServletBean实现了init()方法。
话不多说,直接贴代码·。

首先当我看到这段代码的时候,我是很一脸懵逼的,但是我还是决定硬着头皮来解析这段代码,看到那么多的新结构,首先来对结构进行拆分把。本文标题里面的第x行,指的是上图的第x行。
从init()第154行讲起
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
先看看具体的结构。

下面来看看构造方法如何实现的。为了后续做准备,楼主先把web.xml中的文件修改一下,添加了一个自定义参数,如下图所示

ServletConfigPropertyValues的构造方法,如下图所示

看代码太抽象了点,我们来debug一下吧。
这是debug出来的结果,[contextConfigLocation=classpath:spring/servlet-context.xml, testParam=ojbk],此处便是将web.xml中的<init-Param>的name,value转换为PropertyValue对象。随后保存到MutablePropertyValues类中的propertyValueList中去。requirementProperties应该就是我们自己定义必须存在的属性,若没有就会报错。
总结一下,第154行的作用,将web.xml中servlet的初始化方法注入到MutablePropertyValues的成员变量propertyValueList中去。下面讲一下第157行
第157行
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
先来看看BeanWrapper是什么鬼东西。

PropertyAccessorFactory又是什么鬼东西呢?

下面看看这个工厂方法的具体实现,内容很简单,就一句话
public static BeanWrapper forBeanPropertyAccess(Object target) {
return new BeanWrapperImpl(target);
}
好,那么问题又来了,BeanWrapperImpl又是什么鬼东西。看看下面的结构图,其实往上还有很多层接口,但是没必要贴出来了,主要是分析的是AbstractNestablePropertyAccessor的构造方法。因为new BeanWrapperImpl(target)最终的调用其实是AbstractNestablePropertyAccessor类的构造方法。


第一个方法registerDefaultEditors()是在PropertyEditorRegistrySupport类中实现,将defaultEditorsActive改为true,此处过于简单,至于后面做什么用的再看。
再看第二个方法setWrappedInstance(Object obj),看了源码,其主要作用是对BeanWrapperImpl的父类(AbstractNestablePropertyAccessor)做一些成员变量的封装


从上图可知,便是将生成的BeanWrapperImpl对象,以及httpServletBean对象注入到对应的成员变量中。注:BeanWrapperImpl为PropertyEditorRegistrySupport的子类。
总结一下BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);最终生成了一个对象,该对象的成员变量中,包含了httpServletBean对象,bw.wrapperObject=httpServletBean。然后还有一个TypeConverterDelgate类型转换器的成员变量。现在还不知道有什么作用。后面再看看。
第158行
ResourceLoader resourceLoader=new ServletContextResourceLoader(getServletContext());
看到这个名字,相信大家都知道用于做什么了,资源加载器。先看看这个类的结构

其实说白了就是将servletContext注入到资源加载器这个对象里面去。看具体实现。

那么好了,如何拿到这个servletContext呢,我们来看一下getServletContext()这个方法。还记得通过抽象类GenericServlet中的init(ServletConfig config)这个方法吗,加载servlet配置的时候会将对应的servletConfig注入到GenericServlet的成员变量中去,通过这个方法我们可以拿到servletConfig,从而再拿到servletContext。
总结一下,这个方法是拿到servlet上下文,并且注入资源加载器。
第159行
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
第157行说了那么多,总算要进入主题了。不然都不知道那个对象做什么用的。
先分别看看Resource.class以及ResourceEditor的结构
org.springframework.core.io.Resource这个类纯粹是一个接口,实现了部分的方法。不过多描述。
接下来看一下org.springframework.core.io.ResourceEditor的结构。

ResourceEditor中包含了俩个成员变量,resourceLoader,以及propertyResolver,先来看看构造方法吧

bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));那么getEnvironment()方法中返回的对象必然是PropertyResolver的子类。我们先来看看这个方法的实现。

从代码上来看,没有便会new一个,此处也是将对应的对象注入成员变量罢了。
那么最后,我们看看registerCustomEditor(ClassrequiredType, PropertyEditorpropertyEditor);这个方法的实现吧。org.springframework.beans.BeanWrapperImpl.BeanWrapperImpl这个类一直往上推,最终找到继承的父类org.springframework.beans.PropertyEditorRegistrySupport实现了该方法。

看了一下此代码,其实也是将一些变量进行保存。不做过多分析。将对应的Resource.class以及ResourceEditor放到对应的map(这个map是BeanWrapperImpl的一个父类的属性)里面去。可以猜想一下后期如何使用。
第160行
initBeanWrapper(bw);空方法,啥也没做
第161行
bw.setPropertyValues(pvs,true);
这个方法是由父类org.springframework.beans.AbstractPropertyAccessor实现的,可以看看前面的结构图。下面看看具体实现。

第161行我是没看懂,先跳过
网友评论