SpringMVC笔记(从零搭建一个web项目)

其实SpringMVC就是一个DispatcherServlet来调度的,整个请求的流程都是由DispatcherServlet来控制的,具体流程请看这篇:SpringMVC的执行流程
仔细如果有看过源码后,在回想整个过程其实并没有想象中的难,这里就先不对底层的原理进行过多的剖析了,后面腾出时间我会去看一看源码然后写一篇源码分析的笔记

第一个SpringMVC项目,帮助理解原理

创建maven项目

为了更好的理解整个web项目,这里不使用maven的模版来创建项目,首先创建一个空的maven项目,然后引入依赖

<dependencies>
   <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <scope>test</scope>
       <version>4.12</version>
   </dependency>
   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-webmvc</artifactId>
       <version>5.1.9.RELEASE</version>
   </dependency>
   <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>servlet-api</artifactId>
       <version>2.5</version>
   </dependency>
   <dependency>
       <groupId>javax.servlet.jsp</groupId>
       <artifactId>jsp-api</artifactId>
       <version>2.2</version>
   </dependency>
   <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>jstl</artifactId>
       <version>1.2</version>
   </dependency>
</dependencies>

将项目添加web框架的支持

然后需要添加对框架的支持,就是我们需要将其改成一个web项目,在项目上右键,添加对框架的支持,由于我的idea安装了中文的插件,所以这里显示是中文的
在这里插入图片描述
然后添加web的支持
在这里插入图片描述
点击确定后就可以看见我们的项目下多了一个文件夹,里面的jsp文件夹是我后来创建的
在这里插入图片描述

在web.xml中注册DispatcherServlet

在web.xml中注册DispatcherServlet,这里面springmvc的配置文件还没创建,下面创建

<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!--关联一个springmvc的配置文件,其实就是spring的配置文件,配置bean用的-->
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc-servlet.xml</param-value>
    </init-param>
    <!--启动级别 1-->
    <load-on-startup>1</load-on-startup>
</servlet>
<!-- /  匹配所有的请求(不包括.jsp)-->
<!-- /*  匹配所有的请求(包括.jsp)-->
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

创建并配置springmvc-servlet.xml文件

  • 首先在resources目录下创建springmvc-servlet.xml文件
    这个文件其实就是一个spring的配置文件,我们可以将bean配置到其中
    有三个主要的东西需要我们来配置
    1)处理器映射器
    2)处理器适配器
    3)视图解析器

  • 处理器映射器这里使用 BeanNameUrlHandlerMapping ,这个映射器会根据bean的名称来寻找对应的处理器,实际开发的时候不会使用这个映射器,放在这里只是为了方便理解原理

  • 处理器适配器使用 SimpleControllerHandlerAdapter ,简单处理器适配器

  • 视图解析器使用 InternalResourceViewResolver ,为其配置前缀后缀,Controller返回ModelAndView对象的时候,这个视图解析器会把viewName和前后缀拼到一起,然后跳转对应的资源

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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-3.0.xsd">

    <!--处理器映射器-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <!--处理器适配器-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!--视图解析器:DispatcherServlet给他的ModelAndView-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
        <!--前缀-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

编写controller

1)创建包 com.jxw.controller
2)在包中创建 HelloController类
3)实现 Controller 接口的 handleRequest 方法
3)给ModelAndView 添加数据,key为“msg”,value为“执行了handleRequest方法”,并且设置名称为hello,然后返回这个ModelAndView

public class HelloController implements Controller {

    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","执行了handleRequest方法");
        mv.setViewName("hello");
        return mv;
    }
}

4)这个controller需要注册到spring中,在springmvc-servlet.xml配置文件中注册bean

<bean id="/hello" class="com.jxw.controller.HelloController"/>

编写ModelAndView要跳转的页面

从前面的配置可以看出,上面的Controller返回ModelAndView的时候,视图解析器会给我们拼出一个这样的资源路径
/WEB-INF/jsp/hello.jsp
那么我们需要写个jsp来让他跳转
1)在WEB-INF目录下创建jsp目录
2)创建hello.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
${msg}
</body>
</html>

页面非常简单,就是把我们之前传给ModelAndView的字符串展示出来

配置Tomcat

1)在idea的右上角,idea的版本不同可能位置不同,点击编辑结构
在这里插入图片描述
3)添加到Tomcat
在这里插入图片描述
这里Application Server要填本地的Tomcat目录
4)配置Tomcat
在这里插入图片描述
点击加号后出来两个选项,选择Artifact,然后选择你的项目就可以了
在这里插入图片描述
至此Tomcat基本是配置完成

运行

自动打开的网页,默认给我跳转到了index.jsp
在这里插入图片描述

访问hello页面

访问 http://localhost:8080/springmvc_02_hellomvc_war_exploded/hello
报了404
老项目遇到这个问题,基本就是controller的路由写的不对了
第一次运行的新项目遇到这个问题,首先看是不是jar包不全,因为我没有使用maven的web模版创建项目,所以我这里是lib目录没有添加到部署的文件中,下面解决

解决遇到的404,lib目录未添加

当未使用web模版创建项目的时候,可能会出现404
1)在项目上右键,中文为打开模块设置,英文的应该是类似Module Properties这样的单词
2)然后点击Artifacts
在这里插入图片描述
3)然后在WEB-INF目录上右键,添加lib目录
在这里插入图片描述
4)选中新建的lib目录,然后点击上面的加号
在这里插入图片描述
5)选择Library Files,全选确定
6)重新部署并重启站点

至此lib的添加配置完了
重新访问 http://localhost:8080/springmvc_02_hellomvc_war_exploded/hello
已经不404了
在这里插入图片描述

使用注解开发Controller

通过上面的例子可以对整个SpringMVC的运行流程有个大致的理解,但是这样并没有比使用原生的Servlet开发方便多少,因为一个Controller只能用写一个方法,所以只作为一个对MVC的理解的例子来看,下面开始才是真实的项目开发使用的方式
还是从创建一个空项目开始,前五步和上面一样,就简单一句话带过了,最后两部不同
1)创建一个空的maven项目并添加依赖
2)给当前项目添加web框架的支持
3)模块设置,输出添加lib目录
4)在WEB-INF下创建jsp文件夹,并在jsp文件夹创建一个jsp文件
5)在web.xml文件中注册并配置DispatcherServlet
6)在Resources文件夹下创建springmvc-servlet.xml文件,注意这里有改变了
因为我们要使用注解来开发,所以配置自动扫描包,开启自动扫描包就默认开启了注解支持,使用注解的方式SpringMVC也会自动帮助我们把试图解析器和适配器注入到容器中,所以这里我们也不需要注册他们两个了,但是为了演示视图解析器我们还继续保留,如果要前后端分离,当我们使用@RestController注解的时候,SpringMVC会帮助我们把返回值都转换成 json格式,那此时视图解析器也不需要保留了

<?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
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--启动自动扫描-->
    <context:component-scan base-package="com.jxw.*"/>
    <!--开启注解-->
    <mvc:annotation-driven/>
    <!--静态资源过滤-->
    <mvc:default-servlet-handler/>
    <!--视图解析器:DispatcherServlet给他的ModelAndView-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
        <!--前缀-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

7)创建HelloController,因为我们使用注解开发,所以不再需要实现Controller接口了

@Controller
public class HelloController {
    //@RequestMapping(path = "/hello" ,method = RequestMethod.GET)
    @GetMapping("/hello")
    public String hello(Model model){
        model.addAttribute("msg","Hello");
        return "hello";
    }
}
  • @Controller 注解标记在一个类上面时表示这个类是一个Controller
  • @RequestMapping 注解的path表示一个路由
    • 当我们只给其传递一个Sring类型的参数时,这个参数代表路由,并且支持get和post方式
    • 当我们指定method参数时,表示这个接口支持指定的方式请求,method参数可以指定多个
    • 当该注解使用在类上面时,相当于给该类的所有方法的路由都加上了前缀
  • @GetMapping 本例子中的 @GetMapping 和 @RequestMapping等价,相当于指定了get请求方式的@RequestMapping,与此同理的还有@PostMapping等
    在这里插入图片描述
  • 当我们想要给视图解析器的model添加值的时候就给这个接口添加参数Model,然后给Model添加信息就可以了,请求的时候不需要给model赋值,也不需要返回这个Model,只需要写在这里然后在方法内赋值,视图解析器就可以获取到,同时本方法的返回值是个String,因为视图解析器还是用的 InternalResourceViewResolver ,所以返回值就是要跳转的view的名称
  • 假如我们这里类使用的是@RestController,那么返回给客户端的将会是一个json,因为这里是直接返回的String,所以该方法会直接把字符串的值返回给客户端

Restful风格的实现

直接上Controller的代码

@RestController
public class HelloController {

    //@RequestMapping(path = "/hello/{id}" ,method = RequestMethod.GET)
    @GetMapping("/hello/{id}")
    public String hello(@PathVariable("id") int id){
    	//调用service,一顿操作
        return "hello";
    }
}
  • 获取路径中的值
    @PathVariable(“id”) 获取路径中 {id} 占位符位置的值
  • 指定请求方式
    可以使用 @RequestMapping,给参数method赋值指定请求方式
    也可以直接使用封装好的@GetMapping和@PostMapping等

获取前端参数

  • 在不使用注解指定参数名称的时候,会从前端传递的参数列表中匹配名称相同的参数
  • 使用@RequestParam可以将指定的前端参数赋值给被注解的参数

下面这个例子就是将前端传递的 uid 参数赋值到 id参数上,假如这里不使用@RequestParam注解,那么将会从前端的参数中寻找名称为 id 的参数赋值给方法的 id 参数

@GetMapping("/hello1")
public String hello1(@RequestParam("uid") int id){
    return "hello";
}

假如前端传递的是一个对象,那么直接使用对象来接收参数就可以,SpringMVC会自动帮助我们匹配能匹配上的参数值
比如下面这个方法,我们访问
http://localhost:8080/springmvc_03_annotation_war_exploded/hello?uid=111&name=jxw&age=3

@GetMapping("/hello")
public String hello1(@RequestParam("uid") int id, User user){
    System.out.println(user);
    System.out.println(id);
    return "hello";
}

运行结果
在这里插入图片描述
从运行结果可以看出,参数值都被正确的匹配上了,和User的属性同名的字段都赋值进去了,id参数也被正确赋值了
那么假如有字段和User中的属性同名了会优先给谁赋值呢?想知道的话还是得先写个方法试一下

@GetMapping("/hello")
public String hello1(int id, User user, String name){
    System.out.println(user);
    System.out.println(id);
    System.out.println(name);
    return "hello";
}

还是传上面的几个参数
在这里插入图片描述
从结果可以看出来同名的都赋值成功了,果然和我的猜想一致

返回给前端参数(非Json方式)

前后端分离的项目就不讨论了,反正返回的是Json,方法返回啥就是啥,这里记录一下没有进行前后端分离的方式,如何返回给前端参数
首先有以下几种类型需要知道

  • Model 最简单的类型,使用addAttribute方法填充数据就完事了
  • ModelMap 该类继承自LinkedHashMap,所以有LinkedHashMap的所有方法可用
  • ModelAndView 可以在存储数据的同时,设置返回的逻辑视图,进行控制展示层的跳转

然后如何返回给前端的

@GetMapping("/hello")
public String hello1(Model model, int id, User user, String name){
    model.addAttribute("msg","Hello " + id);
    return "hello";
}

使用Model时我们只需要在参数中添加一个Model 类型的参数,然后将其赋值,视图解析器就会帮助我们把这个model的数据渲染到view中了
ModelMap也是一样的用法
ModelAndView需要返回值是ModelAndView类型的,不需要将ModelAndView加入到参数中

解决post提交表单中文乱码问题

当我们使用post提交一个表单的时候,如果String类型的字段中有中文,则会出现乱码的问题,这是由于post方式提交的参数在解析的时候编码格式不对造成的,所以我们需要将当前的request的编码格式设置成utf-8

自定义过滤器

1)还是最原始的方式,创建一个过滤器实现Filter接口的doFilter方法,将编码设置为 utf-8

public class EncodingFilter implements Filter {

    public void init(FilterConfig filterConfig) throws ServletException { }
    
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setCharacterEncoding("utf-8");
        chain.doFilter(request,response);
    }

    public void destroy() { }
}

2)将过滤器配置到web.xml中,url-pattern 配置为 /* 拦截所有请求

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>com.jxw.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

使用SpringMVC的过滤器

同样在web.xml中配置,只不过将过滤器换成 SpringMVC 的 CharacterEncodingFilter ,然后构造函数给encoding赋值为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>

返回Json格式

使用Jackson,maven引入jar包

<dependency>
 	<groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.0</version>
</dependency>

直接使用Jsckson的对象(根本不会有人这么写)

因为这只是一个例子,所以根本不会有人这么写
在返回之前使用ObjectMapper的writeValueAsString方法转换一个对象为json字符串

  • @ResponseBody 注解表示该方法直接返回值就返回给前端,不走视图解析器,这个例子就是直接把字符串返回给前端
  • produces = “application/json;charset=utf-8” ,@RequestMapping系列注解的 produces 参数设置返回格式并且指定字符编码,要不然前端解析会乱码
@GetMapping(value = "/hello/json",produces = "application/json;charset=utf-8")
@ResponseBody
public String helloJson() throws JsonProcessingException {
    User user = new User("纪喜文",3);
    ObjectMapper mapper = new ObjectMapper();
    return mapper.writeValueAsString(user);
}

使用SpringMVC的message-converters(正确方式)

在springmvc-servlet.xml配置文件中修改<mvc:annotation-driven>配置

  • 配置message-converters,我理解的是返回信息转换器,register-defaults不写也行,因为默认就是true
  • 设置StringHttpMessageConverter的编码为UTF-8
  • 把MappingJackson2HttpMessageConverter注册进去,让使用了@RestController的类的所有方法或使用了@ResponseBody注解的方法返回一个pojo对象的时候有转换器可用,不注册这个的话直接返回pojo对象会报错
<mvc:annotation-driven>
   <!--处理Json乱码问题-->
   <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <constructor-arg value="UTF-8"/>
        </bean>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                    <property name="failOnEmptyBeans" value="false"/>
                </bean>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

配置好这些后在方法上添加@ResponseBody注解就可以返回json了,如果前后端分离或者整个类都是json格式返回的接口,那么直接在类上使用@RestController注解就不用在方法上一个个的写@ResponseBody了

时间类型处理

先看一下代码

@GetMapping(value = "/hello/time")
@ResponseBody
public Date getTime(){
    Date date  = new Date();
    return date;
}

如果直接使用这种方式返回时间类型,那么Jackson会给我们将Date转换成时间戳类型,返回给前端的将会是一串数字,那是没法读的
如果使用下面这种方法,将Date类型进行格式化处理,返回字符串,肯定是不合理的,不能每个方法都这么写吧,要是复杂类型的话那处理起来人都累死了

@GetMapping(value = "/hello/time1")
@ResponseBody
public String getTime1(){
    Date date  = new Date();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    return sdf.format(date);
}

正确的解决办法

  • 使用注解:
    实际工作中我们几乎都是返回一个对象或者集合,Jacksonn给我们提供了一个注解
    在对象的时间Date类型的字段上使用@JsonFormat指定该字段序列化成Json时的格式化类型
    pattern 指定格式,timezone 指定时区
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String name;
    private int age;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date birthday;
}
  • SpringMVC配置
    设置MappingJackson2HttpMessageConverter的objectMapper
<mvc:annotation-driven>
    <!--使返回值序列化成Json,并处理Json乱码问题-->
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <constructor-arg value="UTF-8"/>
        </bean>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                    <property name="failOnEmptyBeans" value="false"/>
                    <!--设置日期格式化类型-->
                    <property name="DateFormat">
                        <bean class="java.text.SimpleDateFormat">
                            <constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss"/>
                        </bean>
                    </property>
                    
                </bean>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

也可以这样写

<mvc:annotation-driven>
    <!--使返回值序列化成Json,并处理Json乱码问题-->
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <constructor-arg value="UTF-8"/>
        </bean>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                    <property name="failOnEmptyBeans" value="false"/>
                    <!--设置日期格式化类型-->
                    <property name="simpleDateFormat" value="yyyy-MM-dd HH:mm:ss"/>
                </bean>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

这两种配置很明显后面的更简单一点,前面可配置的更多一点,一般使用后面的就够用了
看源码发现后面的也是在方法里面创建了一个SimpleDateFormat
在这里插入图片描述
注解的优先级比配置要高,所以实际工作中优先使用配置,有其他个性化需求添加注解就可以了

拦截器

拦截器与过滤器的区别: 拦截器是AOP思想的具体应用
  过滤器

  • 过滤器是servlet规范的一部分,任何javaWeb工程都可以使用
  • 在url-pattern中配置了 /* ,可以对所有要请求的资源进行过滤

  拦截器

  • 拦截器是SpringMVC自己的,只有使用了SpringMVC框架的工程才能用
  • 拦截器只会拦截访问的控制器方法,如果访问的是jsp/html/css/js/image是不会进行拦截的

先设置session超时时间

在web.xml假如如下配置

 <!--设置session超时时间-->
<session-config>
    <session-timeout>15</session-timeout>
</session-config>

设置session超时时间为15分钟,这里的单位是15分钟

自定义拦截器

想要自定义拦截器,必须实现 HandlerInterceptor 接口,该接口有三个方法可以实现

  • preHandle()
    该方法会在控制器方法前执行,其返回值表示是否中断后续操作。当其返回值为true时,表示继续向下执行;
    当其返回值为false时,会中断后续的所有操作(包括调用下一个拦截器和控制器类中的方法执行等)。在该方法内可以进行权限验证,跳转登录页等操作。
  • postHandle()
    该方法会在控制器方法调用之后,且解析视图之前执行。可以通过此方法对请求域中的模型和视图做出进一步的修改。
  • afterCompletion()
    该方法会在整个请求完成,即视图渲染结束之后执行。可以通过此方法实现一些资源清理、记录日志信息等工作。
  • 可以配置多个拦截器成一个拦截器链,拦截器的顺序会按照配置的先后顺序来决定

写一个最简单的登陆验证的例子

先把controller写好,UserController提供登陆登出等接口

@Controller
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/login")
    public String login(User user, HttpSession session){
        session.setAttribute("userName",user.getName());
        return "home";
    }

    @RequestMapping("goLogin")
    public String goLogin(){
        return "login";
    }

    @RequestMapping("logOut")
    public String logOut(String userName, HttpSession session){
        session.removeAttribute("userName");
        return "login";
    }
}

HomeController 就一个跳转首页的接口

@Controller
@RequestMapping("/home")
public class HomeController {

    @RequestMapping("")
    public String home(){
        return "home";
    }
}

实现拦截器:继承并实现 HandlerInterceptor 接口的 preHandle() 方法,判断当前用户是否登陆,没有登陆就直接跳转到登录页

public class MyInterceptor implements HandlerInterceptor {

    //该方法返回true表示放行
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    	//如果当前session中有用户信息就放行
        if(request.getSession().getAttribute("userName") != null){
            return true;
        }
        //如果当前session中没有用户信息就跳转到登陆页
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
        return false;
    }

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

    }

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

    }
}

配置拦截器,因为登陆注册等功能不需要走拦截器,所以把user下面的链接都放出来,否则登录不上

<!--配置拦截器-->
<mvc:interceptors>
    <!--拦截器1-->
    <mvc:interceptor>
        <!--配置拦截器的作用路径-->
        <mvc:mapping path="/**"/>
        <!--不拦截的路径-->
        <mvc:exclude-mapping path="/user/**"/>
        <!--定义在<mvc:interceptor>下面的表示匹配指定路径的请求才进行拦截-->
        <bean class="com.jxw.interceptor.MyInterceptor"/>
    </mvc:interceptor>
    <!--拦截器2,这个拦截器是空的,啥也没干就是演示拦截器链-->
    <mvc:interceptor>
        <mvc:mapping path="/hello"/>
        <bean class="com.jxw.interceptor.MyInterceptor2"/>
    </mvc:interceptor>
</mvc:interceptors>

在index.jsp中写一个跳转到首页的链接,因为不走拦截器的路径只有user下面的链接,所以没登录的话home肯定会被跳转到登录页

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title></title>
  </head>
  <body>
  <form action="${pageContext.request.contextPath}/hello" method="post">
    name:<input type="text" name="name"/><br/>
    id:<input type="text" name="uid"/><br/>
    age:<input type="text" name="age"/><br/>
    <input type="submit" />
  </form>
  <a href="${pageContext.request.contextPath}/home">首页</a>
  </body>
</html>

然后再创建login.jsp和home.jsp
在这里插入图片描述
home.jsp 就展示已经登陆的用户名,还有登出功能

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
	<head>
	    <title>Title</title>
	</head>
	<body>
		${pageContext.session.getAttribute("userName")}
		<a href="${pageContext.request.contextPath}/user/logOut">退出登陆</a>
	</body>
</html>

login.jsp就是提交登录的表单

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>登陆页</title>
    </head>
    <body>
        <form action="${pageContext.request.contextPath}/user/login" method="post">
            <input name="name" type="text" />
            <input name="password" type="password" />
            <input type="submit" />
        </form>
    </body>
</html>

一个简单的登陆验证的例子就做好了,当然这个只是最简单的例子,连数据库都没有访问,真实的生产环境肯定要复杂的多,比如分布式的系统还要处理session共享、跨域访问、单点登录等问题

文件上传和下载

pom文件引入依赖

 <!--文件上传包-->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
</dependency>
<!--高版本servlet-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
</dependency>

springmvc-servlet.xml 配置

<!--文件上传配置,id是固定的不能改-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!--请求的编码格式,必须和JSP的pageEncoding属性一致,以便正确读取表单的内容,默认为IOS-8859-1-->
    <property name="defaultEncoding" value="utf-8"/>
    <!--上传文件大小限制,单位为字节(10485760 = 10M)-->
    <property name="maxUploadSize" value="10485760"/>
    <!--最大内存缓存阈值,低于此值,保留在内存里,超过此值,生成硬盘上的临时文件-->
    <property name="maxInMemorySize" value="40960"/>
</bean>

上传方法

SpringMVC帮助我们做了很多事,而且通过调用 CommonsMultipartFile 的 transferTo 方法可以很方便的将文件保存起来

@RequestMapping("upload")
public String upload(@RequestParam("file")CommonsMultipartFile file, HttpServletRequest request) throws IOException {
    //上传路径保存设置,一般都应该在配置文件中
    String path = request.getServletContext().getRealPath("/upload");
    File realPath = new File(path);
    if(!realPath.exists()){
        realPath.mkdir();
    }
    System.out.println("上传文件保存地址" + realPath);
    //保存文件
    file.transferTo(new File(realPath + "/" + file.getOriginalFilename()));
    return "home";
}

下载方法

这是一个简单的示例,一般静态资源,比如例子中的图片,都会不用这种方式下载的,这对服务器的资源是极大的浪费
举个真实场景的例子,比如下载一张报表,传递进来的肯定是报表ID等信息,从session中获取用户信息,然后获取用户权限,然后根据报表的元数据拼出查询语句的sql,再将一些用户自定义的设置以及需要实时计算的信息填充进去,生成一个报表文件再返回到响应体
或者是PaaS平台的元数据的下载,肯定是要查询数据库的最新数据的,这种也需要使用下载的controller

@RequestMapping("download")
public String download(HttpServletRequest request, HttpServletResponse response) throws IOException {
    //要下载图片的地址
    String path = request.getServletContext().getRealPath("/upload");
    String fileName = "未命名文件.png";

    //1.设置 response 响应头
    response.reset();//设置页面不缓存,清空buffer
    response.setCharacterEncoding("UTF-8");//字符编码
    response.setContentType("multipart/form-data");//二进制传输数据

    File file = new File(path,fileName);
    //设置下载的文件名
    response.addHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(file.getName(), "UTF-8"));
    //2.读取文件---输入流
    InputStream input = new FileInputStream(file);
    //3.写出文件---输出流
    OutputStream out = response.getOutputStream();

    byte[] buff = new byte[1024];
    int index = 0;
    //4.执行写出操作
    while ((index=input.read(buff))!=-1){
        out.write(buff, 0, index);
        out.flush();
    }
    //关闭流
    out.close();
    input.close();
    return null;
}

下载的文件名全是下划线处理方式

URLEncoder.encode(file.getName(), "UTF-8")

一般出现这种问题都是文件名的编码问题,将文件名的编码修改为UTF-8

上传和下载的jsp代码

<form action="${pageContext.request.contextPath}/file/upload" enctype="multipart/form-data" method="post">
    <input type="file" name="file">
    <input type="submit" value="upload">
</form>
<a href="${pageContext.request.contextPath}/file/download">下载文件</a>

SpringMVC的基本到这里就完事了,下一篇写SSM整合

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值