SpringMVC 是 Spring 为表述层开发提供的一整套完备的解决方案。在表述层框架历经 Strust、WebWork、Strust2 等诸多产品的历代更迭之后,目前业界普遍选择了 SpringMVC 作为 Java EE 项目表述层开发的首选方案。之所以能做到这一点,是因为 SpringMVC 具备如下显著优势:
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.1version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.thymeleafgroupId>
<artifactId>thymeleaf-spring5artifactId>
<version>3.0.12.RELEASEversion>
dependency>
dependencies>
由于Maven的传递性,我们不必将所有的包全部配置依赖,而是配置最顶端的依赖,其他考传递性导入
文件名:logback.xml
<configuration debug="true">
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%line] [%msg]%npattern>
encoder>
appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
root>
<logger name="org.springframework.web.servlet.DispatcherServlet" level="DEBUG" />
configuration>
<servlet>
<servlet-name>DispatcherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-mvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>DispatcherServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<context:component-scan base-package="com.atguigu.mvc.handler"/>
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/templates/"/>
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8" />
bean>
property>
bean>
property>
bean>
SpringMVC 对处理请求的类并没有特殊要求,只要是 JavaBean 即可。我们自己习惯上有两种命名方式:
这只是一个命名的习惯,不是语法要求。所以往往把处理请求的类叫做『Handler类』,处理请求的方法叫做『Handler方法』。
package com.atguigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
}
package com.atguigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
@RequestMapping("/sayHello")
public String sayHello(){
System.out.println("Hello world!!");
// 方法的返回值就是对应的Thymeleaf模板的逻辑视图
return "target";
}
@RequestMapping("/sayHaha")
private String sayHaha(){
System.out.println("haha world!!!");
return "haha";
}
}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hahatitle>
head>
<body>
<h1>hahaha!!!h1>
body>
html>
启动tomcat 输入地址查看
从注解名称上我们可以看到,@RequestMapping注解的作用就是将请求的 URL 地址和处理请求的方式关联起来,建立映射关系。
SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的方法来处理这个请求。
<h3>测试@RequestMapping注解标记在类上h3>
<a th:href="@{/user/login}">用户登录a><br/>
<a th:href="@{/user/register}">用户注册a><br/>
<a th:href="@{/user/logout}">用户退出a><br/>
@RequestMapping("/user/login")
@RequestMapping("/user/register")
@RequestMapping("/user/logout")
在类级别:抽取各个方法上@RequestMapping注解地址中前面重复的部分
@RequestMapping("/hello")
在方法级别:省略被类级别抽取的部分
@RequestMapping("/sayHello")
@RequestMapping("/sayHaha")
HTTP 协议定义了八种请求方式,在 SpringMVC 中封装到了下面这个枚举类:
public enum RequestMethod {
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
}
设置RequestMapping注解的method属性
@RequestMapping(value = "/sayHaha", method = RequestMethod.POST)
private String sayHaha(){
System.out.println("haha world!!!");
return "haha";
}
必须使用post方式向此方法发起请求 否则会抛出异常405
原版 | 进阶版 |
---|---|
@RequestMapping(value = “/emp”, method = RequestMethod.GET) | @GetMapping(“/emp”) |
@RequestMapping(value = “/emp”, method = RequestMethod.POST) | @PostMapping(“/emp”) |
@RequestMapping(value = “/emp”, method = RequestMethod.PUT) | @PutMapping(“/emp”) |
@RequestMapping(value = “/emp”, method = RequestMethod.DELETE) | @DeleteMapping(“/emp”) |
针对Http的八种请求方式都有专门的注解
另外需要注意:进阶版的这几个注解是从 4.3 版本才开始有,低于 4.3 版本无法使用。
出现原因:多个 handler 方法映射了同一个地址,导致 SpringMVC 在接收到这个地址的请求时该找哪个 handler 方法处理。
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map ‘demo03MappingMethodHandler’ method com.atguigu.mvc.handler.Demo03MappingMethodHandler#empPost() to { [/emp]}: There is already ‘demo03MappingMethodHandler’ bean method com.atguigu.mvc.handler.Demo03MappingMethodHandler#empGet() mapped.
通过这个注解获取请求消息头中的具体数据。
// 相当于| 原版@RequestMapping(value = "/getHeader",method = RequestMethod.GET) | |
@GetMapping("/getHeader")
public String getHeader(@RequestHeader("user-agent") String userAgent){
// 目标:获取一个名为"user-agent"的请求头的值
System.out.println("user-agent:" + userAgent);
return "target";
}
获取当前请求中的Cookie数据
先创建IndexController类
package com.atguigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpSession;
@Controller
public class IndexController {
@RequestMapping("/")
// 设置session
public String index(HttpSession session){
return "index";
}
}
创建index.html页面
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页title>
head>
<body>
<a th:href="@{/hello/sayHello}">访问sayHelloa> <br>
<a th:href="@{/hello/sayHaha}">访问sayHahaa> <br>
body>
html>
获取session
@GetMapping("/getCookieValue")
public String getCookieValue(@CookieValue(value = "JSESSIONID", defaultValue = "none") String sessionId){
// 目标获取一个名为"JSESSIONID"的cookie的值,如果没有值则赋默认值为none
System.out.println("JSESSIONID:" + sessionId);
return "target";
}
<a th:href="@{/parameter/oneNameOneValue(username='aobama',nickname='sqyx',age=18)}">一个请求参数名对应一个值a>
<br>
package com.atguigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/parameter")
public class ParameterController {
@RequestMapping("oneNameOneValue")
public String oneNameOneValue(String username,
String nickname,
Integer age){
// 最简单的方式获取单个请求参数:就是在handler方法中添加一个和请求参数名同名的参数,来接收请求参数
// 其实这个地方此处省略了一个注解 @RequestParam
System.out.println("获取到的请求参数username=" + username);
System.out.println("获取到的请求参数nickname=" + nickname);
System.out.println("获取到的请求参数age=" + age);
return "target";
}
}
package com.atguigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/parameter")
public class ParameterController {
@RequestMapping("oneNameOneValue")
public String oneNameOneValue(@RequestParam("username") String username,
@RequestParam("nickname") String nickname,
@RequestParam("age") Integer age){
System.out.println("获取到的请求参数username=" + username);
System.out.println("获取到的请求参数nickname=" + nickname);
System.out.println("获取到的请求参数age=" + age);
return "target";
}
}
页面信息说明:
原因可以参考 @RequestParam 注解的 required 属性:默认值为true,表示请求参数默认必须提供
/**
* Whether the parameter is required.
* Defaults to {@code true}, leading to an exception being thrown
* if the parameter is missing in the request. Switch this to
* {@code false} if you prefer a {@code null} value if the parameter is
* not present in the request.
*
Alternatively, provide a {@link #defaultValue}, which implicitly
* sets this flag to {@code false}.
*/
boolean required() default true;
我们可以通过将required 属性设置为 false 表示这个请求参数可有可无:
@RequestParam(value = "userName", required = false)
当然,我们也可以通过设置请求参数的默认值来解决上述400
错误
@RequestParam(value = "userName", defaultValue = "missing")
<a th:href="@{/parameter/oneNameMultiValue(hobby='basketball',hobby='sing',hobby='dance')}">一个请求参数名对应多个值a>
<br>
@RequestMapping("/oneNameMultiValue")
public String oneNameMultiValue(@RequestParam("hobby") List<String> hobby){
System.out.println("获取到的请求hobby的值为:" + hobby);
return "target";
}
<a th:href="@{/parameter/parametersToBean(username='aobama',address='ayd',age=18,phone=1233123123)}">多个请求参数封装到beana>
<br>
注意:实体类的属性名要和请求参数名一致
package com.atguigu.pojo;
import lombok.Data;
@Data
public class User {
private String username;
private String address;
private Integer age;
private String phone;
}
@RequestMapping("/parametersToBean")
public String parametersToBean(User user){
System.out.println("获取请求参数封装到user中:" + user);
return "target";
}
<form th:action="@{/parameter/parametersToBean}" method="post">
用户名: <input type="text" name="username"> <br>
地址: <input type="text" name="address"> <br>
年龄: <input type="text" name="age"> <br>
手机号: <input type="text" name="phone"> <br>
<button>提交button>
form>
到 web.xml 中配置 CharacterEncodingFilter 即可:
<filter>
<filter-name>CharacterEncodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
<init-param>
<param-name>forceRequestEncodingparam-name>
<param-value>trueparam-value>
init-param>
<init-param>
<param-name>forceResponseEncodingparam-name>
<param-value>trueparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>CharacterEncodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
注1:在较低版本的 SpringMVC 中,forceRequestEncoding 属性、forceResponseEncoding 属性没有分开,它们是一个 forceEncoding 属性。这里需要注意一下。
注2:由于 CharacterEncodingFilter 是通过 request.setCharacterEncoding(encoding); 来设置请求字符集,所以在此操作前不能有任何的 request.getParameter() 操作。在设置字符集之前获取过请求参数,那么设置字符集的操作将无效。
<a th:href="@{/parameter/parametersToMap(username='aobama',address='ayd',age=18,phone=1233123123)}">多个请求参数封装到Mapa>
<br>
@RequestMapping("/parametersToMap")
public String parametersToMap(@RequestParam Map parameterMap){
System.out.println("获取请求参数封装到parameterMap中:" + parameterMap);
return "target";
}
Student
package com.atguigu.pojo;
import lombok.Data;
import java.util.List;
import java.util.Map;
@Data
public class Student {
private String stuName;
private School school;
private List<Subject> subjectList;
private Map<String,Double> scores;
}
School
package com.atguigu.pojo;
import lombok.Data;
@Data
public class School {
private String schoolName;
}
Subject
package com.atguigu.pojo;
import lombok.Data;
@Data
public class Subject {
private String subjectName;
}
<form th:action="@{/parameter/toNestEntity}" method="post">
stuName:<input type="text" name="stuName" value="tom"/><br/>
school.schoolName:<input type="text" name="school.schoolName" value="atguigu"/><br/>
subjectList[0].subjectName:<input type="text" name="subjectList[0].subjectName" value="java"/><br/>
subjectList[1].subjectName:<input type="text" name="subjectList[1].subjectName" value="php"/><br/>
subjectList[2].subjectName:<input type="text" name="subjectList[2].subjectName" value="javascript"/><br/>
subjectList[3].subjectName:<input type="text" name="subjectList[3].subjectName" value="css"/><br/>
subjectList[4].subjectName:<input type="text" name="subjectList[4].subjectName" value="vue"/><br/>
scores['Chinese']:<input type="text" name="scores['Chinese']" value="100"/><br/>
scores['English']:<input type="text" name="scores['English']" value="95" /><br/>
scores['Mathematics']:<input type="text" name="scores['Mathematics']" value="88"/><br/>
scores['Chemistry']:<input type="text" name="scores['Chemistry']" value="63"/><br/>
scores['Biology']:<input type="text" name="scores['Biology']" value="44"/><br/>
<input type="submit" value="保存"/>
form>
@RequestMapping("/toNestEntity")
public String toNestEntity(Student student){
System.out.println("接收到得请求参数封装到Student对象:" + student);
return "target";
}
在Web应用中加入图片资源
访问静态资源
在SpringMVC配置文件中增加配置
<mvc:default-servlet-handler/>
再次测试访问图片
新的问题:其他原本正常的请求访问不了了,进一步解决问题:再增加一个配置
<mvc:annotation-driven/>
加了此注解之后解决问题了
所在类:org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler
关键方法:handleRequest()方法
大体机制:SpringMVC 首先查找是否存在和当前请求对应的 @RequestMapping;如果没有,则调用handleRequest()方法转发到目标资源。
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Assert.state(this.servletContext != null, "No ServletContext set");
RequestDispatcher rd = this.servletContext.getNamedDispatcher(this.defaultServletName);
if (rd == null) {
throw new IllegalStateException("A RequestDispatcher could not be located for the default servlet '" +
this.defaultServletName + "'");
}
// 这里执行请求转发操作
rd.forward(request, response);
}
访问Thymeleaf前后缀控制范围之内的页面,直接return
逻辑视图即可,但是访问前后缀控制范围之外的页面,则需要使用重定向或者请求转发
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<h1>范围之外页面h1>
body>
html>
下面配置是访问静态资源所需配置(如果没有它的话,访问静态资源会被DispatcherServlet所拦截):
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
@RequestMapping("/forwardCommand")
public String forwardCommand(){
//使用转发指令:在handler方法中,访问Thymeleaf前后缀控制范围外的页面
//"forward:要转发到的资源路径",相当于使用的是请求转发跳转
//请求转发的绝对路径是:在uri的基础上省略"/项目名"
return "forward:/outter.html";
}
在一个 handler 方法中,仅仅只是完成 @RequestMapping 映射,将请求转发到目标视图,除此之外没有任何其他代码。此时可以使用 SpringMVC 配置文件中的配置代替这样的 handler 方法。
在 SpringMVC 配置文件中使用 mvc:view-controller 配置:
<mvc:view-controller path="/xixi" view-name="xixi">mvc:view-controller>
此时浏览器路径"xixi"可以访问到Thymeleaf前后缀控制范围内的xixi.html页面
在 SpringMVC 配置文件中使用 mvc:view-controller 配置:
<mvc:view-controller path="/outer" view-name="redirect:/outer.html">mvc:view-controller>
此时就相当于代替了之前的重定向访问的哪个handler方法
加入 mvc:view-controller 配置后,其他正常 @RequestMapping 将失效。此时还是需要加入 mvc:annotation-driven 来解决。
通过前面的学习,我们发现无论是添加了default-servlet-handler
还是view-controller
,我们都必须添加annotation-driven
标签。所以annotation-driven
标签是SpringMVC必须添加的
接下来我们从源码角度分析这三个配置:
见名知意,HandlerMapping 封装的数据包含了请求地址和 handler 方法之间的映射关系。所以请求访问是否能生效关键要看 HandlerMapping 在 IOC 容器中加载的情况。为了看到这一点,我们可以在 DispatcherServlet 中找到 doDispatch() 方法设置断点。之所以选择这个方法,是因为每一个由 SpringMVC 处理的请求都会经过这里,便于操作。
我们看到 SpringMVC 加载了三个 HandlerMapping:
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.servlet.function.support.RouterFunctionMapping
其中 RequestMappingHandlerMapping 封装了 @RequestMapping 相关请求,有它在 @RequestMapping 相关请求就能访问到。
这里顺带一提,在较低版本的 SpringMVC 此处要加载的是:
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
其中 DefaultAnnotationHandlerMapping 封装了 @RequestMapping 相关请求,有它在 @RequestMapping 相关请求就能访问到。
我们看到 SpringMVC 加载了两个 HandlerMapping:
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
此时没有了RequestMappingHandlerMapping,所以无法根据@RequestMapping的映射来调用handler方法了
较低版本的 SpringMVC 在这里的情况一样。
配置全部 mvc:view-controller、mvc:default-servlet-handler、mvc:annotation-driven 三个标签。
我们看到 SpringMVC 加载了略有不同的三个 HandlerMapping:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
此时RequestMappingHandlerMapping 封装了 @RequestMapping 相关请求,有它在 @RequestMapping 相关请求就能访问到。
较低版本的 SpringMVC 在这里的情况还是一样。
package com.atguigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequestMapping("/scope")
public class ScopeController {
@RequestMapping("/useRequestScope")
public String useRequestScope(HttpServletRequest httpServletRequest){
// 目标:将数据存储到请求域对象,然后跳转到target页面
// 方式一:使用原始的request来完成
httpServletRequest.setAttribute("requestScopeKey", "requestScopeValue");
return "scopeValue";
}
}
scope.html代码
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
获取请求域中的数据 <span th:text="${requestScopeKey}">span> <br>
body>
html>
// 方式二:使用Model对象往请求域中存储值
@RequestMapping("/useRequestScope")
public String useRequestScope(Model model) {
// 目标:将数据存储到请求域对象,然后跳转到target页面
model.addAttribute("requestScopeKey", "requestScopeValue");
return "scopeValue";
}
@RequestMapping("/attr/request/model/map")
public String useRequestScope(
// 在形参位置声明ModelMap类型变量,用于存储模型数据
ModelMap modelMap) {
// 我们将数据存入模型,SpringMVC 会帮我们把模型数据存入请求域
// 存入请求域这个动作也被称为暴露到请求域
modelMap.addAttribute("requestScopeMessageModelMap","i am very happy[model map]");
return "target";
}
@RequestMapping("/attr/request/map")
public String useRequestScope(
// 在形参位置声明Map类型变量,用于存储模型数据
Map<String, Object> map) {
// 我们将数据存入模型,SpringMVC 会帮我们把模型数据存入请求域
// 存入请求域这个动作也被称为暴露到请求域
map.put("requestScopeMessageMap", "i am very happy[map]");
return "target";
}
@RequestMapping("/attr/request/mav")
public ModelAndView useRequestScope() {
// 1.创建ModelAndView对象
ModelAndView modelAndView = new ModelAndView();
// 2.存入模型数据
modelAndView.addObject("requestScopeMessageMAV", "i am very happy[mav]");
// 3.设置视图名称
modelAndView.setViewName("target");
return modelAndView;
}
SpringMVC 传入的 Model、ModelMap、Map类型的参数其实本质上都是 BindingAwareModelMap 类型的。
使用会话域最简单直接的办法就是使用原生的 HttpSession 对象
// 会话域
@RequestMapping("/useSessionScope")
public String useSessionScope(HttpSession httpSession){
httpSession.setAttribute("sessionScopeKey", "sessionScopeValue");
return "scopeValue";
}
前端代码
获取会话域中的数据 <span th:text="${session.sessionScopeKey}">span> <br>
应用域同样是使用IOC注入的方式来操作:
package com.atguigu.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@Controller
@RequestMapping("/scope")
public class ScopeController {
@Autowired
private ServletContext servletContext;
// 应用域 需要使用IOC注入的方式来操作
@RequestMapping("/useApplicationScope")
public String useApplicationScope(){
servletContext.setAttribute("applicationScopeKey", "applicationValue");
return "scopeValue";
}
}
前端代码
获取应用域中的数据 <span th:text="${application.applicationScopeKey}">span> <br>
浏览器发起请求 输出结果
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.1version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.thymeleafgroupId>
<artifactId>thymeleaf-spring5artifactId>
<version>3.0.12.RELEASEversion>
dependency>
<dependency>
<groupId>org.junit.jupitergroupId>
<artifactId>junit-jupiter-apiartifactId>
<version>5.7.0version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.3.1version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.8version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-ormartifactId>
<version>5.3.1version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.27version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.0.31version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.3.1version>
dependency>
dependencies>
使用之前的t_soldier表
package com.atguigu.pojo;
import lombok.Data;
@Data
public class Soldier {
private Integer soldierId;
private String soldierName;
private String soldierWeapon;
}
package com.atguigu.dao;
import com.atguigu.pojo.Soldier;
import java.util.List;
public interface SoldierDao {
/**
* 根据id删除
* @param soldierId
*/
void deleteById(Integer soldierId);
/**
* 更新
* @param soldier
*/
void update(Soldier soldier);
/**
* 新增士兵
* @param soldier
*/
void add(Soldier soldier);
/**
* 根据id查询士兵
* @param soldierId
* @return
*/
Soldier getSoldierById(Integer soldierId);
/**
* 获取所有士兵
* @return
*/
List<Soldier> findAll();
}
package com.atguigu.impl;
import com.atguigu.dao.SoldierDao;
import com.atguigu.pojo.Soldier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public class SoldierDaoImpl implements SoldierDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void deleteById(Integer soldierId) {
String sql = "delete from t_soldier where soldier_id=?";
jdbcTemplate.update(sql, soldierId);
}
@Override
public void update(Soldier soldier) {
String sql = "update t_soldier set soldier_name=?,soldier_weapon=? where soldier_id=?";
jdbcTemplate.update(sql, soldier.getSoldierName(),soldier.getSoldierWeapon(),soldier.getSoldierId());
}
@Override
public void add(Soldier soldier) {
String sql = "insert into t_soldier(soldier_name,soldier_weapon) values(?,?)";
jdbcTemplate.update(sql, soldier.getSoldierName(),soldier.getSoldierWeapon());
}
@Override
public Soldier getSoldierById(Integer soldierId) {
String sql = "select soldier_id soldierId,soldier_name soldierName,soldier_weapon soldierWeapon from t_soldier where soldier_id=?";
return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Soldier.class), soldierId);
}
@Override
public List<Soldier> findAll() {
String sql = "select soldier_id soldierId,soldier_name soldierName,soldier_weapon soldierWeapon from t_soldier";
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Soldier.class));
}
}
package com.atguigu.service;
import com.atguigu.pojo.Soldier;
import java.util.List;
public interface SoldierService {
/**
* 根据id删除
* @param soldierId
*/
void deleteById(Integer soldierId);
/**
* 更新
* @param soldier
*/
void update(Soldier soldier);
/**
* 新增士兵
* @param soldier
*/
void add(Soldier soldier);
/**
* 根据id查询士兵
* @param soldierId
* @return
*/
Soldier getSoldierById(Integer soldierId);
/**
* 获取所有士兵
* @return
*/
List<Soldier> findAll();
}
package com.atguigu.service.impl;
import com.atguigu.dao.SoldierDao;
import com.atguigu.pojo.Soldier;
import com.atguigu.service.SoldierService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class SoldierServiceImpl implements SoldierService {
@Autowired
private SoldierDao soldierDao;
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void deleteById(Integer soldierId) {
soldierDao.deleteById(soldierId);
}
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void update(Soldier soldier) {
soldierDao.update(soldier);
}
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void add(Soldier soldier) {
soldierDao.add(soldier);
}
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
@Override
public Soldier getSoldierById(Integer soldierId) {
return soldierDao.getSoldierById(soldierId);
}
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
@Override
public List<Soldier> findAll() {
return soldierDao.findAll();
}
}
<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>dispatcherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-web.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>dispatcherServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<filter>
<filter-name>CharacterEncodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
<init-param>
<param-name>forceRequestEncodingparam-name>
<param-value>trueparam-value>
init-param>
<init-param>
<param-name>forceResponseEncodingparam-name>
<param-value>trueparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>CharacterEncodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
web-app>
<configuration debug="true">
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%line] [%msg]%npattern>
encoder>
appender>
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
root>
<logger name="org.springframework.web.servlet.DispatcherServlet" level="DEBUG"/>
configuration>
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven>mvc:annotation-driven>
<mvc:default-servlet-handler>mvc:default-servlet-handler>
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/templates/"/>
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8"/>
bean>
property>
bean>
property>
bean>
<import resource="spring.xml">import>
beans>
<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 https://www.springframework.org/schema/context/spring-context.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">
<context:component-scan base-package="com.atguigu">context:component-scan>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource">property>
bean>
<context:property-placeholder location="classpath:jdbc.properties">context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${datasource.username}">property>
<property name="password" value="${datasource.password}">property>
<property name="url" value="${datasource.url}">property>
<property name="driverClassName" value="${datasource.driver}">property>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
<tx:annotation-driven>tx:annotation-driven>
<aop:aspectj-autoproxy>aop:aspectj-autoproxy>
beans>
datasource.url=jdbc:mysql://localhost:3306/mybatis2?characterEncoding=utf8&serverTimezone=UTC
datasource.driver=com.mysql.cj.jdbc.Driver
datasource.username=root
datasource.password=123456
package com.atguigu;
import com.atguigu.pojo.Soldier;
import com.atguigu.service.SoldierService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@SpringJUnitConfig(locations = "classpath:spring.xml")
public class SoldierTest {
@Autowired
private SoldierService soldierService;
@Test
public void testFindAll(){
System.out.println(soldierService.findAll());
}
@Test
public void testGetSoldierById(){
Soldier soldier = soldierService.getSoldierById(2);
System.out.println(soldier);
}
}
index.html
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页title>
head>
<body>
<h1>欢迎来到首页h1>
<a th:href="@{/soldier/findAll}">展示士兵列表a>
body>
html>
spring-web.xml中添加
<mvc:view-controller path="/" view-name="index.html">mvc:view-controller>
效果:
package com.atguigu.controller;
import com.atguigu.pojo.Soldier;
import com.atguigu.service.SoldierService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@Controller
@RequestMapping("/soldier")
public class SoldierController {
public static final String LIST_ACTION = "redirect:/soldier/findAll";
public static final String PAGE_EDIT = "edit";
public static final String PAGE_LIST = "list";
@Autowired
private SoldierService soldierService;
@RequestMapping("/findAll")
public String findAll(Model model){
List<Soldier> soldierList = soldierService.findAll();
model.addAttribute("soldierList", soldierList);
return PAGE_LIST;
}
}
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>士兵列表页面title>
head>
<body>
<table>
<tr>
<td>序号td>
<td>士兵名称td>
<td>士兵武器td>
<td>修改td>
<td>删除td>
tr>
<tr th:each="soldier,status:${soldierList}">
<td th:text="${status.count}">td>
<td th:text="${soldier.soldierName}">td>
<td th:text="${soldier.soldierWeapon}">td>
<td>
<a>修改a>
td>
<td>
<a>删除a>
td>
tr>
<tr>
<td >
<a>添加士兵a>
td>
tr>
table>
body>
html>
展示效果:
<td>
<a th:href="@{/soldier/deleteById(id=${soldier.soldierId})}">删除a>
td>
@RequestMapping("/deleteById")
public String deleteById(@RequestParam("id") Integer soldierId){
soldierService.deleteById(soldierId);
// 重新查询所有,重定向查询所有方法
return LIST_ACTION;
}
<tr>
<td colspan="5">
<a th:href="@{/add.html}">添加士兵a>
td>
tr>
<mvc:view-controller path="/add.html" view-name="add">mvc:view-controller>
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>添加士兵页面title>
head>
<body>
<form th:action="@{/soldier/add}" method="post">
士兵名称: <input type="text" name="soldierName"> <br>
士兵武器:<input type="text" name="soldierWeapon"> <br>
<button>提交button>
form>
body>
html>
@RequestMapping("/add")
public String add(Soldier soldier){
// 调用业务层的方法添加Soldier
soldierService.add(soldier);
// 重新查询所有
return LIST_ACTION;
}
<td>
<a th:href="@{/soldier/getSoldierById(id=${soldier.soldierId})}">修改a>
td>
@RequestMapping("/getSoldierById")
public String getSoldierById(@RequestParam("id") Integer soldierId, Model model){
Soldier soldier = soldierService.getSoldierById(soldierId);
model.addAttribute("soldier", soldier);
return PAGE_EDIT;
}
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>编辑士兵页面title>
head>
<body>
<form th:action="@{/soldier/update}">
<input type="hidden" name="soldierId" th:value="${soldier.soldierId}">
士兵姓名:<input type="text" name="soldierName" th:value="${soldier.soldierName}"> <br>
士兵武器:<input type="text" name="soldierWeapon" th:value="${soldier.soldierWeapon}"> <br>
<button>提交button>
form>
body>
html>
@RequestMapping("/update")
public String update(Soldier soldier){
soldierService.update(soldier);
return LIST_ACTION;
}
view-controller
:访问页面例如:访问首页、访问add.html添加页面,使用view-controller
标签实现
例如:查询士兵列表、更新前的数据回显。它们的具体步骤:
例如:添加士兵、删除士兵、更新士兵。它们的具体步骤:
redirect
指令重定向访问查询所有士兵
的功能