域对象共享数据
1、向request域对象共享数据
1.1、ServletAPI
HttpServletRequest对象,作为控制器方法的形参
setAttribute()方法
: 存数据
@RequestMapping(value = "/test")
public String test(HttpServletRequest request){
request.setAttribute("k","v");
return "success";
}
1.2、ModelAndView(推荐)
Model : 主要用于请求域共享数据
addObject()方法
: 存数据
View : 主要用于设置视图,实现页面跳转
setViewName()方法
: 设置视图信息
@RequestMapping(value = "/test")
public ModelAndView test(){
ModelAndView mav = new ModelAndView();
mav.addObject("k1","ModelAndView");
mav.setViewName("success");
return mav;
}
<!-- 如果是请求域的数据直接写k就可以取v -->
<p th:text="${k1}"></p>
1.3、Model
Model对象,可以作为控制器方法的形参
addAttribute()方法
: 存数据
@RequestMapping( "/test")
public String test(Model model){
model.addAttribute("k1","mode");
return "success";
}
1.4、Map
Map对象,可以作为控制器方法的形参
put()方法
: 存数据
@RequestMapping( "/test")
public String test(Map<String,String> map){
map.put("k1","map");
return "success";
}
1.5、ModelMap
ModelMap对象,可以作为控制器方法的形参
addAttribute()方法
: 存数据
@RequestMapping( "/test")
public String test(ModelMap modelMap){
modelMap.addAttribute("k1","modelMap");
return "success";
}
2、Model、ModelMap、Map的关系
Model、ModelMap、Map类型的参数其实本质上都是 BindingAwareModelMap
类型的
public interface Model{}
public class ModelMap extends LinkedHashMap<String, Object> {}
public class ExtendedModelMap extends ModelMap implements Model {}
public class BindingAwareModelMap extends ExtendedModelMap {}
3、控制器方法执行之后,都会返回统一的ModelAndView对象
这里modle
为什么没有值,因为我们用的是原生的ServletAPI
@RequestMapping( "/test")
public String test(HttpServletRequest request){
request.setAttribute("k1","HttpServletRequest");
return "success";
}
下面不用原生的ServletAPI就可以看到值
@RequestMapping( "/test")
public String test(Map<String,String> map){
map.put("k1","map");
return "success";
}
3、向session域共享数据
HttpSession对象,可以作为控制器方法的形参
setAttribute()方法
: 存数据
@RequestMapping( "/test")
public String test(HttpSession session){
session.setAttribute("sessionK","sessionV");
return "success";
}
<!--通过session.的方式获取session域中的数据-->
<p th:text="${session.sessionK}"></p>
4、向application域共享数据
HttpSession对象,可以作为控制器方法的形参,之后通过getServletContext()方法
获取application
对象
setAttribute()方法
: 存数据
@RequestMapping( "/test")
public String test(HttpSession session){
ServletContext application = session.getServletContext();
application.setAttribute("applicationK","applicationV");
return "success";
}
SpringMVC的视图
SpringMVC中的视图是View接口
,视图的作用渲染数据,将模型Model中的数据展示给用户
SpringMVC视图的种类很多,默认有转发视图
和重定向视图
举例
若当工程引入jstl
的依赖,转发视图会自动转换为JstlView
若使用的视图技术为Thymeleaf
,在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视图解析器解析之后所得到的是ThymeleafView
1、ThymeleafView
当控制器方法中所设置的视图名称没有任何前缀
时,此时的视图名称会被SpringMVC配置文件中所配置 的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发
的方式实现跳转
@RequestMapping("/testThymeleafView")
public String testThymeleafView(){
return "success";
}
细🔒1下
DispatcherServlet
// 返回ModelAndView对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 拦截器的方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
// 处理ModelAndView的视图信息,执行转发结果
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
processDispatchResult()
方法,部分
if (mv != null && !mv.wasCleared()) {
this.render(mv, request, response); // render()方法
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
render()方法
....
Locale locale = this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale(); // 本地化zh_CH
...
View view;
String viewName = mv.getViewName(); // 获取视图名称
if (viewName != null) {
view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request); // 解析视图名称获取视图对象
ModelAndView
类的 getModelInternal()
方法
@Nullable
protected Map<String, Object> getModelInternal() {
return this.model;
}
2、InternalResourceView
SpringMVC中默认的转发视图是InternalResourceView
SpringMVC中创建转发视图的情况
当控制器方法中所设置的视图名称以forward:
为前缀时,创建InternalResourceView视图,此时的视 图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀forward:
去掉,剩余部分作为最终路径通过转发的方式实现跳转
代码实现
这里,我们用testForward
转发访问 testThymeleafView
,第一次是InternalResourceView
第二次,访问success
,就是ThymeleafView
@RequestMapping("/testThymeleafView")
public String testThymeleafView(){
return "success";
}
@RequestMapping("/testForward")
public String testForward(){
return "forward:/testThymeleafView";
}
3、RedirectView
当控制器方法中所设置的视图名称以redirect:
为前缀时,创建RedirectView视图,此时的视图名称不 会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀redirect:
去掉,剩余部分作为最 终路径通过重
定向的方式实现跳转 例如redirect:/
,redirect:/testThymeleafView
@RequestMapping("/testThymeleafView")
public String testThymeleafView(){
return "success";
}
@RequestMapping("/testDirect")
public String testDirect(){
return "redirect:/testThymeleafView";
}
4、view-controller
当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称
时,可以将处理器方法使用view-controller标签
进行表示
path属性
:
view-name属性
:
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
@RequestMapping("/")
public String index(){return "index";}
当我们在springMVC的文件中配置了view-controller,控制器的所有的请求映射都会失效
<!-- 开启MVC注解驱动 -->
<mvc:annotation-driven/>
RESTful
REST:Representational State Transfer,表现层资源状态转移
1、RESTful的实现
操作 | 传统方式 | REST风格 |
---|---|---|
查询 | getUserById?id=1 | user/1 |
保存 | saveUser | user |
删除 | deleteUser?id=1 | user/1 |
更新 | updateUser | user |
2、HiddenHttpMethodFilter
可以将 POST
请求转化为 DELETE
或 PUT
请求
在web.xml
中注册HiddenHttpMethodFilter
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在xml
中filter
的顺序按照filter-mapping
顺序,所以必须先注册CharacterEncodingFilter
,再注册HiddenHttpMethodFilter
源码细🔒
我们来到HiddenHttpMethodFilter
的 doFilterInternal()
方法
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest requestToUse = request;
// 所以,首先需要是POST请求
if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
// 我们在源码中找找methodParam
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
// 我们在源码中找找ALLOWED_METHODS
if (ALLOWED_METHODS.contains(method)) {
// 创建一个新的请求对象
requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
}
}
}
// 放行的时候,放的是新创建的请求对象
filterChain.doFilter((ServletRequest)requestToUse, response);
}
下面为methodParam
private String methodParam = "_method";
下面为ALLOWED_METHODS
static {
ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
// 我们接着看看HttpMethod是什么
}
HttpMethod
是一个枚举
public enum HttpMethod {
GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS,TRACE;
...
代码实现
<input type="hidden" name="_method" value="PUT">
<input type="hidden" name="_method" value="DELETE">
RESTful案例
1、代码实现
功能 | URL地址 | 请求方式 |
---|---|---|
访问首页 | / | GET |
查询全部数据 | /employee | GET |
删除 | /employee/1 | DELETE |
跳转到添加数据页面 | /toAdd | GET |
执行保存 | /employee | GET |
跳转到更新页面数据 | /employee/1 | GET |
执行更新 | /employee | PUT |
员工Bean
public class Employee {
private Integer id;
private String lastName;
private String email;
// 1 male 0 female
private Integer gender;
...
}
模拟Dao层
@Repository
public class EmployeeDao {
private static Map<Integer, Employee> employees = null;
// 初始员工信息
static {
employees = new HashMap<Integer, Employee>();
employees.put(1,new Employee(1,"A","A@flzj.com",1));
employees.put(2,new Employee(2,"B","B@flzj.com",0));
employees.put(3,new Employee(3,"C","C@flzj.com",0));
employees.put(4,new Employee(4,"D","D@flzj.com",1));
}
private static Integer initId = 5;
// 保存员工信息
public void save(Employee employee){
if(employee != null) {
if(employee.getId() == null) {
employee.setId(initId++);
}
employees.put(employee.getId(),employee);
}
}
// 返回全部员工信息
public Collection<Employee> getAll(){
return employees.values();
}
// 通过id获取员工信息
public Employee get(Integer id){
return employees.get(id);
}
// 通过id删除员工信息
public void delete(Integer id){
employees.remove(id);
}
}
Controller层
@Controller
public class EmployeeController {
@Autowired
private EmployeeDao employeeDao;
@RequestMapping(value = "/employee", method = RequestMethod.GET)
public String getAllEmployee(Model model){
Collection<Employee> employeeList = employeeDao.getAll();
model.addAttribute("employeeList", employeeList);
return "employee_list";
}
@RequestMapping(value = "/employee/{id}", method = RequestMethod.DELETE)
public String deleteEmployee(@PathVariable("id") Integer id){
employeeDao.delete(id);
return "redirect:/employee";
}
@RequestMapping(value = "/employee", method = RequestMethod.POST)
public String addEmployee(Employee employee){
employeeDao.save(employee);
return "redirect:/employee";
}
@RequestMapping(value = "/employee/{id}", method = RequestMethod.GET)
public String getEmployeeById(@PathVariable("id") Integer id, Model model){
Employee employee = employeeDao.get(id);
model.addAttribute("employee", employee);
return "employee_update";
}
@RequestMapping(value = "/employee", method = RequestMethod.PUT)
public String updateEmployee(Employee employee){
employeeDao.save(employee);
return "redirect:/employee";
}
}
员工列表 employee_list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Employee Info</title>
</head>
<body>
<table id="dataTable" border="1" cellspacing="0" cellpadding="0" style="text-align: center;">
<tr>
<th colspan="5">Employee Info</th>
</tr>
<tr>
<th>id</th>
<th>lastName</th>
<th>email</th>
<th>gender</th>
<th>options(<a th:href="@{/toAdd}">add</a>)</th>
</tr>
<tr th:each="employee : ${employeeList}">
<td th:text="${employee.id}"></td>
<td th:text="${employee.lastName}"></td>
<td th:text="${employee.email}"></td>
<td th:text="${employee.gender}"></td>
<td>
<a @click="deleteEmployee" th:href="@{'/employee/'+${employee.id}}">delete</a>
<a th:href="@{'/employee/'+${employee.id}}">update</a>
</td>
</tr>
</table>
<form id="deleteForm" method="post">
<input type="hidden" name="_method" value="delete">
</form>
<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
<script type="text/javascript">
var vue = new Vue({
el:"#dataTable",
methods:{
deleteEmployee:function (event) {
//根据id获取表单元素
var deleteForm = document.getElementById("deleteForm");
//将触发点击事件的超链接的href属性赋值给表单的action
deleteForm.action = event.target.href;
//提交表单
deleteForm.submit();
//取消超链接的默认行为
event.preventDefault();
}
}
});
</script>
</body>
</html>
添加员工,employee_add.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>add employee</title>
</head>
<body>
<form th:action="@{/employee}" method="post">
lastName:<input type="text" name="lastName"><br>
email:<input type="text" name="email"><br>
gender:<input type="radio" name="gender" value="1">male
<input type="radio" name="gender" value="0">female<br>
<input type="submit" value="add"><br>
</form>
</body>
</html>
<mvc:view-controller path="/toAdd" view-name="employee_add"></mvc:view-controller>
更新员工,employee_update.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>update employee</title>
</head>
<body>
<form th:action="@{/employee}" method="post">
<input type="hidden" name="_method" value="put">
<input type="hidden" name="id" th:value="${employee.id}">
lastName:<input type="text" name="lastName" th:value="${employee.lastName}"><br>
email:<input type="text" name="email" th:value="${employee.email}"><br>
gender:<input type="radio" name="gender" value="1" th:field="${employee.gender}">male
<input type="radio" name="gender" value="0" th:field="${employee.gender}">female<br>
<input type="submit" value="update"><br>
</form>
</body>
</html>
2、开放对静态资源的访问
我们一看,原来是没有我们写的static
那么我们就重新打包1下把
发现还是访问不了
defaultservlet
是我们原来处理静态资源
的servlet
<!-- 开放对静态资源的访问 -->
<mvc:default-servlet-handler/>
如果springMVC没有找到,就交给默认的servlet找
终于上网课了
我超,我健康码黄了😰,🏃♂️🏃♂️