spring-web

web配置

在servlet 3.1以后,可以通过代码的方式来配置DispatcherServlet,而不用配置在web.xml中。在Servlet 3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,如果发现就会用他来配置Servlet容器。Spring提供了这个接口的实现SpringServletContainerInitializer,这个类又会查找实现了webAppInitializerClasses接口的类,在Spring 3.2中引入了AbstractAnnotationConfigDispatcherServletInitializer来实现该接口,而我们的SpitterWebInitializer继承AbstractAnnotationConfigDispatcherServletInitializer,所以在部署到servlet 3.0容器中时,容器就会自动发现它,并用它来配置servlet。

package com.cooper.spittr.config;import com.cooper.spittr.web.WebConfig;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;/*** @description: SpitterWebInitializer* @author: sunzhilong* @create: 2021-04-26**/
public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {@Overrideprotected Class<?>[] getRootConfigClasses() {return new Class[]{RootConfig.class};}@Overrideprotected Class<?>[] getServletConfigClasses() {return new Class[]{WebConfig.class};}@Overrideprotected String[] getServletMappings() {return new String[]{"/"};}
}

这里有两个应用上下文DispatcherServlet和ContextLoaderListener。AbstractAnnotationConfigDispatcherServletInitializer会同时创建这两个应用上下文。getServletConfigClasses返回的带有@Configuration注解的类将会用来定义DispatcherServlet应用上下文中的bean,getRootConfigClasses返回的带有@Configuration注解的类将会用来配置ContextLoaderListener创建的应用上下文的bean。

WebConfig

  • @Configuration说明这是一个配置类
  • @ComponentScan(“com.cooper.spittr.web”)指明了配置类的扫描范围。
  • @EnableWebMvc指明要启用Spring MVC。
package com.cooper.spittr.web;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;/*** @description: WebConfig* @author: sunzhilong* @create: 2021-04-26**/
@Configuration
@EnableWebMvc
@ComponentScan("com.cooper.spittr.web")
public class WebConfig implements WebMvcConfigurer {// 配置JSP视图解析器,访问的视图解析器最终会加上前缀/WEB-INF/views/,后缀.jsp@Beanpublic ViewResolver viewResolver() {InternalResourceViewResolver resolver = new InternalResourceViewResolver();resolver.setPrefix("/WEB-INF/views/");resolver.setSuffix(".jsp");return resolver;}// 配置静态资源的处理,这样静态资源就会转发到默认的Servlet而不是DispatcherServlet上了@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {configurer.enable();}
}

RootConfig

  • @Configuration说明这是一个配置类
  • @ComponentScan说明了要扫描的范围
    • 显然这里要扫描的范围com.cooper.spittr包含了com.cooper.spittr.web
    • excludeFilters则将包含的com.cooper.spittr.web排除出去
package com.cooper.spittr.config;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;/*** @description: RootConfig* @author: sunzhilong* @create: 2021-04-26**/
@Configuration
@ComponentScan(basePackages = {"com.cooper.spittr"},excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class)})
public class RootConfig {}

控制器

HomeController

实现

这里将/和/homepage的访问都映射到home上,访问这两个的get请求都会被映射成访问资源/WEB-INF/views/home.jsp

package com.cooper.spittr.web;import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;import static org.springframework.web.bind.annotation.RequestMethod.GET;/*** @description: HomeController* @author: sunzhilong* @create: 2021-04-27**/
@Controller
@RequestMapping({"/", "/homepage"})
public class HomeController {@RequestMapping(method = GET)public String home(Model model) {// 视图名是homereturn "home";}
}

测试

这里的测试方式与以前不太一样,这里是使用Spring MVC的方式进行测试的,有了对资源的get请求访问

package com.cooper.spittr.web;import org.junit.Test;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;public class HomeControllerTest {@Testpublic void testHomePage() throws Exception {HomeController controller = new HomeController();MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();mockMvc.perform(MockMvcRequestBuilders.get("/homepage")).andExpect(MockMvcResultMatchers.view().name("home"));}
}

SpittleController

实现

  • @Autowired用来在调用构造器的时候自动注入SpittleRepository
  • spittles方法直接返回了一个查询结果,没有指定视图名,也没有现实的设定视图模型
    • 处理器方法返回对象或者集合时,这个值会放到模型中,模型的key会根据类型推断得出,我们这里返回的是List,视图模型的key会被推断为spittleList
    • 逻辑视图的名字会根据请求路径推断得出,这里请求路径是/spittles,因此视图名称会是spittles
  • defaultValue 由于查询的蚕食都是String类型的值,defaultValue也需要时String,在绑定到方法的参数时会自动进行转化
  • PathVariable 用来使访问的参数作为路径的一部分,而不是通过?的传参方式
package com.cooper.spittr.web;import com.cooper.spittr.data.SpittleRepository;
import com.cooper.spittr.model.Spittle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;import java.util.List;/*** @description: SpittleController* @author: sunzhilong* @create: 2021-04-28**/
@Controller
@RequestMapping("/spittles")
public class SpittleController {private static final String MAX_LONG_AS_STRING = "9223372036854775807";private SpittleRepository spittleRepository;@Autowiredpublic SpittleController(SpittleRepository spittleRepository) {this.spittleRepository = spittleRepository;}@RequestMapping(method = RequestMethod.GET)public List<Spittle> spittles(@RequestParam(value = "max", defaultValue = MAX_LONG_AS_STRING) long max,@RequestParam(value = "count", defaultValue = "20") int count) {return spittleRepository.findSpittles(max, count);}@RequestMapping(value = "/{spittleId}", method = RequestMethod.GET)public Spittle spittle(@PathVariable("spittleId") long spittleId) {return spittleRepository.findOne(spittleId);}
}

测试

这次的测试在MockMvc中设置了视图名,这样MockMvc就不会解析返回的视图名,之所以我们要在这里进行设置是因为访问的视图名和请求路径非常相似,MockMvc直接解析会发生失败。很多场景下是没有必要这样设置的。

package com.cooper.spittr.web;import com.cooper.spittr.data.SpittleRepository;
import com.cooper.spittr.model.Spittle;
import org.hamcrest.CoreMatchers;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.servlet.view.InternalResourceView;import java.util.ArrayList;
import java.util.Date;
import java.util.List;public class SpittleControllerTest {@Testpublic void shouldShowRecentSpittlesDefault() throws Exception {List<Spittle> expectedSpittles = createSpittleList(20);SpittleRepository mockRepository = Mockito.mock(SpittleRepository.class);Mockito.when(mockRepository.findSpittles(Long.MAX_VALUE, 20)).thenReturn(expectedSpittles);SpittleController controller = new SpittleController(mockRepository);// 设置视图名MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).setSingleView(new InternalResourceView("/WEB-INF/views/spittles.jsp")).build();// 断言视图名是spittles,key值是spittleList,value是expectedSpittlesmockMvc.perform(MockMvcRequestBuilders.get("/spittles")).andExpect(MockMvcResultMatchers.view().name("spittles")).andExpect(MockMvcResultMatchers.model().attributeExists("spittleList")).andExpect(MockMvcResultMatchers.model().attribute("spittleList",CoreMatchers.hasItems(expectedSpittles.toArray())));}@Testpublic void shouldShowRecentSpittlesSpecial() throws Exception {List<Spittle> expectedSpittles = createSpittleList(50);SpittleRepository mockRepository = Mockito.mock(SpittleRepository.class);Mockito.when(mockRepository.findSpittles(23566, 50)).thenReturn(expectedSpittles);SpittleController controller = new SpittleController(mockRepository);MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).setSingleView(new InternalResourceView("/WEB-INF/views/spittles.jsp")).build();mockMvc.perform(MockMvcRequestBuilders.get("/spittles?max=23566&count=50")).andExpect(MockMvcResultMatchers.view().name("spittles")).andExpect(MockMvcResultMatchers.model().attributeExists("spittleList")).andExpect(MockMvcResultMatchers.model().attribute("spittleList",CoreMatchers.hasItems(expectedSpittles.toArray())));}@Testpublic void testSpittle() throws Exception {Spittle expectedSpittle = new Spittle("Hello", new Date());SpittleRepository mockRepository = Mockito.mock(SpittleRepository.class);Mockito.when(mockRepository.findOne(12345)).thenReturn(expectedSpittle);SpittleController controller = new SpittleController(mockRepository);MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();// 这里没有设置视图名,推断出返回的视图名就会是spittles/12345mockMvc.perform(MockMvcRequestBuilders.get("/spittles/12345")).andExpect(MockMvcResultMatchers.view().name("spittles/12345")).andExpect(MockMvcResultMatchers.model().attributeExists("spittle")).andExpect(MockMvcResultMatchers.model().attribute("spittle", expectedSpittle));}private List<Spittle> createSpittleList(int count) {List<Spittle> spittles = new ArrayList<Spittle>();for (int i=0; i < count; i++) {spittles.add(new Spittle("Spittle " + i, new Date()));}return spittles;}
}

SpitterController

实现

  • POST 提交表单数据必须用这种方法
  • 这里使用了Valid进行数据的校验
    • @Valid 指明要校验的属性
    • Errors 必须根子校验参数后边,用来接收校验结果,如果有多个参数校验就要有多个Errors 跟随
    • 值得一提的是校验用到的validation-api和hibernate-validator是有版本配套关系的,我在hibernate-validator使用6.1.5和7.0.1时都进行了实验,是不能通过测试用例的
  • redirect 是重定向的意思,是一个get方法
package com.cooper.spittr.web;import com.cooper.spittr.data.SpitterRepository;
import com.cooper.spittr.model.Spitter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;import javax.validation.Valid;import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;/*** @description: SpitterController* @author: sunzhilong* @create: 2021-04-29**/
@Controller
@RequestMapping("/spitter")
public class SpitterController {private SpitterRepository spitterRepository;@Autowiredpublic SpitterController(SpitterRepository spitterRepository) {this.spitterRepository = spitterRepository;}@RequestMapping(value = "/register", method = GET)public String showRegistrationForm() {return "registerForm";}@RequestMapping(value = "/register", method = POST)public String processRegistration(@Valid Spitter spitter, Errors errors) {if (errors.hasErrors()) {return "registerForm";}spitterRepository.save(spitter);return "redirect:/spitter/" + spitter.getUsername();}@RequestMapping(value = "/{username}", method = GET)public String showSpitterProfile(@PathVariable String username, Model model) {Spitter spitter = spitterRepository.findByUsername(username);model.addAttribute(spitter);return "profile";}
}

测试

最后一个测试用例,测试了不符合校验参数的情况

package com.cooper.spittr.web;import com.cooper.spittr.data.SpitterRepository;
import com.cooper.spittr.model.Spitter;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;/*** @description: SpitterControllerTest* @author: sunzhilong* @create: 2021-04-30**/
public class SpitterControllerTest {@Testpublic void shouldShowRegistration() throws Exception {SpitterRepository mockRepository = Mockito.mock(SpitterRepository.class);SpitterController controller = new SpitterController(mockRepository);MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();mockMvc.perform(MockMvcRequestBuilders.get("/spitter/register")).andExpect(MockMvcResultMatchers.view().name("registerForm"));}@Testpublic void shouldProcessRegistration() throws Exception {SpitterRepository mockRepository = Mockito.mock(SpitterRepository.class);Spitter unsaved = new Spitter("jbauer", "24hours", "Jack", "Bauer", "jbauer@ctu.gov");Spitter saved = new Spitter(24L, "jbauer", "24hours", "Jack", "Bauer", "jbauer@ctu.gov");Mockito.when(mockRepository.save(unsaved)).thenReturn(saved);SpitterController controller = new SpitterController(mockRepository);MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();mockMvc.perform(MockMvcRequestBuilders.post("/spitter/register").param("firstName", "Jack").param("lastName", "Bauer").param("username", "jbauer").param("password", "24hours").param("email", "jbauer@ctu.gov")).andExpect(MockMvcResultMatchers.redirectedUrl("/spitter/jbauer"));Mockito.verify(mockRepository, Mockito.atLeastOnce()).save(unsaved);}@Testpublic void shouldFailValidationWithNoData() throws Exception {SpitterRepository mockRepository = Mockito.mock(SpitterRepository.class);SpitterController controller = new SpitterController(mockRepository);MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();mockMvc.perform(MockMvcRequestBuilders.post("/spitter/register")).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.view().name("registerForm")).andExpect(MockMvcResultMatchers.model().errorCount(5)).andExpect(MockMvcResultMatchers.model().attributeHasFieldErrors("spitter", "firstName", "lastName", "username", "password", "email"));}
}

模型

Spitter

这里即使不用email的校验,最终也要在maven配置中配置上hibernate-validator

package com.cooper.spittr.model;import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;/*** @description: Spitter* @author: sunzhilong* @create: 2021-04-29**/
public class Spitter {private Long id;@NotNull@Size(min = 5, max = 16)private String username;@NotNull@Size(min = 5, max = 25)private String password;@NotNull@Size(min = 2, max = 30)private String firstName;@NotNull@Size(min = 2, max = 30)private String lastName;@NotNull
//    @Emailprivate String email;public Spitter() {}public Spitter(String username, String password, String firstName, String lastName, String email) {this(null, username, password, firstName, lastName, email);}public Spitter(Long id, String username, String password, String firstName, String lastName, String email) {this.id = id;this.username = username;this.password = password;this.firstName = firstName;this.lastName = lastName;this.email = email;}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 Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getFirstName() {return firstName;}public void setFirstName(String firstName) {this.firstName = firstName;}public String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName = lastName;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}@Overridepublic boolean equals(Object that) {return EqualsBuilder.reflectionEquals(this, that, "firstName", "lastName", "username", "password", "email");}@Overridepublic int hashCode() {return HashCodeBuilder.reflectionHashCode(this, "firstName", "lastName", "username", "password", "email");}
}

Spittle

package com.cooper.spittr.model;import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;import java.util.Date;/*** @description: Spittle* @author: sunzhilong* @create: 2021-04-28**/
public class Spittle {private final Long id;private final String message;private final Date time;private Double latitude;private Double longitude;public Spittle(String message, Date time) {this(null, message, time, null, null);}public Spittle(Long id, String message, Date time, Double longitude, Double latitude) {this.id = id;this.message = message;this.time = time;this.longitude = longitude;this.latitude = latitude;}public long getId() {return id;}public String getMessage() {return message;}public Date getTime() {return time;}public Double getLongitude() {return longitude;}public Double getLatitude() {return latitude;}@Overridepublic boolean equals(Object that) {return EqualsBuilder.reflectionEquals(this, that, "id", "time");}@Overridepublic int hashCode() {return HashCodeBuilder.reflectionHashCode(this, "id", "time");}
}

持久化接口定义

这里并没有对持久化接口的实现,测试用例中都是mock了实现方法

package com.cooper.spittr.data;import com.cooper.spittr.model.Spitter;/*** @description: SpitterRepository* @author: sunzhilong* @create: 2021-04-30**/
public interface SpitterRepository {Spitter save(Spitter spitter);Spitter findByUsername(String username);
}
package com.cooper.spittr.data;import com.cooper.spittr.model.Spittle;import java.util.List;/*** @description: SpittleRepository* @author: sunzhilong* @create: 2021-04-28**/
public interface SpittleRepository {List<Spittle> findSpittles(long max, int count);Spittle findOne(long id);
}

spring-web相关推荐

  1. Spring Web MVC(一)

    概述 Spring Web MVC框架的特点 五大核心组件 编程步骤 五大核心组件 DispatcherServlet前端控制器 WebApplicationContext中特殊的bean 处理过程 ...

  2. 用Spring Web Flow和Terracotta搭建Web应用

    什么是Spring Web Flow? Spring Web Flow是Spring Framework中的web应用组件,它提供了一种编写有状态和基于会话的web应用的简便手段.Spring Web ...

  3. 【Spring Web MVC】Spring Web MVC 注解开发环境搭建

    为什么80%的码农都做不了架构师?>>>    1.创建maven项目 创建一个名为:springwebmvc-first的maven项目 2.添加依赖包 要使用springWebM ...

  4. MESSL(maven + extjs + spring portlet mvc + spring web flow + liferay )整合架构 5

    流控制文件很简单,就是根元素是<view>,然后用<view-state>来代表一个一个的页面,用<transition>来代表从一个状态到另外一个状态的跳转,如果 ...

  5. spring_了解Spring Web应用程序体系结构:经典方法

    spring 每个开发人员必须了解两件事: 架构设计是必要的. 精美的架构图并未描述应用程序的真实架构. 真正的体系结构是从开发人员编写的代码中找到的,如果不设计应用程序的体系结构,最终将得到一个具有 ...

  6. spring初始化web_了解Spring Web初始化

    spring初始化web 几年前,我们大多数人习惯到处编写XML配置文件,甚至可以设置简单的Java EE应用程序. 如今,使用Java或Groovy来配置项目已成为首选方式–您只需要看一下Sprin ...

  7. 强烈推荐Spring Web Flow权威指南

    关于Spring Web Flow权威指南 评论 读后感:这是Spring Web Flow创始人写的书.内容是基于1.0的.此书原版出版时其实2.0已经推出了,为什么老大并没有追新呢?我猜想,书中写 ...

  8. Understanding Spring Web Application Architecture: The Classic Way--转载

    原文地址:http://www.petrikainulainen.net/software-development/design/understanding-spring-web-applicatio ...

  9. Spring Web 应用的最大败笔

    来源:http://t.cn/EM8ROEb 开发人员在使用Spring应用时非常擅长谈论依赖注入的好处.不幸的是,他们没有真正的利用到它的好处,如单一职责原则,分离关注原则.如果我们一起来看看大部分 ...

  10. Spring Web Flow实例教程

    目录: 参考文献 购物车用例 什么情况下可以使用 Spring Web Flow? 配置 Spring Web MVC 配置 Spring Web Flow 2.0 的基础 在购物车示例应用中配置 S ...

最新文章

  1. .vue文件中style标签的几个标识符
  2. Android Stadio调试gradle 插件 || Android Stadio 远程调试 || Anroid APT调试
  3. AWS DataPipline 的一次尝试。
  4. 网易云IM云服务的稳定原来是这样实现的
  5. 云漫圈 | 寻找无序数组的第k大元素
  6. python线程等待_python3 中 Event.wait 多线程等待
  7. JVM优化系列-Stop-The-World实战
  8. [转]Android应用的自动更新
  9. HttpClient 入门与正确使用姿势
  10. 如何让用户留在生态系统里?向苹果学习!【转载】
  11. Understanding Growth
  12. readonly 关键字与 const 关键字不同
  13. 账龄分析表excel模板_华为财务EXCEL内训手册(共131套模板,带公式).xls
  14. [Mac OS] Homebrew简介及安装wine
  15. ffmpeg推流到流媒体服务器
  16. NOWAIT及SKIP LOCKED的使用
  17. 传感器学习——ESP8266(无线串口使用)
  18. 计算机网络.第四节课.笔记.CRC循环冗余检验、透明传输、SOH、EOT、PPP点对点协议、零比特填充、字节填充
  19. win2008 磁盘碎片整理
  20. 深度分享|姚劲波:创业要趁早,要做未来的事情!

热门文章

  1. 微软快捷键截图_所有最好的Microsoft Excel键盘快捷键
  2. 采用 MRT-LBM 模拟旋转圆柱绕流2---MATLAB代码--王富海2017--基于 MRT-LBM 的流场与声场仿真计算
  3. widows下安装pycurl并利用pycurl请求https地址
  4. 数据结构:串(String)【详解】
  5. 软件测试实验学习笔记系列2 -- lint,splint的使用
  6. 北京找工作之艰难困苦
  7. 东南大学计算机科学沈桥,走进东南大学,金中学子要做“未来卓越工程师”!...
  8. dns劫持是什么 dns被劫持了怎么办、dns被劫持怎么解决
  9. 2019年DevOps最新现状研究报告解读
  10. Linux日期计算器,【C++】日期类+日期万年历+日期计算器