需要澄清在不使用注解的情况下如何使用Spring MVC。

3
我将尝试更详细地理解Spring MVC,现在我只知道一些基本概念。我正在参考在线教程和书籍。据我所知,Spring MVC框架具有“前置控制器”和“MVC”设计模式。
前置控制器(DispatcherServlet)使用HandlerMapping来将URL映射到控制器类。因为想了解框架背后的原理,所以我没有使用注释。
为了达到这个目的,我创建了一个基于Spring MVC的简单Web应用程序,下面是代码:

Controller 代码:

public class SimpleSpringController extends AbstractController {

    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest req, HttpServletResponse res) throws Exception {

        ModelAndView mw = new ModelAndView("welcome","welcomeMessage", "welcome user!");
        return mw;
    }
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
    <web-app>
        <display-name>FirstSpringMVCProject</display-name>
        <servlet>
            <servlet-name>spring-dispatcher</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        </servlet>

    <servlet-mapping>
        <servlet-name>spring-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

Spring xml configuration

<beans>

    <bean id="HandlerMapping1" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

    <bean name="/welcome" class="com.example.controller.SimpleSpringController"/>

    <bean id="viewResolver1" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix"> <value>/WEB-INF/</value> </property>
        <property name="suffix"> <value>.jsp</value> </property>
    </bean>

</beans>

应用程序按预期工作。
我无法理解的概念在于Spring配置文件中指定HandlerMappingViewResolver实现的部分。
例如,在上述Spring XML配置中,我们有以下bean定义:
<bean id="HandlerMapping1" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

在上面的xml配置中,org.springframework.web.servlet.handler.BeanNameUrlHandlerMappingHandlerMapping接口的实现之一。
这个bean的id是HandlerMapping1,只是一个随机标识符(我也可以选择MyHandlerMapping)。以下是疑问:
  1. 谁读取这个配置文件?前端控制器读取这个配置文件吗?

  2. 框架如何知道上述情况下HandlerMapping实现的id是HandlerMapping1。通常我们会使用getBean("beanId")来获取特定的bean id,我们如何知道spring框架自动推断出这是HandlerMapping的实现类呢?

希望能对理解有所帮助。
3个回答

3

初始化

在Spring中,前端控制器是DispatcherServlet,它继承了FrameworkServlet。默认情况下,FrameworkServlet会创建一个XmlWebApplicationContext并尝试读取名为<servlet-name>-servlet.xml的XML文件(在使用web.xml时!)。 (所有这些都可以通过为配置的DispatcherServlet设置适当的init-param来覆盖)。

加载完DispatcherServlet后,它最终会调用initStrategies方法。该方法将为DispatcherServlet配置委派策略。它首先尝试按类型检测大多数策略(例如HandlerMapingHandlerAdapterViewResolver等),然后再按名称检测一些策略(例如localeResolverthemeResolvermessageSource等)。如果找不到,则会安装给定接口的默认策略。这些默认值在Spring的DispatcherServlet.properties中定义。

重要提示:一旦您自己定义了策略,该策略的默认值就不再适用!例如,如果您在配置中有一个BeanNameUrlHandlerMapping,则不再加载HandlerMapping的默认配置,因为您提供了显式配置。

请求处理

启动后,DispatcherServlet已准备好处理请求。

DispatcherServlet Flow 图片来自Spring Framework参考指南

处理程序选择和执行

当一个请求被DispatcherServlet处理时,它会检查所有配置的HandlerMapping,以查看是否有一个能够为给定请求提供处理程序(Controller@Controller只是可以处理的处理程序类型之一)。一旦找到处理程序,它将查找用于检测处理程序的HandlerAdapter。如果也找到了,则找到的处理程序连同当前的HttpServletRequestHttpServletResponse一起传递给HandlerAdapterhandle方法。

视图解析

现在,如果从HandlerAdapter.handle方法返回ModelAndView,则如果ModelAndView.getViewName()方法返回名称,则将解析View,这将传递给ViewResolver以将其解析为View
View正在被解析或从ModeAndView.getView()方法返回时,将调用render方法以让选择的View自行呈现。使用ModelAndView类中的(可选)模型。

异常处理

当在处理请求期间发生异常时,首先将此异常传递给配置的HandlerExceptionResolver之一。如果其中一个可以处理它,则会调用所选的HandlerExceptionResolverresolveException方法。就像HandlerAdapter.handle方法一样,该方法也可以返回ModelAndView,当这种情况发生时,它与普通请求一样进行处理(参见视图解析)。

注意: 这也在DispatcherServletjavadoc中有一定程度的记录。您可以在其中找到所有默认策略和知名名称,以及请求处理流程的说明。


1
非常感谢您精彩的解释,我需要全神贯注地阅读它 :)。顺便说一下,您描述的流程方式很棒,我很想知道如何建立这种理解(以及自信心!)。我参考的Spring书籍并没有涵盖Spring内部工作原理,我的思维总是渴望了解其内部运作方式。我觉得参考文档有点复杂(目前为止),您能否建议我如何更好地理解,而不仅仅是“如何使用框架执行给定任务”?再次感谢! - CuriousMind
2
这是我与Spring一起工作、为Spring做出贡献并撰写有关Spring的书籍已有14年了。同时,我曾是Interface21/SpringSource的顾问,这也对我的帮助很大 : )。但我喜欢知道事情是如何运作的,因为它是开源的,所以可以查看源代码并阅读一些好的相关书籍。 - M. Deinum
很棒!顺便问一下,你能推荐一些初学者级别和高级别的书籍吗,包括你自己的书 :)。 - CuriousMind

2
  1. 控制器并不读取xml文件,Spring框架负责读取。
  2. 你的Spring xml配置文件由Spring框架加载并进行连接。自动装配是根据类型进行连接的,其中HandlerMapping1可以为接口HandlerMapping提供服务。

    有趣的是,BeanNameUrlHandlerMapping是默认的HandlerMapping类,如果Spring找不到任何已声明的HandlerMapping类,则DispatcherServlet创建它。

    如果你想试试看让一些东西出问题的话,可以创建一个名为HandlerMapping2的重复bean,然后查看错误日志。


谢谢您的回复,这意味着正在进行按类型自动装配。但是在配置文件中没有提到自动装配?另外,我尝试了您建议的重复bean,但应用程序仍然可以正常工作。现在我更加困惑了! - CuriousMind

1
  1. Spring framework reads the xml.
  2. Well, for spring MVC to work, the handler should be instantiated. The spring MVC framework needs to resolve the mappings and so we have handlers like BeanNameUrl..., ControllerBeanNameHandlerMapping, etc

    However, HandlerMapping is not generally used by your application code. So you need not specify whether this bean is autowired by type or not. It is used by the framework. So it is autowired in the framework classes by type.

    You can try defining the bean in the xml without any id, name something like

    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    

    and it should still work.

    Id or name is required only when you want to refer to the bean by id or name for your use. Framework classes which need this handler mapping don't refer to the bean of HandlerMapping type by id or name. Framework will 'scan' the xml (or the loaded classes) and if it finds a concrete class in there of type (org.springframework.web.servlet.HandlerMapping) then it won't complain.

    To think of a similar case for example, when you run the junit tests, the engine will scan for all the methods annotated with @Test and it will 'know' that these are the methods it needs to execute as tests. It does not matter what the names of test methods are.

希望这回答了您的问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接