Spring MVC

文章目录

  • START
  • SpringMVC
    • MVC设计模式
    • 代码
      • 使用配置文件
      • 使用注解
    • 聊聊整个过程 执行原理
      • 执行原理
    • 深入学习
      • 视图模型分离
      • @RequestMapping
      • 内置统一字符集处理
      • 传参
      • 返回json数据
      • 数据转化
      • 数据校验
      • 重定向和转发
    • restful
    • 拦截器
      • 拦截器规则
      • 登录拦截器
    • 文件上传
    • 文件下载
    • 异常捕获
    • 国际化
  • ssm整合
    • 静态资源处理问题

START

  • author:空巷

  • WeChat Applet :Java空巷

  • QQ: 2399014502

  • Mail: [email protected]

  • WeiBo : KongXiang_

  • WeChat:

在这里插入图片描述

SpringMVC

推荐学习路径:http://c.biancheng.net/spring_mvc/

MVC设计模式

MVC 设计不仅限于 Java Web 应用,还包括许多应用,比如前端、PHP、.NET 等语言。之所以那么做的根本原因在于解耦各个模块。

MVC 是 Model、View 和 Controller 的缩写,分别代表 Web 应用程序中的 3 种职责。

  • 模型:用于存储数据以及处理用户请求的业务逻辑。
  • 视图:向控制器提交数据,显示模型中的数据。
  • 控制器:根据视图提出的请求判断将请求和数据交给哪个模型处理,将处理后的有关结果交给哪个视图更新显示。

基于 Servlet 的 MVC 模式的具体实现如下。

  • 模型:一个或多个 JavaBean 对象,用于存储数据(实体模型,由 JavaBean 类创建)和处理业务逻辑(业务模型,由一般的 Java 类创建)。
  • 视图:一个或多个 JSP 页面,向控制器提交数据和为模型提供数据显示,JSP 页面主要使用 HTML 标记和 JavaBean 标记来显示数据。
  • 控制器:一个或多个 Servlet 对象,根据视图提交的请求进行控制,即将请求转发给处理业务逻辑的 JavaBean,并将处理结果存放到实体模型 JavaBean 中,输出给视图显示。

Spring MVC_第1张图片

Spring MVC 框架主要由 DispatcherServlet、处理器映射、控制器、视图解析器、视图组成,其工作原理如图 所示。

Spring MVC_第2张图片

从图 可总结出 Spring MVC 的工作流程如下:

  1. 客户端请求提交到 DispatcherServlet。
  2. 由 DispatcherServlet 控制器寻找一个或多个 HandlerMapping,找到处理请求的 Controller。
  3. DispatcherServlet 将请求提交到 Controller。
  4. Controller 调用业务逻辑处理后返回 ModelAndView。
  5. DispatcherServlet 寻找一个或多个 ViewResolver 视图解析器,找到 ModelAndView 指定的视图。
  6. 视图负责将结果显示到客户端。

传统的模型层被拆分为了业务层(Service)和数据访问层(DAO,Data Access Object)。 在 Service 下可以通过
Spring 的声明式事务操作数据访问层,而在业务层上还允许我们访问 NoSQL ,这样就能够满足异军突起的NoSQL 的使用了,它可以大大提高互联网系统的性能。

特点: 结构松散,几乎可以在 Spring MVC 中使用各类视图 松耦合,各个模块分离 与 Spring 无缝集成

代码

使用配置文件

pom.xml


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>
    <groupId>org.examplegroupId>
    <artifactId>springmvc-studyartifactId>
    <version>1.0-SNAPSHOTversion>

    <packaging>warpackaging>
    <properties>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <java.version>1.8java.version>
    properties>
    <dependencies>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.12version>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-webmvcartifactId>
            <version>5.2.6.RELEASEversion>
        dependency>
        <dependency>
            <groupId>javax.servletgroupId>
            <artifactId>javax.servlet-apiartifactId>
            <version>4.0.0version>
            <scope>providedscope>
        dependency>
        <dependency>
            <groupId>javax.servlet.jspgroupId>
            <artifactId>jsp-apiartifactId>
            <version>2.2version>
            <scope>providedscope>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.12version>
        dependency>
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>1.2.9version>
        dependency>
        
        <dependency>
            <groupId>org.hibernategroupId>
            <artifactId>hibernate-validatorartifactId>
            <version>6.1.5.Finalversion>
        dependency>

    dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <version>3.1version>
                <configuration>
                    <source>${java.version}source> 
                    <target>${java.version}target> 
                    <encoding>${project.build.sourceEncoding}encoding>
        configuration>
      plugin>
    plugins>
    <resources>
      <resource>
        <directory>src/main/javadirectory>
        <includes>
          <include>**/*.propertiesinclude>
          <include>**/*.xmlinclude>
        includes>
        <filtering>falsefiltering>
      resource>
      <resource>
        <directory>src/main/resourcesdirectory>
        <includes>
          <include>**/*.propertiesinclude>
          <include>**/*.xmlinclude>
        includes>
        <filtering>falsefiltering>
      resource>
    resources>
  build>


project>

在web工程下配置DispatcherServlet的servlet


    <servlet>
        <servlet-name>spring-mvcservlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>

        <load-on-startup>1load-on-startup>
    servlet>
    
    
    <servlet-mapping>
        <servlet-name>spring-mvcservlet-name>
        <url-pattern>/url-pattern>
    servlet-mapping>

在springMvc中,视图都放在WEB-INF中以保证视图安全。客户端必须通过访问servlet才能跳转到WEB-INF


<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
    http://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">

    
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
    
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        
        <property name="prefix" value="/WEB-INF/page/" />
        
        <property name="suffix" value=".jsp" />
    bean>
beans>

编写一个Controller,继承Controller接口

public class FirstController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) throws Exception {
        //ModelAndView 封装了模型和视图
        ModelAndView mv = new ModelAndView();
        //模型里封装数据
        mv.addObject("HelloMvc","Hello springMVC!");
        //封装跳转的视图
        mv.setViewName("HelloMvc");
        //不是有个视图解析器吗
        //这玩意就是为了省事的,自动给你加个前缀后缀
        //就成了 /jsp/hellomvc.jsp 不就是拼串吗
        return mv;
    }
}

spingMvc默认去找WEB-INF下的spring-mvc-servlet.xml ,要创建一个spring-mvc-servlet.xml 文件

<bean id="/hellomvc" class="com.xinzhi.controller.FirstController"/>

使用注解

注解就要扫包


<context:component-scan base-package="com.xinzhi"/>

<mvc:default-servlet-handler />

<mvc:annotation-driven />
@Controller//说明是Controller层
@RequestMapping("/user/") //路径
  public class UserController {
  //一个主路径可以有多个子路径,多个功能可以由一个servlet来处理
  @RequestMapping("login")
  public String login(Model model) {
    model.addAttribute("ogin","denlu!");
    return "Login";
  } 
  @RequestMapping("register")
  public String register(Model model) {
    model.addAttribute("egister","zhuce!");
    return "Register";
	}
}

聊聊整个过程 执行原理

当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,在将结果返回给请求者。

Spring MVC_第3张图片

执行原理

Spring MVC_第4张图片

简要分析执行流程

  1. DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。

    我们假设请求的url为 : http://localhost:8080/SpringMVC/hello

    如上url拆分成三部分:

    http://localhost:8080服务器域名

    SpringMVC部署在服务器上的web站点

    hello表示控制器

    通过分析,如上url表示为:请求位于服务器localhost:8080上的SpringMVC站点的hello控制器。

  2. HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler。

  3. HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello。

  4. HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。

  5. HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。

  6. Handler让具体的Controller执行。

  7. Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。

  8. HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。

  9. DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。

  10. 视图解析器将解析的逻辑视图名传给DispatcherServlet。

  11. DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。

  12. 最终视图呈现给用户。

深入学习

视图模型分离

之前返回的是模型加视图,耦合性太高,有时候不需要跳转视图也会强行跳转使用Model参数

Spring 视图解析器是 Spring MVC 中的重要组成部分,用户可以在配置文件中定义 Spring MVC 的一个视图解析器(ViewResolver),示例代码如下:

 
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        
        <property name="prefix" value="/WEB-INF/page/" />
        
        <property name="suffix" value=".jsp" />
    bean>

看代码

@RequestMapping(value = "login")
public String login(Model model) {
    model.addAttribute("ogin","登录!");
    return "Login";
}
//视图解析器将会自动添加前缀和后缀。

@RequestMapping

这个注解很强大,可以放在方法上也可以放在类上,放在类上所有方法都默认加上该注解有六个参数

  • value:访问路径
  • method :指定请求类型,如post、get等
  • consumes :指定处理请求后提交的数据类型(Content-Type),如json,text
  • produces :指定返回值类型
  • params :指定request中必须有哪些参数,有才能让此方法执行
  • headers :指定request中必须包含某些指定的header值 ,有才能让此方法执行

例如:

@RequestMapping(value = "login",method = RequestMethod.POST)
public String login(Model model, User user) {
  System.out.println(user.getUsername());
  System.out.println(user.getId());
  System.out.println(user.getPassword());l
  model.addAttribute("login","登录!");
  return "Login";
}

好处:

  • 一个一班处理一类事物,可以统一家前缀,好区分
  • 简化书写复杂度

内置统一字符集处理

在 web.xml 中配置一个字符集过滤器即可


<filter>
  <filter-name>CharacterEncodingFilterfilter-name>
  <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
  <init-param>
    <param-name>encodingparam-name>
    <param-value>utf-8param-value>
  init-param>
filter>
<filter-mapping>
  <filter-name>CharacterEncodingFilterfilter-name>
  <url-pattern>/*url-pattern>
filter-mapping>

传参

建个首页

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>




Insert title here


    没注册的用户,请 注册
已注册的用户,去 登录

建个实体类

package pojo;

public class UserForm {
    private String uname; // 与请求参数名称相同
    private String upass;
    private String reupass;

    // 省略getter和setter方法
}

不需要一个个的getParameter(),要什么直接拿

通过 @RequestParam 接收请求参数

通过 @RequestParam 接收请求参数适用于 get 和 post 提交请求方式,可以将“通过实体 Bean 接收请求参数”部分控制器类 UserController 中 register 方法

@RequestMapping("/register")
/**
* 通过@RequestParam接收请求参数
*/
public String register(@RequestParam String uname,
    @RequestParam String upass, Model model) {
    if ("zhangsan".equals(uname) && "123456".equals(upass)) {
        logger.info("成功");
        return "login"; // 注册成功,跳转到 login.jsp
    } else {
        // 在register.jsp页面上可以使用EL表达式取出model的uname值
        model.addAttribute("uname", uname);
        return "register"; // 返回 register.jsp
    }
}
@RequestMapping(value = "login",method = RequestMethod.POST)
public String login(Model model, User user) {
  System.out.println(user.getUsername());
  System.out.println(user.getId());
  System.out.println(user.getPassword());l
  model.addAttribute("login","登录!");
  return "Login";
}

通过 @PathVariable 接收 URL 中的请求参数

@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/user")
    // 必须节method属性
    /**
     * 通过@PathVariable获取URL的参数
     */
    public String register(@PathVariable String uname,@PathVariable String upass,Model model) {
        if ("zhangsan".equals(uname)
                && "123456".equals(upass)) {
            logger.info("成功");
            return "login"; // 注册成功,跳转到 login.jsp
        } else {
            // 在register.jsp页面上可以使用EL表达式取出model的uname值
            model.addAttribute("uname", uname);
            return "register"; // 返回 register.jsp
        }
    }
}

但参数多了这么传是不是很啰嗦。更牛逼了 直接传个对象

@RequestMapping(value = "login",method = RequestMethod.POST)
public String login(Model model, User user) {
  System.out.println(user.getUsername());
  System.out.println(user.getId());
  System.out.println(user.getPassword());l
  model.addAttribute("login","登录!");
  return "Login";
}

这里介绍了几种常用的传参方式,还有很多,可以自行去学习。

返回json数据

可以使用fastjson

导入依赖

<dependency>
 <groupId>com.alibabagroupId>
 <artifactId>fastjsonartifactId>
 <version>1.2.68version>
dependency>
@RequestMapping(value = "getUsers",produces = {"text/json;charset=utf-8"})
@ResponseBody
public String getUsers(){
  List<User> users =  new ArrayList<User>(){{
    add(new User("wangwu","2222",23));
    add(new User("空巷","333",23));
 }};
  return JSONArray.toJSONString(users);
}

@ResponseBody能将处理的结果放在响应体中,直接返回,不走视图解析器

每次都需要自己处理数据麻烦 那么springMVC 封装了一个更好用的 只需要在配置文件中注入一个消息转换器

<mvc:annotation-driven >
  <mvc:message-converters>
    <bean id="fastjson"
          class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
      <property name="supportedMediaTypes">
        <list>
          <value>text/html;charset=UTF-8value>
          <value>application/json;charset=UTF-8value>
        list>
      property>
    bean>
  mvc:message-converters>
mvc:annotation-driven>
@RequestMapping("set")
//直接将返回值以数据的形式返回到响应体
@ResponseBody
public User set(@Validated User user, BindingResult br) {
  List<ObjectError> allErrors = br.getAllErrors();
  for (ObjectError allError : allErrors) {
    System.out.println(allError.getCode()+"---"+allError.getDefaultMessage());
  }
  System.out.println(user.getId());
  return user;
}

数据转化

Spring MVC 提供了几个内置的格式化转换器,具体如下。

  • NumberFormatter:实现 Number 与 String 之间的解析与格式化。
  • CurrencyFormatter:实现 Number 与 String 之间的解析与格式化(带货币符号)。
  • PercentFormatter:实现 Number 与 String 之间的解析与格式化(带百分数符号)。
  • DateFormatter:实现 Date 与 String 之间的解析与格式化。
@DateTimeFormat(pattern = "yyyy-mm-dd")
private Date birthday;
@NumberFormat(pattern = "#,###,###.#")
private Double salary;

数据校验

@NotNull 对象不为空
@AssertTrue Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false
@Size(min =, max =) 长度是否在给定的范围
@Min(value=) 验证 Number 对象是否大等于指定的值
@Max(value=) 验证 Number 对象是否小等于指定的值
@Pattern 验证 String 对象是否符合正则表达式的规则
@Past @Future验证 Date 和 Calendar 时间对象是否在当前时间之前/之后
@Email 是否是正确的邮箱地址

需要引入jar


<dependency>
    <groupId>org.hibernategroupId>
    <artifactId>hibernate-validatorartifactId>
    <version>5.1.1.Finalversion>
dependency>

在 springmvc.xml 中注册 hibernate 的校验器,并在原来的 mvc 注解驱动中设置 validator 属性:


<mvc:annotation-driven validator="validator" />



	property>  		
bean>

java:

@Max(value = 20L,message = "id不能大于20")
private int id;
@NotNull(message = "名字不能为空")
private String username;
@Null
private String password;

@RequestMapping("set")
@ResponseBody
//用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。
public User set(@Validated User user, BindingResult br) {
  //拿到错误,不会挡住请求
  List<ObjectError> allErrors = br.getAllErrors();
  for (ObjectError allError : allErrors) {
    System.out.println(allError.getCode()+"---"+allError.getDefaultMessage());
  }
  System.out.println(user.getId());
  return user;
}

注意点:

  • 在需要校验的对象前加 @Validated注解。

  • 在该对象后加一个参数:BindingResut 来接收校验信息。

  • 调用 BindingResult 对象的 hasErrors 方法判断前台传过来的参数是否符合校验规则,有一个不符合规则,hasErrors 方法就会返回 true。

  • 然后调用 getAllErrors() 方法获取 ObjectError 的 list 列表。

  • 遍历集合,调用每个对象的 getDefaultMessage() 方法,获取相应的错误提示信息(在实体类上配置的)

  • 获取到校验信息后再返回给前台或做一些其他的处理即可。

重定向和转发

请求转发:

    @RequestMapping("/annotation")
    public String testAnnotation(Model model) {
        model.addAttribute("annotation", "hello Springmvc");
        return "annotation";
    }

重定向:(只需要在return 语句中加入 redirect:+ 地址 即可访问)

    @RequestMapping(value = "/jump")
    public String jumpUser(HttpServletRequest request,HttpServletResponse response){
        //return "redirect:https://www.baidu.com/";
        return "redirect:add";
    }

restful

请求url 请求方式 操作
/admin/get/1 get 根据id查询用户
/admin/add Post 添加一个用户
/admin/update/1 Put 根据id修改用户
/admin/delete/1 Delete 删除一个用户

Restful web service是一种常见的rest的应用,是遵守了rest风格的web服务;rest式的web服务是一种ROA(The Resource-Oriented Architecture)(面向资源的架构)

常用状态码 工具类

/**
 * @author 空巷
 * @Date 2020/6/4
 */
@AllArgsConstructor
@Data
@NoArgsConstructor
public class R implements Serializable {
    private int code;
    private String msg;
    private Map<String,String> data;

    //返回成功后的信息
    public static R success(){
        return new R(200,"成功",null);
    }

    //返回失败后的信息
    public static R fail(){
        return new R(500,"fail",null);
    }

    //其他类型的返回
    public static R build(int code,String msg){
        return new R(500,msg,null);
    }

    public R put(String key,String msg){
        if (msg == null){
            this.getData().put(key,msg);
        }
        return this;
    }
}

controller类

/**
 * @author 空巷
 * @Date 2020/6/4
 */
@Controller
@RequestMapping("/admin")
public class RestfulController {
    @GetMapping("/get/{id}")
    @ResponseBody
    public User getUser(@PathVariable int id){
        System.out.println(id);
        return new User(13,"123","123456",new Date(),12123.12);
    }

    @PostMapping("/add")
    @ResponseBody
    public R addUser(User user){
        System.out.println(user);
        if (user == null){
            return R.fail();
        }else{
            return R.success();
        }
    }

    @PutMapping("/update/{id}")
    @ResponseBody
    public R updateUser(@PathVariable int id){
        System.out.println(id);
        return R.success();
    }

    @DeleteMapping("/delete/{id}")
    @ResponseBody
    public R deleteUser(@PathVariable int id){
        System.out.println(id);
        return R.success();
    }
}

建立一个http文件或者在postman测试

GET http://localhost:8080/admin/get/1
Accept: application/json

###
POST http://localhost:8080/admin/add
Content-Type: text/html

###
PUT http://localhost:8080/admin/update/2
Content-Type: application/json
{
   "id":2,
   "username":"123",
   "password":2,
   "birthday":"2000-5-25",
   "salary":"1,111.14"
}

###
DELETE http://localhost:8080/admin/delete/1
Content-Type: text/html

update 测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-81uCSZvO-1591533532699)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20200604172838216.png)]

delete 测试:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9WFpgcgf-1591533532700)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20200604173022938.png)]

get 测试:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HctHedNB-1591533532701)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20200604173108784.png)]

add 测试:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ovlp1zQ5-1591533532702)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20200604173200239.png)]

拦截器

  1. SpringMVC提供的拦截器类似于JavaWeb中的过滤器,只不过SpringMVC拦截器只拦截被前端控制器拦截的请求,而过滤器拦截从前端发送的任意请求。
  2. 熟练掌握 SpringMVC 拦截器对于我们开发非常有帮助,在没使用权限框架( shiro,spring security )之前,一般使用拦截器进行认证和授权操作。
  3. SpringMVC拦截器有许多应用场景,比如:登录认证拦截器,字符过滤拦截器,日志操作拦截器等等。

拦截器规则

自定义的拦截器需要实现 Spring 的 HandlerInterceptor 接口,其中有三个方法。

  1. preHandle:Controller方法处理请求前执行,根据拦截器定义的顺序,正向执行。
  2. postHandle:Controller方法处理请求后执行,根据拦截器定义的顺序,逆向执行。需要所有的preHandle方
    法都返回true时才会调用。
  3. afterCompletion:View视图渲染后处理方法:根据拦截器定义的顺序,逆向执行。preHandle返回true就会
    调用

登录拦截器

/**
 * @author 空巷
 * @Date 2020/6/4
 */
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        User user = (User) session.getAttribute("user");
        if (user == null){
            response.sendRedirect("/admin/toLogin");
        }else {
            return true;
        }
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

还需要在 SpringMVC.xml 配置文件名中,配置编写的拦截器


    <mvc:interceptors>
        <mvc:interceptor>
            
            <mvc:mapping path="/**"/>
            
            <mvc:exclude-mapping path="/admin/toLogin"/>
            <mvc:exclude-mapping path="/admin/login"/>
            
            <bean id="loginInterceptor" class="com.xinzhi.interceptor.LoginInterceptor"/>
        mvc:interceptor>
    mvc:interceptors>

controller

    @GetMapping("/toLogin")
    public String toLogin(User user){
        return "login";
    }

    @PostMapping("/login")
    @ResponseBody
    public R login(User user, HttpServletRequest request){
        request.getSession().setAttribute("user",user);
        return R.success();
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n6xjbSrg-1591533532703)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20200604184134261.png)]

文件上传

Spring MVC 框架的文件上传是基于 commons-fileupload 组件的文件上传,只不过 Spring MVC 框架在原有文件上传组件上做了进一步封装,简化了文件上传的代码实现,取消了不同上传组件上的编程差异。

commons-fileupload组件

由于 Spring MVC 框架的文件上传是基于 commons-fileupload 组件的文件上传,因此需要将 commons-fileupload 组件相关的 JAR(commons-fileupload-1.3.1.jar 和 commons-io-2.4.jar)复制到 Spring MVC 应用的 WEB-INF/lib 目录下。下面讲解如何下载相关 JAR 包。

Commons 是 Apache 开放源代码组织中的一个 Java 子项目,该项目包括文件上传、命令行处理、数据库连接池、XML 配置文件处理等模块。fileupload 就是其中用来处理基于表单的文件上传的子项目,commons-fileupload 组件性能优良,并支持任意大小文件的上传。文件上传

multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。

一旦设置了enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应。在2003年,Apache Software Foundation发布了开源的CommonsFileUpload组件,其很快成为Servlet/JSP程序员上传文件的最佳选择。

首先需要导入文件上传的 jar 包


<dependency>
    <groupId>commons-fileuploadgroupId>
    <artifactId>commons-fileuploadartifactId>
    <version>1.3.3version>
dependency>

在 SpringMVC 中配置bean

    
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        
        <property name="defaultEncoding" value="utf-8"/>
        
        <property name="maxUploadSize" value="10485760"/>
        <property name="maxInMemorySize" value="40960"/>
    bean>

CommonsMultipartFile 的 常用方法:

  • String getOriginalFilename():获取上传文件的原名
  • InputStream getInputStream():获取文件流
  • void transferTo(File dest):将上传文件保存到一个目录文件中

controller

@PostMapping("/upload")
@ResponseBody
public R upload(@RequestParam("file") CommonsMultipartFile file,HttpServletRequest request){
    //getOriginalFilename 可以获得文件的名字
    String filename = file.getOriginalFilename();
    //文件路径
    String path = "E:\\upload";
    File relPath = new File(path);
    //判断路径是否存在,不存在则创建一个
    if (!relPath.exists()){
        relPath.mkdir();
    }
    try {
        file.transferTo(new File(path +"\\"+ filename));
    } catch (IOException e) {
        e.printStackTrace();
    }
    return R.success();
}

文件下载

实现文件下载有以下两种方法:

  • 通过超链接实现下载。
  • 利用程序编码实现下载。

使用 ResponseEntity

    @GetMapping("/download")
    public ResponseEntity<byte[]> download() {
        String fileName = "6.png";
        try {
            //FileUtils 中有个方法 readFileToByteArray 可以读取文件
            byte[] bytes = FileUtils.readFileToByteArray(new File("E:\\upload" + "\\" + fileName));

            HttpHeaders handler = new HttpHeaders();
            //下载文件时需要设置响应头
            handler.set("Content-Disposition", "attachment;filename=" +
                    URLEncoder.encode(fileName, "UTF-8"));
            handler.set("charsetEncoding", "utf-8");
            handler.set("content-type", "multipart/form-data");
            ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(bytes, handler, HttpStatus.OK);
            return responseEntity;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gsYxOaI3-1591533532704)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20200604194010851.png)]

异常捕获

当页面出现异常时,不能显示在页面,之前是通过在web.xml配置错误页面,但往往这种处理办法是不好的。因为不知道他出现了什么错误。因此,Spring MVC提供了一个HandlerExceptionResolver接口,可用于统一异常处理。

/**
 * @author 空巷
 * @Date 2020/6/4
 */
@Component
public class MyException implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        //打印错误到控制台
        e.printStackTrace();
        ModelAndView mv = new ModelAndView();
        mv.setViewName("error");
        return mv;
    }
}

国际化

国际化是设计软件应用的过程中应用被使用与不同语言和地区

spring-mvc.xml配置文件


<mvc:interceptor>
    <mvc:mapping path="/**"/>
    
    <bean id="localeChangeInterceptor"
    class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
    <property name="paramName" value="lang" />
    bean>
mvc:interceptor>
        
        
        

    <bean id="localeResolver"
          class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
        <property name="defaultLocale" value="zh_CN" />
    bean>

    
    <bean id="messageSource"
          class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="defaultEncoding" value="UTF-8" />
        <property name="useCodeAsDefaultMessage" value="true" />
        <property name="basenames">
            <list>
                <value>classpath:messagevalue>
            list>
        property>
    bean>

国际化语言属性文件:message_zh_CN.properties

TITLE = 开始冒险之旅
USERNAME = 账号:
PASSWORD = 密码:
LOGIN = 登录

message_en_US.properties

TITLE = BEGIN TO START
USERNAME = UserName
PASSWORD = PassWord
LOGIN = Login

chinese.jsp

<%--
  Created by IntelliJ IDEA.
  User: 空巷
  Date: 2020/6/4
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    Title


中文

英文

controller

  @GetMapping("/getLogin")
    public String getLogin(){
        return "result";
    }

result.jsp

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%--
  Created by IntelliJ IDEA.
  User: 空巷
  Date: 2020/6/4
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    Title





ssm整合

pom依赖:

    <dependencies>
        
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.12version>
            <scope>testscope>
        dependency>
        
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-webmvcartifactId>
            <version>5.1.10.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-coreartifactId>
            <version>5.2.6.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-testartifactId>
            <version>5.2.6.RELEASEversion>
            <scope>testscope>
        dependency>
        
        <dependency>
            <groupId>org.mybatisgroupId>
            <artifactId>mybatisartifactId>
            <version>3.5.2version>
        dependency>
        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.47version>
        dependency>
        
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-jdbcartifactId>
            <version>5.2.6.RELEASEversion>
        dependency>
        
        <dependency>
            <groupId>org.aspectjgroupId>
            <artifactId>aspectjweaverartifactId>
            <version>1.9.4version>
        dependency>
        
        <dependency>
            <groupId>org.mybatisgroupId>
            <artifactId>mybatis-springartifactId>
            <version>2.0.2version>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.16.18version>
        dependency>
    dependencies>
    
    <build>
        <resources>
            <resource>
                <directory>src/main/javadirectory>
                <includes>
                    <include>**/*.propertiesinclude>
                    <include>**/*.xmlinclude>
                includes>
                <filtering>falsefiltering>
            resource>
            <resource>
                <directory>src/main/resourcesdirectory>
                <includes>
                    <include>**/*.propertiesinclude>
                    <include>**/*.xmlinclude>
                includes>
                <filtering>falsefiltering>
            resource>
        resources>
    build>

mybatis-config.xml

一班用来开启驼峰式,别名等等



<configuration>

    <typeAliases>
        
        
        
        <package name="com.xinzhi.entity"/>
    typeAliases>

configuration>

springmvc-servlet.xml


<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
       http://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">

    
    <mvc:default-servlet-handler />

    


    
    <mvc:annotation-driven >
        <mvc:message-converters>
            <bean id="fastjson" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/html;charset=UTF-8value>
                        <value>application/json;charset=UTF-8value>
                    list>
                property>
            bean>
        mvc:message-converters>
    mvc:annotation-driven>


    
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        
        <property name="defaultEncoding" value="utf-8"/>
        
        <property name="maxUploadSize" value="10485760"/>
        <property name="maxInMemorySize" value="40960"/>
    bean>

    
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
    
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
        
        <property name="prefix" value="/WEB-INF/pages/"/>
        
        <property name="suffix" value=".jsp"/>
    bean>
beans>

application.xml (配置数据源)


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://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/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    
    <context:property-placeholder location="classpath:db.properties"/>
    
    <import resource="classpath:springmvc-servlet.xml"/>
    
    <context:component-scan base-package="com.xinzhi"/>

    
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        
        <property name="basePackage" value="com.xinzhi.dao"/>
    bean>

    
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
    bean>

    
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:mappers/*.xml"/>
    bean>

    
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    bean>


    
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            
            <tx:method name="add*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="search*" propagation="REQUIRED"/>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        tx:attributes>
    tx:advice>
    
    <aop:config>
        <aop:pointcut id="txPointcut" expression="execution(* com.xinzhi.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
    aop:config>
    


beans>

logback.xml(日志配置文件)


<configuration scan="true" scanPeriod="60 seconds" debug="false">
    
    
    
    <property name="log.level" value="debug" />
    <property name="log.maxHistory" value="0.5" />
    <property name="log.filePath" value="D:/log" />
    <property name="log.pattern"
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n" />
    
    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${log.pattern}pattern>
        encoder>
    appender>
    
    <appender name="debugAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        
        <file>${log.filePath}/debug.logfile>
        
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            
            <fileNamePattern>${log.filePath}/debug/debug.%d{yyyy-MM-dd}.log.gz
            fileNamePattern>
            
            <maxHistory>${log.maxHistory}maxHistory>
        rollingPolicy>
        <encoder>
            <pattern>${log.pattern}pattern>
        encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUGlevel>
            <onMatch>ACCEPTonMatch>
            <onMismatch>DENYonMismatch>
        filter>
    appender>
    
    <appender name="infoAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        
        <file>${log.filePath}/info.logfile>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            
            <fileNamePattern>${log.filePath}/info/info.%d{yyyy-MM-dd}.log.gz
            fileNamePattern>
            
            <maxHistory>${log.maxHistory}maxHistory>
        rollingPolicy>
        <encoder>
            <pattern>${log.pattern}pattern>
        encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFOlevel>
            <onMatch>ACCEPTonMatch>
            <onMismatch>DENYonMismatch>
        filter>
    appender>

    
    <logger name="com.xinzhi" level="${log.level}" additivity="true">
        <appender-ref ref="debugAppender"/>
        <appender-ref ref="infoAppender"/>
    logger>

    
    <root level="info">
        <appender-ref ref="consoleAppender"/>
    root>

    
    <appender name="MyBatis" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.filePath}/sql_log/mybatis-sql.logfile>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${log.filePath}/sql_log/mybatis-sql.log.%d{yyyy-MM-dd}FileNamePattern>
            <maxHistory>30maxHistory>
        rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%thread|%d{yyyy-MM-dd HH:mm:ss.SSS}|%level|%logger{36}|%m%npattern>
        encoder>
    appender>

    <logger name="sql" level="DEBUG">
        <appender-ref ref="MyBatis"/>
    logger>

configuration>

db.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/backlog?useSSL=false&useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=root

filters=wall,stat
maxActive=20
initialSize=3
maxWait=5000
minIdle=3
maxIdle=15
timeBetweenEvictionRunsMillis=60000
minEvictableIdleTimeMillis=300000
validationQuery=SELECT 'x'
testWhileIdle=true
testOnBorrow=false
testOnReturn=false
maxOpenPreparedStatements=20
removeAbandoned=true
removeAbandonedTimeout=1800
logAbandoned=true

web.xml


<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <servlet>
        <servlet-name>springmvcservlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
        <init-param>
            <param-name>contextConfigLocationparam-name>
            <param-value>classpath:application.xmlparam-value>
        init-param>
        <load-on-startup>1load-on-startup>
    servlet>
    <servlet-mapping>
        <servlet-name>springmvcservlet-name>
        <url-pattern>/url-pattern>
    servlet-mapping>

    <filter>
        <filter-name>CharacterEncodingFilterfilter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
        <init-param>
            <param-name>encodingparam-name>
            <param-value>utf-8param-value>
        init-param>
    filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilterfilter-name>
        <url-pattern>/*url-pattern>
    filter-mapping>

    
    <filter>
        <filter-name>DruidWebStatFilterfilter-name>
        <filter-class>com.alibaba.druid.support.http.WebStatFilterfilter-class>
        <init-param>
            <param-name>exclusionsparam-name>
            <param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*param-value>
        init-param>
    filter>
    <filter-mapping>
        <filter-name>DruidWebStatFilterfilter-name>
        <url-pattern>/*url-pattern>
    filter-mapping>
    <servlet>
        <servlet-name>DruidStatView servlet-name>
        <servlet-class>com.alibaba.druid.support.http.StatViewServletservlet-class>
        <init-param>
            
            <param-name>loginUsernameparam-name>
            <param-value>xinzhiparam-value>
        init-param>
        <init-param>
            
            <param-name>loginPasswordparam-name>
            <param-value>123param-value>
        init-param>
    servlet>
    <servlet-mapping>
        <servlet-name>DruidStatViewservlet-name>
        <url-pattern>/druid/*url-pattern>
    servlet-mapping>

web-app>

xxxMapper.xml



<mapper namespace="com.xinzhi.dao.UserMapper">

mapper>

静态资源处理问题

静态资源的访问问题

访问 jsp 页面时 首先通过访问controller,通过请求转发到jsp页面 需要跳转首页时 建立一个indexConrtoller 使用GetMapping直接转发到首页

当一些页面的css,js等静态资源 放在WEB-INF 下,通过相对路径的方式是访问不到的

解决安案一:(不被保护的,外界可以查看)

将静态资源放入 webapp 下 通过 相对路径 或绝对路径的方式访问

解決方案二:(受保护的)

当把静态资源放入 WEB-INF 下的时候:

需要在spring-mvc配置文件中加入:

<mvc:resources mapping="/static/**" location="/WEB-INF/static/"/>

**eg: ** 当你使用了拦截器时,你会发现css等静态资源加不进来,打开 F12 进行调试,看到 css等静态资源并没有报红,你会发现,莫名其妙的重定向了。

解决方案一:(针对静态资源放入 webapp 下的处理办法)

在拦截器中加入

<mvc:exclude-mapping path="/**/*.css" />
<mvc:exclude-mapping path="/**/*.js" />
<mvc:exclude-mapping path="/**/*.jsp" />....

解决方案二:(针对静态资源存入WEB-INF 下)

在拦截器中加入

<mvc:exclude-mapping path="/static/**" />

你可能感兴趣的:(ssm)