文章目录
一、Spring MVC设计概述及其框架结构
1.Spring MVC介绍
Spring框架的一个分支 利用容器简化配置。
2.Spring MVC优点
- 清晰的角色划分:前端控制器、处理器映射器、处理器适配器、视图解析器、处理器或页面控制器、验证器等
- 分工明确, 易扩展
- 命令对象直接作为业务对象
- 与spring其他框架无缝集成
- 可适配,通过处理器适配器可以支持任意的类作为处理器
- 可定制性,自定义->处理器映射器、视图解析器等
- 功能强大的数据验证、格式化和绑定机制
- web层单元测试
- 较简单的国际化和主题切换
- 强大的JSP标签库,使JSP编写更容易
3.Spirng MVC工作原理
1. DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。
请求的url为 : http://localhost:8080/SpringMVC/hello
拆分为三部分:
-1 http://localhost:8080服务器域名
-2 SpringMVC部署在服务器上的web站点
-3 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。
-
DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。
-
视图解析器将解析的逻辑视图名传给DispatcherServlet。
-
DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。
-
最终视图呈现给用户。
二、SpringMVC三大核心—处理器映射器–处理器映适配器-视图解析器—及web.xml中处理器控制器
1.maven依赖
<dependencies>
<!--数据库连接-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<!-- mybatis连接-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!-- mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!-- spring-webmvc5.3.3依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.3</version>
</dependency>
<!-- spring jdbc操作 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<!-- 织入事态代理 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<!-- 日志文件-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- jsp文件-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<!--jstl标签库依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- servlet4.0-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- junit测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<!-- jackson json转换包-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.2</version>
</dependency>
<!-- 阿里 FastJSON -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
</dependencies>
<!-- 资源坐标包位置-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
2.初学原理无注解SpringMVC配置
//controller控制层类
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv = new ModelAndView();
//业务代码
String result = "HelloSpringMVC";
mv.addObject("msg",result);
//视图跳转
mv.setViewName("test");
return mv;
}
}
<!-- web.xml 配置 -->
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!-- 注册DispatcherServlet处理器 拦截访问的url-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- DispatcherServlet 要绑定一个springmvc配置文件 -->
<!-- 关联一个springmvc的配置文件:【servlet-name】-servlet.xml -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!-- 启动级别1 tomcat服务器启动就会启动-->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 请求配置-->
<!-- / 匹配所有的请求: (不匹配.jsp页面) -->
<!-- /* 匹配所有的请求: (包括.jsp页面) 视图解析器会拼接 前缀和后缀 变成死循环 所以一般不用-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
<!-- SpringMVC-xml -->
<?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.xsd">
<!-- springmvc 核心三要素 !!!-->
<!-- 1添加 处理器映射器-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- 2添加 处理器适配器 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!-- 3添加 视图解析器:DispatcherServlet给她的ModelAndView 版本引擎 ThymeLeof、 Freemarker... -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/"/> <!-- 注意 jsp后面要写/ -->
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
<!-- BeanNameUrlHandlerMapping: bean 根据bean的名字来找 /hello路径名-->
<bean id="/hello" class="com.xqh.controller.HelloController"/>
</beans>
测试本地访问:localhost:8080/hello
3.注解配置快速搭建SpringMVC
<!-- web.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!-- 注册DispatcherServlet处理器控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- DispatcherServlet 要绑定一个springmvc配置文件 -->
<!-- 关联一个springmvc的配置文件:【servlet-name】-servlet.xml -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!-- 启动级别1 tomcat服务器启动就会启动此控制器-->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 请求配置-->
<!-- / 匹配所有的请求: (不匹配.jsp页面) -->
<!-- /* 匹配所有的请求: (包括.jsp页面) 视图解析器会拼接 前缀和后缀 变成死循环-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
<!-- SpringMVC-xml -->
<?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.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">
<!-- 1.自动扫描包, 让指定包下的注解生效,由IOC容器统一管理 -->
<context:component-scan base-package="com.xqh.controller"/>
<!-- 2.注解驱动
支持mvc注解驱动
在spring中一般采用@RequestMapping注解来完成映射关系
要想使用@RequestMapping注解生效
必须向上下文中 注册DefaultAnnotationHandlerMapping 处理器映射器
和一个 AnnotationMethodHandlerAdapter 处理器适配器实例
这两个实例分别在类级别和方法级别处理。
而annotation-driven配置帮助我们自动完成上述两个实例的注入。
annotation-driven简化 1,2部
-->
<mvc:annotation-driven/>
<!-- 3添加 视图解析器:DispatcherServlet给她的ModelAndView 版本引擎 ThymeLeof、 Freemarker... -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/"/> <!-- 注意 jsp后面要写/ -->
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
//java controller层 控制层类
package com.xqh.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
//注解配置bean id @Controller在spring.xml中使用 用于声明Spring类的实例是一个控制器 代表会被Spring类接管
//如果所有的方法 返回值是string,并且有具体页面可以跳转,那么就会被视图解析器解析;
@Controller
//@RequestMapping("/hello") 父子级关系-- url访问时 /hello/h1 -- 不配置父子及则 /h1访问
public class HelloController {
@RequestMapping("/h1")
public String hello(Model model){
//封装数据 Model model
model.addAttribute("msg","Hello SpringMVC");
return "hello";//会被视图解析器处理 拼接为 /WEB-INF/jsp/hello.jsp
}
}
测试本地访问:localhost:8080/h1
三、Spring MVC数据绑定
!web.xml–
!springmvc-servlet.xml引用上文注解 的两个 xml
<!-- jsp 页面 -->
<form action="/user/t2" method="post">
编号:<input type="text" name="id" />
用户:<input type="text" name="name" />
年龄:<input type="text" name="age" />
<input type="submit" value="提交">
</form>
//java 工具类
/*
使用 FastJson可以不用写工具类
*/
public class JsonUtils {
public static String getJson(Object object){
return getJson(object,"yyyy-MM-dd HH:mm:ss");
}
public static String getJson(Object object,String dateFormat){
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRAP_EXCEPTIONS,false);
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
mapper.setDateFormat(sdf);
try {
return mapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
}
//java controller控制层 底层是servlet
package com.xqh.controller;
import 。。。。。。;
//@RestController 不走视图解析器 直接返回字符串
@Controller //走视图解析器
@RequestMapping("/user")
public class UserController {
/*
1. @RequestParam 接收前端数据 name 和 age
@RequestParam 将请求参数绑定到你控制器的方法参数上
@RequestParam(value=”参数名”,required=”true/false”,defaultValue=””)
value:参数名
required:是否包含该参数,默认为true,表示该请求路径中必须包含该参数,如果不包含就报错。
defaultValue:默认参数值,如果设置了该值,required=true将失效,自动为false,如果没有传该参数,就使用默认值
*/
@GetMapping("/t1")
public String test1(@RequestParam("name") String name, @RequestParam("age") String age, Model model){
//1. 接受参数
System.out.println("就收到前端的参数为:"+name+age);
//2. 将返回的结果传给前端, Model
model.addAttribute("msg",name+age);
//3. 视图跳转
return "hello";
}
/*
接收前端对象数数据 直接引用实体类 (前端的name属性值 和实体类中的属性值 必须一样 才能自动识别!)
*/
@PostMapping("/t2")
public String test2(User user,Model model){
System.out.println("就收到前端的参数为:"+user);
if(user.getName().equals("xqh") && user.getAge().equals("18")){
model.addAttribute("msg",user);
return "redirect:/user/select";
}else{
model.addAttribute("erro","用户或年龄输入错误!!!");
return "forward:/index.jsp";
}
/*
json转换
produces = "application/json;charset=utf-8"设置json编码格式 也可在spring中 统一配置过滤器优化代码
*/
@RequestMapping(value = "/j1",produces = "application/json;charset=utf-8")
@ResponseBody//配合@Controller 使用的(表示此方法不走 图层解析器 不会自动拼接 jsp) 若类的注解为@RestController则可以不用
public String json() throws JsonProcessingException {
//jackson. ObjectMapper
ObjectMapper mapper = new ObjectMapper();
User user = new User("1","虚千寒","18");
String str = mapper.writeValueAsString(user);//抛出异常 转换为json数据格式{"id":"1","name":"虚千寒","age":"18"}
return str;
// return user.toString();
}
/*
json转换 集合接收及输出
*/
@RequestMapping(value = "/j2")
@ResponseBody
public String json2() throws JsonProcessingException {
ArrayList<User> list = new ArrayList<>();
User user = new User("1","虚千寒","18");
User user1 = new User("1","虚千寒","18");
User user2 = new User("1","虚千寒","18");
User user3 = new User("1","虚千寒","18");
list.add(user);
list.add(user1);
list.add(user2);
list.add(user3);
return new ObjectMapper().writeValueAsString(list);
// return user.toString();
}
/*
json转换 工具类实现
*/
@RequestMapping(value = "/j3")
@ResponseBody//配合@Controller 使用的 若类的注解为@RestController则可以不用
public String json3() throws JsonProcessingException {
Date date = new Date();
return JsonUtils.getJson(date,"yyyy-MM-dd HH:mm:ss");
}
/*
json转换 工具类实现
*/
@RequestMapping(value = "/j4")
@ResponseBody//配合@Controller 使用的 若类的注解为@RestController则可以不用
public String json4() throws JsonProcessingException {
ArrayList<User> list = new ArrayList<>();
User user = new User("1","虚千寒","18");
User user1 = new User("1","虚千寒","18");
User user2 = new User("1","虚千寒","18");
User user3 = new User("1","虚千寒","18");
list.add(user);
list.add(user1);
list.add(user2);
list.add(user3);
return JsonUtils.getJson(list);
}
/*
FastJson json数据转换器 不用写工具类
*/
@RequestMapping("/fj1")
@ResponseBody
public String fastJson2(){
Date date = new Date();
return JSON.toJSONStringWithDateFormat(date,"yyyy-MM-dd HH:mm:ss"); //可不用工具类
}
/*
FastJson json数据转换器 不用写工具类
*/
@RequestMapping("/fj2")
@ResponseBody
public String fastJson1(){
ArrayList<User> list = new ArrayList<>();
User user = new User("1","虚千寒","18");
User user1 = new User("1","虚千寒","18");
User user2 = new User("1","虚千寒","18");
User user3 = new User("1","虚千寒","18");
list.add(user);
list.add(user1);
list.add(user2);
list.add(user3);
return JSON.toJSONString(list); //可不用工具类
}
}
四、所遇的问题
1.访问页面404
- 看一下控制台输出,是否少jar包
- 重启Tomcat
2.servlet传值到jsp页面乱码
-
不能在页面设置 response/ request.setCharacterEncoding(“utf-8”) 试过没用的 !
-
可以在controller类中方法上加一个produces属性 设置@RequestMapping(value = “自定义路径”,produces = “application/json;charset=utf-8”) 但每个方法都设置的话太繁琐了
-
修改Tomcat配置文件 conf>server.xml
-
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
-
在web.xml设置springMVC自带的编码过滤器 对post友好 但极限部分对get不友好
-
<!-- web.xml 中spring 自带的 过滤器fileter设置编码 一般解决大部分编码问题 --> <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>
-
如果springmvc 自带的过滤都没用的话 我直接用的大神自制的编码过滤器 (无解了~.)
-
1.创建自定义过滤的java类
-
package com.xqh.controller; import javax.servlet.*; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import java.io.UnsupportedEncodingException; import java.util.Map; /* 解决顽强的乱码问题 */ public class EncodingFilter implements Filter { @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //处理response的字符编码 HttpServletResponse myResponse=(HttpServletResponse) response; myResponse.setContentType("text/html;charset=UTF-8"); // 转型为与协议相关对象 HttpServletRequest httpServletRequest = (HttpServletRequest) request; // 对request包装增强 HttpServletRequest myrequest = new MyRequest(httpServletRequest); chain.doFilter(myrequest, response); } @Override public void init(FilterConfig filterConfig) throws ServletException { } } //自定义request对象,HttpServletRequest的包装类 class MyRequest extends HttpServletRequestWrapper { private HttpServletRequest request; //是否编码的标记 private boolean hasEncode; //定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰 public MyRequest(HttpServletRequest request) { super(request);// super必须写 this.request = request; } // 对需要增强方法 进行覆盖 @Override public Map getParameterMap() { // 先获得请求方式 String method = request.getMethod(); if (method.equalsIgnoreCase("post")) { // post请求 try { // 处理post乱码 request.setCharacterEncoding("utf-8"); return request.getParameterMap(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } else if (method.equalsIgnoreCase("get")) { // get请求 Map<String, String[]> parameterMap = request.getParameterMap(); if (!hasEncode) { // 确保get手动编码逻辑只运行一次 for (String parameterName : parameterMap.keySet()) { String[] values = parameterMap.get(parameterName); if (values != null) { for (int i = 0; i < values.length; i++) { try { // 处理get乱码 values[i] = new String(values[i] .getBytes("ISO-8859-1"), "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } } hasEncode = true; } return parameterMap; } return super.getParameterMap(); } //取一个值 @Override public String getParameter(String name) { Map<String, String[]> parameterMap = getParameterMap(); String[] values = parameterMap.get(name); if (values == null) { return null; } return values[0]; // 取回参数的第一个值 } //取所有值 @Override public String[] getParameterValues(String name) { Map<String, String[]> parameterMap = getParameterMap(); String[] values = parameterMap.get(name); return values; } }
-
2.在web.xml中配置此过滤器
-
<!-- web.xml中 --> <filter> <filter-name>encoding</filter-name> <filter-class>com.xqh.controller.EncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
3.JSON数据在页面乱码
-
在Springmvc-serlvet.xml中配置json的过滤器
-
<!-- JSON乱码问题配置 --> <mvc:annotation-driven> <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>