1、SpringMVC请求:
请求---->DispatcherServlet(请求的第一站,前端控制器,将请求委托给其他应用程序来处理)
DispatcherServlet---->相应的控制器(通过查询处理器映射器来查询到底传递给那个控制器)
控制器--(模型和视图名)-->DispatcherServlet---->视图解析器--(视图)-->相应
2、配置DispatcherServlet
1:传统的方法是讲DispatcherServlet配置在web.xml中。
2:这里我们将DispatcherServlet配置在Servlet中。
这种方法是传统的xml配置的替代,但是需要部署在支持Spring3.0的Web容器中。(Tomcat7或者更高)
/**
DispatcherServlet启动会引起Spring应用上下文的创建,应用上下文加载Web组件的bean(控制器、视图解析器、处理器)
ContextLoaderListener的创建会加载其他bean(后端中间层、数据层的bean)
AbstractAnnotationConfigDispatcherServletInitializer会同时创建 DispatcherServlet和ContextLoaderListener。
getServletClasses()用于指定Spring应用上下文的bean的文件(包含@Configuration注解的java类)
getRootConfigClasses()用于指定ContextLoaderListener创建的上下文加载的bean的配置文件(包含@Configuration注解的java类)
*/
public class SpitterWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{
@Override
protected String[] getServletMappings(){
return new String[]{"/"}; //将DispatcherServlet映射到“/”,将会处理应用的所有请求。
}
//用来指定ContextLoaderListener上下文中需要加载的bean的配置文件。
@Override
protected Class<?>[] getRootConfigClasses(){
return new Class<?>[] {RootConfig.class};
}
//DispatcherServlet启动的时候会创建Spring应用上下文。并加载bean。此处要求加载定义在WebConfig中的bean。
@Override
protected Class<?>[] getServletConfigClasses(){
return new Class<?>[] {WebConfig.class};
}
}
3、启用Spring MVC
1:使用xml配置:
2:使用java文件配置(@EnableWebMVC注解)
注意如果只加@EnableWebMVC注解,这个配置文件不会配置视图解析器、没有启动组件扫描、等功能缺陷。
解决上述问题的配置:
@Configuration
@EnableWebMvc //启用SpringMVC
@ComponentScan("spitter.web")
public class WebConfig extends WebMvcConfigurerAdapter{
@Bean
public ViewResolver viewResolver(){ //配置jsp视图解析器
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
//配置静态资源处理(要求DispatcherServlet对静态资源的请求转发到Servlet容器中默认的Servlet上,而不是DispatcherServlet类来处理)
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
configurer.enable();
}
}
Spring应用上下文的已近随着DispatcherServlet的创建而创建了。接下来配置RootConfig。
@Configuration
@ComponentScan(basePackage={"spitter"},excludeFilters = {@Filter(type=FilterType.ANNOTATION,value=EnableWebMvc.class)})
public class RootConfig{}
4、编写基本的控制器
例如:
@Controller //声明控制器
public class HomeController{
@RequestMapping(value="/",method = RequestMethod.GET) //匹配“/”的GET请求
public String home(){
return "home"; //返回视图名(结合视图解析器:视图为:/WEB-INF/views/home.jsp)
}
}
5、定义类级别的请求处理
例如:
@Controller
@RequestMapping("/")
public class HomeController{
@RequestMapping(method = RequestMethod.GET) //这个方法匹配的依然是"/"的GET请求。只不过将匹配路径的"/"放到了类级别。
public String home(){
return "home";
}
}
//匹配多个路径
@controller
@RequestMapping("/","/homepage") //可以同时匹配"/"和"/homepage"
public class HomeController{...}
6、传递模型数据到视图中(返回视图的同时给视图传递模型)
1:首先我们定义一个用于访问数据的Repository:
public interface SpittleRepository{
List<Spittle> findSpittles(long max, int count);
}
2:编写控制器
@Controller
@RequestMapping("/spittles")
public class SpittleController{
private SpittleRepository spittleRepository;
@Autowired
public SpittleController(SpittleRepository spittleRepository){ //构造器中传入用于访问数据的属性
this.spittleRepository = spittleRepository;
}
@RequestMapping(method = RequestMethod.GET)
public String spittles(Model model){ //Model其实就是一个Map,它会被传递给视图。
model.addAttribute(spittleRepository.findSpittles(Long.MAX , 20)); //没有写key,会根据类型自动判断,这里判断为spittleList。
//也可以明确key
//model.addAttribute("spittleList",spittleRepository.findSpittles(Long.MAX , 20));
return "spittles";
}
}
//使用Map类型封装数据:
例如:
@RequestMapping(method = RequestMethod.GET)
public String spittles(Map model){
model.put("spittleList",spittleRepository.findSpittles(Long.MAX , 20));
return "spittles";
}
//不写key不写返回视图的方法
@RequestMapping(method = RequestMethod.GET)
public List<Spittle> spittles(){
return spittleList",spittleRepository.findSpittles(Long.MAX , 20);
}
//key:根据类型推断为:spittleList
//视图名:因为这个方法匹配"/spittles",所以视图名为spittles。
7、接收请求的输入
SpringMVC从客户端中获取属性的方法:
查询参数、表单参数、路径参数。
7.1:处理查询参数:
//实现翻页功能,需要传入两个值,max、count。
@RequestMapping(method = RequestMethod.GET)
public List<Spittle> spittles(@RequestParam("max") long max , @RequestParam("count") int count){
return spittleRepository.findSpittles(max , count);
}
//设点参数的默认值
@RequestMapping(method = RequestMethod.GET)
public List<Spittle> spittles(
@RequestParam(value = "max" , defaultValue = MAX_LONG_AS_STRING) long max ,
@RequestParam(value = "count" , defaultValue = 10) int count
){
return spittleRepository.findSpittles(max , count);
}
7.2:通过路径参数接收输入:
场景:我们需要一个名为SpittleID的参数查询指定的某一条记录:
/**
通过参数查询:
下面这个类中的showPaittle方法将配“/spittles/show?spittle_id”的GET请求。(/spittles/show?spittle_id=123445)
*/
@Controller
@RequestMapping("/spittles")
public class SpittleController{
@RequestMapping(value = "/show" , method = RequestMethod.GET)
public String showPaittle(
@RequestParam("spittle_id" long spittleId),
Model model
){
model.addAttribute("key" , spittleRepository.findSpittles(spittleId));
return "spittle";
}
}
/**
通过URL路径标识
下面的方法可以匹配“/spittles/spittleId”URL路径(/spittles/234423)
*/
@Controller
@RequestMapping("/spittles")
public class SpittleController{
@RequestMapping(value = "/{spittleId}" , method = RequestMethod.GET) //{}为占位符
public String spittle(@PathVariable("spittleId") Long spittleId , Model model){ //无论占位符部分的值是什么都会传到处理方法中。
model.addAttribute("key" , spittleRepository.findSpittles(spittleId));
return "spittle";
}
}
//@PathVariable("spittleId" Long spittleId , Model model)由于方法参数名和占位符一直所以可以胜率为:
//@PathVariable(Long spittleId , Model model)
//如果@PathVariable中的Value属性没有的话,会假设方法参数名称和路径占位符名称相同。
8、处理表单
例如表单:
<html>
<head>
<title>Spittr</title>
</head>
<body>
<form>
First Name: <input type = "text" name firstName="firstName" />
Last Name : <input type = "text" name LastName = "lastName" />
UserName : <input type = "text" name UserName = "userName" />
PassWord : <input type = "password" name = "password" />
<input type = "submit" value = "Register">
</form>
</body>
</html>
控制器:
@Controller
@RequestMapping("/spitter")
public class SpitterController{
private SpitterRepository spitterRepository;
@Autowired
public SpitterController(SpitterRepository spitterRepository){ //SpitterRepository是DAO层的类
this.spitterRepository = spitterRepository;
}
@RequestMapping(value = "/register" , method = GET) //这个请求用于跳转到注册页面
public String showRegisterFrom(){
return "register"
}
@RequestMapping(value = "/register" , method = POST)
public String processRegister(Spitter spitter){
spitterRepository.save(spitter); //Spitter类中的属性对应了表单中的属性。按照名称匹配。
return "redirect:/spitter/"+spitter.getUserName; //重定向到别的页面。(redirect:会被解析为重定向。forward:是跳转)
}
}
处理重定向的控制器:
@RequestMapping(value ="/{username}" , method = GET)
public String ShowSpitterProfile(@PathVariable String userName , Model model){
Spitter spitter = spitterRepository.findByUserName(userName);
model.addAttribute(spitter);
return "profile";
}
表单验证:
(表单中,填写的数据为空、数据太长等异常情况需要解决)
1、一种比较初级的方法是,在控制其中添加一些校验代码。(校验代码污染了控制器)
2、利用Spring对java校验API(JSR-303)的支持。(只需要在类路径下包含实现了Java校验API的类即可)
java校验API提供的校验注解(只需放在属性上就可以限制属性的值,这些注解位于 javax.validation.constraints):
注 解 描 述
@AssertFalse 所注解的元素必须是 Boolean 类型,并且值为false
@AssertTrue 所注解的元素必须是 Boolean 类型,并且值为true
@DecimalMax 所注解的元素必须是数字,并且它的值要小于或等于给定的BigDecimalString值
@DecimalMin 所注解的元素必须是数字,并且它的值要大于或等于给定的BigDecimalString值
@Digits 所注解的元素必须是数字,并且它的值必须有指定的位数
@Future 所注解的元素的值必须是一个将来的日期
@Max 所注解的元素必须是数字,并且它的值要小于或等于给定的值
@Min 所注解的元素必须是数字,并且它的值要大于或等于给定的值
@NotNull 所注解元素的值必须不能为null
@Null 所注解元素的值必须为null
@Past 所注解的元素的值必须是一个已过去的日期
@Pattern 所注解的元素的值必须匹配给定的正则表达式
@Size 所注解的元素的值必须是String、集合或数组,并且它的长度要符合给定的范围
在我们例子中只需要在Spittle类的属性上添加@NotNull和@Size注解即可
1:添加校验注解
public class Spittle{
private Long id;
@NotNull
@Size(min = 5 , max = 6)
private String userName;
@NotNull
@Size(min = 5 , max = 6)
private String passWord;
@NotNull
@Size(min = 5 , max = 6)
private String firstName;
@NotNull
@Size(min = 5 , max = 6)
private String lastName;
}
2:启用校验功能(修改接收数据的控制器)
@RequestMapping(value = "/register" , method = POST)
public String processRegister(@Valid Spitter spitter , Errors errors){ //校验Spitter输入
if(errors.hasErrors){
return "registerForm" //校验不通过,返回到注册页面
}
spitterRepository.save(spitter);
return "redirect:/spitter/"+spitter.getUserName;
}