文章目录
1. MVC请求流程
- 请求携带着URL和提交的其它信息被DispatcherServlet拦截
- DispatcherServlet查询处理器映射决定下一站要给哪个控制器
- DispatcherServlet将请求发送给选择的控制器,将数据提交给它并等待处理
- 控制器完成处理后会返回一些信息(model),这些信息需要一个视图(view)来显示,所以它选定一个视图名(只是一个视图名,不代表视图本身),将它们一起发回DispatcherServlet
- DispatcherServlet使用视图解析器(view resolver)将视图名匹配为一个特定的视图实现(这里是视图主体)
- DispatcherServlet交付数据模型(model)给特定的视图
- 视图渲染好数据再返回给客户端
2. 搭建SpringMVC
配置DispatcherServlet
package spittr.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class SpittrWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {RootConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {WebConfig.class};
}
}
- getServletMappings()方法,将一个或多个路径映射到DispatcherServlet。“/”代表它会处理应用的所有请求
- getServletConfigClasses()和getRootConfigClasses():
Spring中可以创建出多个DispatcherSerlet(只要有多个继承AbstractAnnotationConfigDispatcherServletInitializer的类就好了)。每个DispatcherServlet都有自己的上下文(WebApplicationContext),且配置只对它自己有效,这也就是getServletConfigClasses()方法所获取的它自己的配置类。例如Controller,ViewResolver,HandlerMapping等会在WebConfig中配置。
除了每个DispatcherServlet配置类的上下文,还有一个根应用上下文,这个RootConfig的作用是在多个DispatcherServlet之间共享Bean,这就是getRootConfigClasses()的作用。例如数据源等公共的bean会在RootConfig中配置。 - DispatcherServlet获取想要的bean时,如果没有在自己的应用上下文中找到,则会自动到根应用上下文中去找。
WebConfig类:
package spittr.config;
@Configuration
@EnableWebMvc
@ComponentScan("spittr.web")
public class WebConfig implements WebMvcConfigurer{
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
- @EnableWebMvc :启用SpringMVC
- @ComponentScan :启用组件扫描(扫描spitter.web中的带@Controller注解的控制器),如果不启用,Spring只拿找到显式配置类中的控制器
- viewResolver : 配置JSP视图解析器
- configureDefaultServletHandling :配置静态资源的处理,否则DispatcherServlet会处理所有的请求即使并不需要处理信息。
RootConfig类:
package spittr.config;
@Configuration
@ComponentScan(basePackages= {"spittr"},
excludeFilters= {@Filter(type=FilterType.ANNOTATION, value=EnableWebMvc.class)})
public class RootConfig {
}
3. 编写控制器
处理对“/”的请求的控制器,展现首页:
package spittr.web;
@Controller
public class HomeController {
@RequestMapping(value="/", method=RequestMethod.GET)
public String home() {
return "home";
}
}
@Controller注解和@Component注解效果一样,目的是辅助实现组件扫描。
测试类:
package BaseTest;
public class HomeControllerTest {
@Test
public void testHomePage() throws Exception{
HomeController controller = new HomeController();
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
mockMvc.perform(MockMvcRequestBuilders.get("/")).andExpect(MockMvcResultMatchers.view().name("home"));
}
}
4. 接收请求参数
接收请求参数除了URL传参还可以通过路径来传参:
@RequestMapping("/{spittleId}") //这里上一级的RequestMapping注解的路径是/Spittle
public ModelAndView spittleid(ModelAndView modelAndView, @PathVariable("spittleId") long spittleId) {
System.out.println(spittleId);
modelAndView.addObject(repository.findSpittle(12345, 1));
modelAndView.setViewName("Spittle");
return modelAndView;
}
通过使用{}占位符和@PathVariable注解,路径是“*/Spittle/12345” 就可以将12345接收为“spittleId”。
5. 对表单校验(使用Java校验API检查bean的属性是否合法)
Java校验API提供多个放在Bean属性上的注解来限制属性的值,达到校验的作用:
- @AssertFalse : 元素必须是布尔值,且为false
- @AssertTrue
- @DecimalMax:元素必须数字,且它要小于等于给定的BigDecimalString值
- @DecimalMin:大于等于
- @Digits:元素必须是数字,且要有指定的位数
- @Future:元素的值必须是一个将来的日期
- @Past:过去的日期
- @Max:元素必须数字,且它要小于等于给定的值
- @Min:大于等于
- @NotNull:元素不能为空
- @Null
- @Patten:元素值必须匹配给定的正则表达式
- @Size:元素值必须是String、集合、数组,并且长度要符合给定的限制
以上注解是添加在Bean属性上的,在Controller中要配合@Valid注解和Errors类使用:
@RequestMapping("xxx")
public String validBean(@Valid Bean bean, Errors errors){
if(errors.hasErrors()){
return "错误页面";
}
return "成功页面";
}
6. SpringMVC的其它配置
6.1 自定义DispatcherServlet配置****
除了之前必须要重载的三个方法,还可以进行额外的配置,可以借助customizeRegistration()方法通过参数Dynamic进行额外的配置:
在配置DispatcherServlet类中(前文的配置类是SpittrWebAppInitializer)加上:
@Override
protected void customizeRegistration(Dynamic registration) {
registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads"));
}
这个样例设置了对multipart(例如处理上传图片的二进制数据)的支持,设置了上传文件的临时存储目录。
6.2 添加其它的Servlet和Filter
使用Java方式想在Web容器中注册其它组件的话,只需要创建一个新的初始化器(实现WebApplicationInitializer接口)。可以定义任意数量的初始化器:
注册Servlet:
public class MyServletInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// TODO Auto-generated method stub
Dynamic myServlet = servletContext.addServlet("myServlet", MyServlet.class);
//注册Filter这里就使用addFilter方法
myServlet.addMapping("/custom/*");
//filter.addMappingForUrlPatterns(null, false, "/custom/*")
}
}
6.3 Web.xml中声明DispatcherServlet
<web-app>
<context-param>
<param-name>contextConfigLocation</param-name> <!-- 设置根上下文配置文件位置 -->
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<listener>
<listener-class> <!-- 注册ContextLoaderListener -->
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>addServlet</servlet-name> <!-- 注册DispatcherServlet-->
<servlet-class>
org.springframework.web.context.DispatcherServlet
<servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>addServlet</servlet-name> <!-- 将DispatcherServlet映射到“/”-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
6.4 处理异常
- 添加@ResourceStatus注解,将异常映射为HTTP状态码
例如:新定义一个异常,并将它映射为404
@ResourceStatus(value=HTTPStatus.NOT_FOUND, reason="Spittle Not Found")
public class NewException extends RuntimeException{
}
- 还可以在方法上添加@ExceptionHandler注解来处理抛出的异常
@ExceptionHandler(NewException.class)
public String handleNewException(){
return "error";
}
这个注解只能处理同一个Controller中的NewException异常
6.5 为控制器添加通知(通常是异常处理通知)*
控制器通知是任意带有@ControllerAdvice注解的类。
它最常用的场景是将所有@ExceptionHandler方法收集到一个类中。
例如:实现在所有控制器抛出NewException时都能进行统一处理:
@ControllerAdvice
public class AppWideExeptionHandler{
@ExceptionHandler(NewException.class)
public String handleNewException(){
return "error";
}
}
7. 重定向请求携带数据
7.1 URL模板重定向
如果是简单的值,例如name,id之类,可以通过URL来传递路径变量和参数。
@RequestMapping(value="/regitster", method=GET)
public String xxxx(Username username, Model model){
model.addAttribute("username", username);
return "redirect:/other/{username}";
//如果传参数,比如再传递id,只需要model.addAttribute("userId",id),返回的URL会自动变成 /other/username?userId=111 ,
}
// 配合@RequestMapping("/other/{username}")控制器使用
返回“redirect:/路径”可以实现重定向(再发一次HTTP请求),“forward:/路径”可以实现请求转发。
在URL传参时要使用{}占位符,可以对不安全的字符进行转义,更加安全。
7.2 使用flash属性
Spring提供了将数据发送为flash属性的功能,flash属性会一直携带这些属性直到下一次请求,然后才消失。
Spring提供了RedirectAttributes类来设置flash属性,它是Model的一个子接口。
//这次我们传送一个User对象作为参数
@RequestMapping(value="/regitster", method=POST)
public String xxxx(User user, RedirectAttributes model){
model.addAttribute("username", username);
model.addFlashAttribute("user", user); //这个key默认名是value的类型,User就是"user"
return "redirect:/other/{username}";
}
// 配合@RequestMapping("/other/{username}")控制器使用
在重定向前,flash属性会赋值到会话中,重定向完成后,再从会话转移到模型中。属性可以直接在视图页面中拿到,也可以在下一个控制器通过model.getFlashAttributes取出。