SpringMVC详细使用总结教程

一、SpringMVC

1.1 介绍

Spring MVC 是 Spring 框架中的一个模块,用于构建基于 Java 的 Web 应用程序。它基于 MVC(Model-View-Controller)架构模式,提供了一种灵活而强大的方式来开发 Web 应用程序。Spring MVC 框架充分利用了依赖注入和面向切面编程等 Spring 核心功能,使开发者能够轻松地开发出高效、灵活且易于维护的 Web 应用程序。它具有以下特点:

  1. 松耦合性:Spring MVC 鼓励开发者将应用程序的各个部分分开,使得模型、视图和控制器之间的耦合度降低,代码更易于理解、测试和维护。
  2. 灵活性:Spring MVC 提供了丰富的配置选项和扩展点,允许开发者根据需求定制和扩展框架的行为,满足不同应用场景的需求。
  3. 易用性:Spring MVC 提供了一套简洁而强大的 API,使得开发者能够快速地构建出高质量的 Web 应用程序,并且易于集成到现有的 Spring 或 Java EE 应用中。
    1.2 MVC架构
    MVC(Model-View-Controller)是一种软件架构模式,用于设计和组织应用程序的代码。它将应用程序分为三个主要部分:
  4. 模型(Model):模型负责管理应用程序的数据和业务逻辑。它表示应用程序的数据结构,处理数据的读取、存储、验证和操作等任务。
  5. 视图(View):视图负责显示模型的数据给用户,并接受用户的输入。它是用户界面的呈现部分,负责与用户交互。
  6. 控制器(Controller):控制器是模型和视图之间的中介,负责处理用户的输入并更新模型和视图之间的关系。它接收用户的请求,处理请求并调度适当的操作。
    MVC模式的优势在于将应用程序的不同功能分离开来,使得代码更加模块化、可维护和可扩展。它提供了清晰的结构,使开发人员可以更容易地理解和管理应用程序的各个部分。

二、环境搭建

2.1 导入依赖

bash 复制代码
<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-webmvc</artifactId>
     <version>6.1.6</version>
 </dependency>

2.2 配置核心(前端)控制器

作为一个MVC框架,首先要解决的是:如何能够收到请求!所以MVC框架大都会设计一款前端控制器,选型在 Servlet 或 Filter两者之一,在框架最前沿率先工作,接收所有请求。此控制器在接收到请求后,还会负责springMVC的核心的调度管理,所以既是前端又是核心。

bash 复制代码
<servlet>
     <servlet-name>mvc</servlet-name>
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     <!-- 局部参数:声明配置文件位置 -->
     <init-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>classpath:mvc.xml</param-value>
     </init-param>
     <!-- Servlet启动时刻:可选 -->
     <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
     <servlet-name>mvc</servlet-name>
     <url-pattern>/</url-pattern>
 </servlet-mapping>

2.3 后端控制器

等价于之前定义的Servlet

java 复制代码
@Controller //声明这是一个控制器
 @RequestMapping("/hello")  //访问路径 ,等价于url-pattern
 public class HelloController {
     @RequestMapping("/test1")  //访问路径
     public String hello1(){
         System.out.println("hello world");
         return "index"; // 跳转:/index.jsp  
     }
     @RequestMapping("/test2") //访问路径
     public String hello2(){
         System.out.println("hello c9");
         return "views/users";//  跳转:/views/user.jsp
     }
 }

2.4 配置文件

默认名称:核心控制器名-servet.xml 默认位置:WEB-INF随意名称:mvc.xml 随意位置:resources 但需要配置在核心控制器中

xml 复制代码
<beans  xmlns="http://www.springframework.org/schema/beans"
         xmlns:context="http://www.springframework.org/schema/context"
         xmlns:mvc="http://www.springframework.org/schema/mvc" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://www.springframework.org/schema/beans
                             http://www.springframework.org/schema/beans/spring-beans.xsd
                             http://www.springframework.org/schema/context
                             http://www.springframework.org/schema/context/spring-context.xsd
                             http://www.springframework.org/schema/mvc
                             http://www.springframework.org/schema/mvc/spring-mvc.xsd">
 
     <!-- 告知springmvc  哪些包中 存在 被注解的类 -->
     <context:component-scan base-package="com.ts.controller"></context:component-scan>
     <!-- 注册注解开发驱动 -->
     <mvc:annotation-driven></mvc:annotation-driven>
     <!-- 视图解析器
          作用:1.捕获后端控制器的返回值="index"
               2.解析: 在返回值的前后 拼接 ==> "/index.jsp"
      -->
     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
         <!-- 前缀 -->
         <property name="prefix" value="/"></property>
         <!-- 后缀 -->
         <property name="suffix" value=".jsp"></property>
     </bean>
 </beans>

2.5 访问

bash 复制代码
http://localhost:8989/hello/test1
 http://localhost:8989/hello/test2

三、接收请求参数

通过控制器中方法的形参 接收请求参数

3.1 基本类型参数

请求参数和方法的形参 同名即可springMVC默认可以识别的日期字符串格式为: YYYY/MM/dd HH:mm:ss通过@DateTimeFormat可以修改默认日志格式

bash 复制代码
@RequestMapping("/test1")
 public String testParam1(Integer id,
                          String name,
                          Boolean gender,
                          @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")Date birth){
     System.out.println("test param1");
     return "index";
 }

3.2 实体收参(建议)

请求参数和实体的属性 同名即可

bash 复制代码
public class User {
     private Integer id;
     private String name;
     @DateTimeFormat(pattern="yyyy-MM-dd")
     private Date birth;
     private Boolean gender;
     //set/get ...
 }
 
 //http://localhost:8989/.../test2?id=1&name=zzz&gender=false&birth=2018-12-12 12:20:30
 @RequestMapping("/test2")
 public String testParam2(User user){
     System.out.println("test param2");
     System.out.println("user:"+user);
     return "index";
 }

3.3 数组收参

简单类型的 数组

bash 复制代码
<form>
     ......
     <input type="checkbox" name="hobby" value="fb"/>足球 
     <input type="checkbox" name="hobby" value="bb"/>篮球 
     <input type="checkbox" name="hobby" value="vb"/>排球
     
 </form>

//http://localhost:8989/.../test3?hobby=football\&hobby=basketball

bash 复制代码
@RequestMapping("/test3")
public String testParam3(String[] hobby){
    for(String h:hobby){
        System.out.print(h+" ");
    }
    return "index";
}

3.4 集合收参 (了解)

java 复制代码
public class VO {
	//private User[] users;
	private List<User> users;
	//set/get..
}

// <input type="text" name="users[0].id"/>
//http://...?users[0].id=1&users[0].name=zhangsan&users[0].birth=2018-12-12&users[1].id=2&....
@RequestMapping("/test4")
public String testParam4(VO9 vo){
    for(User user:vo.getUsers()){
        System.out.println(user);
    }
    return "index";
}

3.5 路径参数

java 复制代码
@RequestMapping("/hello/{id}")
public String testParam5(@PathVariable("id") Integer id){
    System.out.println("id:"+id);            
    return "index";
}

3.6 中文乱码

首先,页面中字符集统一

JSP : <%@page pageEncoding="utf-8" %>

HTML :

其次,tomcat中字符集设置,对get请求中,中文参数乱码有效

Tomcat配置:URIEncoding=utf-8

最后,设置此filter,对post请求中,中文参数乱码有效

html 复制代码
<!-- 此过滤器会进行:request.setCharactorEncoding("utf-8"); -->
<filter>
    <filter-name>encoding</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encoding</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

四、跳转

跳转关键字 forward:redirect

4.1 转发

java 复制代码
@RequestMapping("/forw")
class ForwardController{
    
    @RequestMapping("/test1")
    public String testForward(){
        System.out.println("test forward1");
        // 转发跳转 /views/users.jsp
        // return "views/users";//和下一行等价
        return "forward:/views/users.jsp";
    }
    
    @RequestMapping("/test2")
    public String testForward2(){
        System.out.println("test forward2");
        //转发到  /forw/test1
        //return "forward:test1";//相对路径(转发到本类中的test1)
        转发到  /forw/test1
        return "forward:/forw/test1"; //绝对路径
    }
}

4.2 重定向

java 复制代码
@RequestMapping("/redir")
class RedirectController{
    @RequestMapping("/test1")
    public String testRedirect1(){
        System.out.println("test redirect1");
        //重定向到 /redir/test1
        //return "redirect:test1"; //相对路径(转发到本类中的test1)
        return "redirect:/redir/test1";//绝对路径
    }
    @RequestMapping("/test2")
    public String testRedirect2(){
        System.out.println("test redirect2");
        //重定向到 /views/users.jsp
        return "redirect:/view/user.jsp";
    }
}

4.3 跳转细节

1.在增删改之后,为了防止请求重复提交,重定向跳转2.在查询之后,可以做转发跳转

五、传值

C得到数据后,跳转到V,并向V传递数据。进而V中可以渲染数据,让用户看到含有数据的页面转发跳转:Request作用域重定向跳转:Session作用域

5.1 Request和Session

java 复制代码
//形参中 即可获得 request 和 session对象
@RequestMapping("/test1")
public String testData(HttpSession session,HttpServletRequest req,Integer id){
    session.setAttribute("user",new User());
    req.setAttribute("age", 18);
    req.setAttribute("users",Arrays.asList(new User(),new User()));
    //return "test2";
    return "forward:/WEB-INF/test2.jsp";
}

5.2 JSP中取值

建议:重点复习 EL JSTL

java 复制代码
//jsp中用EL表达式 取值即可
<fmt:formatDate value="${sessionScope.user.birth}" pattern="yyyy-MM-dd"/> <br/>
${sessionScope.user.birth} <br>
${requestScope.age}
5.3 Model
//model中的数据,会在V渲染之前,将数据复制一份给request
@RequestMapping("/test")
public String testData(Model model){
    model.addAttribute("name", "张三");
    return "index";
}

//jsp中用EL表达式 取值即可
${requestScope.name}
5.4 ModelAndView
//modelandview 可以集中管理 跳转和数据
@RequestMapping("/test")
public ModelAndView testData(){//返回值类型为ModelAndView
    //新建ModelAndView对象
    ModelAndView mv = new ModelAndView();
    // 设置视图名,即如何跳转
    mv.setViewName("forward:/index.jsp");
    // 增加数据
    mv.addObject("age",18);
    return mv;
}

//jsp中用EL表达式 取值即可
${requestScope.age}
5.5 @SessionAttributes
@SessionAttributes({"gender","name"}) model中的 name和gender 会存入session中SessionStatus 移除session
@Controller
@SessionAttributes({"gender","name"}) // model中的 name和gender 会存入session中
public class UserController {

    @RequestMapping("/hello")
    public String hello(Model m){
        m.addAttribute("gender",true); // 会存入session
        mv.addObject("name","zhj"); // 会存入session
        return "index";
    }
    
    @RequestMapping("/hello2")
    public String hello(SessionStatus status){
        // 移除通过SessionAttributes存入的session
        status.setComplete();
        return "index";
    }
}

六、 静态资源

静态资源:html,js文件,css文件,图片文件静态文件没有url-pattern,所以默认是访问不到的,之所以可以访问,是因为,tomcat中有一个全局的servlet:org.apache.catalina.servlets.DefaultServlet,它的url-pattern是 "/",是全局默认的Servlet. 所以每个项目中不能匹配的静态资源的请求,有这个Servlet来处理即可。但,在SpringMVC中DispatcherServlet也采用了 "/" 作为url-pattern, 则项目中不会再使用全局的Serlvet, 则静态资源不能完成访问。

6.1 解决方案1

DispathcerServlet采用其他的url-pattern此时,所有访问handler的路径都要以 action结尾!!

bash 复制代码
<servlet>
  	<servlet-name>mvc9</servlet-name>
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>mvc9</servlet-name>
    <url-pattern>*.action</url-pattern>
</servlet-mapping>

6.2 解决方案2

DispathcerServlet的url-pattern依然采用 "/",但追加配置

bash 复制代码
<!-- 
  额外的增加一个handler,且其requestMapping:  "/**" 可以匹配所有请求,但是优先级最低
  所以如果其他所有的handler都匹配不上,请求会转向 "/**" ,恰好,这个handler就是处理静态资源的
  处理方式:将请求转会到tomcat中名为default的Servlet
  -->
<mvc:default-servlet-handler/>

6.3 解决方案3

bash 复制代码
<!--
    1) mapping是访问路径,location是静态资源存放的路径
    
	2) 将/html/** 中 /**匹配到的内容,拼接到 /hhh/后
        http://..../html/a.html  访问 /hhh/a.html
        http://..../html/page/b.hmtl 访问 /hhh/page/b.html
    
-->
<mvc:resources mapping="/html/**" location="/hhh/"/>

七、Json处理

springMVC默认的Json解决方案选择是 Jackson,所以只需要导入jackson的jar,即可使用。

7.1 导入依赖

bash 复制代码
<!-- Jackson -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.8</version>
</dependency>
<!-- FastJson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.54</version>
</dependency>

7.2 使用@ResponseBody

java 复制代码
@Controller
public class JsonController{    
	@RequestMapping("/test1")
    @ResponseBody //将handler的返回值,转换成json(jackson),并将json响应给客户端。
    public User hello1(){
        System.out.println("hello world");
        User user = new User();
        return user;
    }
    // @ResponseBody还可以用在handler的返回值上
    @RequestMapping("/test2")
    public @ResponseBody List<User> hello2(){
        System.out.println("hello world");
        List<User> users = Arrays.asList(new User(),new User());
        return users;
    }
    // 如果返回值已经是字符串,则不需要转json,直接将字符串响应给客户端 
    @RequestMapping(value="/test3",produces = "text/html;charset=utf-8") //produces 防止中文乱码
    @ResponseBody 
    public String hello2(){
        System.out.println("hello world");
        return "你好";
    }
}

7.3 使用@RestController

java 复制代码
Controller类上加了@RestController注解,等价于在类中的每个方法上都加了@ResponseBody
@Controller
@RestController
public class JsonController{
    @RequestMapping("/test1")
    public User hello1(){
        System.out.println("hello world");
        User user = new User();
        return user;
    }
    //@ResponseBody还可以用在handler的返回值上
    @RequestMapping("/test2")
    public List<User> hello2(){
        System.out.println("hello world");
        List<User> users = Arrays.asList(new User(),new User());
        return users;
    }
}

7.4 使用@RequestBody

@RequestBody, 接收Json参数

7.4.1 定义Handler

java 复制代码
class User{
    private Integer id;
    private String name;
    private Boolean gender;
    //set get
}
@RequestMapping("/users")
public String addUser(@RequestBody User user){//@RequestBody将请求体中的json数据转换为java对象
    System.out.println("cap2");
    System.out.println("Post user :"+user);
    return "index";
}

7.4.2 Ajax发送json

bash 复制代码
var xhr = new XMLHttpRequest();
xhr.open("post","${pageContext.request.contextPath}/users?"+new Date().getTime());
xhr.setRequestHeader("content-type","application/json");//设置请求头
xhr.send('{"id":1,"name":"shine","gender":"true"}');//传递json串
//ajax
var user = {id:1,name:"shine"};
$.ajax({
    url:'${pageContext.request.contextPath}/json2/test4',
    type:'post',
    contentType:"application/json",//声明请求参数类型为 json
    data:JSON.stringify(user),// 转换js对象成json
    success:function(ret){
        console.log(ret);
    }
});

7.5 Jackson常用注解

7.5.1 日期格式化

java 复制代码
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
public class User{
	private Integer id;
	private String name;
	@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
	private Date birth;
    ....
    get/set
}

7.5.2 属性名修改

java 复制代码
@JsonProperty("new_name")
public class User{
	@JsonProperty("new_id") //不再使用原属性名,而是 "new_id"
    private Integer id;
	private String name;
    ....
    get/set
}

输出的json:{"new_id":xx,"name":"xx"}

7.5.3 属性忽略

java 复制代码
@JsonIgnore
public class User{
    private Integer id;
    @JsonIgnore // 生成json时,忽略此属性
	private String name;
    ....
    get/set
}

输出json时: {"id":xx}

7.5.4 null和empty属性排除

java 复制代码
Jackson 默认会输出null值的属性,如果不需要,可以排除。@JsonInclude(JsonInclude.Include.NON_NULL) //null值 属性不输出@JsonInclude(value= JsonInclude.Include.NON_EMPTY) // empty属性不输出( 空串,长度为0的集合,null值)
public class User{
    private Integer id;
    @JsonInclude(JsonInclude.Include.NON_NULL) // 若"name==null" 忽略此属性
	private String name;
    @JsonInclude(value= JsonInclude.Include.NON_EMPTY)  // 若hobby长度为0或==null 忽略此属性
    private List<String> hobby;
    ....
    get/set
}

如果name=null,且 hobby长度为0,则输出json时:{"id":xx}

7.5.5 自定义序列化

java 复制代码
@JsonSerialize(using // 使用MySerializer输出某属性 
public class User {
    private Integer id;
    private String name;
    @JsonSerialize(using = MySerializer.class)
    private Double salary = 10000.126;//在输出此属性时,使用MySerializer输出
    ....
    get/set
}
则输出json时:{"id":xx,"name":"xxx","salary":10000.13}
public class MySerializer extends JsonSerializer<Double> {

    // value即 Double salary的值
    @Override 
    public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        // 将Double salary的值 四舍五入
        String number = BigDecimal.valueOf(value).setScale(2, BigDecimal.ROUND_HALF_UP).toString();
        // 输出 四舍五入后的值
        gen.writeNumber(number);
    }
}

7.6 FastJson

如果不想使用Jackson,则也可以安装其他的 Json处理方案:FastJson

7.6.1 安装FastJson

xml 复制代码
<mvc:annotation-driven>
    <!-- 安装FastJson,转换器 -->
    <mvc:message-converters>
        <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
            <!-- 声明转换类型:json -->
            <property name="supportedMediaTypes">
                <list>
                    <value>application/json</value>
                </list>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

7.6.2 使用

@ResponseBody @RequestBody @RestController 使用方法不变

7.6.3 常用注解

日期格式化:@JSONField(format="yyyy/MM/dd")属性名修改:@JSONField(name="birth")忽略属性:@JSONField(serialize包含null值:@JSONField(serialzeFeatures 默认会忽略所有null值,有此注解会输出null @JSONField(serialzeFeatures null的String输出为"" 自定义序列化:

java 复制代码
@JSONField(serializeUsing
public class User implements Serializable{
    
	@JSONField(serialize = false)
    private Integer id;
    
    @JSONField(name="NAME",serialzeFeatures = SerializerFeature.WriteNullStringAsEmpty)
	private String name;
    
    @JSONField(serialzeFeatures = SerializerFeature.WriteMapNullValue) 
    private String city;
    
	@JSONField(format="yyyy/MM/dd")
	private Date birth;
    
    @JSONField(serializeUsing = MySerializer2.class)
    private Double salary;
	...
}
public class MySerializer2 implements ObjectSerializer {
    @Override
    public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType,
                      int features) throws IOException {
        Double value = (Double) object; // salary属性值
        String text = value + "元";// 在salary后拼接 "元"
        serializer.write(text); // 输出拼接后的内容
    }

}

FastJson中如果属性为空,则输出的json字符串中没有改属性。new User(1,null,null,new Date(),100.5) ;输出json:{NAME:"",city:null,"birth":"2020/12/12","salary":"100.5元"}

八、异常解析器

8.1 现有方案,分散处理

Controller中的每个Handler自己处理异常此种处理方案,异常处理逻辑,分散在各个handler中,不利于集中管理

java 复制代码
public String xxx(){
    try{
    	...
    }catch(Exception1 e){
    	e.printStackTrace();
        return "redirect:/xx/error1";
    }catch(Exception2 e){
    	e.printStackTrace();
        return "redirect:/xx/error2";
    }

}

8.2 异常解析器,统一处理

Controller中的每个Handler不再自己处理异常,而是直接throws所有异常。定义一个"异常解析器" 集中捕获处理 所有异常此种方案,在集中管理异常方面,更有优势!

java 复制代码
public class MyExResolver implements HandlerExceptionResolver{
	/**
	 * 异常解析器:主体逻辑
	 * 执行时刻:当handler中抛出异常时,会执行:捕获异常,并可以跳到错误页面
	 */
	@Override
	public ModelAndView resolveException(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex) {
		ex.printStackTrace();//打印异常栈
		//创建一个ModelAndView
		ModelAndView mv = new ModelAndView();
		//识别异常
		if (ex instanceof Exception1) {
			mv.setViewName("redirect:/xxx/error1");
		}else if(ex instanceof Exception2){
			mv.setViewName("redirect:/xxx/error2");
		}else{
			mv.setViewName("redirect:/xxx/error");
		}
		return mv;
	}
}

8.3 注解实现

java 复制代码
@ControllerAdvice
public class GlobalExceptionHandler {

	@ExceptionHandler(value = Exception.class)
	@ResponseBody
	public Map<String, String> jsonExceptionHandler(HttpServletRequest request, Exception ex) {
		Map<String,String> map = new HashMap<String, String>();
		map.put("errcode", "500");
		map.put("errmessage", "系统出现错误");
		return map;
	}
	
	// 处理业务异常
	@ExceptionHandler(value = BusinessException.class)
	@ResponseBody
	public Map<String, String> jsonExceptionHandler2(HttpServletRequest request, BusinessException ex) {
		Map<String,String> map = new HashMap<String, String>();
		map.put("errcode", ex.getCode().toString());
		map.put("errmessage", ex.getMessage());
		return map;
	}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BusinessException extends RuntimeException {

	private Integer code;

	private String message;

}

九、拦截器(重点)

重要但不紧急作用:抽取C中的冗余功能

9.1 定义拦截器

执行顺序: preHandle--postHandle--afterCompletion

java 复制代码
public class MyInter1 implements HandlerInterceptor{
	//主要逻辑:在handler之前执行:抽取handler中的冗余代码
	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		System.out.println("pre~~~");
        /*
        response.sendRedirect("/springMVC_day2/index.jsp");//响应
        return false;//中断请求
        */
		return true;//放行,后续的拦截器或handler就会执行
	}
	//在handler之后执行:进一步的响应定制
	@Override
	public void postHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("post~~");
	}
	//在页面渲染完毕之后,执行:资源回收
	@Override
	public void afterCompletion(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		System.out.println("after~~");
	}
}

9.2 配置拦截路径

bash 复制代码
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/inter/test1"/>
        <mvc:mapping path="/inter/test2"/>
        <mvc:mapping path="/inter/test*"/> <!-- test开头 -->
        <mvc:mapping path="/inter/**"/> <!-- /** 任意多级任意路径 -->
        <mvc:exclude-mapping path="/inter/a/**"/>   <!--不拦截此路径-->
        <bean class="com.ts.interceptor.MyInter1"></bean>   <!--拦截器类-->
    </mvc:interceptor>
</mvc:interceptors>

十、上传(非重点)

10.1 导入jar

xml 复制代码
    <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>
    <exclusions>
        <exclusion>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
        </exclusion>
    </exclusions>
</dependency>

10.2 表单

xml 复制代码
<form action="${pageContext.request.contextPath }/upload/test1" method="post" 
      enctype="multipart/form-data">
  file: <input type="file" name="source"/> <br>
  <input type="submit" value="提交"/>
</form>

10.3 上传解析器

xml 复制代码
<!-- 上传解析器 
	     id必须是:"multipartResolver"
 -->
<bean id="multipartResolver" 
      class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 最大可上传的文件大小   byte 超出后会抛出MaxUploadSizeExceededException异常,可以异常解析器捕获 -->
    <property name="maxUploadSize" value="2097152"></property>
</bean>

10.4 Handler

java 复制代码
@RequestMapping("/test1")
public String hello1(String username,MultipartFile source,HttpSession session) {
    //文件的原始名称
    String filename = source.getOriginalFilename();
    //定制全局唯一的命名
    String unique = UUID.randomUUID().toString();
    //获得文件的后缀
    String ext = FilenameUtils.getExtension(filename);//abc.txt   txt    hello.html  html
    //定制全局唯一的文件名
    String uniqueFileName = unique+"."+ext;
    System.out.println("唯一的文件名:"+uniqueFileName);

    //文件的类型
    String type = source.getContentType();
    System.out.println("filename:"+filename+" type:"+type);

    //获得 upload_file的磁盘路径 ==> 在webapp目录下创建一个目录"upload_file",且此目录初始不要为空,否则编译时被忽略
    String real_path = session.getServletContext().getRealPath("/upload_file");
    System.out.println("real_path:"+real_path);

    //将上传的文件,存入磁盘路径中
    //source.transferTo(new File("d:/xxxx/xxxx/xx.jpg"))
    source.transferTo(new File(real_path+"\\"+uniqueFileName));
    return "index";
}

十一、REST【重点】

13.1 开发风格

是一种开发风格,遵从此风格开发软件,是restful的。

● 每个资源都有自己的标识

bash 复制代码
http://localhost:8989/xxx/usershttp://localhost:8989/xxx/users/1http://localhost:8989/xxx/users/1/orders

● 每个请求都有明确的动词 ( GET, POST, PUT, DELETE )

GET :http://localhost:8989/xxx/users 查询所有用户POST:http://localhost:8989/xxx/users 增加一个用户PUT :http://localhost:8989/xxx/users/1 修改用户1DELETE :http://localhost:8989/xxx/users/1 删除用户1GET:http://localhost:8989/xxx/users/1/orders 查询用户1的订单POST:http://localhost:8989/xxx/users/1/orders 为用户1增加一个订单

13.2 优点

● 看Url就知道要什么

● 看http method就知道干什么

13.3 使用

13.3.1 定义Rest风格的 Controller

xml 复制代码
@RequestMapping(value="/users",method = RequestMethod.GET)等价@GetMapping("/users")
@RestController
public class RestController {
    @GetMapping("/users")
    public List<User> queryAllUsers(){
        System.out.println("get");
        List<User> users = ....
        return users;
    }

    @PostMapping("/users")
    public String addUser(@RequestBody User user){
        System.out.println("Post user :"+user);
        return "{status:1}";
    }
    
    @PutMapping("/users")
    public String updateUser(@RequestBody User user){
        System.out.println("Put user" user:"+user);
        return "{status:1}";
    }

    @GetMapping("/users/{id}")
    public String queryOneUser(@PathVariable Integer id){//@PathVariable 接收路径中的值
        System.out.println("Get user id:"+id);
        return "{status:1}";
    }

    @DeleteMapping("/users/{id}")
    public String deleteOneUser(@PathVariable Integer id){//@PathVariable 接收路径中的值
        System.out.println("delete user id:"+id);
        return "{status:1}";
    }
}

13.3.2 Ajax请求

xml 复制代码
<script>    
	function putUser(){ // 发送更新请求 (增加请求发送方式也是如此)
        var xhr = new XMLHttpRequest();
    	//定义 put,delete,get,post方式 即可,不用定义_method
        xhr.open("put","${pageContext.request.contextPath}/rest04/users");
    	// 设置请求头
        xhr.setRequestHeader("content-type","application/json");
        // 设置请求参数
        var user = {id:1,NAME:"shine",city:"bj","birth":"2020/12/12","salary":100.5};
        xhr.send(JSON.stringify(user));
        xhr.onreadystatechange=function(){
            if(xhr.readyState==4 && xhr.status==200){
                var ret = xhr.responseText;
                // 解析json,并输出
                console.log(JSON.parse(ret));
            }
        }
    	/*$.ajax({
            url:'${pageContext.request.contextPath}/rest04/users',
            type:'put',
            contentType:"application/json",//声明请求参数类型为 json
            data:JSON.stringify(user),// 转换js对象成json
            success:function(ret){
                console.log(JSON.parse(ret));
            }
        });*/
    }

	function delUser(){  // 发送删除请求
        var xhr = new XMLHttpRequest();
        //定义 put,delete,get,post方式 即可,不用定义_method
        xhr.open("delete","${pageContext.request.contextPath}/rest04/users/1");
        xhr.send();
        xhr.onreadystatechange=function(){
            if(xhr.readyState==4 && xhr.status==200){
                var ret = xhr.responseText;
                console.log(JSON.parse(ret));
            }
        }
    }
</script>

十二、SpringMVC执行流程

十三、Spring整合

13.1 整合思路

此时项目中有两个工厂

● DispatcherServlet 启动的springMVC工厂负责生产C及springMVC自己的系统组件
● ContextLoaderListener 启动的spring工厂负责生产其他所有组件

● springMVC的工厂会被设置为spring工厂的子工厂,可以随意获取spring工厂中的组件

● 整合过程,就是累加:代码+依赖+配置。然后将service注入给controller即可

13.2 整合技巧

两个工厂不能有彼此侵入,即,生产的组件不能有重合。

xml 复制代码
<!-- 告知SpringMVC  哪些包中 存在 被注解的类
	use-default-filters=true 凡是被 @Controller @Service  @Repository注解的类,都会被扫描
	use-default-filters=false 默认不扫描包内的任何类, 只扫描include-filter中指定的类
	只扫描被@Controller注解的类
-->
<context:component-scan base-package="com.ts" use-default-filters="false">
 	<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 告知Spring
     唯独不扫描@Controller注解的类 -->
<context:component-scan base-package="com.ts" use-default-filters="true">
	<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

13.3SpringMVC整合Thymeleaf

13.3.1Thymeleaf简介

Thymeleaf 是新一代 Java 模板引擎,与 Velocity、FreeMarker 等传统 Java 模板引擎不同,Thymeleaf 支持 HTML 原型,其文件后缀为".html",因此它可以直接被浏览器打开,此时浏览器会忽略未定义的 Thymeleaf 标签属性,展示 thymeleaf 模板的静态页面效果;当通过 Web 应用程序访问时,Thymeleaf 会动态地替换掉静态内容,使页面动态显示

Thymeleaf 通过在 html 标签中,增加额外属性来达到"模板+数据"的展示方式,示例代码如下。

html 复制代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--th:text 为 Thymeleaf 属性,用于在展示文本-->
<h1 th:text="迎您来到Thymeleaf">欢迎您访问静态页面 HTML</h1>
</body>
</html>

当直接使用浏览器打开时,浏览器展示结果如下。

欢迎您访问静态页面HTML

当通过 Web 应用程序访问时,浏览器展示结果如下。

当通过 Web 应用程序访问时,浏览器展示结果如下。

环境搭建

1、导入依赖

xml 复制代码
<dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
            <version>3.0.13.RELEASE</version>
        </dependency>

2、配置thymeleaf视图解析器

xml 复制代码
<!-- 配置Thymeleaf视图解析器 -->
    <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
        <property name="order" value="1"/>
        <property name="characterEncoding" value="UTF-8"/>
        <property name="templateEngine">
            <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
                <property name="templateResolver">
                    <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
                        <!-- 视图前缀 -->
                        <property name="prefix" value="/"/>

                        <!-- 视图后缀 -->
                        <property name="suffix" value=".html"/>
                        <property name="templateMode" value="HTML5"/>
                        <property name="characterEncoding" value="UTF-8" />
                    </bean>
                </property>
            </bean>
        </property>
    </bean>

3、定义页面(index.html)

xml 复制代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <base th:href="${#servletContext.getContextPath()+'/'}">
</head>
<body>
index<br>
<span th:text="动态内容">静态内容</span>
</body>
</html>

4、定义controlelr

xml 复制代码
@Controller
public class ThymeleafController {

    @GetMapping("/hello")
    public String hello(ModelMap modelMap) {
        modelMap.put("key", "value");
        return "index";
    }
}

5、访问

Thymeleaf 的特点

Thymeleaf 模板引擎具有以下特点:

● 动静结合:Thymeleaf 既可以直接使用浏览器打开,查看页面的静态效果,也可以通过 Web 应用程序进行访问,查看动态页面效果。

● 开箱即用:Thymeleaf 提供了 Spring 标准方言以及一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。

● 多方言支持:它提供了 Thymeleaf 标准和 Spring 标准两种方言,可以直接套用模板实现 JSTL、 OGNL 表达式;必要时,开发人员也可以扩展和创建自定义的方言。

● 与 SpringBoot 完美整合:SpringBoot 为 Thymeleaf 提供了的默认配置,并且还为 Thymeleaf 设置了视图解析器,因此 Thymeleaf 可以与 Spring Boot 完美整合。

Thymeleaf 语法规则

在使用 Thymeleaf 之前,首先要在页面的 html 标签中声明名称空间,示例代码如下。
Thymeleaf 作为一种模板引擎,它拥有自己的语法规则。Thymeleaf 语法分为以下 2 类: ● 标准表达式语法:${} ● th 属性: ### 13.3.2Thymeleaf常用操作

xml 复制代码
// 1.引入命名空间
 <html lang="en" xmlns:th="http://www.thymeleaf.org/">
     
 // 2.从域对象中取值
 req:<span th:text="${reqKey}"></span><br>
 session:<span th:text="${session.sessionKey}"></span><br>
 application:<span th:text="${application.applicationKey}"></span><br>
 内置对象:<span th:text="${#request.getAttribute('reqKey')}"></span>
     
 // 3.thymeleaf其他常用操作
 <base th:href="${#request.getContextPath()+'/'}">
 
 <td th:text="${#dates.format(user.createTime,'yyyy-MM-dd')}"></td>
 
 <input type="hidden" name="id" th:value="${user.id}" >
 
 th:οnclick="|admin_edit('user/getUserById/${user.id}')|"
 
 <p class="miaoshu" th:utext="${goods.ginfo}"></p>
 
 <tr th:each="topic:${page.list}">
 
 <span th:text="${sex ==1?'男':'女'}"></span>
 
 <span th:text="${#numbers.formatDecimal(price,4,3)}"></span>
 #numbers.formatDecimal(numbwe,整数位,小数位)。
 整数位表示,整数位不足四位,用0补齐
 
 <img th:each="img:${#strings.listSplit(gg.gimage,'|')}" th:src="${img}">
 
 <div th:switch="${age}">
     <p th:case="10">10岁</p>
     <p th:case="20">20岁</p>
     <p th:case="*">大于30岁(默认值)</p> 
 </div>
 
 <script th:inline="javascript">
     var message = [[${user.username}]];
     var username=[[${#request.getParameter('returnUrl')}]];
     console.log(message);
 </script>

 
相关推荐
拾光师1 天前
spring与springmvc整合
spring·springmvc
jingde5289 天前
SpringMVC中使用REST风格
springmvc
垚森18 天前
SpringBoot兼容SpringMVC带有.do后缀的请求
springmvc·.do后缀
_leyilea18 天前
Java内存马系列 | SpringMVC内存马 - 上 | SpringMVC代码分析
java·开发语言·springmvc·内存马
沿途欣赏i21 天前
关于SpringMVC的理解
springmvc
m0_7194145623 天前
【SpringMVC】
java·springmvc
王小二(海阔天空)1 个月前
浅谈SpringMvc的核心流程与组件
springmvc·核心流程·核心组件
S-X-S1 个月前
SpringMVC核心机制环境搭建
springmvc
S-X-S1 个月前
自定义@ResponseBody以及SpringMVC总结
springmvc