0%

Spring MVC

Spring MVC入门

1、Spring MVC 执行流程

(1)用户通过客户端向服务器发送请求,请求会被 Spring MVC 的前端控制器 DispatcherServlet 所拦截。

(2)DispatcherServlet 拦截到请求后,会调用 HandlerMapping 处理器映射器。

(3)处理器映射器根据请求的 URL 找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet。

(4)DispatcherServlet 会通过返回信息选择合适的 HandlerAdapter(处理器适配器)。

(5)HandlerAdapter 会调用并执行 Handler(处理器),这里的处理器指的就是程序中编写的 Controller 类,也被称为后端处理器。

(6)Controller 执行完成后,会返回一个 ModelAndView 对象,该对象中会包含视图名或包含模型和视图名。

(7)HandlerAdapter 将 ModelAndView 对象返回给 DispatcherServlet。

(8)DispatcherServlet 会根据 ModelAndView 对象选择一个合适的 ViewResolver(视图解析器)。

(9)ViewResolver 解析后,会向 DispatcherServlet 中返回具体的 View(视图)。

(10)DispatcherServlet 对 View 进行渲染(即将模型数据填充至视图中)。

(11)视图渲染结果会返回给客户端浏览器显示。

2、第一个 Spring MVC 程序

导入依赖

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
27
28
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.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>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>

web.xml

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
<?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>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!--启动级别-->
<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>

classpath 只会到 class 路径中查找文件;

classpath* 不经包含 class 路径,还包括 jar 文件。

springmvc-servlet.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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">

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

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

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

<!--自定义处理器-->
<bean id="/hello" class="com.yqx.controller.HelloController"/>
</beans>

HelloController.java

1
2
3
4
5
6
7
8
9
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
mv.setViewName("test");
mv.addObject("msg", "login");
return mv;
}
}

test.jsp

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
发送了一条msg:${msg}
</body>
</html>

3、注解实现 Spring MVC

springmvc-servlet.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!--扫描包-->
<context:component-scan base-package="com.yqx.Controller"/>
<!--让Spring MVC不处理静态资源-->
<mvc:default-servlet-handler/>
<!--支持mvc注解驱动,可以自动帮我们注入处理器映射器和处理器适配器-->
<mvc:annotation-driven/>

<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>

Controller.java

1
2
3
4
5
6
7
8
9
@Controller
@RequestMapping("/test")
public class MyController {
@RequestMapping("/1")
public String test(Model model){
model.addAttribute("msg", "注解实现");
return "annotation";
}
}

@Controller 代表组件,表示被 spring 容器托管,使用 @Repository、@Service 亦或是 @Component 也是一样的效果。

@RequestMapping 用来映射请求,通过它来指定控制器可以处理哪儿些 URL 请求,相当于 Servlet 在 web.xml 中的配置。

4、Restful 风格

4.1、什么是 Restful 风格

Restful 就是要一个资源定位及资源操作的风格。既不是标准也不是协议,只是一种风格。基于这个风格设计的软件会更简洁,更有层次,更易于实现缓存等机制。

1
2
3
4
<!--普通的url-->
https://www.baidu.com?search=123
<!--采用restful风格的url-->
https://www.baidu.com/123
4.2、路径变量

在 Spring MVC 中,我们可以通过 @PathVariable 来实现。

MyController.java

1
2
3
4
5
6
7
8
@Controller
public class MyController {
@RequestMapping("/restful/{a1}/{b1}")
public String test1(Model model, @PathVariable("a1") int a,@PathVariable("b1") int b){
model.addAttribute("res", a-b);
return "restful";
}
}

@PathVariable 中的参数值代表了 url 中对应的占位符名。如果不指定@PathVariable 的参数值,默认就会使用参数列表的参数名。

restful.jsp

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${res}
</body>
</html>

同时它可以指定提交的格式。

1
2
3
4
5
6
7
8
9
10
11
12
// 下述两种方式等价
@RequestMapping(value = "/restful/{a}/{b}", method = RequestMethod.POST)
public String test2(Model model, @PathVariable int a,@PathVariable int b){
model.addAttribute("res", a * b);
return "restful";
}

@PostMapping("/restful/{a1}/{b1}")
public String test3(Model model, @PathVariable int a,@PathVariable int b){
model.addAttribute("res", a * b);
return "restful";
}

5、重定向和转发

5.1、转发

浏览器的请求发送给组件1,组件1经过一些处理之后,将 request 和 response 对象传递给组件2,由组件2继续处理,然后输出响应(也可以继续向其他组件“传递”)。

在上述转发的整个过程中,只涉及到一次浏览器和服务器之间的“请求-响应”,转发过程中的组件共享同一个请求(request)和响应(reponse)对象。

抓饭只能在同一个应用的组件之间进行,不可以转发给其他应用的地址。

5.2、重定向

浏览器向组件1发送请求信息,组件1向浏览器发回一个重定向响应信息,该响应信息不包含具体的数据内容,只是在响应头信息中包含了需要重定向到的地址信息(该地址可以是任何有效的 URL)。而浏览器收到该重定向响应后会自动地向该响应信息头中所指示的地址发出请求。

在上述重定向的整个过程中,涉及到两次浏览器和服务器之间的“请求-响应”。

5.3、区别
  • 转发是服务器内部跳转,数据不会丢失,浏览器只提交了一次请求,速度较快。
  • 重定向是客户端二次跳转,数据会丢失,浏览器提交了二次请求,速度较慢。

做增删改查时最好使用重定向,否则每次刷新页面就相当于在请求一次,可能会做额外的操作,导致数据错误。

转发是服务器内部跳转,因此可以访问到 /WEB-INF 下的资源;而重定向是客户端跳转,因此无法访问到 /WEB-INF 下的资源。

5.3、代码实现

Controller.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Controller
public class IndexController{
@RequestMapping("/forward1")
public String test1(Model model){
model.addAttribute("msg", "forward1");
// 默认使用转发
return "test";
}

@RequestMapping("/forward2")
public String test2(Model model){
model.addAttribute("msg", "forward2");
// 显式使用转发
return "forward:/WEB-INF/jsp/test.jsp";
}

@RequestMapping("/redirect")
public String test3(Model model){
model.addAttribute("msg", "redirect");
// 显式使用重定向
return "redirect:/index.jsp";
}
}

6、请求参数

6.1、通过 url 传递一般参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Controller
public class ParameterController {
// url参数必须和参数列表参数名一致,否则无法获取
@RequestMapping("parameter1")
public String test1(String name, Model model){
model.addAttribute("name", name);
return "parameter";
}

// url参数必须和@RequestParam中的参数值一致,否则无法获取且会报400错误!!!
@RequestMapping("parameter2")
public String test2(@RequestParam("username") String name, Model model){
model.addAttribute("name", name);
return "parameter";
}
}
6.2、通过 url 传递实体类参数
1
2
3
4
5
@RequestMapping("/parameter3")
public String test3(User user123, Model model){
model.addAttribute(user123);
return "user";
}

user.jsp

1
2
3
4
5
6
7
8
9
10
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<!--如果没有设置键值对的键,默认使用值的类型首字母小写作为键-->
姓名:${user.name}, 年龄:${user.age}
</body>
</html>

7、乱码问题

无论什么时候,只要是 web 应用服务项目,就不可能逃脱乱码的痛苦。

不过万幸的是,Spring 为我们提供了十分强大的过滤器,几乎可以解决所有的乱码问题,只需要在 web.xml 中配置即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
<filter>
<filter-name>encodingFilter</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>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

8、JSON

在前后端进行数据交互时,需要一种统一的数据格式,否则后端传的数据前端不知道怎么解析就毫无意义了。

8.1、json 优点
  • JSON类似于XML,比XML更小、更快、更容易解析。但同时XML也有它的不可替代性,应根据使用场景进行更合适的选择;

  • JSON语法简单,很容易理解,并且对象信息自描述,数据传输量小不占用带宽;

  • JSON的可读性、可扩展性都非常好,编码难度也比较低,即使不借助工具也能写出比较规范的JSON

8.2、Jackson

导入依赖

1
2
3
4
5
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>

Controller.java

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
27
28
29
30
31
32
33
34
35
@RestController
public class MyController {
@RequestMapping("/json1")
public String test1() {
// 返回普通的字符串
return new User("余千禧", "男").toString();
}

@RequestMapping("/json2")
public String test2() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
User user = new User("余千禧", "男");
String str = objectMapper.writeValueAsString(user);
// 返回json格式的字符串
return str;
}

@RequestMapping("/json3")
public String test3() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
User user1 = new User("余千禧1", "男");
User user2 = new User("余千禧2", "男");
User user3 = new User("余千禧3", "男");
User user4 = new User("余千禧4", "男");
User user5 = new User("余千禧5", "男");
List<User> list = new ArrayList<>();
list.add(user1);
list.add(user2);
list.add(user3);
list.add(user4);
list.add(user5);
String str = objectMapper.writeValueAsString(list);
return str;
}
}

结果

1
2
3
4
5
6
7
8
/json1
User(name=余千禧, sex=男)

/json2
{"name":"余千禧","sex":"男"}

/json3
[{"name":"余千禧1","sex":"男"},{"name":"余千禧2","sex":"男"},{"name":"余千禧3","sex":"男"},{"name":"余千禧4","sex":"男"},{"name":"余千禧5","sex":"男"}]

如果出现了乱码,可以复制下述代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--解决返回字符串乱码问题-->
<mvc:annotation-driven>
<mvc:message-converters>
<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>

@RestController 修饰在类上,表示该类不会通过视图解析器,即该类中所有请求映射方法所返回的字符串当做普通的 String 类型来处理;

同理还有一个 @RequestBody 修饰在方法上,表示该方法不会通过视图解析器。

封装成工具类

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
package com.yqx.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.text.SimpleDateFormat;

public class JacksonUtil {
// 重载复用
public static String objectToJson(Object object){
return objectToJson(object, "yyyy-MM-dd HH:mm:ss");
}

public static String objectToJson(Object object, String format){
ObjectMapper objectMapper = new ObjectMapper();
// 如果是Date对象,则会format
objectMapper.setDateFormat(new SimpleDateFormat(format));
try {
return objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
}
8.3、Fastjson

使用较 Jackson 而言更加便捷,同时默认就使用 “yyyy-MM-dd HH:mm:ss” 来格式化日期。

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
27
28
29
30
31
32
33
34
35
@RequestMapping("/json5")
public String test5(){
User user1 = new User("余千禧1", "男");
User user2 = new User("余千禧2", "男");
User user3 = new User("余千禧3", "男");
User user4 = new User("余千禧4", "男");
User user5 = new User("余千禧5", "男");
List<User> list = new ArrayList<>();
list.add(user1);
list.add(user2);
list.add(user3);
list.add(user4);
list.add(user5);

System.out.println("*******Java对象转JSON字符串*******");
String str1 = JSON.toJSONString(list) ;
System.out.println( "SON.toSONString(list)==>"+str1);
String str2 = JSON.toJSONString(user1);
System.out.println( "JSON.toJSONString(user1)==>"+str2);

System.out.println("\n******JSON字符串转Java对象*******");
User jp_user1=JSON.parseObject( str2,User.class);
System.out.println("3SON.parseObject(str2,User.class)==>"+jp_user1);

System.out.println("\n******Java对象转JSON对象******");
JSONObject jsonObject1 = (JSONObject)JSON.toJSON(user2);
System.out.println("(JSONObject) JSON.to3SON(user2)z=>"+jsonObject1.getString("name"));

System.out.println("\n******JSON对象转Java对象******");
User to_java_user = JSON.toJavaObject(jsonObject1, User.class);
System.out.println("JSON.toJavaObject(jsonObject1, User.class)==>" + to_java_user);

String str = JSON.toJSONString(list);
return str;
}
1
2
3
4
5
6
7
8
9
10
11
12
*******Java对象转JSON字符串*******
SON.toSONString(list)==>[{"name":"余千禧1","sex":"男"},{"name":"余千禧2","sex":"男"},{"name":"余千禧3","sex":"男"},{"name":"余千禧4","sex":"男"},{"name":"余千禧5","sex":"男"}]
JSON.toJSONString(user1)==>{"name":"余千禧1","sex":"男"}

******JSON字符串转Java对象*******
3SON.parseObject(str2,User.class)==>User(name=余千禧1, sex=男)

******Java对象转JSON对象******
(JSONObject) JSON.to3SON(user2)z=>余千禧2

******JSON对象转Java对象******
JSON.toJavaObject(jsonObject1, User.class)==>User(name=余千禧2, sex=男)

9、SSM整合

9.1、配置文件代码(以后可以直接复制拿来用)
  • pom.xml

    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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    <?xml version="1.0" encoding="UTF-8"?>
    <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">
    <parent>
    <artifactId>SpringMVC</artifactId>
    <groupId>org.example</groupId>
    <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>SpringMVC-05-SSM</artifactId>

    <dependencies>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.0.RELEASE</version>
    </dependency>
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
    </dependency>
    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.2</version>
    </dependency>
    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.6</version>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.0.RELEASE</version>
    </dependency>
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
    </dependency>
    <dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
    </dependency>
    <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.2</version>
    </dependency>
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.18</version>
    </dependency>
    </dependencies>

    <build>
    <resources>
    <resource>
    <directory>src/main/resources</directory>
    <includes>
    <include>**/*.properties</include>
    <include>**/*.xml</include>
    </includes>
    <filtering>true</filtering>
    </resource>
    <resource>
    <directory>src/main/java</directory>
    <includes>
    <include>**/*.properties</include>
    <include>**/*.xml</include>
    </includes>
    <filtering>true</filtering>
    </resource>
    </resources>
    </build>
    </project>
  • applicationContext.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?xml version="1.0" encoding="UTF8"?>
    <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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.yqx.service"/>
    <import resource="springmvc-servlet.xml"/>
    <import resource="spring.xml"/>
    </beans>
  • mybatis-config.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?xml version="1.0" encoding="UTF8" ?>
    <!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    <typeAliases>
    <package name="com.yqx.pojo"/>
    </typeAliases>
    </configuration>
  • spring.xml

    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
    27
    28
    29
    30
    31
    32
    33
    34
    <?xml version="1.0" encoding="UTF8"?>

    <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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <!--读取配置文件-->
    <context:property-placeholder location="classpath:db.properties"/>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <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 name="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <property name="mapperLocations" value="classpath*:com/yqx/mapper/*.xml"/>
    </bean>

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

    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
    </bean>
    </beans>
  • springmvc-servlet.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <?xml version="1.0" encoding="UTF8"?>

    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!--静态资源过滤-->
    <mvc:default-servlet-handler/>
    <!--处理器映射器及处理器适配器-->
    <mvc:annotation-driven/>
    <context:component-scan base-package="com.yqx.controller"/>

    <!--视图解析器-->
    <bean name="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
    </bean>

    </beans>
  • web.xml

    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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    <?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">

    <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
    </servlet-mapping>

    <filter>
    <filter-name>encodingFilter</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>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>
    </web-app>
9.2 踩雷

(1)db.properties

报错信息如下。

1
2
org.apache.ibatis.exceptions.PersistenceException: 
Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLException: Access denied for user '11499'@'localhost' (using password: YES)

这是因为 db.properties 里面 username 必须用 jdbc.username。否则 username 就变成了系统管理员(‘11499‘@’localhost’)的名字。

1
2
3
4
5
# 其实可以只将username改成jdbc.username
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssmbuild?useSSL=true&useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456

将 spring.xml 中 DataSource 属性也一并修改。

1
2
3
4
5
6
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<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>

(2)mapper.java

报错信息如下。

1
Type interface com.yqx.mapper.BookMapper is not known to the MapperRegistry.

这个问题有点傻,因为 mapper.xml 文件我都是直接使用模板,如下。

但这个项目是 BookMapper,然后就忘记改了…

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yqx.mapper.UserMapper">

</mapper>

(3)web.xml

报错信息如下。

1
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.yqx.service.BookMapperImpl' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

因为在 web.xml 中只引入了 springmvc-servlet.xml,因此不可能有 BookMapperImpl 这个 bean。

1
<param-value>classpath:springmvc-servlet.xml</param-value>

只需要引入总的配置文件 applicationContext.xml ,再在 applicaitonContext.xml 中引入所有其他文件就行。

1
<param-value>classpath:applicationContext.xml</param-value>
1
2
<import resource="springmvc-servlet.xml"/>
<import resource="spring.xml"/>

(4)post 表单提交出现乱码

添加过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
<filter>
<filter-name>encodingFilter</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>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

(5)xml 文件编码

报错信息

1
com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 1字节的 UTF-8 序列的字节 1 无效

该错误是由一些配置文件引起的:如applicationContext.xml的编码问题等。

对于Maven 项目,在项目依赖配置文件 pom.xml 中加入构建项目编码属性(注意顺序):

1
2
3
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

10、Ajax

10.1、异步

异步(Asynchronous, async)是与同步(Synchronous, sync)相对的概念。

在我们学习的传统单线程编程中,程序的运行是同步的(同步不意味着所有步骤同时运行,而是指步骤在一个控制流序列中按顺序执行)。而异步的概念则是不保证同步的概念,也就是说,一个异步过程的执行将不再与原有的序列有顺序关系。

简单来理解就是:同步按你的代码顺序执行,异步不按照代码顺序执行,异步的执行效率更高。

10.2、什么时候使用异步编程

在前端编程中(甚至后端有时也是这样),我们在处理一些简短、快速的操作时,例如计算 1 + 1 的结果,往往在主线程中就可以完成。主线程作为一个线程,不能够同时接受多方面的请求。所以,当一个事件没有结束时,界面将无法处理其他请求。

现在有一个按钮,如果我们设置它的 onclick 事件为一个死循环,那么当这个按钮按下,整个网页将失去响应。

为了避免这种情况的发生,我们常常用子线程来完成一些可能消耗时间足够长以至于被用户察觉的事情,比如读取一个大文件或者发出一个网络请求。因为子线程独立于主线程,所以即使出现阻塞也不会影响主线程的运行。但是子线程有一个局限:一旦发射了以后就会与主线程失去同步,我们无法确定它的结束,如果结束之后需要处理一些事情,比如处理来自服务器的信息,我们是无法将它合并到主线程中去的。

为了解决这个问题,JavaScript 中的异步操作函数往往通过回调函数来实现异步任务的结果处理

回调函数

回调函数就是一个函数,它是在我们启动一个异步任务的时候就告诉它:等你完成了这个任务之后要干什么。这样一来主线程几乎不用关心异步任务的状态了,他自己会善始善终。

1
2
3
setTimeout(function () {
document.getElementById("demo").innerHTML="RUNOOB!";
}, 3000);

这段程序中的 setTimeout 就是一个消耗时间较长(3 秒)的过程,它的第一个参数是个回调函数,第二个参数是毫秒数,这个函数执行之后会产生一个子线程,子线程会等待 3 秒,然后执行回调函数 “print”,在命令行输出 “RUNOOB!”。

10.3、利用 JQuery 实现 Ajax

初体验

jsp 页面,js 写在 html 前,要加$(function())

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
27
28
29
30
31
32
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<script src="${pageContext.request.contextPath}/static/js/jquery.js"></script>
<script>
// 需要等页面加载完才能获取到页面元素
$(function (){
$("#btn").click(function (){
$.post("/ajax1", function (data){
let html = "<table><thead><td>姓名</td><td>年龄</td></thead><tbody>";
for(let i=0;i<data.length;i++) {
html += "<tr>" +
"<td>" + data[i].name + "</td>" +
"<td>" + data[i].age + "</td>" +
"</tr>"
}
html += "</tbody></table>"
console.log(html)
$("#content").html(html)
})
})
})
</script>

<html>
<head>
<title>Title</title>
</head>
<body>
<button id="btn">查询</button>
<div id="content">
</div>
</body>
</html>

controller.java

1
2
3
4
5
6
7
8
9
10
11
@RestController
public class AjaxController {
@RequestMapping("/ajax1")
public List<User> test1(){
List<User> list = new ArrayList<>();
list.add(new User("余千禧", 1));
list.add(new User("紫梦沁香", 2));
list.add(new User("自杀之王", 3));
return list;
}
}

No converter found for return value of type: class java.util.ArrayList 是因为无法将返回的对象转换为 Json 输出到网页上,只需导入 Jackson 或 FastJson 的 jar 包就行

验证用户名密码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RequestMapping("/ajax2")
public String test2(String name, String pwd){
String msg="";
if(name!=null){
if (name.equals("yqx")){
msg="ok";
}else {
msg="用户名输入错误";
}
}
if(pwd!=null){
if (pwd.equals("123")){
msg="ok";
}else {
msg="密码输入错误";
}
}
return msg;
}

返回时出现字符串乱码请查看8.2

jsp 页面

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
27
28
29
30
31
32
33
34
35
36
37
38
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<script src="${pageContext.request.contextPath}/static/js/jquery.js"></script>
<script>
function name_blur(){
$.post("/ajax2", {"name": $("#name").val()}, function (data){
if(data == "ok"){
$("#span1").text(data).css("color", "green");
}else{
$("#span1").text(data).css("color", "red");
}
})
}

function pwd_blur(){
$.post("/ajax2", {"pwd": $("#pwd").val()}, function (data){
if(data == "ok"){
$("#span2").text(data).css("color", "green");
}else{
$("#span2").text(data).css("color", "red");
}
})
}
</script>

<html>
<head>
<title>Title</title>
</head>
<body>
<form>
用户名:<input id="name" type="text" onblur="name_blur()">
<span id="span1"></span>
<br>
密码:<input id="pwd" type="password" onblur="pwd_blur()">
<span id="span2"></span>
</form>
</body>
</html>

script 标签没有自闭合!!!必须写全!!!

11、拦截器

在系统中,经常需要在处理用户请求之前和之后执行一些行为,例如检测用户的权限,或者将请求的信息记录到日志中,即平时所说的“权限检测”及“日志记录”。当然不仅仅这些,所以需要一种机制,拦截用户的请求,在请求的前后添加处理逻辑

Spring MVC 提供了 Interceptor 拦截器机制,用于请求的预处理和后处理。

MyInterceptor.java,需实现 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
26
27
28
29
30
31
32
33
34
35
36
37
package com.yqx.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class MyInterceptor implements HandlerInterceptor {
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("afterCompletion方法在控制器的处理请求方法执行完成后执行,即视图渲染结束之后执行");

}

@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle方法在控制器的处理请求方法调用之后,解析视图之前执行");
}

@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle方法在控制器的处理请求方法调用之前执行");
HttpSession session = request.getSession();
if (session.getAttribute("name") == null){
response.sendRedirect("/toLogin");
return false;
}
return true;
}
}
  • preHandle( ):该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作。
  • postHandle( ):该方法在控制器的处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步的修改。
  • afterCompletion( ):该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作。

写完拦截器需在Spring MVC 的配置文件中进行配置。

1
2
3
4
5
6
7
8
9
10
11
12
<!--关于拦截器的配置-->
<mvc:interceptors>
<mvc:interceptor>
<!--/** 包括路径及其子路径-->
<!--/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截-->
<!--/admin/** 拦截的是/admin/下的所有-->
<!--此处我们只需要判断进入首页时有没有登录,因此只需拦截/toMain请求-->
<mvc:mapping path="/toMain"/>
<!--bean配置的就是拦截器-->
<bean class="com.yqx.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>