SpringMVC
感谢狂神的分享:B站视频地址
SpringMVC:官网地址
参考博客:地址
1. SpringMVC简介
1.1 概念
- MVC:是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计规范。代码实现上:模型(service、dao)控制器(controller)视图(jsp)。
- 概念:SpringMVC 是Spring框架中的一个分支,是基于Java实现MVC的轻量级Web框架
- 核心:Spring的web框架围绕DispatcherServlet [ 调度Servlet ] 设计的
- 特点:轻量级,简单易学;高效,基于请求响应的mvc框架;与spring兼容性好,无缝结合;约定大于配置;功能强大:RESTful,数据验证,格式化,本地化,主题
1.2 SpringMVC执行原理
- DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。
- 假设url为 : http://localhost:8080/SpringMVC/hello,拆成三个部分:
- 服务器域名:http://localhost:8080
- 服务器上的站点:SpringMVC
- 控制器:hello
- HandlerMapping(处理器映射器)。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler。
- HandlerExecution,表示具体的 Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello。
- HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。
- HandlerAdapter(处理器适配器),其按照特定的规则去执行Handler。
- Handler 让具体的 Controller 执行
- Controller 将具体的执行信息返回给HandlerAdapter,如ModelAndView。
- HandlerAdapter 将视图逻辑名或模型传递给 DispatcherServlet。
- DispatcherServlet调用ViewResolver(视图解析器)来解析HandlerAdapter传递的逻辑视图名
DispatchServlet是接收前端的所有路径的请求,接收到之后根据路径去找对应的controller方法,因为每一个controller方法都被用@RequestMapping注解给予对应的路径 - ViewResolver将解析的逻辑视图名传给DispatcherServlet
- DispatcherServlet根据 ViewResolver 解析的视图结果,调用具体的视图
1-4相当于原来servletservlet-mapping,5-8,原来servlet,10-12就是servlet 请求转发和重定向
2. 第一个SpringMVC
2.1 不使用注解
创建一个maven项目,导入jar包依赖
<!--导入依赖-->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.20</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl-api -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
项目结构如下
web.xml中注册 DispatcherServlet
<?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 : SpringMVC的核心-->
<servlet>
<servlet-name>SpringMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--指定contextConfigLocation对应的配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring_servlet.xml</param-value>
</init-param>
</servlet>
<!--配置要处理的请求路径:
/ : 只匹配请求,不包含.jsp
/* : 匹配所有请求,包含.jsp
-->
<servlet-mapping>
<servlet-name>SpringMvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
配置SpringMvc核心配置文件
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://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/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 处理器映射器HandlerMapping: 根据类的名称,查找访问的url 的控制器 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- 处理器适配器HandlerAdapter:controller将出处理好的数据,返回给handlerAdapter -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!--视图解析器 ViewResolver
将后端处理好的数据返回给 DispatcherServlet,DS交给ViewResolver再解析一遍,确认无误后传给前端
1. 获得 ModelAndView 数据
2. 解析 ModelAndView 的视图名字
3. 拼接视图名字和前、后缀,找到对应的视图:/WEB-INF/jsp/hello.jsp
4. 将数据渲染到视图上(hello.jsp)
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<!--前缀和后缀,添加在modelAndView的前后-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--BeanNameUrlHandlerMapping处理器:绑定跳转的url=页面访问的网址-->
<bean name="/hello" class="com.kuang.controller.HelloController"/>
</beans>
自定义类,实现Controller接口
//创建一个类,实现Controller接口
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//创建 ModelAndView 对象
ModelAndView modelAndView = new ModelAndView();
//在对象中添加属性值
modelAndView.addObject("msg","hello,Spring MVC !!!");
//设置要跳转的视图。
modelAndView.setViewName("hello"); //结合 prefix 、suffix ,会映射成路径: WEB-INF/jsp/hello.jsp
return modelAndView;
}
}
前端JSP页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
注:如果项目无法正常启动,查看 project structure --> artifacts右侧 -->Output Layout 下 WEB-INF是否导入相关jar包
SpringMVC底层工作原理:
2.2 使用注解
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">
<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.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>
springMVC.xml
- 省略了 handlerMapping、HandlerAdapter的手动配置,由annotation-driven 完成
- 配置注解扫描指定的包
<?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
https://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">
<!--自动扫描包,让指定包下的注解生效,由IOC容器统一管理-->
<context:component-scan base-package="com.kuang.controller"/>
<!--让SpringMVC不处理静态资源-->
<mvc:default-servlet-handler/>
<!--支持Mvc注解驱动
在spring中一般采用@RequestMapping来完成映射关系
要使@RequestMapping生效,必须在上线文注册 DefaultAnnotationHandlerMapping
和 AnnotationMethodHandlerAdapter 的实例
这两个实例,分别在类级别和方法级别处理
而Annotation-driven自动帮我们完成,上面两个实例的注入
-->
<mvc:annotation-driven/>
<!-- 配置视图解析器,定义前后缀 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
controller
@Controller
public class HelloController {
/*添加方法的访问路径
如果方法加上 @ResponseBody,或者类用@RestController标识
则方法返回字符串,不再被视图解析器处理
*/
@RequestMapping("/hello")
public String hello(Model model){
//封装返回数据
model.addAttribute("msg","hello,annotation method!!!");
return "test"; //被视图解析器处理,拼装前、后缀形成路径
}
}
3. Controller & RestFul
3.1 RestFul
优点:
- 安全:看不出源代码的参数也意义
- 地址复用:使得 get 和 post 访问相同的URL,框架自动进行类型转换
- 高效:支持缓存
缺点:
- 不像原生URL见名只意,URL理解不直观
实现方式
- 注解:
@GetMapping("/addRest/{a}/{b}")
+ 参数@PathVariable int a, @PathVariable int b
url地址:http://localhost:8080/xxx/addRest/1/2 - 注解:
@PostMapping("/addRest/{a}/{b}")
+ 参数@PathVariable int a, @PathVariable int b
使用Post提交url地址:http://localhost:8080/xxx/addRest/1/2
@Controller
public class RestFulController {
/**
* 原生的url:http://localhost:8080/xxx/add?a=1&b=1
*/
@RequestMapping("/add")
public String getAdd1(int a, int b, Model model) {
int result = a + b;
model.addAttribute("add", "原生的url:结果为" + result);
return "add";
}
/**
* RestFul方式一:method = get
* RequestMapping("/addRest/{a}/{b}" method=requestMethod.GET) = @GetMapping()
* http://localhost:8080/springmvc_04/addRest/1/1
*/
@GetMapping("/addRest/{a}/{b}")
public String getAdd2(@PathVariable int a, @PathVariable int b, Model model) {
int result = a + b;
model.addAttribute("add", "Rest的url:结果为" + result);
return "addRest";
}
/**
* 复用相同的url
* RestFul方式二:method=post,使用RestFul的话,请求的url和GET就一样了
*/
@PostMapping("/addRest/{a}/{b}")
public String getAdd3(@PathVariable int a, @PathVariable int b, Model model) {
int result = a + b;
model.addAttribute("add", "Rest的url:结果为" + result);
return "addRest";
}
}
3.2 转发与重定向
- 可以使用原生的request转发或者response重定向
- 推荐使用SpringMvc的
return “forward:xxx”/"redirect:xxx"
@Controller
public class ModelTest1 {
//原生的转发:返回值是void,没有经过视图解析器;原生的重定向同样如此,都不走视图解析器,直接重定向
@RequestMapping("/test1")
public void test1(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String id = request.getSession().getId();
System.out.println(id);
request.getRequestDispatcher("index.jsp").forward(request,response);
}
//SpringMvc转发:测试结果是不走视图解析器,url没变是转发
@RequestMapping("/test2")
public String test2(Model model) {
model.addAttribute("demo1","这是test2中的Spring转发");
return "forward:/WEB-INF/jsp/demo1.jsp";
}
//SpringMvc重定向:测试结果是不走视图解析器
@RequestMapping("/test3")
public String test3() {
System.out.println("跳转回首页index.jsp");
return "redirect:index.jsp";
}
}
3.3 接收参数与数据回显
- 前端提交的name和后端映射器接受的形参名一样,则直接接受
- 前端提交的name和后端映射器接受的形参名不用一样,在形参前
@RequestParam("xxx")
更改名称一致 - 如果后端参数封装成一个pojo,前端传过来的name会自动pojo中的成员属性,不匹配的属性=null/0
@Controller
public class UserController {
/** http://localhost:8080/springmvc_04/t1?id=1&name=abc&age=18
* @param user SpringMvc 会自动封装数据到参数里的pojo,不匹配的属性=null/0
*/
@GetMapping("/t1")
public String getUser(User user){
System.out.println(user);
return "test1";
}
}
3.4 乱码问题
- 方法一:web.xml里面配置的SpringMvc自带的过滤器
CharacterEncodingFilter
,<url-pattern>/*</url-pattern>
:因为要跳转到xxx.jsp页面,所以url是 /* (≠/)
<!--web容器解决中文乱码问题-->
<filter>
<filter-name>encode</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>encode</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 方法二:一劳永逸,但需要重启Tomcat服务器,修改Tomcat里面的server.xml配置文件:URIEncoding = “UTF-8”
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding = "UTF-8"/>
<!-- A "Connector" using the shared thread pool-->
4. JSON
4.1 初识JSON
JSON(JavaScript Object Notation, JS对象标记)是一种轻量级的数据交换格式
。它基于 ECMAScript的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据
。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>json</title>
<script type="text/javascript">
//user是一个js对象
var user = {
name: "张三",
age: 18,
sex: "男"
};
//后端传的json其实是一个字符串,前端将后端传的json转换成js对象渲染在页面上
//jsonUser:模拟后端传的json数据
//js转换成json
var jsonUser = JSON.stringify(user);
console.log(jsonUser);
//jsUser:js是一个对象
//json转换成js
var jsUser = JSON.parse(jsonUser);
console.log(jsUser)
</script>
</head>
</html>
4.2 Jackson-Databind
1. 导入依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>xxx</version>
</dependency>
- json=一个字符串,所以会有中文乱码问题,需要在springmvc.xml配置
<!--注解加载映射器、适配器,解决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>
2. 编写Controller
@RestControoler
:该类下所有方法不走视图解析器,返回一个json数据@ResponseBody
:该方法不走视图解析器,返回一个json数据
@RestController
public class UserController {
//Json返回一个对象
@RequestMapping("/t1")
public String json1() throws JsonProcessingException {
User user = new User(1, "张三", 20);
ObjectMapper jacksonMapper = new ObjectMapper();
String str_user = jacksonMapper.writeValueAsString(user);
//str_user有中文乱码问题,springMvc可以统一配置
return str_user;
}
}
4.3 FastJson
1. 导包
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>xxx</version>
</dependency>
2. 介绍
- JSONObject 代表JSON对象
JSONObject实现了Map接口,对应的是json对象,通过各种形式的get()方法可以获取对象中的数据,使用诸如size(),isEmpty()方法获取 "键:值"的个数或判断是否为空。本质是通过实现Map接口,病调用其中的方法完成的。 - JSONArray 代表JSON数组
内部是List接口中的方法,完成操作的 - JSON 代表 JSONObject与JSONArray的转化
主要实现 json对象、json数组、Javabean对象、json字符串之间的相互转化
5. SSM整合
5.1 环境准备
数据库:
CREATE TABLE `books` (
`bookId` int(10) NOT NULL AUTO_INCREMENT COMMENT '书id',
`bookName` varchar(100) NOT NULL COMMENT '书名',
`bookCounts` int(11) NOT NULL COMMENT '数量',
`detail` varchar(200) NOT NULL COMMENT '描述',
KEY `bookId` (`bookId`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
pom.xml
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.kuang</groupId>
<artifactId>SpringMVC</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>spring_json</module>
<module>spring_ssm</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--junit单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<!--c3po连接池,一般用 druid-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!--jsp 页面所需的包-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<!--mybatis-spring-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.21</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.21</version>
</dependency>
</dependencies>
<!--Maven资源过滤设置-->
<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>
5.2 项目结构
5.3 整合Mybatis
mybatis-config.xml配置如下(数据库连接交给 spring_dao.xml 管理):
<?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">
<!--mybatis 核心配置文件-->
<configuration>
<!--配置mybatis日志-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--配置数据源,交给Spring去做-->
<!-- 配置别名 -->
<typeAliases>
<!-- ResultMap 默认类名小写为使用id -->
<package name="com.kuang.pojo"/>
</typeAliases>
<mappers>
<mapper class="com.kuang.dao.BooksMapper"></mapper>
</mappers>
</configuration>
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/stu?useSSL=false&useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=123456
5.4 整合Spring
分成spring_dao.xml,spring_service.xml,spring_mvc.xml 三层整合,然后通过Spring核心配置文件,将三部分合并到一起
Spring_dao.xml主要配置mybatis-spring.jar中的信息
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--关联数据库配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--2 连接池 这次使用c3p0的连接池。
常见的数据库驱动:
dbcp:半自动操作,不能自动连接
c3p0:自动化操作,并且可以配置到对象中
druid,hikari(SpringBoot)-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!--c3p0的私有属性-->
<property name="maxPoolSize" value="30"/>
<property name="minPoolSize" value="10"/>
<property name="autoCommitOnClose" value="false"/><!--关闭连接后,不自动commit-->
<property name="checkoutTimeout" value="10000"/>
<property name="acquireRetryAttempts" value="2"/>
</bean>
<!--3 sqlSessionFactory-->
<bean name="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--关联数据源-->
<property name="dataSource" ref="dataSource"/>
<!--绑定mybatis核心配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!--4.配置dao接口扫描,动态实现了讲dao接口注入到Spring容器,不用手动配置mapper.xml-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 注入 sqlSessionFactoryBean -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
<!-- 配置要扫描的包 -->
<property name="basePackage" value="com.kuang.dao"/>
</bean>
</beans>
spring_service.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--1. 扫描Service下的包-->
<context:component-scan base-package="com.kuang.service"/>
<!--2. 将所有的业务类,注入到Spring中。这里使用配置,平常使用注解-->
<bean id="booksServiceImpl" class="com.kuang.service.BooksServiceImpl">
<property name="booksMapper" ref="booksMapper"/>
</bean>
<!--3. 声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--4. aop横切事务,这里用不到-->
</beans>
通过applicationContext.xml,合并spring的几个配置文件
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="spring_dao.xml"/>
<import resource="spring_service.xml"/>
<import resource="spring_mvc.xml"/>
</beans>
5.5 整合SpringMVC
添加web项目支持,并配置web.xml
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">
<!--SpringMVC配置 : 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:applicationContext.xml</param-value>
</init-param>
</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>
spring_mvc.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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://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
https://www.springframework.org/schema/context/spring-context.xsd">
<!--1. 注解驱动-->
<mvc:annotation-driven/>
<!--2. 静态资源过滤-->
<mvc:default-servlet-handler/>
<!--3. 扫描的controller包-->
<context:component-scan base-package="com.kuang.controller"/>
<!--4. 试图解析器-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
5.6 dao层
dao层有 BooksMapper.java、 BooksMapper.xml,内容如下:
public interface BooksMapper {
//增加一本书
int addBook(Books books);
//删除一本书
int deleteBookById(@Param("bookId") int id);
//修改一本书
int updateBook(Books books);
//查询一本书根据id
Books queryBookById(@Param("bookId")int id);
//查询全部书
List<Books> queryAllBook();
Books queryBookByName(@Param("bookName") String bookName);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--mybatis 核心配置文件-->
<mapper namespace="com.kuang.dao.BooksMapper">
<insert id="addBook" parameterType="Books">
insert into ssmbuild.books(bookName, bookCounts, detail)
values (#{bookName},#{bookCounts},#{detail});
</insert>
<delete id="deleteBookById" parameterType="int">
delete from ssmbuild.books where bookId = #{bookId}
</delete>
<update id="updateBook" parameterType="Books">
update ssmbuild.books set
bookName=#{bookName},
bookCounts=#{bookCounts},
detail=#{detail}
where bookId=#{bookId};
</update>
<select id="queryBookById" parameterType="int" resultType="Books">
select * from ssmbuild.books where bookId = #{bookId};
</select>
<select id="queryAllBook" resultType="Books">
select * from ssmbuild.books;
</select>
<select id="queryBookByName" parameterType="String" resultType="Books">
select * from ssmbuild.books where bookName=#{bookName}
</select>
</mapper>
5.7 service层
public interface BooksService {
//增加一本书
int addBook(Books books);
//删除一本书
int deleteBookById(int id);
//修改一本书
int updateBook(Books books);
//查询一本书根据id
Books queryBookById(int id);
//查询全部书
List<Books> queryAllBook();
Books queryBookByName(String bookName);
}
public class BooksServiceImpl implements BooksService {
//在spring中,注入Dao层
private BooksMapper booksMapper;
public void setBooksMapper(BooksMapper booksMapper) {
this.booksMapper = booksMapper;
}
@Override
public int addBook(Books books) {
return booksMapper.addBook(books);
}
@Override
public int deleteBookById(int id) {
return booksMapper.deleteBookById(id);
}
@Override
public int updateBook(Books books) {
return booksMapper.updateBook(books);
}
@Override
public Books queryBookById(int id) {
return booksMapper.queryBookById(id);
}
@Override
public List<Books> queryAllBook() {
return booksMapper.queryAllBook();
}
@Override
public Books queryBookByName(String bookName) {
return booksMapper.queryBookByName(bookName);
}
}
5.8 controller层
@Controller
@RequestMapping("book")
public class BooksController {
@Autowired
@Qualifier("booksServiceImpl")
private BooksService booksService;
@RequestMapping("allBook")
public String allBook(Model model){
List<Books> books = booksService.queryAllBook();
model.addAttribute("list", books);
return "allBook";
}
//跳转到增加书籍页面
@RequestMapping("/toAddBook")
public String toAddBook() {
return "addBook";
}
//添加书籍,成功后跳转至所有书籍页面
@RequestMapping("/addBook")
public String addBook(Books books) {
int result = booksService.addBook(books);
if (result > 0) {
System.out.println("添加书籍成功");
}
return "redirect:/book/allBook";
}
@RequestMapping("/toUpdateBook")
public String toUpdate(int bookId, Model model) {
Books books = booksService.queryBookById(bookId);
model.addAttribute("book", books);
return "updateBook";
}
//没有提交事务操作,更新会失败
@RequestMapping("/updateBook")
public String updateBook(Books books) {
int result = booksService.updateBook(books);
if (result > 0) {
System.out.println("修改书籍成功");
}
return "redirect:/book/allBook";
}
//删除书籍,回顾RestFul风格
@RequestMapping("/deleteBook/{bookId}")
public String deleteBook(@PathVariable("bookId") int bookId) {
int result = booksService.deleteBookById(bookId);
if (result > 0) {
System.out.println("删除书籍成功");
}
return "redirect:/book/allBook";
}
@RequestMapping("/queryBook")
public String queryBook(String queryBookName, Model model) {
Books books = booksService.queryBookByName(queryBookName);
//复用,这样就显示一个
List<Books> list = new ArrayList<>();
list.add(books);
if (books == null) {
list= booksService.queryAllBook();
model.addAttribute("errMsg","未查任何书籍");
}
model.addAttribute("list", list);
return "allBook";
}
}
5.9 前端页面
allBook.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>所有书籍</title>
<%--BootStrap美化界面--%>
<link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row clearfix">
<%--屏幕分成12列--%>
<div class="col-md-12 column">
<div class="page-header">
<h1>
<small> 书籍列表——————————显示所有书籍</small>
</h1>
</div>
</div>
<div class="row">
<div class="col-md-4 column">
<a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增书籍</a>
<a class="btn btn-primary" href="${pageContext.request.contextPath}/book/allBook">显示全部书籍</a>
</div>
<div class="col-md-4 column"></div>
<div class="col-md-8 column">
<form action="${pageContext.request.contextPath}/book/queryBook" method="post" style="float: right">
<%--前端未传任何信息,就显示错误提示信息:未查任何书籍--%>
<span style="color: red;font-weight: bold">${errMsg}</span>
<%--class="form-inline"保证在同一行--%>
<input type="text" name="queryBookName" class="form-inline" placeholder="请输入要查询的书籍名称">
<input type="submit" value="查询" class="btn btn-primary">
</form>
</div>
</div>
</div>
<div class="row clearfix">
<div class="col-md-12 column">
<table class="table table-hover table-striped">
<thead>
<tr>
<th>书籍编号</th>
<th>书籍名称</th>
<th>书籍数量</th>
<th>书籍详情</th>
<th>操作</th>
</tr>
</thead>
<%--书籍从数据库中查询出来,从这个list中遍历出来,foreach--%>
<tbody>
<c:forEach var="book" items="${list}">
<tr>
<td>${book.bookId}</td>
<td>${book.bookName}</td>
<td>${book.bookCounts}</td>
<td>${book.detail}</td>
<td>
<a href="${pageContext.request.contextPath}/book/toUpdateBook?bookId=${book.bookId}">修改</a>
|
<a href="${pageContext.request.contextPath}/book/deleteBook/${book.bookId}">删除</a>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>
addBook.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>添加书籍</title>
<%--BootStrap美化界面--%>
<link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="row clearfix">
<%--屏幕分成12列--%>
<div class="col-md-12 column">
<div class="page-header">
<h1>
<small>新增书籍</small>
</h1>
</div>
</div>
</div>
<%--BootStrap官网拿"表单"数据 name属性保证pojo属性名称一致 required保证必须提交--%>
<form action="${pageContext.request.contextPath}/book/addBook" method="post">
<div class="form-group">
<label for="bName">书籍名称:</label>
<input type="text" name="bookName" class="form-control" id="bName" required>
</div>
<div class="form-group">
<label for="bCount">书籍数量:</label>
<input type="text" name="bookCounts" class="form-control" id="bCount" required>
</div>
<div class="form-group">
<label for="bDesc">书籍描述:</label>
<input type="text" name="detail" class="form-control" id="bDesc" required>
</div>
<div class="form-group">
<input type="submit" class="form-control" value="添加">
</div>
</form>
</body>
</html>
updateBook.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>修改书籍</title>
<title>Title</title>
<%--BootStrap美化界面--%>
<link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="row clearfix">
<%--屏幕分成12列--%>
<div class="col-md-12 column">
<div class="page-header">
<h1>
<small>修改书籍</small>
</h1>
</div>
</div>
</div>
<%--BootStrap官网拿"表单"数据 name属性保证pojo属性名称一致 required保证必须提交--%>
<form action="${pageContext.request.contextPath}/book/updateBook" method="post">
<%--提交失败:没有回显:问题:
1. 事务没有提交
2. sql执行失败,没有提交BookId,需要前端的隐藏域
--%>
<%--添加bookId的隐藏域--%>
<input type="hidden" name="BookId" value="${book.bookId}">
<div class="form-group">
<label for="bName">书籍名称:</label>
<input type="text" name="bookName" class="form-control" id="bName" value="${book.bookName}" required>
</div>
<div class="form-group">
<label for="bCount">书籍数量:</label>
<input type="text" name="bookCounts" class="form-control" id="bCount" value="${book.bookCounts}"required>
</div>
<div class="form-group">
<label for="bDesc">书籍描述:</label>
<input type="text" name="detail" class="form-control" id="bDesc" value="${book.detail}" required>
</div>
<div class="form-group">
<input type="submit" class="form-control" value="修改">
</div>
</form>
</body>
</html>
6. AJax
6.1 概念
Ajax:Asynchronous Javascript And XML(异步JavaScript和XML),一个异步无刷新请求,无需更新整个页面就异步加载一些数据。
使用iframe,模拟异步请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>模拟Ajax异步请求</title>
<script>
function go() {
var url = document.getElementById("url").value;
document.getElementById("iframe1").src = url;
}
</script>
</head>
<body>
<div>
<p>请输入地址:</p>
<p>
<input type="text" id="url">
<input type="button" id="button" value="提交" onclick="go()">
</p>
</div>
<div>
<iframe id="iframe1" style="width:100%;height: 500px">
</iframe>
</div>
</body>
</html>
6.2 通过JQ发送Ajax
初试Ajax
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
<%--加载动态的JQ资源--%>
<script src="https://code.jquery.com/jquery-3.4.1.js"></script>
<script>
/*
AJax参数:
url:后端接受的地址
data:后端接受到的请求参数,json数据格式=前后端分离时候,后端传的数据便于前端接受就是json数据
success:后端接受成功返回的函数
error:后端失败接受返回的函数
*/
function username() {
$.post({
url: "${pageContext.request.contextPath}/a1",
data: {"name": $("#username").val()},
success: function (data, status) {
console.log("data:" + data);
console.log("status:" + status);
}
})
}
</script>
</head>
<body>
<%--
实现Ajax异步请求
1 绑定单击事件
2 单击事件函数使用Jq:$.post({})
--%>
用户名:<input type="text" id="username" onclick="username()">
</body>
</html>
异步加载
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Ajax</title>
<script src="https://code.jquery.com/jquery-3.4.1.js"></script>
<script>
/*页面加载完,触发函数*/
$(function () {
/*绑定btn单击事件*/
$("#btn").click(function () {
/*JQ使用Ajax异步请求*/
$.post("${pageContext.request.contextPath}/a2", function (data) {
// data接受返回的值
//console.log(data);
var html = "";
for (let i = 0; i < data.length; i++) {
html += "<tr>" +
"<td>" + data[i].name + "</td>" +
"<td>" + data[i].age + "</td>" +
"<td>" + data[i].sex + "</td>" +
+"<tr>"
}
$("#content").html(html);
});
});
})
</script>
</head>
<body>
<input type="button" id="btn" value="加载数据">
<table>
<tr>
<td>姓名</td>
<td>年龄</td>
<td>性别</td>
</tr>
<tbody id="content">
<!-- 将后台的数据,拼接成之后,放入到这里面 -->
</tbody>
</table>
</body>
</html>
登录验证
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>login</title>
<script src="https://code.jquery.com/jquery-3.4.1.js"></script>
<script>
function nameFun() {
$.post({
url: "${pageContext.request.contextPath}/a3",
data: {"name": $("#name").val()},
success: function (data) {
//console.log(data);
if (data.toString() === "用户名成功") {
$("#userInfo").css("color", "green");
} else {
$("#userInfo").css("color", "red");
}
$("#userInfo").html(data);
}
})
}
function passwordFun() {
$.post({
url: "${pageContext.request.contextPath}/a3",
data: {"password": $("#password").val()},
success: function (data) {
if (data.toString() === "密码正确") {
$("#userPasswordInfo").css("color", "green");
}else {
$("#userPasswordInfo").css("color", "red");
}
$("#userPasswordInfo").html(data);
}
})
}
</script>
</head>
<body>
<p>
用户名:<input type="text" id="name" onclick="nameFun()">
<%--span提示信息--%>
<span id="userInfo"></span>
</p>
<p>
用户密码:<input type="password" id="password" onclick="passwordFun()">
<span id="userPasswordInfo"></span>
</p>
</body>
</html>
7. 拦截器
7.1 概念
拦截器(Interceptor)基于反射机制(动态代理)实现,Struts、SpringMVC
都有拦截器功能,不依赖Tomcat等容器,不能修改 Request
中的内容,用于验证信息(用户是否登录、权限验证、日志记录等
)
过滤器(Filter)与拦截器(Interceptor)都体现了AOP
的思想,区别如下:
实现方式不同
Filter
基于回调函数实现,依赖于Tomcat等容器,只能用在web项目中。而Interceptor
是Spring的一个组件,不依赖于Tomcat等容器,也可以用在Application、Swing等容器中。
触发时机不同
Filter
在请求进入容器之后,但进入Servlet容器之前进行预处理,请求结束是在Servlet处理完之后。
Interceptor
在请求进入Servlet后,但进入Controller之前进行预处理,Controller中渲染了对应的视图之后请求结束。
拦截范围不同
Filter
几乎可以对所有进入容器的请求起作用
Interceptor
只对Controller中请求或static目录下的资源请求起作用
7.2 自定义拦截器
自定义拦截器,实现HandlerInterceptor
接口:
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//return true:执行下一个拦截器
System.out.println("===========处理前,这里进行拦截处理=================");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("===========处理后,通常进行日志管理=================");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("===========清理中=================");
}
}
在Spring配置文件中注册:
<!--拦截器配置-->
<mvc:interceptors>
<mvc:interceptor>
<!--
/** 是拦截所有的请求,比如/admin/a1/2131
-->
<mvc:mapping path="/**"/>
<bean class="com.ssl.config.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
7.3 登录验证
拦截器
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
request.getRequestURL();
//URL: http://localhost:8080/springmvc_07_interceptor/user/main
System.out.println("URL:" + request.getRequestURL());
//URI: /springmvc_07_interceptor/user/main
System.out.println("URI:" + request.getRequestURI());
if (session.getAttribute("username") == null || session.getAttribute("password") == null) {
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
} else if (session.getAttribute("username").equals("admin") && session.getAttribute("password").equals("123456")) {
return true;
}
if (request.getRequestURI().contains("login")) {
return true;
}
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false;
}
}
控制层
@Controller
@RequestMapping("/user")
public class LoginController {
@RequestMapping("/main")
public String main() {
//沒登陸就不等進入首頁
return "main";
}
@RequestMapping("/gologin")
public String gologin() {
return "login";
}
@RequestMapping("/login")
public String login(String username, String password, HttpSession session, Model model) {
session.setAttribute("username", username);
session.setAttribute("password", password);
model.addAttribute("username", username);
return "main";
}
@RequestMapping("/outUser")
public String outUser(HttpSession session) {
session.removeAttribute("username");
session.removeAttribute("password");
return "main";
}
}
8. 文件上传&下载
8.1 上传
- 前端form添加enctype=“multipart/form-data”,method=“post”
<form enctype="multipart/form-data" method="post" action="">
</form>
- 后端pom导包
<dependencies>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!--导入高版本的api-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
</dependencies>
- Spring自带的文件上传,application.xml配置
<!--4 文件上传配置-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"/>
<!--最大上传大小:单位是1字节-->
<property name="maxUploadSize" value="10485760"/>
<property name="maxInMemorySize" value="40960"/>
</bean>
@RestController
public class FileController {
@RequestMapping("/upFile")
public String upFile(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
//设置文件保存路径
String path = request.getServletContext().getRealPath("/upload");
System.out.println("path:" + path);
File realPath = new File(path);
if (!realPath.exists()) {
realPath.mkdir();
}
System.out.println("上传的文件地址:" + realPath);
//CommonsMultipartFile的方法写文件,简化
file.transferTo(new File(realPath + "/" + file.getOriginalFilename()));
return "redirect:/index.jsp";
}
}
8.2 下载
- 方式一:写方法下载
@RequestMapping(value = "/download")
public String downLoad(HttpServletResponse response, HttpServletRequest request) throws IOException {
//手动设置,要下载的图片地址
String path = request.getServletContext().getRealPath("/upload");
String fileName = "1.png";
//设置响应头
response.reset();//设置页面不缓存,清空buffer
response.setCharacterEncoding("UTF-8");
response.setContentType("multipart/form-data");//二进制传输数据
response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8"));
File file = new File(path, fileName);
//读取文件-输入流
InputStream input = new FileInputStream(file);
//写入文件-输出流
OutputStream out = response.getOutputStream();
byte[] buff = new byte[1024];
int index = 0;
while ((index = input.read(buff)) != -1) {
out.write(buff,0,index);
out.flush();
}
input.close();
out.close();
return "redirect:/index.jsp";
}
- 方法二:标签直接获取web下的静态资源
<a href="${pageContext.request.contextPath}/static/1.png">图片下载</a>