Java Web系列:Spring MVC基础

澳门新浦京8455com 4

1.Web MVC基础

MVC的本质是表现层模式,我们以视图模型为中心,将视图和控制器分离出来。就如同分层模式一样,我们以业务逻辑为中心,把表现层和数据访问层代码分离出来是一样的方法。框架只能在技术层面上给我们帮助,无法在思考和过程上帮助我们,而我们很多人都不喜欢思考和尝试。

澳门新浦京8455com 1

最近看了spring mvc的工作流程,怕忘,所以要记录一下。

2.实现Web MVC的基础

实现Web MVC基础可以概括为1个前段控制器和2个映射。

澳门新浦京8455com,(1)前端控制器FrontController

ASP.NET和JSP都是以Page路径和URL一一对应,Web
MVC要通过URL映射Controller和View,就需要一个前端控制器统一接收和解析请求,再根据的URL将请求分发到Controller。由于ASP.NET和Java分别以IHttpHandler和Servlet作为核心,因此ASP.NET
MVC和Spring
MVC分别使用实现了对应接口的MvcHandler和DispatcherServlet作为前段控制器。

澳门新浦京8455com 2

ASP.NET中通过HttpModule的实现类处理URL映射,UrlRoutingModule根据URL将请求转发给前端控制器MvcHandler。Spring
MVC中,则根据URL的配置,直接将请求转发给前端控制器DispatcherServlet。

(2)URL和Contrller的映射

ASP.NET
MVC将URL和Controller的映射规则存储在RouteCollection中,前端控制器MvcHandler通过IController接口查找控制器。Spring
MVC则通过RequestMapping和Controller注解标识映射规则,无需通过接口依赖实现控制i器。

(3)URL和View的映射

ASP.NET MVC
默认通过RazorViewEngine来根据URL和视图名称查找视图,核心接口是IViewEngine。Spring
MVC
通过internalResourceViewResolver根据URL和视图名称查找视图,核心接口是ViewResolver。

  1. 首先客户端提交请求,请求包含URL以及可能的内容信息。这些将会被传递给DispatcherServlet。Spring
    MVC的所有请求都会通过DispatcherServlet。它将请求委托给应用程序的其他组件来执行实际的处理。
  2. DispatcherServlet需要将请求发给控制器(Controller),DispatcherServlet通过URL来查询处理器映射(Handler
    Mapping),然后就知道要发送给哪个控制器。
  3. 选择合适的控制器后,DispatcherServlet会将请求发送给选中的控制器。
  4. 请求到达控制器后,会卸下其负载,并等待控制器处理这些信息。控制器通常会把信息交给一个或多个服务来完成具体工作。控制器完成工作后,会返回一个Model以及一个View的逻辑名称。最后会将ModelAndView发送回DispatcherServlet。
  5. DispatcherServlet把逻辑视图名称发送给视图解析器(View
    Resolver),后者会根据配置的规则,通过逻辑视图名称来确认真实的视图。
  6. DispatcherServlet
    确认真实视图后,会把Model丢入真实的视图中,通过视图渲染给客户端。

3.Spring MVC的基础配置

(1)前端控制器DispatcherServlet初始化:AbstractAnnotationConfigDispatcherServletInitializer

ASP.NET
MVC初始化需要我们在HttpApplication.Application_Start方法中注册默认的URL和Controller规则,Spring
MVC由于采用注解映射URL和Controller,因此没有对应的步骤。ASP.NET在根web.config中配置了UrlRoutingModule可以将请求转发给MvcHandler,Spring
MVC我们需要我们配置DispatcherServlet以及其对应的URL来达到接管所有请求的目的,Spring已经利用Servlet3.0定义的ServletContainerInitializer机制,为我们提供了内置的AbstractAnnotationConfigDispatcherServletInitializer,只要只需要像继承HttpApplication的MvcApplication一样,写一个MvcInitializer。

package s4s;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class MvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { MvcConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

}

(2)URL和View的映射:WebMvcConfigurerAdapter

ASP.NET的RazorViewEngine内置了View的Path和扩展名.cshtml的规则。Spring
MVC的internalResourceViewResolver没有提供默认值,一般我们会指定将View放置在统一的视图目录中,使用特定的扩展名。Spring同样提供了内置的WebMvcConfigurerAdapter,我们只需写一个自己的MvcConfig继承它,重写configureViewResolvers方法即可。

package s4s;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@EnableWebMvc
@ComponentScan
@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
        registry.viewResolver(viewResolver);
    }
}

注:Model是一个Map(key-value)的集合。可以作为请求方法的参数。这样,该方法就能将处理的结果添加到model中。如果model.addAttribute不指定key类型,那么会根据返回的结果类型进行推定判定。如:返回结果是个List<User>,那么model中key的判定结果就是userList。同理:如果不显示的指明返回的view逻辑名称,同样会根据返回类型进行推定。如:返回结果是个List<User>,那么视图的逻辑名称判定就是:“userList”。

4.Spring MVC的Controller、Model和View

(1)URL和Controller的映射:

Spring MVC和ASP.NET
MVC的不同,不通过IController接口标识Controller,也不通过RouteCollection定义URL和Controller,取而代之的是两个注解:Controller和RequestMapping。因此我们在普通的POJO类上应用@Controller和@RequestMapping即可。

package s4s;

import javax.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MyController {

    @ResponseBody
    @RequestMapping(value = "/")
    public String home() {
        return "home";
    }

    @RequestMapping(value = "/register")
    public String register(@ModelAttribute("model") RegisterUserModel model) {
        return "register";
    }

    @RequestMapping(value = "/register", method = RequestMethod.POST)
    public String register(@ModelAttribute("model") @Valid RegisterUserModel model, BindingResult result) {
        if (!result.hasErrors()) {
            return "redirect:/account";
        }
        return "register";
    }
}

(2)Model:

通过使用@ModelAttribute、@Valid和BindingResult参数,我们可以指定Model的Name是否参与验证并获取验证结果。为在Model上使用注解验证,还需要引入validation-api和hibernate-validator。

ASP.NET将视图最终编译为WebViewPage<object>,View和Model是一一对应并且类型匹配的,Model可以是任意的POCO。Spring
MVC中View和Model是一对多的,提供了ModelMap和其子类ModelAndView提供类似ASP.NET
MVC中ViewResult的功能。ModelMap的基类是LinkedHashMap<String,
Object>。

Spring MVC中没有ViewResult类型。在Spring
MVC中,我们一般返回String类型,可以有多种含义:

a.返回View的名称。

b.返回文本:在Action上应用@ResponseBody注解时。

c.返回跳转:以”redirect:”开头时。如:return “redirect:/success”

模型的验证:

(1)在Model字段上使用JSR-303定义的注解(需要引入hibernate validator)。

(2)在Controller的Model参数上应用@ModelAttribute、@Valid

(3)在View中使用<form:errors>标签

Spring
MVC需要添加jstl和spring的tag支持才能完成模型相关的操作。由于Spring
MVC中的View和ASP.NET
MVC中的区别较大,没有办法指定View持有的Model类型也就没有了智能提示和错误检测的优势。

package s4s;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class RegisterUserModel {
    @Size(max = 20, min = 5)
    private String userName;
    @Size(max = 20, min = 5)
    private String password;
    @NotNull
    private String confirmPassword;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getConfirmPassword() {
        return confirmPassword;
    }

    public void setConfirmPassword(String confirmPassword) {
        this.confirmPassword = confirmPassword;
    }
}

register.jsp

<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="s"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<!DOCTYPE HTML>
<html>
<head>
<title>Getting Started: Serving Web Content</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
    <h2>Register</h2>
    <form:form modelAttribute="model">
        <s:bind path="*">
            <c:if test="${status.error}">
                <div id="message" class="error">Form has errors</div>
            </c:if>
        </s:bind>
        <div>
            <form:label path="userName">userName</form:label>
            <form:input path="userName" />
            <form:errors path="userName" cssClass="error" />
        </div>
        <div>
            <form:label path="password">password</form:label>
            <form:password path="password" />
            <form:errors path="password" cssClass="error" />
        </div>
        <div>
            <form:label path="confirmPassword">confirmPassword</form:label>
            <form:password path="confirmPassword" />
            <form:errors path="confirmPassword" cssClass="error" />
        </div>
        <input type="submit" value="submit">
    </form:form>
</html>

DispatcherServlet
是整个SpringMVC的核心。第一步就要学会配置DispatcherServlet。有两种方式配置它:1.DispatcherServlet
被配置在web.xml文件中,并被打包进应用的war包。

5.Spring MVC的初始化机制

Spring实现了Servlet
3.0规范定义的javax.servlet.ServletContainerInitializer接口并通过javax.servlet.annotation.HandlesTypes注解引用了WebApplicationInitializer接口。因此在Servlet容器初始化时,在当前class
path路径下的WebApplicationInitializer实现类的onStartup方法会自动执行(这和ASP.NET的Application_Start作用类似,在系列中的Java
Web基础时曾经提到过)。

ASP.NET中我们在Application_Start中初始化依赖注入容器。在Spring
MVC中,我们实现WebApplicationInitializer接口同样可以执行依赖注入的初始化。在Web环境中,我们使用的ApplicationContext接口的实现类为基于注解的AnnotationConfigWebApplicationContext(在系列中的Spring依赖注入基础中曾经提到过),但我们无需直接实现WebApplicationInitializer并手动初始化AnnotationConfigWebApplicationContext对象,因为Spring已经定义了AbstractAnnotationConfigDispatcherServletInitializer作为WebApplicationInitializer接口的实现类,已经包含了AnnotationConfigWebApplicationContext的初始化。

采用基于Annotation注解时可以通过@Configurateion指定POJO来替代web.xml配置依赖注入。同样,@ComponentScan可以替代web.xml中的扫描配置功能,使用ComponentScan配合Configurateion可以达到0xml配置的方式。上文中提到的Contrller相关的注解,都是启用ComponentScan后才会被扫描生效。

AbstractAnnotationConfigDispatcherServletInitializer类的父类AbstractDispatcherServletInitializer中已经包含DispatcherServlet的初始化。相关类图如下:

澳门新浦京8455com 3

  1. 使用Java将DispatcherServlet 配置在Servlet容器中。

5.Spring MVC的Action Filter

.NET
MVC提供了众多Filter接口和一个ActionFilterAttribute抽象类作为Filter的基础,其中以实现了IAuthorizationFilter接口的AuthorizeAttribute拦截器最为我们熟知。Spring
MVC则提供了基于HandlerInterceptor接口的众多接口、抽象类和实现类,其中也有和.NET
MVC类似的权限验证UserRoleAuthorizationInterceptor拦截器。内置的拦截器可以满足大部分需求,为了省事图就画在一张上了,上面是Spring
MVC的,下面是.NET MVC的。

澳门新浦京8455com 4

servlet容器:servlet容器是通过实现了javax.servlet.ServletContainerInitializer接口的类来完成配置的。Spring
提供了这个接口的实现,名字叫做SpringServletContainerInitializer,这个类(SpringServletContainerInitializer)又依赖于实现了WebApplicationInitializer接口的类,后者会负责完成创建配置的任务。Spring
3.2
引入了WebApplicationInitializer的实现,名字叫做AbstractAnnotationConfigDispatcherServletInitializer,所以,它最终负责具体的配置任务。

总结

(1)MVC实现的要点是前端控制器、URL和Controller的映射、URL和View的映射

(2)MvcHandler和DispatcherServlet

(3)ServletContainerInitializer和HttpApplication.Application_Start

(4)RazorViewEngine和internalResourceViewResolver

(5)IMvcFilter和HandlerInterceptor

目前没有找到类似ASP.NET中的从特性(注解)生成客户端JavaScript验证的方式,如果大家有相关资料分享,提前谢谢大家。

新建一个类,扩展AbstractAnnotationConfigDispatcherServletInitializer,就等于实现了WebApplicationInitializer接口。因此当它部署到Servlet
3.0 容器中时,容器会自动发现它,并用它来配置Servlet 上下文。

参考:

(1)

(2)

(3)

随笔里的文章都是干货,都是解决实际问题的,这些问题我在网上找不到更好的解决方法,才会有你看到的这一篇篇随笔,因此如果这篇博客内容对您稍有帮助或略有借鉴,请您推荐它帮助更多的人。如果你有提供实际可行的更好方案,请推荐给我。

配置Servlet说明:任何继承了AbstractAnnotationConfigDispatcherServletInitializer的类,会自动配置DispatcherServlet和
ContextLoaderListener。

DispatcherServlet启动时,会创建SpringApplicationContext,并加载配置类中所声明的Bean。如Handler
Mapping,Controller,以及ViewResolver等。然而,Spring
Web程序还有一个应用上下文,是由ContextLoaderListener创建的,它负责创建驱动应用后端的中间层和数据库组件。

因此,AbstractAnnotationConfigDispatcherServletInitializer的扩展类需要overwrite两个方法,来完成这2个不同应用上下文的创建:

 //中间层和数据库组件 @Nullable @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[] {RootConfig.class}; } //指定配置类,Handler Mapping,Controller,以及ViewResolver等 @Nullable @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[] {WebConfig.class}; }

RootConfig和WebConfig都是具有@Configuration注解的类。

启动Spring MVC通过配置一个带有@EnableWebMvc注解的类,来完成启动。

之后,完成配置视图解析器、启用组件扫描(类上添加@ComponentScan、设置静态资源等操作。这一切,如果放在上面的WebConfig.class里,就再好不过了。

一个最简单的Spring MVC的框架就搭建完成了。

You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图