SpringMVC是什么?

  1. Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架
  2. 将web层进行职责解耦
  3. 简化开发

image-20200810095037521

springmvc工作流程图:

image-20200512143317781

image-20200512143002247

栗子:

举一个springMVC栗子

1.导包

具体需要什么包就不一 一解释了

image-20200810200458370

2.配置web.xml

配置DisPatcherServlet

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
<?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">

<!-- 描述性的文字说明,没实际意义,一般情况下直接删除掉 -->
<display-name></display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>

<!------配置DisPatcherServlet----->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<!-- 配置springmvc核心配置文件启动-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>


</web-app>
3.配置springmvc.xml

1.头文件约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
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
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">

  1. 后端控制器(controller )交给spring来管理、配置处理器映射器、处理器适配器、配置视图解析器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

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

<!--配置后端控制器controller 可以用注解替换,前提是开启注解扫描-->
<bean name="/test" class="com.gec.controller.first"></bean>


<!--配置视图解析器 可以省略,但是看情况,一般不建议省略-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>

4、在web.xml中配置springmvc核心文件启动
1
2
3
4
5
6

<!-- 配置springmvc核心配置文件启动-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
5、测试

新建一个后台控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.gec.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

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

public class first extends AbstractController {
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
return new ModelAndView("index");
}
}

新建一个hello world 界面

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>
<h1>springmvc hello!!!!!!!!!!</h1>
</body>
</html>

改进:

处理器映射器、处理器适配器、视图解析器三大核心组件都是目前都是使用xml方式配置。

​ 上面的开发太过复杂,springmvc.xml文件中处理器映射器、处理器适配器、视图解析器 ,这些东西都有springmvc框架帮我做了

配置注解的处理器映射器和处理器适配器

  1. 凭什么处理器映射器、处理器适配器可以省略呢?

image-20200810213326272

image-20200810213334140

image-20200810213340862

开发总结:

处理器映射器 org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping

1)负责寻找SpringMVC项目中哪个后端控制器去执行任务

2)寻找到对应的后端控制器之后返回给前端控制器 (DispatcherServlet)

处理器适配器 org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter

1)根据处理器映射器找到的 后端控制器 去执行该 后端控制器

2)执行完毕后,返回ModleAndView

视图解析器 org.springframework.web.servlet.view.InternalResourceViewResolver

1)根据处理器适配器执行的后的ModelAndView进行解析

以上三大核心组件都是目前都是使用xml方式配置

由于以上的xml配置非常的繁琐,虽然 SpringMVC中三个组件可以省略不写,我们也知道为什么呢?但是 后端控制器无法省略,因为如果省略了,那么SpringMVC将无法进行处理请求和响应

注意:如果只配置了一个最新的处理器映射器,而最新的处理器适配器没有配置,则直接抛出异常信息!

image-20200810202353509

经过上面的配置,也就是说,如果我们既想使用注解开发SpringMVC也想提高一些性能方面的东东,我们必须写下面的配置

完整配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!--  开启注解扫描  -->
<context:component-scan base-package="com.shop.web"></context:component-scan>

<!-- 使用最新版的 处理器映射器 -->

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>
<!--使用新版的处理器适配器 -->

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"></bean>

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

</bean> -->

由于每次都要写上那么多配置,所以SpringMVC官方推出一个

1
2
3
4
5
6
7
8
9
10
11
12
<!--    开启扫描,,,用于注解开发  注意:这是个双标签,如果用单标签,可能会不扫描-->
<context:component-scan base-package="com.gec.controller"></context:component-scan>


<mvc:annotation-driven/><mvc:annotation-driven/>

<!--配置视图解析器 可以省略,但是看情况,一般不建议省略-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>

注解代替后端处理控制器

1
2
3
4
5
6
7
8
9
10
@Controller
public class homeContoller{
// 配置url的访问映射
@RequestMapping(value="/index")
public ModelAndView aa()
{
return new ModelAndView("index");
}
}

SSM整合

Spring + Spring MVC + Mybatis

image-20200810203324979

举个栗子:

1.导包(贼多)

image-20200810203232865

2.建立目录结构

image-20200810203526191

3.创建数据库

4.逆向工程生成

5.编写配置文件

image-20200810204041094

mybatis

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>

dao

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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
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-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 数据库连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${driverName}" />
<property name="url" value="${urlName}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
<property name="maxActive" value="10" />
<property name="maxIdle" value="5" />
</bean>
<!---
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mvc" />
<property name="username" value="root" />
<property name="password" value="root" />
<property name="maxActive" value="10" />
<property name="maxIdle" value="5" />
</bean>
-->


<!-- mapper配置 -->
<!-- 让spring管理sqlsessionfactory 使用mybatis和spring整合包中的 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据库连接池 -->
<property name="dataSource" ref="dataSource" />
<!-- 加载mybatis的全局配置文件 -->
<property name="configLocation" value="classpath:mybatis-config.xml" />
</bean>

<!-- 配置Mapper扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.shop.dao"/>
</bean>

</beans>



service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
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-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<!-- 配置扫描servcie层 -->
<context:component-scan base-package="com.shop.service"></context:component-scan>
</beans>

transaction

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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
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-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">


<!-- 数据库连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mvc" />
<property name="username" value="root" />
<property name="password" value="root" />
<property name="maxActive" value="10" />
<property name="maxIdle" value="5" />
</bean>

<!-- 事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource" />
</bean>

<!-- 通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 传播行为 -->
<tx:method name="save*" propagation="SUPPORTS" read-only="false"/>
<tx:method name="insert*" propagation="SUPPORTS" read-only="false" />
<tx:method name="delete*" propagation="SUPPORTS" />
<tx:method name="update*" propagation="SUPPORTS" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>

<!-- 切面 -->
<aop:config>
<aop:advisor advice-ref="txAdvice"
pointcut="execution(* com.shop.service.*.*(..))" />
</aop:config>
</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
36
37
38
39
<?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">



<!-- spring IOC监听,当tomcat启动的时候同时将IOC中的类加载好,准备给web层调用-->
<!-- <listener>-->
<!-- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>-->
<!-- </listener>-->
<!-- &lt;!&ndash;这个配置属于整个web项目&ndash;&gt;-->
<!-- &lt;!&ndash; tomcat启动后加载其他的xml文件&ndash;&gt;-->
<!-- <context-param>-->
<!-- <param-name>ContextConfigLocation</param-name>-->
<!-- <param-value>classpath:applicationContext-*.xml</param-value>-->
<!-- </context-param>-->
<!--
三大:1.servlet 2.filter 3.listener

-->

<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--springmvc核心配置文件启动-->
<!-- 这里面配置的是tomcat的-->
<init-param>
<param-name>ContextConfigLocation</param-name>
<param-value>classpath:springmvc.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>
</web-app>

  1. load-on-startup 元素标记容器是否应该在web应用程序启动的时候就加载这个servlet,(实例化并调用其init()方法)。
  2. 它的值必须是一个整数,表示servlet被加载的先后顺序。
  3. 如果该元素的值为负数或者没有设置,则容器会当Servlet被请求时再加载。
  4. 如果值为正整数或者0时,表示容器在应用启动时就加载并初始化这个servlet,值越小,servlet的优先级越高,就越先被加载。值相同时,容器就会自己选择顺序来加载。

bug

1
2
3
 <listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

在web.xml中使用监听器加载springIOC,会报错找不到ContextLoaderListener类

解决方案:直接用

1
2
3
4
5
6
<init-param>
<param-name>ContextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<!---直接设置启动级别,在tomcat容器启动时就加载servlet-->

springmvx.xml中写入

1
2
3
<import resource="applicationContext-dao.xml"/>
<import resource="applicationContext-service.xml"/>
<import resource="applicationContext-transaction.xml"/>

1.SpringMVC中参数传递

1.View to Controller

SpringMVC对前端传来的参数-自动封装

1.基本类型

Int float String double

login.jsp

1
2
3
4
5
<form action="${pageContext.request.contextPath}/login/add" method="post">
<input type="text" name="name"><br/>
<input type="submit" value="提交">
</form>
</body>

loginController

1
2
3
4
5
6
7
8
9
10
@Controller
@RequestMapping(value="/login")
public class loginController {
@RequestMapping(value="/add")
public ModelAndView add(String name)
{
System.out.println(name);
return null;
}
}

对于基本类型直接通过参数的形式接收

2.实体对象封装

user

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
public class User {
private String name;
private Integer age;
private String gender;
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + ", gender=" + gender + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
1
2
3
4
5
6
7
8
9
<body>
<form action="${pageContext.request.contextPath}/login/delete"
method="post">
姓名:<input type="text" name="name"><br />
年龄:<input type="text" name="age"><br />
性别:<input type="text" name="gender"><br />
<input type="submit" value="提交">
</form>

1
2
3
4
5
6
@RequestMapping(value="/delete")
public ModelAndView delete(User user)
{
System.out.println(user);
return null;
}

image-20200810211903715

springmvc直接将参数封装成对象返回给contoller

3.list

4.Map

2.requestMapping

实际开发中,一个项目会有很多个模块,比如用户管理(UserController)、订单管理(OrderController)、产品管理(ProductController)等众多模块,而每个模块中都会有添加、删除、修改、更新等操作?

1.作用在类上

url地址: http://localhost:8080/spring03/add

@RquestMapping加在方法上,还可以加在类上.

1
2
3
4
5
6
7
8
9
10
@Controller
@RequestMapping(value="/Index")
public class IndexController{ @RequestMapping(value="/add")
public ModelAndView aa(String name)
{
System.out.println("name:"+name);
return new ModelAndView("/WEB-INF/jsp/index.jsp");
}
}

【开发小结】:@RequestMapping映射的请求信息必须保证全局唯一

2.限定请求参数

@RquestMapping作为请求的一个映射,除了可以配置url地址,还可以配置请求方式,还可以在请求时,限定请求参数

  • value=”/aaa”

  • method=RequestMethod.GET

  • params=”username”

1
2
3
4
5
6
7
8
9
10
11
@Controller
@RequestMapping("/order")
public class OrderController {
@RequestMapping(value="/aaa",method=RequestMethod.GET,params="username")
public ModelAndView index(String name)
{
System.out.println("...."+name);
return new ModelAndView("index");
}
}

同时支持get和post请求

img

测试请求类型

image-20200810205437409

测试请求参数名称

img

3.SpringMVC中参数传递

1.Controller to View

方式一:

使用ModelAndView

ModelAndView:控制器处理方法的返回值若为ModelAndView,则既包含视图信息,又包含模型数据信息。

  • 添加模型数据

ModelAndView addObject(String attributeName, Object attributeValue);

ModelAndView addAllObjects(Map<String,?> modelMap);

  • 设置视图

void setView(View view);

void setViewName(String viewName);

方式二:

直接使用Model

控制器处理方法的返回值为String(逻辑视图名),模型数据信息放入到Model中即可。

Model的数据结构,处理方法的入参为Model类型即可

springMVC会隐式帮我们创建一个model对象,所以不需要new

1
2
3
4
5
@RequestMapping(Value="aa2")
public String index(Model m){
m.addAttribute("hello","hello springmvc");
return " index";
}

Model对象的用法:

  • 使用Model对象,通过key-value键值方式存储值,在视图层使用key来获取对应的值。
  • 使用Modle对象,我们可以不指定任何的key,只需要传入一个值就可以了,但是需要注意,如果不传入任何的key,那么在视图(jsp)中获取的时候,使用该对象的类型作为key来获取。

此处对象的类型一定必须是小写

例如:字符串 -> string

如果使用大写那么不会报错吗,但是结果出不来

  • 使用Model对象,不仅可以传入一些基本数据类型,还可以传入一个复杂类型(对象),同样key可以省略,在视图层获取的时候,也是一样需要使用对象类型来获取。

image-20200810215110060

Model其实本质上就是一个Map,那么我们是否可以使用Map作为参数直接入参呢? 可以

方式三

image-20200810215154803

image-20200810215158868

image-20200810215226669

4.乱码问题

1.post乱码

image-20200811102911639

image-20200811102946848

web.xml

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

以上可以解决post请求乱码问题。

2.get乱码

对于get请求中文参数出现乱码解决方法有两个:

方式一:

修改tomcat配置文件添加编码与工程编码一致,如下:

方式二

对参数进行重新编码:

String username= new String(request.getParamter(“userName”).getBytes(“ISO8859-1”),”utf-8”)

ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码

5.VO

image-20200811103447480

1
2
3
4
5
6
7
8
9
10
11
12
13
14

查询条件:
<table width="100%" border=1>
<tr>
<td>商品名称:<input type="text" name="product.name"></td>
<td>商品价格:<input type="text" name="product.price"></td>


<td>姓名:<input type="text" name="user.username"></td>
<td>性别:<input type="text" name="user.sex"></td>


<td><input type="submit" value="查询"/></td>

注意:输入框的name属性

创建一个VO类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class QueryVo {
private Product product;
private User user;

public User getUser() {
return user;
}

public void setUser(User user) {
this.user = user;
}

public Product getProduct() {
return product;
}

public void setProduct(Product product) {
this.product = product;
}
}

控制类接收前端参数

1
2
3
4
5
6

@RequestMapping(value="/query")
public ModelAndView query(QueryVo vo)
{
return null;
}

直接用QueryVo接收参数

6.自定义转换器

image-20200811105706184

时间日期转换

1.自定义转换器类

(实现converter接口)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class CustomerConveter implements Converter<String, Date>{
@Override
public Date convert(String source) {
try {
Date date = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(source);
return date;
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}

}

2.springmvc.xml配置

springMVC.xml配置自定义转换器

将自定义的转换器关联到FormattingConversionServiceFactoryBean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--配置自定义转换器 -->
<bean name="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.shop.conveter.CustomerConveter"></bean>
<!--可以配置多个
<bean class="com.shop.conveter.CustomerConveter2"></bean>
<bean class="com.shop.conveter.CustomerConveter3"></bean>
-->
</set>
</property>
</bean>

4.转换器挂载

1
2
3
<mvc:annotation-driven
conversion-service="conversionService"></mvc:annotation-driven>

7.绑定参数

image-20200811140406973

数组

List

批量删除(数组传值)

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>查询商品列表</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/deleteAll" method="post">
批量删除:
<table width="100%" border=1>
<tr>
<!-- <td>商品名称:<input type="text" name="product.name"></td>
<td>商品价格:<input type="text" name="product.price"></td>


<td>姓名:<input type="text" name="user.username"></td>
<td>性别:<input type="text" name="user.sex"></td> -->


<td><input type="submit" value="批量删除"/></td>
</tr>
</table>
商品列表:
<table width="100%" border=1>
<tr>
<td></td>
<td>商品名称</td>
<td>商品价格</td>
<td>生产日期</td>
<td>商品描述</td>
<td>操作</td>
</tr>
<c:forEach items="${productList }" var="item">
<tr>
<td><input type="checkbox" name="ids" value="${item.id }"></td>
<td>${item.name }</td>
<td>${item.price }</td>
<td><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/></td>
<td>${item.detail }</td>

<td><a href="${pageContext.request.contextPath }/itemEdit?id=${item.id}">修改</a></td>

</tr>
</c:forEach>

</table>
</form>
</body>
</html>

创建一个QueryVo

1
2
3
4
5
public class QueryVo {
private Product product;
private User user;
private Integer[]ids;
//以及set方法

ProductController接收参数

1
2
3
4
5
6
7
@RequestMapping(value="/deleteAll")
public ModelAndView deleteAll(QueryVo vo)
{
System.out.println(vo);
return null;
}

8.springMVC文件上传

SpringMVC 中,文件的上传,是通过 MultipartResolver 实现的。 所以,如果要实现文件的上传,只要在 spring-mvc.xml 中注册相应的 MultipartResolver 即可。

MultipartResolver 的实现类有两个:

  1. CommonsMultipartResolver

  2. StandardServletMultipartResolver

两个的区别:

  1. 第一个需要使用 Apache 的 commons-fileupload 等 jar 包支持,但它能在比较旧的 servlet 版本中使用。

  2. 第二个不需要第三方 jar 包支持,它使用 servlet 内置的上传功能,但是只能在 Servlet 3 以上的版本使用

1、导包

image-20200811194055473

2、jsp准备

1.提供一个File上传表单控件

image-20200811194246974

2、指定表单类型
1
2
3
!-- 上传图片是需要指定属性 enctype="multipart/form-data" -->
<form id="itemForm" action="${pageContext.request.contextPath }/updateProduct" method="post" enctype="multipart/form-data">

3.配置CommonsMultipartResolver

在springmvc中配置

1
2
3
4
5
<!--记得一定要起名称,不然找不到,而且名只能是这个-->
<bean name="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 配置上传文件最大,以字节来算 :5M=1024*1024*5*5242880=-->
<property name="maxUploadSize" value="5242880" />
</bean>

注意:这个类必须起这个名字不然会报500错误,说找不到这个类

4、上传控制类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@RequestMapping("/updateitem.action")
public void update(Product product,HttpServletRequest req, HttpServletResponse resp, MultipartFile pictureFile) throws IOException {
//截取
//得到文件名“点”下标
int lastIndexOf = pictureFile.getOriginalFilename().lastIndexOf(".");
//截取文件后缀
String subfix = pictureFile.getOriginalFilename().substring(lastIndexOf);
//拼接得到一个唯一文件名
String filename = ""+UUID.randomUUID().toString()+subfix;

String path=req.getServletContext().getRealPath("/images");
//File.separator得到当前系统的文件分隔符
pictureFile.transferTo(new File(path+File.separator+filename));
product.setPic(filename);

//数据库更新操作
int update = productservice.update(product);
if(update>0){
System.out.println("更新成功");
}else {
System.out.println("更新失败");
}
resp.sendRedirect("/product");
}

getOriginalFilenmae()得到上传文件的名字

5、查看上传文件

由于web.xml文件中,前端处理器(DispatcherServlet)的原因,会将所有请求拦截,之放行.jsp文件

1
2
3
4
5
6
7
8
 <servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- /:拦截除了jspd的所哟请求,/*:拦截所有jsp请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>

所以在springMVC.xml文件中要放行资源

1
2
3
4
5
6
7
8
9
<!--前端处理器放行资源,配置放行哪些资源不被拦截-->

<mvc:resources mapping="/images/**" location=" /images/"></mvc:resources>
<!--
<mvc:resources mapping="/image/**" location=" /imgaes/"></mvc:resources>
<mvc:resources mapping="/js/**" location=" /js/"></mvc:resources>
<mvc:resources mapping="/css/**" location=" /css/"></mvc:resources>
-->

mapping 表示url访问路径 ef:http://localhost:8080/images/ftp.txt

location:表示本地项目文件目录

9、json数据交互

1、导包

image-20200812094504628

前端传递json到contoller

编写ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script src="/js/jquery-1.8.3.min.js"></script>
<script>
/* $(document).ready(function () {
})*/
$(function(){
$("#btn").click(function () {
$.ajax({
// type: "GET",
url:"${pageContext.request.contextPath}/test01",
contentType:"application/json;charset=utf-8",
data:{'name':'iphoneX', 'price':8000.9},
success:function (data) {
alert(data);
}
}
)
}
)
})
</script>

400错误

出现这个请求无效说明请求没有进入后台服务器里

原因: (1)前端提交的字段名称或者字段类型和后台的实体类不一样 或者前端提交的参数跟后台需要的参数个数不一致,导致无法封装

   (2)前端提交到后台的数据应该是JSON字符串类型,而前端没有将对象转化为字符串类型;

解决方法: 对照字段名称,类型保证一致

编写controller

1
2
3
4
5
@RequestMapping(value = "/test01")
public void test01(Product product){
System.out.println(product);
System.out.println("接收参数成功");
}

注意:前端传递的json数据也会被转到对象中,springmvc帮我们做了

后端转递json到前端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RequestMapping(value = "/test02")
@ResponseBody
public List<Product>test02(Product product){
ArrayList<Product> list = new ArrayList<>();
Product p1 = new Product();
Product p2 = new Product();
Product p3 = new Product();
p1.setName("123");
p2.setName("123");
p3.setName("123");
list.add(p1);
list.add(p2);
list.add(p3);
System.out.println(product);
System.out.println("接收参数成功");
return list;
}
结果:

image-20200812112858692

  • 用@ResponseBody 实现将对象转json,springmvc已经风好

  • @ResponseBody可以放在方法上,表示这个方法返回数据转成json;也可以放在类上,表示这个类所有方法都参数都转成json.

  • 如果@ResponseBody用在类上,可以用**@RestController**,替代@controller和@ResponseBody,它后面两个注解的合体。如图;

image-20200812113532260

10、Restful风格

1、什么是Restful

REST: Representational State Transfer,即表述性转移,是一种软件架构的风格(一个规范),不是一门技术[e1]

[e1]PHP、NET、Java中都有

传统方式:

https://movie.douban.com/subject?id=26416062

restful

风格:https://movie.douban.com/subject/26416062

2、REST风格好处:
  • 使用传统url方式进行参数参数的时候,如果需要传递参数,那么中文乱码很头疼

  • 传统url方式,对于各大搜索引擎的收录,并不是很友好,但是rest风格方式,搜索引擎就更加酷爱

3. RESTful使用

使用springMVC+REST改造前面的商品案例。

  • 修改userlist中js的跳转链接

  • 在@requestMapping的url请求后面加上**{id}** ======== 设置rest风格

1
<td><a href="${pageContext.request.contextPath }/itemEdit/${item.id}">修改</a></td>
  • 在控制器方法参数的前面加上**@PathVariable**注解 =========解析rest风格
1
2
3
4
5
6
7
8
@RequestMapping(value="/itemEdit/{id}")
public ModelAndView getProductById(@PathVariable("id")Integer aa,HttpServletRequest req,
HttpServletResponse response,
HttpSession ss)
{
Product product = productservice.getProductById(aa);
}

11、controller方法返回类型

controller方法返回值(指定返回到哪个页面, 指定返回到页面的数据)

  • ModelAndView

​ modelAndView.addObject(“itemList”, list); 指定返回页面的数据

​ modelAndView.setViewName(“itemList”); 指定返回的页面

  • String(推荐使用)

​ 返回普通字符串,就是页面去掉扩展名的名称, 返回给页面数据通过Model来完成

  • 返回void(使用它破坏了springMvc的结构,所以不建议使用)

​ 可以使用request.setAttribut 来给页面返回数据

​ 可以使用request.getRquestDispatcher().forward()来指定返回的页面

​ 如果controller返回值为void则不走springMvc的组件,所以要写页面的完整路径名称

1、返回void (不推荐)

使用它破坏了springMvc的结构,所以不建议使用。这是servlet原生的,mvc已经将它封装好了。

转发:

1
2
3
4
5
	@RequestMapping(value="/test")   
public void test(HttpServletRequest req,HttpServletResponse response) throws ServletException, IOException {
req.setAttribute("msg", "阿黄很大~~~");
req.getRequestDispatcher("/WEB-INF/jsp/demo.jsp").forward(req, response);
}

image-20200812142658504

重定向:

1
2
3
4
5
6
@RequestMapping(value="/test")
public void test(HttpServletRequest req,HttpServletResponse response) throws ServletException, IOException
{
response.sendRedirect("/ssm5/list");
}

2、.ModelAndView(一般)

1
2
modelAndView.addObject("itemList",  list); //指定返回页面的数据    
modelAndView.setViewName("itemList"); // 指定返回的页面

3、String

推荐使用String类型

1、普通跳转

返回普通字符串,就是页面去掉扩展名的名称,返回给页面数据通过Model来完成。

1
2
3
4
5
6
7
8
@RequestMapping(value="/updateProduct")
public String updateProduct(Product product,Model model) throws ServletException
{
productservice.update(product);
model.addAttribute("xxx",”xxxx”);
return " productItem ";
}

2、请求转发
1
2
3
4
5
6
7
8
@RequestMapping(value="/updateProduct")
public String updateProduct(Product product,Model model) throws ServletException
{
productservice.update(product);
model.addAttribute("msg","今天天气不错呀");
return "forward:list";
}

image-20200812192406663

3、请求重定向
1
2
3
4
5
6
7
8
9
@RequestMapping(value="/updateProduct")
public String updateProduct(Product product,Model model) throws ServletException
{
productservice.update(product);
model.addAttribute("msg","今天天气不错呀");
//return "forward:list";
return "redirect:list";
}

image-20200812192443870

12、自定义异常

思想:做一个全局异常处理器,处理所有没有处理过的运行时异常用于更友好地提示用户。

1、定义全局异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class myException implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {

String msg="出现bug了,请联系管理员处理";

if(e instanceof MyException2){
msg = ((MyException2) e).getMsg();
}
ModelAndView mv = new ModelAndView();
mv.addObject("msg",msg);
mv.setViewName("error");
return mv;
}
}

2、自定义异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

public class MyException2 extends Exception{
String msg;

public String getMsg() {
return msg;
}

public void setMsg(String msg) {
this.msg = msg;
}

public MyException2() {
super();
}

public MyException2(String message) {
super();
this.msg=message;
}
}

3、注册异常

在springmvc.xml中注册全局异常

1
2
<!--注册全局异常-->
<bean class="com.shop.myException.myException"></bean>

4、编写异常界面

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>
<h1>${msg}</h1>
</body>
</html>

5、测试:

制造异常

1
2
3
4
5
6
7
8
9
@RequestMapping(value="/exception")
public ModelAndView exception() throws MyException2 {
if(true){
throw new MyException2("找不到商品");
}

/*int i=1/0;
return null;*/
}

13、拦截器

1、定义

​ Spring Web MVC 的处理器拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理和后处理。

  • 预(前)处理( preHandle):在请求真正到达 后端控制器之前,我们可以通过 拦截器进行 拦截处理!

  • 后处理(postHandle+afterCompletion):在 后端控制器处理完毕后,再使用我们的 拦截器进行拦截处理!

实现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
38
39

Public class HandlerInterceptor1 implements HandlerInterceptor{
/**
* controller执行前调用此方法
* 返回true表示继续执行,返回false中止执行
* 这里可以加入登录校验、权限拦截等
*/
@Override
Public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
// TODO Auto-generated method stub
Return false;
}
/**
* controller执行后但未返回视图前调用此方法
* 这里可在返回用户前对模型数据进行加工处理,比如这里加入公用信息以便页面显示
*/
@Override
Public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub

}
/**
* controller执行后且视图返回后调用此方法
* 这里可得到执行controller时的异常信息
* 这里可记录操作日志,资源清理等
*/
@Override
Public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub

}

}

2、拦截器配置

1、针对某种mapping
1
2
3
4
5
6
7
8
9
10
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="handlerInterceptor1"/>
<ref bean="handlerInterceptor2"/>
</list>
</property>
</bean>
<bean id="handlerInterceptor1" class="com.shop.intercept.interceptor"/>
<bean id="handlerInterceptor2" class="com.shop.intercept.interceptor"/>
2、针对所有mapping配置全局拦截器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--拦截器 -->
<mvc:interceptors>
<!--多个拦截器,顺序执行 -->
<mvc:interceptor>
<!-- /*表示只拦截一级请求, /**表示拦截表示1,2级请求都会被拦截-->
<mvc:mapping path="/**"/>
<bean class="com.shop.springmvc.filter.HandlerInterceptor1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.shop.springmvc.filter.HandlerInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>

或者

1
2
3
4
5
6
7
<bean name="hi2" class="com.shop.intercept.interceptor"></bean>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<ref bean="hi2"></ref>
</mvc:interceptor>
</mvc:interceptors>

3、流程测试

1
2
3
4
5
6
7
8
//执行controller之前
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {}
//执行controller后,但未返回视图
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
//执行controller后,而且返回视图
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {}


以上三者的执行流程

定义两个拦截器分别为:HandlerInterceptor1和HandlerInteptor2,每个拦截器的preHandler方法都返回true。

image-20200812211401336

1、运行流程

HandlerInterceptor1..preHandle..

HandlerInterceptor2..preHandle..

HandlerInterceptor2..postHandle..

HandlerInterceptor1..postHandle..

HandlerInterceptor2..afterCompletion..

HandlerInterceptor1..afterCompletion..

2、中断流程测试

从日志看出第一个拦截器的preHandler方法返回false后第一个拦截器只执行了preHandler方法,其它两个方法没有执行,第二个拦截器的所有方法不执行,且controller也不执行了。

HandlerInterceptor1的preHandler方法返回true,HandlerInterceptor2返回false,运行流程如下:

HandlerInterceptor1..preHandle..

HandlerInterceptor2..preHandle..

HandlerInterceptor1..afterCompletion..

3、总结:

preHandle按拦截器定义顺序调用

postHandler按拦截器定义逆序调用

afterCompletion按拦截器定义逆序调用

postHandler在拦截器链内所有拦截器返成功调用 ,afterCompletion只有preHandle返回true才调用

登录拦截

1、编写拦截器

实现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
38
39
40
41

public class interceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("controller执行前调用此方法");
String requestURI = httpServletRequest.getRequestURI();
//如果URI结尾时login就放行
/**
if(request.getRequestURI().indexOf("login")>=0){
return true;
}
*/
if(requestURI.endsWith("login")){
return true;
}else
{
HttpSession session = httpServletRequest.getSession(false);
if(null!=session){
User user = (User) session.getAttribute("user");
if(null!=user)
if(user.getUsername()!=null){
//如果能拿到username就放行
System.out.println(user.getUsername());
return true;
}
}
httpServletResponse.sendRedirect("/tologin");
System.out.println("重定向跳转登录界面");
return false;
}
}

@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("controller执行后但未返回视图前调用此方法");
}

@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("controller执行后且视图返回后调用此方法");
}