Spring 框架文档之 Web —— Servlet 技术栈

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 前置控制器 DispatcherServlet 根据 HandlerMapping 配置的规则拦截请求,转发给相应的 Controller 进行处理,返回的模型数据用于视图渲染。

Spring Web MVC

DispatcherServlet

  • 使用 Java 配置或 web.xml 根据 Servlet 规范进行声明和映射。
  • 然后使用 Spring 配置来发现请求映射、视图解析、异常处理等所需的委托组件。

Servlet 配置

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletCxt) {

        // Load Spring web application configuration
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        ac.register(AppConfig.class);
        ac.refresh();

        // 注册并初始化 DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/app/*");
    }
}

或者

public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { RootConfig.class };//没有用 null 代替
    }

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

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/app1/*" };
    }
    
    @Override
    protected void customizeRegistration(Dynamic registration) {
        registration.setInitParameter("enableLoggingRequestDetails", "true");
    }
}

处理过程

  • 在请求中搜索 WebApplicationContext 并将其绑定为控制器和流程中的其他元素可以使用的属性
  • 将语言环境解析器绑定到请求,以便让流程中的元素解析处理请求时使用的语言环境(呈现视图、准备数据等)
  • 主题解析器被绑定到让视图等元素决定使用哪个主题的请求
  • 如果指定了 multipart 文件解析器,将检查请求的 multipart。找到则将请求封装在 MultipartHttpServletRequest中
  • 搜索适当的处理程序。找到则执行与处理程序(预处理程序、后处理程序和控制器)相关联的执行链,以便准备模型或呈现
  • 如果是返回模型,则呈现视图。如果没有返回模型,则不会呈现视图,因为请求已经被执行了

特殊 Bean

  • HandlerMapping —— 将请求映射到带有前置和后置拦截器的处理程序。细节

    • 主要实现者 RequestMappingHandlerMapping 和 BeanNameUrlHandlerMapping
    • 拦截器实现 HandlerInterceptor 的方法

      • preHandle(..): 在处理程序之前执行
      • postHandle(..): 在处理程序之后执行
      • afterCompletion(..): 在请求完成之后执行
public interface HandlerMapping {
  /**
   * 将 HttpServletRequest 映射到 Handler,并将匹配的 HandlerInterceptor 绑定到 HandlerExecutionChain
   */
  HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

public class HandlerExecutionChain {
  private final Object handler;
  private HandlerInterceptor[] interceptors;
}
  • HandlerAdapter —— 辅助 DispatcherServlet 调用映射到请求的处理程序。控制
public interface HandlerAdapter {
  /**
   * 使用给定 handler 处理请求,返回模型数据和视图名字封装的 ModelAndView
   */
  ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}

/**
 * 字符和 Map 封装成的对象
 */
public class ModelAndView {
  private Object view;
  private ModelMap model;
}
  • ViewResolver —— 将从处理程序返回的字符串视图名称解析为要呈现给响应的实际视图

    • InternalResourceViewResolver —— 将视图解析为 Web 应用的内部资源
    • UrlBasedViewResolver —— 直接根据视图的名称解析视图
    • ContentNegotiatingViewResolver —— 根据请求文件名或 Accept 标头解析视图
    • FreeMarkerViewResolver —— 将视图解析为 FreeMarker 模板
    • ResourceBundleViewResolver —— 将视图解析为资源属性文件
    • AbstractCachingViewResolver —— 抽象的视图解析器类,提供了缓存视图的功能
public interface ViewResolver {
  /**
   * 根据指定的视图名称解析返回
   */
  View resolveViewName(String viewName, Locale locale) throws Exception;
}

public interface View {
  /**
   * 根据指定的模型数据渲染视图
   */
  void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
  • HandlerExceptionResolver —— 解析异常的策略,可能将异常映射到处理程序、HTML 错误视图或其他目标

    • SimpleMappingExceptionResolver —— 异常类名称和错误视图名称间的映射
    • DefaultHandlerExceptionResolver —— 解析 Spring MVC 引发的异常,并将它们映射到 HTTP 状态代码
    • ResponseStatusExceptionResolver —— 使用 @ResponseStatus 解析异常,并将它们映射到指定 HTTP 状态代码
    • ExceptionHandlerExceptionResolver —— 在 @Controller 或 @ControllerAdvice 类调用 @ExceptionHandler 解析异常
@RestController
public class ErrorController {

    @RequestMapping(path = "/error")
    public Map<String, Object> handle(HttpServletRequest request) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("status", request.getAttribute("javax.servlet.error.status_code"));
        map.put("reason", request.getAttribute("javax.servlet.error.message"));
        return map;
    }
}
  • LocaleResolver —— 提供国际化的视图,解析客户端本地环境
  • ThemeResolver —— 提供个性化的布局,解析 web 应用程序可以使用的主题
  • MultipartResolver —— 提供表单文件上传,解析 multi-part 请求

注解 Controller

  • 声明配置 —— 激活 @Controller 的自动检测
@Configuration
@ComponentScan("org.example.web") 
public class WebConfig {

    // ...
}
  • 请求映射 —— @RequestMapping 完成请求到 Controller 方法的映射

    • 变种:@GetMapping、@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping
    • 通过 @PathVariable 访问 URI 变量(支持置于类和方法上)
    • {varName:regex} 使用正则表达式声明 URI 变量
    • consumes 指定请求的 Content-Type 来限制请求的映射
    • produces 指定请求头的 Accept 来限制请求的映射
    • params 指定请求的参数
    • headers 指定请求的请求头
@RestController
@RequestMapping("/persons")
class PersonController {

    @GetMapping("/{id}")
    public Person getPerson(@PathVariable Long id) {
        // ...
    }
    
    @GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}")
    public void handle(@PathVariable String version, @PathVariable String ext) {
        // ...
    }

    @PostMapping(path = "/pets", consumes = "application/json") 
    public void addPet(@RequestBody Pet pet) {
        // ...
    }
    
    @GetMapping(path = "/pets/{petId}", produces = "application/json;charset=UTF-8") 
    @ResponseBody
    public Pet getPet(@PathVariable String petId) {
        // ...
    }
    
    @GetMapping(path = "/pets/{petId}", params = "myParam=myValue") 
    public void findPet(@PathVariable String petId) {
        // ...
    }
    
    @GetMapping(path = "/pets", headers = "myHeader=myValue") 
    public void findPet(@PathVariable String petId) {
        // ...
    }
    
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public void add(@RequestBody Person person) {
        // ...
    }
}
  • 异常处理
@Controller
public class SimpleController {

    // ...

    @ExceptionHandler
    public ResponseEntity<String> handle(IOException ex) {
        // ...
    }
}
  • ControllerAdvice —— 全局异常处理、全局数据预处理、全局数据绑定

@ControllerAdvice(basePackages = "com.cc.controller")
public class SpringControllerAdvice {
  @ExceptionHandler(RuntimeException.class)
  public ModelAndView runtimeException(RuntimeException e) {
    return new ModelAndView("error");
  }
}

@ControllerAdvice(basePackages = "com.cc.controller")
public class SpringControllerAdvice {
  @InitBinder
  public void globalInitBinder(WebDataBinder binder) {
    binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
  }
}

@ControllerAdvice(basePackages = "com.cc.controller")
public class SpringControllerAdvice {
  @ModelAttribute(value = "message")
  public String globalModelAttribute() {
    return "this is from model attribute";
  }
}

MVC 配置

  • @EnableWebMvc 激活
  • 实现 WebMvcConfigurer 添加功能
  • 重写 addFormatters 进行类型转换
  • 重写 getValidator 自定义全局 Validator 实例,也可以通过 @InitBinder 添加
@Controller
public class MyController {

    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.addValidators(new FooValidator());
    }

}
  • 重写 addInterceptors 添加拦截器
  • 重写 configureContentNegotiation 配置请求的媒体类型
  • 重写 configureMessageConverters 配置消息转换器
  • 重写 addViewControllers 添加视图控制器
  • 重写 configureViewResolvers 配置视图解析器
  • 重写 addResourceHandlers 添加静态资源处理器
  • 重写 configureDefaultServletHandling 配置默认 Servlet 处理器
  • 重写 configurePathMatch 配置路径匹配
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // ...
    }
    
     @Override
    public Validator getValidator(); {
        // ...
    }
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LocaleChangeInterceptor());
        registry.addInterceptor(new ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
        registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*");
    }
    
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.mediaType("json", MediaType.APPLICATION_JSON);
        configurer.mediaType("xml", MediaType.APPLICATION_XML);
    }
    
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
                .indentOutput(true)
                .dateFormat(new SimpleDateFormat("yyyy-MM-dd"))
                .modulesToInstall(new ParameterNamesModule());
        converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
        converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
    }
    
    
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("home");
    }
    
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.enableContentNegotiation(new MappingJackson2JsonView());
        registry.jsp();
    }
    
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**")
            .addResourceLocations("/public", "classpath:/static/")
            .setCachePeriod(31556926);
    }
    
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();//或者 configurer.enable("myCustomDefaultServlet");
    }
    
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer
            .setUseSuffixPatternMatch(true)
            .setUseTrailingSlashMatch(false)
            .setUseRegisteredSuffixPatternMatch(true)
            .setPathMatcher(antPathMatcher())
            .setUrlPathHelper(urlPathHelper())
            .addPathPrefix("/api",
                    HandlerTypePredicate.forAnnotation(RestController.class));
    }

    @Bean
    public UrlPathHelper urlPathHelper() {
        //...
    }

    @Bean
    public PathMatcher antPathMatcher() {
        //...
    }
}

视图技术

  • Thymeleaf —— 现代化服务器端 Java 模板引擎,支持原生 HTML,可在浏览器中双击预览
  • FreeMarker —— 用于生成从 HTML 到电子邮件等类型文本输出的模板引擎
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.freemarker();
    }

    // Configure FreeMarker...

    @Bean
    public FreeMarkerConfigurer freeMarkerConfigurer() {
        FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
        configurer.setTemplateLoaderPath("/WEB-INF/freemarker");
        return configurer;
    }
}
<#import "/spring.ftl" as spring/>
<html>
    ...
    <form action="" method="POST">
        Name:
        <@spring.bind "myModelObject.name"/>
        <input type="text"
            name="${spring.status.expression}"
            value="${spring.status.value?html}"/><br>
        <#list spring.status.errorMessages as error> <b>${error}</b> <br> </#list>
        <br>
        ...
        <input type="submit" value="submit"/>
    </form>
    ...
</html>
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.groovy();
    }

    // Configure the Groovy Markup Template Engine...

    @Bean
    public GroovyMarkupConfigurer groovyMarkupConfigurer() {
        GroovyMarkupConfigurer configurer = new GroovyMarkupConfigurer();
        configurer.setResourceLoaderPath("/WEB-INF/");
        return configurer;
    }
}
yieldUnescaped '<!DOCTYPE html>'
html(lang:'en') {
    head {
        meta('http-equiv':'"Content-Type" content="text/html; charset=utf-8"')
        title('My page')
    }
    body {
        p('This is an example of HTML contents')
    }
}
  • 脚本视图
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.scriptTemplate();
    }

    @Bean
    public ScriptTemplateConfigurer configurer() {
        ScriptTemplateConfigurer configurer = new ScriptTemplateConfigurer();
        configurer.setEngineName("nashorn");
        configurer.setScripts("mustache.js");
        configurer.setRenderObject("Mustache");
        configurer.setRenderFunction("render");
        return configurer;
    }
}
<html>
    <head>
        <title>{{title}}</title>
    </head>
    <body>
        <p>{{body}}</p>
    </body>
</html>
  • JSP
<form:form>
    <table>
        <tr>
            <td>First Name:</td>
            <td><form:input path="firstName"/></td>
        </tr>
        <tr>
            <td>Last Name:</td>
            <td><form:input path="lastName"/></td>
        </tr>
        <tr>
            <td colspan="2">
                <input type="submit" value="Save Changes"/>
            </td>
        </tr>
    </table>
</form:form>
  • RSS and Atom
public class SampleContentAtomView extends AbstractAtomFeedView {

    @Override
    protected void buildFeedMetadata(Map<String, Object> model,
            Feed feed, HttpServletRequest request) {
        // implementation omitted
    }

    @Override
    protected List<Entry> buildFeedEntries(Map<String, Object> model,
            HttpServletRequest request, HttpServletResponse response) throws Exception {
        // implementation omitted
    }

}
public class SampleContentRssView extends AbstractRssFeedView {

    @Override
    protected void buildFeedMetadata(Map<String, Object> model,
            Channel feed, HttpServletRequest request) {
        // implementation omitted
    }

    @Override
    protected List<Item> buildFeedItems(Map<String, Object> model,
            HttpServletRequest request, HttpServletResponse response) throws Exception {
        // implementation omitted
    }
}
  • PDF and Excel
public class PdfWordList extends AbstractPdfView {

    protected void buildPdfDocument(Map<String, Object> model, Document doc, PdfWriter writer,
            HttpServletRequest request, HttpServletResponse response) throws Exception {

        List<String> words = (List<String>) model.get("wordList");
        for (String word : words) {
            doc.add(new Paragraph(word));
        }
    }
}
public class ExcelViewBuilder extends AbstractExcelView {
  protected void buildExcelDocument(Map<String,Object> model,HSSFWorkbook wb,HttpServletRequest req, HttpServletResponse resp) {
    // ...
  }
}
  • XSLT
@EnableWebMvc
@ComponentScan
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Bean
    public XsltViewResolver xsltViewResolver() {
        XsltViewResolver viewResolver = new XsltViewResolver();
        viewResolver.setPrefix("/WEB-INF/xsl/");
        viewResolver.setSuffix(".xslt");
        return viewResolver;
    }
}
@Controller
public class XsltController {

    @RequestMapping("/")
    public String home(Model model) throws Exception {
        Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
        Element root = document.createElement("wordList");

        List<String> words = Arrays.asList("Hello", "Spring", "Framework");
        for (String word : words) {
            Element wordNode = document.createElement("word");
            Text textNode = document.createTextNode(word);
            wordNode.appendChild(textNode);
            root.appendChild(wordNode);
        }

        model.addAttribute("wordList", root);
        return "home";
    }
}
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="html" omit-xml-declaration="yes"/>

    <xsl:template match="/">
        <html>
            <head><title>Hello!</title></head>
            <body>
                <h1>My First Words</h1>
                <ul>
                    <xsl:apply-templates/>
                </ul>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="word">
        <li><xsl:value-of select="."/></li>
    </xsl:template>

</xsl:stylesheet>
相关文章
|
1月前
|
前端开发 安全 Java
技术进阶:使用Spring MVC构建适应未来的响应式Web应用
【9月更文挑战第2天】随着移动设备的普及,响应式设计至关重要。Spring MVC作为强大的Java Web框架,助力开发者创建适应多屏的应用。本文推荐使用Thymeleaf整合视图,通过简洁的HTML代码提高前端灵活性;采用`@ResponseBody`与`Callable`实现异步处理,优化应用响应速度;运用`@ControllerAdvice`统一异常管理,保持代码整洁;借助Jackson简化JSON处理;利用Spring Security增强安全性;并强调测试的重要性。遵循这些实践,将大幅提升开发效率和应用质量。
53 7
|
2月前
|
存储 缓存 前端开发
Servlet与JSP在Java Web应用中的性能调优策略
Servlet与JSP在Java Web应用中的性能调优策略
25 1
|
2月前
|
Java Spring Maven
Struts 2遇见Spring:这个组合如何颠覆你的Web开发?
【8月更文挑战第31天】在现代Web开发中,Struts 2与Spring的结合使用能显著增强应用的模块化和可维护性。本文将介绍如何整合这两个框架,并提供代码示例。首先,在`pom.xml`中添加Struts 2和Spring的依赖,然后在`struts.xml`中配置Struts 2以识别Spring插件。接着,在Spring配置文件中定义bean,并在Struts 2的Action类中使用`@Autowired`注解进行自动注入。
30 0
|
2月前
|
Java 开发者 前端开发
Struts 2、Spring MVC、Play Framework 上演巅峰之战,Web 开发的未来何去何从?
【8月更文挑战第31天】在Web应用开发中,Struts 2框架因强大功能和灵活配置备受青睐,但开发者常遇配置错误、类型转换失败、标签属性设置不当及异常处理等问题。本文通过实例解析常见难题与解决方案,如配置文件中遗漏`result`元素致页面跳转失败、日期格式不匹配需自定义转换器、`&lt;s:checkbox&gt;`标签缺少`label`属性致显示不全及Action中未捕获异常影响用户体验等,助您有效应对挑战。
63 0
|
2月前
|
Java 数据库连接 数据库
强强联手!JSF 与 Hibernate 打造高效数据访问层,让你的应用如虎添翼,性能飙升!
【8月更文挑战第31天】本文通过具体示例详细介绍了如何在 JavaServer Faces (JSF) 应用程序中集成 Hibernate,实现数据访问层的最佳实践。首先,创建一个 JSF 项目并在 Eclipse 中配置支持 JSF 的服务器版本。接着,添加 JSF 和 Hibernate 依赖,并配置数据库连接池和 Hibernate 配置文件。然后,定义实体类 `User` 和 DAO 类 `UserDAO` 处理数据库操作。
51 0
|
2月前
|
Java Spring
🔥JSF 与 Spring 强强联手:打造高效、灵活的 Web 应用新标杆!💪 你还不知道吗?
【8月更文挑战第31天】JavaServer Faces(JSF)与 Spring 框架是常用的 Java Web 技术。本文介绍如何整合两者,发挥各自优势,构建高效灵活的 Web 应用。首先通过 `web.xml` 和 `ContextLoaderListener` 配置 Spring 上下文,在 `applicationContext.xml` 定义 Bean。接着使用 `@Autowired` 将 Spring 管理的 Bean 注入到 JSF 管理的 Bean 中。
37 0
|
2月前
|
Java Spring Apache
Spring Boot邂逅Apache Wicket:一次意想不到的完美邂逅,竟让Web开发变得如此简单?
【8月更文挑战第31天】Apache Wicket与Spring Boot的集成提供了近乎无缝的开发体验。Wicket以其简洁的API和强大的组件化设计著称,而Spring Boot则以开箱即用的便捷性赢得开发者青睐。本文将指导你如何在Spring Boot项目中引入Wicket,通过简单的步骤完成集成配置。首先,创建一个新的Spring Boot项目并在`pom.xml`中添加Wicket相关依赖。
58 0
|
2月前
|
Java 前端开发 Apache
Apache Wicket与Spring MVC等Java Web框架大PK,究竟谁才是你的最佳拍档?点击揭秘!
【8月更文挑战第31天】在Java Web开发领域,众多框架各具特色。Apache Wicket以组件化开发和易用性脱颖而出,提高了代码的可维护性和可读性。相比之下,Spring MVC拥有强大的生态系统,但学习曲线较陡;JSF与Java EE紧密集成,但在性能和灵活性上略逊一筹;Struts2虽成熟,但在RESTful API支持上不足。选择框架时还需考虑社区支持和文档完善程度。希望本文能帮助开发者找到最适合自己的框架。
31 0
|
2月前
|
Java Spring 开发者
Java Web开发新潮流:Vaadin与Spring Boot强强联手,打造高效便捷的应用体验!
【8月更文挑战第31天】《Vaadin与Spring Boot集成:最佳实践指南》介绍了如何结合Vaadin和Spring Boot的优势进行高效Java Web开发。文章首先概述了集成的基本步骤,包括引入依赖和配置自动功能,然后通过示例展示了如何创建和使用Vaadin组件。相较于传统框架,这种集成方式简化了配置、提升了开发效率并便于部署。尽管可能存在性能和学习曲线方面的挑战,但合理的框架组合能显著提升应用开发的质量和速度。
33 0
|
2月前
|
Java 前端开发 Spring
技术融合新潮流!Vaadin携手Spring Boot、React、Angular,引领Web开发变革,你准备好了吗?
【8月更文挑战第31天】本文探讨了Vaadin与Spring Boot、React及Angular等主流技术栈的最佳融合实践。Vaadin作为现代Java Web框架,与其他技术栈结合能更好地满足复杂应用需求。文中通过示例代码展示了如何在Spring Boot项目中集成Vaadin,以及如何在Vaadin项目中使用React和Angular组件,充分发挥各技术栈的优势,提升开发效率和用户体验。开发者可根据具体需求选择合适的技术组合。
35 0
下一篇
无影云桌面