简介
Spring web MVC 框架提供了模型-视图-控制的体系结构和可以用来开发灵活、松散耦合的 web 应用程序的组件。MVC 模式导致了应用程序的不同方面(输入逻辑、业务逻辑和 UI 逻辑)的分离,同时提供了在这些元素之间的松散耦合。
- 模型封装了应用程序数据,并且通常它们由 POJO 组成。
- 视图主要用于呈现模型数据,并且通常它生成客户端的浏览器可以解释的 HTML 输出。
- 控制器主要用于处理用户请求,并且构建合适的模型并将其传递到视图呈现。
mvc原理
总结一下springMVC几个关键的步骤,总共可以分为六个步骤,分别为:
(1) 客户端向spring容器发起一个http请求
(2) 发起的请求被前端控制起所拦截(DispatcherServlet),前端控制器会去找恰当的映射处理器来处理这次请求。
(3) 根据处理器映射(Handler Mapping)来选择并决定将请求发送给那一个控制器。
(4) 在控制器中处理所发送的请求,并以modelandView(属性值和返回的页面)的形式返回给向前端控制器。
(5) 前端控制器通过查询viewResolver对象来试着解决从控制返回的视图。
(6) 如果前端控制找到对应的视图,则将视图返回给客户端,否则抛异常。
Spring Web MVC 框架例子
- 需求的配置
你需要映射你想让 DispatcherServlet 处理的请求,通过使用在 web.xml 文件中的一个 URL 映射。下面是一个显式声明和映射 DispatcherServlet 的示例;
接下来,servlet-mapping
标签表明哪些 URLs 将被 DispatcherServlet 处理。这里所有以 .jsp 结束的 HTTP 请求将由 DispatcherServle t处理。
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
上述的配置的就是前段控制器,在servlet-mapping配置了*.html,意味着所有以.html结尾的请求多会通过这个servlet,当dispatcherServlet启动时,他默认会在web-info目录下查找一个spring-servlet.xml的配置文件。上面我们通过显示指定了这个文件的位置,即在类路径底下的spring-servlet.xml.这个文件我们会在第二步点给他家做详细介绍。
- 配置请求
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--开启注解 -->
<context:annotation-config></context:annotation-config>
<context:component-scan base-package="springmvc.controller"></context:component-scan>
<!--视图解析器 -->
<bean name="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
要点:
- [servlet-name]-servlet.xml 文件将用于创建 bean 定义,重新定义在全局范围内具有相同名称的任何已定义的 bean。
- context:component-scan标签将用于激活 Spring MVC 注释扫描功能,该功能允许使用注释,如 @Controller 和 @RequestMapping 等等。
- UrlBasedViewResolver将使用定义的规则来解决视图名称。按照上述定义的规则,一个名称为 hello 的逻辑视图将发送给位于 /WEB-INF/jsp/sky.jsp 中实现的视图。
- 定义控制器
DispatcherServlet 发送请求到控制器中执行特定的功能。@Controller 注释表明一个特定类是一个控制器的作用。@RequestMapping 注释用于映射 URL 到整个类或一个特定的处理方法。
@Controller
@RequestMapping("user")
public class HelloController {
/*@ResponseBody 作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,
写入到response对象的body区,通常用来返回JSON数据或者是XML数据。*/
@ResponseBody
@GetMapping("hello")
public String hello() {
return "sky";
}
@GetMapping("gz")
public String gz() {
return "sky";
}
@GetMapping("lbz")
public ModelAndView lbz(ModelAndView mav) {
mav.addObject("sss","123456");
mav.setViewName("sky");
return mav;
}
}
- 创建 JSP 视图
对于不同的表示技术,Spring MVC 支持许多类型的视图。这些包括 JSP、HTML、PDF、Excel 工作表、XML、Velocity 模板、XSLT、JSON、Atom 和 RSS 提要、JasperReports 等等。但我们最常使用利用 JSTL 编写的 JSP 模板。
<html>
<head>
<title>Hello Spring MVC</title>
</head>
<body>
<h2>${sss}</h2><!--EL表达式: ${sss} 是我们在控制器内部设置的属性-->
</body>
</html>
springMVC拦截器
Spring MVC可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor接口。这个接口中定义了三个方法:preHandle()、postHandle()、afterCompletion()。
- preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求request进行处理。如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true;如果程序员决定不需要再调用其他的组件去处理请求,则返回false。
- postHandle():这个方法在业务处理器处理完请求后,但是DispatcherServlet向客户端返回请求前被调用,在该方法中对用户请求request进行处理。
- afterCompletion():这个方法在DispatcherServlet完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。
下面通过一个例子来说明如何使用Spring MVC框架的拦截器。
要求编写一个拦截器,拦截所有不在工作时间的请求,把这些请求转发到一个特定的静态页面,而不对它们的请求进行处理。
首先编写UserInterceptor.Java,代码如下:
public class UserInterceptor extends HandlerInterceptorAdapter{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("qian");
return super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("zhong");
super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("hou");
super.afterCompletion(request, response, handler, ex);
}
}
下面是在springmvc-servlet.xml中对拦截器进行的配置,代码如下
<!--springMVC拦截器 -->
<bean name="ui" class="springmvc.interceptor.UserInterceptor"></bean>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<ref bean="ui"/>
</mvc:interceptor>
</mvc:interceptors>
json格式数据的输入和输出
Spring mvc处理json需要使用jackson的类库,因此为支持json格式的输入输出需要先修改pom.xml增加jackson包的引用
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-lgpl</artifactId>
<version>1.8.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.8.0</version>
</dependency>
在springmvc-servlet.xml中必须加入这段代码:<mvc:annotation-driven />
根据前面的分析,在spring mvc中解析输入为json格式的数据有两种方式
1:使用@RequestBody来设置输入
@RequestMapping("/json1")
@ResponseBody
public JsonResult testJson1(@RequestBody User u){
log.info("get json input from request body annotation");
log.info(u.getUserName());
returnnew JsonResult(true,"return ok");
}
2:使用HttpEntity来实现输入绑定
@RequestMapping("/json2")
public ResponseEntity<JsonResult> testJson2(HttpEntity<User> u){
log.info("get json input from HttpEntity annotation");
log.info(u.getBody().getUserName());
ResponseEntity<JsonResult> responseResult = new ResponseEntity<JsonResult>( newJsonResult(true,"return ok"),HttpStatus.OK);
return responseResult;
对应Json格式的输出也对应有两种方式
1:使用@responseBody来设置输出内容为context body
@RequestMapping(value="/kfc/brands/{name}", method = RequestMethod.GET)
public@ResponseBody List<Shop> getShopInJSON(@PathVariable String name) {
List<Shop>shops = new ArrayList<Shop>();
Shop shop = new Shop();
shop.setName(name);
shop.setStaffName(new String[]{"mkyong1", "mkyong2"});
shops.add(shop);
Shop shop2 = new Shop();
shop2.setName(name);
shop2.setStaffName(new String[]{"mktong1", "mktong2"});
shops.add(shop2);
return shops;
}
当我们在地址栏中输入:http://localhost:8080/springJson/kfc/brands/kfc_name.html
服务器端会返回给我们jason格式的数据,这样我们就可以省去手工繁琐的组并了
2:返回值设置为ResponseEntity<?>类型,以返回context body
@RequestMapping("/json2")
public ResponseEntity<JsonResult> testJson2(HttpEntity<User>u){
log.info("get json input from HttpEntity annotation");
log.info(u.getBody().getUserName());
ResponseEntity<JsonResult> responseResult = new ResponseEntity<JsonResult>( newJsonResult(true,"return ok"),HttpStatus.OK);
return responseResult;
}
springmvc文件上传
Spring mvc使用jakarta的commonsfileupload来支持文件上传,因此我们需要在pom.xml中导入所依赖的两个包:
<!-- upload -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
在spring-servlet.xml中加入以下这段代码:
<!-- 上传下载 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"></property>
<property name="maxUploadSize" value="1073741824"></property>
</bean>
其中的property中可以限制最大和最小文件上传。
在客户端的代码如下:
<form method="post" action="/upload.html" enctype="multipart/form-data">
<input type="text" name="name"/>
<input type="file" name="file"/>
<input type="submit"/>
</form>
服务器端的代码如下:
@Controller
public class UploadController {
@PostMapping("upload")
public String upload(String name,@RequestParam("file") MultipartFile config,HttpServletRequest req) throws IllegalStateException, IOException {
String path = req.getServletContext().getRealPath("/");
path += "upload";
File file = new File(path);
if(!file.exists()) {
file.mkdir();
}
String fname = config.getOriginalFilename();
//第一种方法
File f2 = new File(path+"\\"+fname);
config.transferTo(f2);
System.out.println(name);
//第二种方法
byte[] bytes = config.getBytes();
FileOutputStream output =new FileOutputStream(new File(path+"\\"+fname));
output.write(bytes);
output.close();
System.out.println(name);
return "";
}
}
使用jsr303进行验证
JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。
Spring在进行数据绑定时,可通过调用校验框架完成数据校验工作。在SpringMVC中,可直接通过注解驱动的方式进行数据校验。
Spring的LocalValidatorFactoryBean既实现了Spring的Validator接口,也实现了JSR 303的Validator接口。只要在Spring容器中定义了一个LocalValidatorFactoryBean,即可将注入到需要的数据校验的Bean中。
在maven配置文件中加入Hibernate Validator的依赖包。
<!-- 數據驗證 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.0.Final</version>
</dependency>
配置springmvc-config.xml
< mvc:annotation-driven/ > 会默认装配好一个LocalValidatorFactoryBean,通过在处理方法的入参上标注@valid注解即可让Spring MVC在完成数据绑定后执行数据校验的工作。
<context:annotation-config/>
<context:component-scan
base-package="spring.controller"></context:component-scan>
<!-- 开启注解驱动,bean -->
<mvc:annotation-driven/>
<!--视图解析器 -->
<bean name="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView"></property>
<property name="suffix" value=".jsp"></property>
</bean>
实体层,在bean的属性上添加对应的注解
public class User{
private int id;
@NotBlank(message = "用户名不能为空!")
private String username;
@NotBlank(message = "密码不能为空!")
@Length(min = 3,max = 9,message = "密码长度为3-9")
private String password;
}
通过在控制层的处理方法入参上标注@Valid注解即可让SpringMVC在完成数据绑定后执行数据校验的工作。
@Controller
public class JsrController {
@PostMapping("log")
public String login(@ModelAttribute@Validated User user,BindingResult result) {
if(result.hasErrors()) {
System.out.println(result.getFieldError());
}
return "index";
}
@GetMapping("login")
public String login1(@ModelAttribute User user) {
return "index";
}
}
创建 JSP 页面 index.jsp
<body>
<form:form action="log.html" modelAttribute="user" method="post">
<form:input path="username" />
<form:errors path="username"></form:errors>
<form:input path="password" />
<form:errors path="password"></form:errors>
<input type="submit" value="登录">
</form:form>
</body>
Bean Validation 中内置的 constraint
Hibernate Validator 附加的 constraint