文章目录
- 第一个SpringMVC项目,帮助理解原理
- 创建maven项目
- 将项目添加web框架的支持
- 在web.xml中注册DispatcherServlet
- 创建并配置springmvc-servlet.xml文件
- 编写controller
- 编写ModelAndView要跳转的页面
- 配置Tomcat
- 运行
- 访问hello页面
- 解决遇到的404,lib目录未添加
- 使用注解开发Controller
- Restful风格的实现
- 获取前端参数
- 返回给前端参数(非Json方式)
- 解决post提交表单中文乱码问题
- 自定义过滤器
- 使用SpringMVC的过滤器
- 返回Json格式
- 直接使用Jsckson的对象(根本不会有人这么写)
- 使用SpringMVC的message-converters(正确方式)
- 时间类型处理
- 拦截器
- 先设置session超时时间
- 自定义拦截器
- 写一个最简单的登陆验证的例子
- 文件上传和下载
- 上传方法
- 下载方法
- 下载的文件名全是下划线处理方式
- 上传和下载的jsp代码
其实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整合