系列二来了…这次是SpringMVC
写此系列的目的是做一个学习笔记管理,方便以后复习用。
Spring集成web环境
原理:
- 添加坐标 servlet和jsp
- 添加web层servlet,在web.xml配置servlet
- web通过spring容器获取Service对象—–在web项目中通过ServletContextListener监听Web应用的活动,在web应用启动时,加载spring配置文件,创建上下文对象ApplicationContext,将其存储到最大的与servletContext域中,这样就可以在任意位置从域中获取ApplicationContext对象了。(web.xml配置监听器)
步骤
配置ContextLoaderListener监听器
1
2
3
4
5
6
7
8
9<!--全局化初始参数-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--配置监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>使用WebapplicationcontextUtils获取应用上下文
1
2
3
4ServletContext servletContext = this.getServletContext();
ApplicationContext app = (ApplicationContext) WebApplicationContextUtils.getWebApplicationContext(servletContext);
UserService userService = app.getBean(UserService.class);
userService.save();
快速入门
导入坐标
1
2
3
4
5
6<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
<!--<scope>provided</scope>疑问导入spring-web包时加了范围会出错-->
</dependency>配置核心控制器DispatcherServlet web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<!--配置springmvc的前端控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--加载servlet时加载配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>创建Controller类和视图页面,注入Spring容器
1
2
3
4
5
6
7
8
public class UserController {
"/quick")//访问路径 (
public String save(){
System.out.println("controller running");
return "success.jsp";
}
}使用注解配置Controller类中业务方法的映射地址
配置SpringMVC核心文件spring-mvc.xml
配置扫描组件,和context空间
1
2
3
4
5
6
7<!--Controller组件扫描-需要加载context空间-->
<context:component-scan base-package="com.tsuki.controller"/>
<!--配置组件扫描,小tip,可以设置扫或者不扫-->
<context:component-scan base-package="com.tsuki">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>客户端发起请求测试
访问路径”/quick”
相关组件
DispathcherServlet; HandlerMapping; HandlerAdapter; Handler; View Resolver; View;
注解解析
- @RequestMapping: URL与处理请求的方法之间建立联系
参数: value,method(RequestMethod.POST), params
视图解析器配置: 有默认配置—-可以指定功能进行增强–配置内部资源视图解析器 redirect和forward
1
2
3
4
5<!--配置内部资源视图解析器,但是这样做不好阅读啊(小疑问),而且做了代码内部修改-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
SpringMVC的请求和响应
数据响应的方式
1. 页面跳转
返回字符串–参见快速入门, prefix + String + suffix 代表页面跳转
返回ModelAndView
save()参数还可以是HttpServletRequest等官方的定义,但是不建议使用,model和view可以拆开使用也可以一起使用,可以new也可以作为形参传入。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26"/quick4") (
public String save4(Model model){
model.addAttribute("username", "lisi");
return "success.jsp";
}
"/quick3") (
public ModelAndView save3(ModelAndView modelAndView){
modelAndView.setViewName("success.jsp");
modelAndView.addObject("username", "zhangsan");
return modelAndView;
}
"/quick2") (
public ModelAndView save2(){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("success.jsp");
modelAndView.addObject("username", "helloworld");
return modelAndView;
}
"/quick") (
public String save(){
System.out.println("controller running");
return "success.jsp";
}
2. 回写数据
直接返回字符串 @ResponseBody
1
2
3
4
5
6
7
8
9
10"/quick6") (
public String save6() throws JsonProcessingException {
User user = new User();
user.setUsername("henry");
user.setAge(30);
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(user);//jackson进行转换
return json;
}
返回对象和集合–设置将对象转换为json对象输出(配置处理器映射器)—可以通过mvc的注解替代以上操作
spring-mvc.xml, 同时要加入springmvc的命名空间,就可以不需要json转换工具了
1
2
3
4
5xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation=" http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"
<!--mvc的注解驱动-->
<mvc:annotation-driven/>
数据请求
1. 实现数据请求方式
基本数据类型参数
POJO类型参数
数组类型参数
1
2
3
4
5
6//localhost/Springdemo/quick8?strs='111'&strs='222'
"/quick8") (
public void save8(String[] strs){
System.out.println(Arrays.asList(strs));
}以上三种都比较简单,只要名称一致框架就会自动封装
集合类型参数
将集合包装到一个POJO中
1
2
3
4
5
6
7
8
9
10
11
12public class VO {
private List<User> userList;
//提供get 和set
}
//VO作为形式参数
"/quick10") (
public void save10(VO vo){
System.out.println(vo);
}
//此处要求VO中的属性名称和表单提交的名称一致
<input type="text" name="userList[0].username"><br/>
ajax请求时指定contextType为json,使用
@RequestBody
访问jQuery失败:
1
2<!--开发资源的访问 spring-mvc.xml, 开放静态资源的访问-->
<mvc:default-servlet-handler/>1
2
3
4
5
6
7
8
9
10var userList = new Array();
userList.push({username:"zhangsan", age:12});
userList.push({username: "lisi", age: 15});
$.ajax({
type:"POST",
url:"${pageContext.request.contextPath}/quick9",
data:JSON.stringify(userList),//将数组转为json数据
contentType: "application/json; charset=utf-8"
});1
2
3
4
5"/quick9") (
public void save9(@RequestBody List<User> userList){
System.out.println(userList);
}
2. 获取数据细节
中文乱码问题
@RequestParam (value, required, defaultValue)和 @PathVariable
1
2
3
4
5
6
7
8
9
10
11
12//localhost/quick11?name=zhangsan
"/quick11") (
public void save11(@RequestParam("name") String username){
System.out.println(username);
}
//localhost/quick11?username=zhangsan
"/quick12/{username}") (
public void save12(@PathVariable("username") String username){
System.out.println(username);
}自定义类型转换器–
- 定义com.tsuki.converter.DateConverter实现Converter接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23package com.tsuki.converter;
import org.springframework.core.convert.converter.Converter;//注意不要导错包
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateConverter implements Converter<String, Date> {
public Date convert(String dateStr) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date date=null;
try {
date=format.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}- 在配置文件中声明转换器,在
<annotation-driven>
中引用转换器。spring-mvc.xml
1
2
3
4
5
6
7
8
9
10
11
12<!--mvc的注解驱动-->
<mvc:annotation-driven conversion-service="conversionService"/>
<!--开发资源的访问-->
<mvc:default-servlet-handler/>
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.tsuki.converter.DateConverter"></bean>
</list>
</property>
</bean>获取Servlet相关API:直接形参注入
@RequestHeader和@CookieValue
1
2
3
4
5
6
7
8
9
10
11"/quick14") (
public void save14(@CookieValue(value = "JSESSIONID", required = false) String user_agent){
System.out.println(user_agent);
}
"/quick13") (
public void save13(@RequestHeader(value = "User-Agent", required = false) String user_agent){
System.out.println(user_agent);
}文件上传
表单编写
1
2
3
4
5
6<%--注意enctype的填写--%>
<form action="${pageContext.request.contextPath}/quick15" method="post" enctype="multipart/form-data">
名称<input type="text" name="username"><br/>
文件<input type="file" name="uploadFile"><br/>
<input type="submit" value="提交">
</form>
pom.xml导入坐标
1
2
3
4
5
6
7
8
9
10<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>配置文件上传解析器 spring-mvc.xml
1
2
3
4
5<!--配置文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"/>
<property name="maxUploadSize" value="500000"/>
</bean>编写文件上传代码
1
2
3
4
5
6
7
8
9"/quick15") (
public void save15(String username, MultipartFile uploadFile) throws IOException {
//uploadFile形参名字与表单的提交文件name名称一致
System.out.println(username);
String originalFilename = uploadFile.getOriginalFilename();
//保存到某位置
uploadFile.transferTo(new File("F:\\图片\\"+originalFilename));
}
拦截器
快速入门
创建拦截器实现HandlerInterceptor接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26public class MyInterceptor1 implements HandlerInterceptor {
//目标方法执行之前 执行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle running...");
String param = request.getParameter("param");
if("yes".equals(param)){
return true;
}else{
request.getRequestDispatcher("success.jsp").forward(request,response);
return false;
}
}
//目标方法执行之后,视图对象返回之前执行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
modelAndView.addObject("name", "Java");
System.out.println("postHandle running...");
}
//在流程都执行完毕后执行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("aftercomletion running...");
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}配置拦截器 spring-mvc.xml
1
2
3
4
5
6
7
8<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--对那些资源进行拦截-->
<mvc:mapping path="/**"/>
<bean class="com.tsuki.interceptor.MyInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>
测试
1
2
3
4
5
6
7
8
9
10
11
public class TargetController {
"/target") (
public ModelAndView show(){
System.out.println("target is running...");
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("name", "Tomcat");
modelAndView.setViewName("index.jsp");
return modelAndView;
}
}
登录验证案例(待实践)
过滤器:
- 获取session对象 request.getSession().getAttribute(“user”)
- 判断是否有user,否(return false) 是 (return true)
登录:
- login(username, password){} 调用userService.login():返回User(查表并用jdbcTemplate接收)
- 判断user是不是空,有(存到Session,并跳转首页) 否(跳转到登录页面)
异常处理
配合日志输出使用
1 | <!--配置异常处理器--> |
自定义异常处理
1. 创建异常处理器类实现HandlerExceptionResolver
2. 配置异常处理器到spring-mvc.xml
3. 编写异常页面
JdbcTemplate(待补充)
补充
SimpleDateFormat中有两个方法,parse()和format()
parse()把String型的字符串转换成特定格式的date类型
format()把Date型的字符串转换成特定格式的String类型
equals:参考博客
public boolean equals(Object obj) { return (this == obj); }
运行问题
- 报错1:Servlet不能正常访问,错误代码500,原因在于版本,我原本用的是tomcat10,改成tomcat9之后成功解决。论版本兼容的重要性。tomcat各版本的兼容
- 创建web项目还是不熟练。教程
- 本来Servlet要注入到web.xml中才会运行的,但是不知道什么原因不需注入也可以访问到。
- SpringMVC错误:org.springframework.web.servlet.DispatcherServlet noHandlerFound ,我的错误原因是访问静态资源,改为访问jsp就成功了
- 在导入了jackson的坐标后发生了新的错误:java.lang.NoClassDefFoundError: com/fasterxml/jackson/databind/exc/InvalidDefinitionException ,找到问题了,版本不兼容,致命
- 在测试jsp页面发出post请求,用$.ajax响应集合类型参数是出现找不到jQuery和访问路径/quick的情况,原因是路径问题。
url:"${pageContext.request.contextPath}/quick9",
是jsp获取绝对路径的方式,这篇博客解析 - SpringMVC中已经配置了解决中文乱码问题的过滤器后,还会出现中文乱码现象,尝试了博客所述的方法但是还未解决乱码问题,但是使用maven发布时不会乱码