SpringMVC(Spring Web MVC 框架)
一、SpringMVC
SpringMVC是一个spring家族 MVC(Model View Controller 模型视图控制器)框架
Spring web MVC 框架提供了模型-视图-控制的体系结构和可以用来开发灵活、松散耦合的 web 应用程序的组件。MVC 模式导致了应用程序的不同方面(输入逻辑、业务逻辑和 UI 逻辑)的分离,同时提供了在这些元素之间的松散耦合。
-
模型封装了应用程序数据,并且通常它们由 POJO 组成。
-
视图主要用于呈现模型数据,并且通常它生成客户端的浏览器可以解释的 HTML 输出。
- 控制器主要用于处理用户请求,并且构建合适的模型并将其传递到视图呈现。
DispatcherServlet
Spring Web 模型-视图-控制(MVC)框架是围绕 DispatcherServlet 设计的,DispatcherServlet 用来处理所有的 HTTP 请求和响应.
-
收到一个 HTTP 请求后,DispatcherServlet 根据 HandlerMapping 来选择并且调用适当的控制器。
-
控制器接受请求,并基于使用的 GET 或 POST 方法来调用适当的 service 方法。Service 方法将设置基于定义的业务逻辑的模型数据,并返回视图名称到 DispatcherServlet 中。
-
DispatcherServlet 会从 ViewResolver 获取帮助,为请求检取定义视图。
- 一旦确定视图,DispatcherServlet 将把模型数据传递给视图,最后呈现在浏览器中。
二、Spring MVC基于Annotation开发-Spring MVC环境搭建
(一)创建maven工程
(二)添加依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
(三)web.xml中配置前端控制器(核心控制器),其实就是一个Servlet
<?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 Web MVC 框架-->
<!--配置spring核心的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.xml</param-value>
</init-param>
<!--
1)load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法)。
2)它的值必须是一个整数,表示servlet应该被载入的顺序
3)当值为0或者大于0时,表示容器在应用启动时就加载并初始化这个servlet;
4)当值小于0或者没有指定时,则表示容器在该servlet被选择时才会去加载。
5)正数的值越小,该servlet的优先级越高,应用启动时就越先加载。
6)当值相同时,容器就会自己选择顺序来加载。
-->
<!-- tomcat启动时候创建DispatcherServlet并初始化-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--正斜杠 所有的请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--配置编码过滤器(配置spring的CharacterEncodingFilter解决中文乱码)-->
<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>
(四)springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置扫描的基本包
扫描四大注解的类(@Controller,@Service,@Repository,@Component),纳入springmvc管理
-->
<context:component-scan base-package="com.tjetc"></context:component-scan>
<!--开启注解驱动-->
<!--开启用后,Spring会默认帮我们注册处理请求,参数和返回值的类。
如:将被@Controller和@RequestMapping注解的处理器方法放入了映射表,使其生效-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--对于图片等静态资源使用默认的servlet处理器实际就是Tomcat-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
<!--配置视图解析器 查找指定的视图-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置视图 jstl视图 解析view的代码转换成html
JstlView可以使用jstl解析展示数据
-->
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
<!--配置前缀和后缀 简化返回页面的代码
例如: @RequestMapping注解到的方法中,return "hello" , 实际上 return "/hello.jsp" 页面
-->
<!--配置前缀 prefix代表自动在返回值前面添加 /-->
<property name="prefix" value="/"></property>
<!--配置后缀 suffix代表自动返回值后加上 .jsp-->
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
(五)HelloController
package com.tjetc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
//@Controller 注释表明一个特定类是一个控制器的作用。
@Controller
public class HelloController {
/*@RequestMapping 注释用于映射 URL 到整个类或一个特定的处理方法。
访问的url映射到加上@RequestMapping的方法上面,注解的值是URL
*/
@RequestMapping("/hello")
//ModeL对象:用来存储数据,传输的页面中,在页面就可以获取数据
public String hello(Model model) {
model.addAttribute("msg", "Hello Spring MVC Framework(框架)!");
//请求转发到页面,/index.jsp 页面
return "index";
}
}
(六)index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hello Spring MVC</title>
<base href="<%=request.getContextPath()%>/">
</head>
<body>
<%--使用el获取--%>
<h1>${msg}</h1>
</body>
</html>
(七)运行效果
(八)@RequestMapping 写在类上, controller 里所有的方法映射加一个前缀
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("user") //@RequestMapping 写在类上, controller 里所有的方法映射加一个前缀
public class HelloController {
@RequestMapping("/hello")
public String hello(Model model) {
model.addAttribute("msg", "Hello Spring MVC Framework(框架)!");
return "index";
}
@RequestMapping("/order")
public String order(Model model) {
model.addAttribute("msg", "order list");
return "order";
}
}
(九)order.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<base href="<%=request.getContextPath()%>/">
</head>
<body>
<%--使用el获取model存储的值--%>
<h1>${msg}</h1>
</body>
</html>
三、Spring MVC工作流程运行原理
1、 用户发送请求至前端控制器DispatcherServlet。
2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3、 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4、 DispatcherServlet调用HandlerAdapter处理器适配器。
5、 HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
6、 Controller执行完成返回ModelAndView。
7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
8、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
9、 ViewReslover解析后返回具体View。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、 DispatcherServlet响应用户。
四、SpringMVC接收请求参数的4种方式
(一)在controller使用参数接收,请求有什么参数,方法就有什么参数
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录</title>
<base href="<%=request.getContextPath()%>/">
</head>
<body>
<form action="login" method="post">
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"/><br/>
<input type="submit" value="登录"/><br/>
</form>
</body>
</html>
package com.tjetc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LoginController {
@RequestMapping("/login")
public String login(Model model, String username, String password) {
//登录判读 TODO
model.addAttribute("username", username);
model.addAttribute("password", password);
return "welcome";
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Welcome</title>
<base href="<%=request.getContextPath()%>/">
</head>
<body>
<h1>username: ${username}</h1><br>
<h1>password: ${password}</h1>
</body>
</html>
(二)在controller使用参数接收, @RequstParam改变controller方法接收请求参数和使用参数不一致
不用@RequstParam,,传值
@Controller
public class LoginController {
@RequestMapping("/login")
/*public String login(Model model, String username, String password) {*/
public String login(Model model,String un,String pd) {
model.addAttribute("username", un);
model.addAttribute("password", pd);
return "welcome";
}
}
@RequstParam
- required=false 表示这个参数可以不传,传和不传 程序不会报错
- defaultValue 表示默认值,不提交字段,使用默认值,提交字段不使用默认值, defaultValue是在required = false情况下有意义
- required=true 表示这个参数必须传,不传的(在login.jsp删掉input的name)报400错误
@Controller
public class LoginController {
@RequestMapping("/login")
public String login(Model model,
@RequestParam(value = "username") String un,
@RequestParam(value = "password", required = false, defaultValue = "default") String pd) {
model.addAttribute("username", un);
model.addAttribute("password", pd);
//请求转发到 welcome.jsp页面
return "welcome";
}
}
(三)在controller使用实体类作为参数接收,请求参数作为实体类的属性
package com.tjetc.entity;
public class User {
private String username;
private String password;
@Override
public String toString() {
return "User{" + "username='" + username + '\'' +", password='" + password + '\'' +'}'; }
public String getUsername() {return username;}
public void setUsername(String username) {this.username = username;}
public String getPassword() {return password;}
public void setPassword(String password) {this.password = password;}
}
@Controller
public class LoginController {
@RequestMapping("/login")
public String login(User user, Model model){
System.out.println("username="+user.getUsername());
System.out.println("password="+user.getPassword());
model.addAttribute("username", user.getUsername());
model.addAttribute("password", user.getPassword());
return "welcome";
}
}
(四)restful格式接收参数
url路径传参
OrderController.java
@Controller
public class OrderController {
@RequestMapping("/del/{id}/{state}")
public String del(@PathVariable("id") int id,
@PathVariable("state") String state,
Model model) {
System.out.println("id=" + id + ",支付状态:" + state);
model.addAttribute("id", id);
model.addAttribute("state", state);
return "success";
}
}
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>订单删除成功</title>
<base href="<%=request.getContextPath()%>/">
</head>
<body>
<h1>订单删除成功,id:${id},支付状态:${state}</h1>
</body>
</html>
五、请求转发和重定向
(一)请求转发
默认就是请求转发
@RequestMapping("/del/{id}/{state}")
public String del(@PathVariable("id") int id,
@PathVariable("state") String state,
Model model) {
model.addAttribute("id", id);
model.addAttribute("state", state);
return "success";
}
(二)重定向
return “redirect:/url”;
@RequestMapping("aa")
public String aa() {
//重定向到订单删除成功页面
return "redirect:/del/3/unpaid";/*中文会乱码*/
}
六、Spring MVC视图解析器
1.什么是 Spring 视图和视图解析器
Spring MVC(Model View Controller)是 Spring 中一个重要的组成部分,而 Spring 视图和视图解析器则是 Spring MVC 中的组成部分。在介绍 Spring 视图和视图解析器前,我们先了解下在 Spring MVC 框架中,一个 Web 请求所需经历的六个阶段:
- 请求会首先被 Spring MVC 的前端请求分发器(Dispatcher)拦截。该拦截器是一个 Servlet, 需要在 web.xml 中配置,所有符合所配置的 URL 样式的访问请求,将都会被该拦截器拦截。Spring 提供了默认的分发器 org.springframework.web.servlet.DispatcherServlet,可以根据需要,决定是否需要定制自己的分发器。
- 在接收到访问请求后,分发器会根据开发人员在 Spring 配置文件或代码中的注解(Annotation),来查找合适的控制器。
- 分发器在查找到合适的控制器后,将请求转交给该控制器处理。
- 通常,控制器会调用相应服务类来处理业务逻辑,在将请求处理后,控制器需返回处理后的结果数据模型(Model)以及下一个需要显示的视图名。
- 在控制器处理结束并返回模型和视图名之后,Spring 会依次调用 Spring 容器中所注册的视图解析器,来查找符合条件的视图。
- 在获得 Spring 视图后,Spring 会根据该视图的配置信息,显示该视图。
①图 1.Spring MVC 处理流程
通过以上 Spring MVC 的介绍,我们可以发现,视图和视图解析器将出现在整个请求处理流程中的最后部分。
那么到底什么是视图和视图解析器?
简而言之,视图是指 Spring MVC 中的 V(View)
视图解析器的功能则是依据指定的规则来查找相应的视图。
2.常用视图和视图解析器简介
在开发中,视图通常就是 JSP、Velocity、FreeMarker等。Spring 默认提供了多种视图解析器,比如,我们可以使用最常用解析器 InternalResourceViewResolver 来查找 JSP 视图(与之相对应的视图类为 InternalResourceView)。通常,一个视图解析器只能查找一个或多个特定类型的视图,在遇到 Spring 不支持的视图或者我们要自定义视图查找规则的情况下,我们就可以通过扩展 Spring 来自定义自己所需的视图解析器。目前,视图解析器都需要实现接口 org.springframework.web.servlet.ViewResolver, 它包含方法 resolveViewName,该方法会通过视图名查找并返回 Spring 视图对象。表 1 列出了常用的 Spring 视图解析器。
①表 1.Spring 常用视图解析器列表
视图解析器 |
描述 |
XmlViewResolver |
接口 ViewResolver 的实现,从 XML 配置文件中查找视图实现(默认 XML 配置文件为 /WEB-INF/views.xml). |
ResourceBundleViewResolver |
接口 ViewResolver 的实现,用于从 properties 文件中查找视图。. |
UrlBasedViewResolver |
接口 ViewResolver 的实现,用于根据请求的 URL 路径返回相应的视图,该视图需为抽象类 AbstractUrlBasedView 的实现,它还有些子类,如 InternalResourceView 和 JstlView 等 . |
InternalResourceViewResolver |
UrlBasedViewResolver 的子类,通常用于查找 JSP(类 InternalResourceView)和 JSTL(类 JstlView,InternalResourceView 的子类)等视图。 |
VelocityViewResolver /FreeMarkerViewResolver |
UrlBasedViewResolver 的子类分别用于支持 Velocity(类 VelocityView)和 FreeMark 视图(类 FreeMarkerView)。 |
ContentNegotiatingViewResolver |
接口 ViewResolver 的实现,用于根据请求文件的后缀名或请求的 header 中的 accept 字段查找视图。 |
在多数项目中,InternalResourceViewResolver 是最常用的,该解析器可以返回指定目录下指定后缀的文件,它支持 JSP 及 JSTL 等视图技术。
在 Web 开发中,我们的前端显示可以是 JSP、Excel、Velocity 、FreeMarker等,在 Spring 中,不同的前端显示技术都有其对应的 Java 视图类,正如表 1 所提到的,InternalResourceView 可以代表 JSP 视图,FreeMarkerView 代表 FreeMarker 视图。目前,Spring 支持多种技术开发的视图,包括 JSP、JSTL、Excel,Velocity、FreeMarker 等
七、Spring MVC与JSON
从Controller得到json:
当controller的方法返回一个对象(实体类对象,也可是集合对象),同时标注了@ResponseBody,jackson在后台默默将对象转化为json字符串
1.SpringMVC处理json的框架是Jackson,要引入pom的依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.4</version>
</dependency>
2.在springmvc.xml配置
<!--开启注解驱动--><!-- 配置注解驱动,同时开启对json(jackson的支持)-->
<!--开启用后,Spring会默认帮我们注册处理请求,参数和返回值的类。
如:将被@Controller和@RequestMapping注解的处理器方法放入了映射表,使其生效-->
<mvc:annotation-driven></mvc:annotation-driven>
3.写controller
@Controller
@RequestMapping("/json")
public class JsonController {
@RequestMapping("get-user")
@ResponseBody
public User getUser() {
return new User("kelly", "123");
}
}
4.浏览器运行效果
@ResponseBody
将方法的返回值写到响应体中,当controller的方法返回一个对象(实体类对象,也可是集合对象),同时标注了@ResponseBody,jackson在后台默默将对象转化为json字符串
@Controller
@RequestMapping("/json")
public class JsonController {
@RequestMapping("get-user-list")
@ResponseBody
public List<User> getUserList() {
return Arrays.asList(new User("The8", "888"),
new User("Jun", "444"),
new User("carat", "17526"));
}
}
(一)课堂练习
在controller写一个方法,该方法返回值是map集合:
{"code":0,"list":[{"username":"zs","password":"123"},{"username":"ls","password":"123456"},{"username":"zl","password":"123"}]}
@Controller
@RequestMapping("/json")
public class JsonController {
@RequestMapping("get-map")
@ResponseBody
public Map<String, Object> getMap() {
Map<String, Object> map = new HashMap<>();
map.put("code", 0);
map.put("list", Arrays.asList(new User("zs", "123"), new User("ls", "123456"), new User("zl", "123")));
return map;
}
}
八、拦截器
(一)什么是拦截器?适用场景?
Spring MVC的拦截器(Interceptor)与Java Servlet的过滤器(Filter)类似,它主要用于拦截用户的请求并做相应的处理。通常应用在权限验证、记录请求信息的日志、判断用户是否登录功能上。
(二)拦截器相关接口?
实现HandlerInterceptor接口,实现接口提供三个方法:
- preHandle:该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作。返回true表示继续向下执行,返回false表示中断后续操作。
- postHandle:该方法在控制器的处理请求方法调用之后,解析视图之前执行。可以通过此方法对请求域中的模型和视图做进一步的修改。
- afterCompletion:该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行。可以通过此方法实现一些资源清理、记录日志信息等工作。
(三)登录拦截器类
public class LoginInterceptor implements HandlerInterceptor { //跳转到登录页面 response.sendRedirect(contextPath + "/login.jsp");} else { //不拦截 return true; }}
|
(四)springmvc.xml配置拦截器
<!--配置拦截器-->
|
九、Spring整合Spring MVC及MyBatis
(一)什么是SSM整合
- SSM:Spring+Spring MVC+Mybatis 三大框架整合
(二)三大框架的角色划分和职责
1.Spring框架
- 1.能整合各种框架
- 2.Spring充当父容器
- 3.ContextLoaderListener启动Spring容器,实例化Spring容器,管理bean(Service,Mapper,数据源,SqlSession,MapperScanConfiguror,Transaction),能管业务层和数据访问层
2.SpringMVC框架
- 1.SpringMVC是子容器
- 2.实例化SpringMVC容器(是Spring容器的子容器,子容器的bean可以访问父容器的bean),管理 bean(controller,视图解析器,CommonsMultipartResolver)
- 3.配置扫描基本包:com.tjetc.controller,管理控制层,视图层
3.Mybatis
- mapper,pojo,配置文件 ,映射文件
(三)父子容器的关系
- 1.父容器是Spring容器
- 2.子容器SpringMVC容器
- 3.子容器的bean可以访问父容器的bean
(四)SSM整合步骤
1.创建maven工程
2.pom.xml
<!--spring-context-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.8</version>
</dependency>
<!--spring-mvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.8</version>
</dependency>
<!--spring事务-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.8</version>
</dependency>
<!--spring-jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.8</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!--mybatis-spring整合-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<!--数据源--><!--SpringBoot-->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.8</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
<!-- aspectj 依赖--><!--AOP-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<!--mybatis的分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.9</version>
</dependency>
<!--处理json-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.4</version>
</dependency>
<!--字符串处理--><!--StringUtils-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>
<!--上传文件-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!--JSTL-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
3.配置web
4.配置web.xml
1.ContextLoaderListener:加载实例化Spring容器(父容器)
2.配置CharacterEncodingFilter:form post提交的乱码(可选)
3.DispatcherServlet:SpringMVC容器(子容器)
<?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容器配置文件和spring的监听器-->
<!--全局配置,上下文参数(spring父容器)-->
<context-param>
<!--contextConfigLocation上下文配置文件位置(名称)-->
<param-name>contextConfigLocation</param-name>
<!--classpath下的applicationContext.xml文件-->
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--配置上下文加载监听器,在web服务器Tomcat启动时,实例化spring容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--配置编码过滤器(配置spring的CharacterEncodingFilter解决中文乱码)-->
<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>
<!--配置spring核心的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.xml</param-value>
</init-param>
<!--
1)load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法)。
2)它的值必须是一个整数,表示servlet应该被载入的顺序
3)当值为0或者大于0时,表示容器在应用启动时就加载并初始化这个servlet;
4)当值小于0或者没有指定时,则表示容器在该servlet被选择时才会去加载。
5)正数的值越小,该servlet的优先级越高,应用启动时就越先加载。
6)当值相同时,容器就会自己选择顺序来加载。
-->
<!-- tomcat启动时候创建DispatcherServlet并初始化-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--正斜杠 所有的请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
5.applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--spring容器负责管理业务层和持久层的类-->
<!--1、配置扫描三大注解类-->
<!--扫描带有@Service,@Repository,@Component注解类,纳入spring容器的管理,所以要排除@Controller注解的类-->
<!--扫描com.tjetc包和子包中带有@Repositry,@Service,@Component这些注解的类,则把这些类注册为bean-->
<!--applicationContext.xml配置了@Service类方法的事务增强处理,所以@Service类在spring容器中扫描并被管理-->
<context:component-scan base-package="com.tjetc"><!--扫描4个-->
<!--扫描排除掉@Controller注解类
因为启动后spring和springmvc是2个容器,@Controller注解的类由springmvc扫描管理,配置web.xml中有体现-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--2、配置加载properties文件和数据源-->
<!--加载属性文件(可以加载多个properties文件)-->
<context:property-placeholder location="classpath*:*.properties"></context:property-placeholder>
<!--配置数据源hikariCP-->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<property name="driverClassName" value="${jdbc.driverName}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--注解方式使用事务
1、开启事务注解驱动,告诉spring使用注解管理事务,创建代理对象;适合中小型项目
<tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven>
2、最后在service主方法上增加@Transactional
-->
<!--3、配置事务-->
<!--事务管理器 声明事务管理对象-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务通知
声明业务方法的事务属性(隔离级别(isolation)、传播行为(propagation)、超时时间(timeout))
id是自定义名称,表示配置内容
transaction-manager:事务管理器对象的ID-->
<!--注解和AOP二选一,不同时使用-->
<!--(1)AOP-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!--tx:method:给具体的方法配置事务属性,method可以有多个,给不同的方法配置事务属性
name:方法名,1)完整的方法名,不带包和类;2)可以使用通配符*,表示任意字符
propagation:传播行为,枚举值
isolation:隔离级别
rollback-for:指定的异常类名,全限定类名,发生异常,一定回滚
-->
<!--使用通配符,指定很多方法-->
<!--添加使用事务 指定新增方法-->
<tx:method name="add*" propagation="REQUIRED"/>
<!--更新使用事务 指定更新方法-->
<tx:method name="update*" propagation="REQUIRED"/>
<!--删除使用事务 指定删除方法-->
<tx:method name="del*" propagation="REQUIRED"/>
<!--查询方法,query,search,find方法等
其他方法是使用只读事务, 可以提高效率-->
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!--配置aop切面-->
<aop:config>
<!--配置切入点表达式,指定哪些包里面的哪些类需要使用事务
配置切入点, 对service包和子孙包下所有的业务类(service)使用事务
id:切入点表达式的名称,唯一值
expression:切入点表达式,指定哪些包哪些类哪些方法要使用事务,aspectj会创建代理对象-->
<aop:pointcut id="txPointCut" expression="execution(* com.tjetc.service..*.*(..))"/>
<!--配置增强器:关联advice和pointcut
advice-ref:通知,上面的tx:advice的配置
pointcut-ref:切入点表达式的id
-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"></aop:advisor>
</aop:config>
<!--(2)通过注解配置事务
1、开启事务注解驱动,告诉spring使用注解管理事务,创建代理对象;适合中小型项目
2、最后在service主方法上增加@Transactional(这个小项目在UserServiceImpl里添加注解)
-->
<!--<tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven>-->
<!--4、配置mybatis-->
<!--配置sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--配置mybatis.xml的配置文件-->
<property name="configLocation" value="classpath:mybatis.xml"></property>
<!--配置mybatis的数据源-->
<property name="dataSource" ref="dataSource"></property>
<!--配置mapper映射文件的文件,自动扫描mapper.xml文件-->
<property name="mapperLocations" value="classpath:com/tjetc/dao/*.xml"/>
</bean>
<!--配置mybatis的dao扫描(扫描映射接口)-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
<!--配置映射接口所在的包
mybatis对spring支持,扫描mapper下的Mapper接口,生成代理bean交给spring管理-->
<property name="basePackage" value="com.tjetc.dao"></property>
</bean>
</beans>
6.db.properties
jdbc.driverName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=123456
7.mybatis.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">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<package name="com.tjetc.entity"/>
</typeAliases>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
</configuration>
8.springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--springmvc子容器只管理控制层-->
<!--扫描使用注解的类(@Controller,纳入springmvc管理-->
<context:component-scan base-package="com.tjetc.controller"></context:component-scan>
<!--开启注解驱动--><!-- 配置注解驱动,同时开启对json(jackson的支持)-->
<!--开启用后,Spring会默认帮我们注册处理请求,参数和返回值的类。
如:将被@Controller和@RequestMapping注解的处理器方法放入了映射表,使其生效-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--对于图片等静态资源使用默认的servlet处理器实际就是Tomcat-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
<!--配置视图解析器 查找指定的视图-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置视图 jstl视图 解析view的代码转换成html
JstlView可以使用jstl解析展示数据
-->
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
<!--配置前缀和后缀 简化返回页面的代码
例如: @RequestMapping注解到的方法中,return "hello" , 实际上 return "/hello.jsp" 页面
-->
<!--配置前缀 prefix代表自动在返回值前面添加 /-->
<property name="prefix" value="/"></property>
<!--配置后缀 suffix代表自动返回值后加上 .jsp-->
<property name="suffix" value=".jsp"></property>
</bean>
<!--配置拦截器-->
<!-- <mvc:interceptors>
<mvc:interceptor>
<!–登录拦截器–>
<mvc:mapping path="/user/**"/>
<bean class="com.tjetc.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>-->
</beans>
9.User
package com.tjetc.entity;
public class User {
private Long id;
private String username;
private String password;
public User() {}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public Long getId() {return id; }
public void setId(Long id) {this.id = id;}
public String getUsername() {return username;}
public void setUsername(String username) {this.username = username;}
public String getPassword() {return password; }
public void setPassword(String password) { this.password = password; }
}
10.UserMapper.xml
<?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.tjetc.dao.UserMapper">
<!--
'%${username}%'直接拼接参数,有SQL注入的风险
#{username}更安全-->
<!--
<if test="username!=null and username!=''">
判断username不为null且不为空字符串
-->
<select id="selectAllForLike" resultMap="userMap">
select id,username,`password` from user
<where>
<if test="username!=null and username!=''">
username like concat('%',#{username},'%')
</if>
</where>
</select>
<resultMap id="userMap" type="user">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
</resultMap>
<select id="selectById" resultMap="userMap">
select id,username,`password` from user where id=#{id}
</select>
<update id="update" parameterType="user">
update `user`
<set>
<if test="username!=null and username!=''">
username = #{username},
</if>
<if test="password!=null and password!=''">
password = #{password},
</if>
</set>
where id=#{id}
</update>
<delete id="delete" parameterType="long">
delete from user where id = #{id}
</delete>
<insert id="add" parameterType="user">
insert into user(username,password) values (#{username},#{password})
</insert>
</mapper>
11.UserMapper.java
package com.tjetc.dao;
import com.tjetc.entity.User;
import java.util.List;
public interface UserMapper {
List<User> selectAllForLike(String username);
User selectById(Long id);
int update(User user);
int delete(Long id);
void add(User user);
}
12.UserService.java
package com.tjetc.service;
import com.github.pagehelper.PageInfo;
import com.tjetc.entity.User;
import org.springframework.stereotype.Service;
import java.util.List;
public interface UserService {
/** 用户名分页模糊查询
* @param username 用户名
* @param pageNo 页码
* @param pageSize 每页数量
* @return
*/
PageInfo<User> findPageForLike(String username, int pageNo, int pageSize);
/**根据id查询用户
* @param id
* @return
*/
User findById(Long id);
/** 更新用户
* @param user
* @return
*/
boolean updateUser(User user);
/**根据id删除用户
* @param id
* @return
*/
boolean delete(Long id);
/** 添加新用户
* @param user
*/
void add(User user);
}
13.UserServiceImpl.java
package com.tjetc.service.impl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.tjetc.dao.UserMapper;
import com.tjetc.entity.User;
import com.tjetc.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public PageInfo<User> findPageForLike(String username, int pageNo, int pageSize) {
//设置页码和每页数量
PageHelper.startPage(pageNo, pageSize);
List<User> users = userMapper.selectAllForLike(username);
//users集合对象包装PageInfo对象
PageInfo<User> userPageInfo = new PageInfo<>(users);
return userPageInfo;
}
@Override
public User findById(Long id) {
User user = userMapper.selectById(id);
return user;
}
@Override
public boolean updateUser(User user) {/*effectCount影响行数*/
int effectCount = userMapper.update(user);
return effectCount > 0;
}
@Override
public boolean delete(Long id) {
int effectCount = userMapper.delete(id);
return effectCount > 0;
}
/*@Transactional//注解方式使用事务*/
@Override
public void add(User user) {
userMapper.add(user);
}
}
14.UserController.java
package com.tjetc.controller;
import com.github.pagehelper.PageInfo;
import com.tjetc.entity.User;
import com.tjetc.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("page")
public String findPage(@RequestParam(value = "username", required = false) String username,
@RequestParam(value = "pageNo", required = false, defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", required = false, defaultValue = "5") Integer pageSize,
Model model) {
PageInfo<User> userPageInfo = userService.findPageForLike(username, pageNo, pageSize);
model.addAttribute("userPageInfo", userPageInfo);
model.addAttribute("username", username);
//请求转发
return "user-list";
}
@RequestMapping("edit/{id}")
public String edit(@PathVariable("id") Long id, Model model) {
User user = userService.findById(id);
model.addAttribute("user", user);
return "user-edit";
}
@RequestMapping("save")
public String save(User user) {
boolean bl = userService.updateUser(user);
return "redirect:/user/page";
}
@RequestMapping("save1")
public String save1(User user) {
userService.add(user);
return "redirect:/user/page";
}
@RequestMapping("delete/{id}")
public String delete(@PathVariable("id") Long id) {
boolean bl = userService.delete(id);
return "redirect:/user/page";
}
@RequestMapping("add")
public String add(User user) {
return "user-add";
}
}
15.user-add.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>添加新用户</title>
<base href="<%=request.getContextPath()%>/">
</head>
<body>
<form method="post" action="user/save1">
用户名:<input type="text" name="username" value="${user.username}"><br>
密码:<input type="password" name="password" value="${user.password}"><br>
<input type="submit" value="保存"><br>
<input type="button" value="返回" onclick="goBack()"/><br/>
</form>
</body>
<script>
function goBack() {
window.history.go(-1);
}
</script>
</html>
16.user-list.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
<base href="<%=request.getContextPath()%>/">
</head>
<body>
<form action="user/page" method="post">
用户名:<input type="text" name="username" value="${username}" id="username"/>
<input type="submit" value="查询"/><br/>
</form>
<c:if test="${userPageInfo!=null}">
<%--<h3><a href="user/add">新增</a></h3>--%>
<input type="button" value="新增" onclick="window.location.href='user/add'">
<table border="1" cellpadding="1" cellspacing="0">
<tr>
<th>序号</th>
<th>用户名</th>
<th>密码</th>
<th colspan="2">操作</th>
</tr>
<c:forEach items="${userPageInfo.list}" var="user">
<tr>
<td>${user.id}</td>
<td>${user.username}</td>
<td>${user.password}</td>
<%--请求edit(编辑)的URL--%>
<td><a href="user/edit/${user.id}">更新</a></td>
<td><a href="user/delete/${user.id}" onclick="return confirm('是否删除该用户?')">删除</a></td>
</tr>
</c:forEach>
</table>
<%--<c:if test="${userPageInfo.isFirstPage}">--%>
<input type="button" value="首页" onclick="query('${userPageInfo.navigateFirstPage}')">
<%--</c:if>--%>
<c:if test="${userPageInfo.hasPreviousPage}">
<input type="button" value="前一页" onclick="query('${userPageInfo.prePage}')">
</c:if>
<c:forEach items="${userPageInfo.navigatepageNums}" var="num">
<input type="button" value="${num}" onclick="query('${num}')">
</c:forEach>
<c:if test="${userPageInfo.hasNextPage}">
<input type="button" value="后一页" onclick="query('${userPageInfo.nextPage}')">
</c:if>
<%--<c:if test="${userPageInfo.isLastPage}">--%>
<input type="button" value="尾页" onclick="query('${userPageInfo.navigateLastPage}')">
<%--</c:if>--%>
</c:if>
</body>
<script>
function query(pageNo) {
let username = document.getElementById("username").value;
window.location.href = "user/page?username=" + username + "&pageNo=" + pageNo;
}
</script>
</html>
17.user-edit.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>更新</title>
<base href="<%=request.getContextPath()%>/">
</head>
<body>
<form action="user/save" method="post">
<%--隐藏id--%>
<input type="hidden" name="id" value="${user.id}">
用户名:<input type="text" value="${user.username}" disabled/><br/><%--不可修改--%>
密码:<input type="password" name="password" value="${user.password}"/><br/>
<input type="submit" value="保存"/><br/>
<input type="button" value="返回" onclick="goBack()"/><br/>
</form>
</body>
<script>
function goBack() {
window.history.go(-1);
}
</script>
</html>