SpringMVC中的参数绑定
参数绑定其实也就是我们前后台交互的一个重要环节,比如你要显示数据啊,拿到数据等等。
下面我们就来学习一下SpringMVC的参数绑定:下面实例分别实现了,查询、批量删除单个删除、修改、批量修改。
首先我们要搭建好基础的SpringMVC的环境:spring-mvc.xml(其中有一个自定义转换是我们常用的一种)和web.xml(这里多配了一个字符集过滤器):
spring-mvc.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
<!-- 1、开启注解方式的映射器处理器、处理器适配器 -->
<mvc:annotation-driven conversion-service="conversionService"/>
<!-- 2.处理器 -->
<context:component-scan base-package="com.RequestMapping"></context:component-scan>
<!-- 3.视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/"></property>
<!-- 后缀 -->
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 自定义参数配置 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<!-- 自定义转换器 -->
<property name="converters">
<set>
<bean class="com.RequestMapping.Converter.CustomerDateConverter"></bean>
</set>
</property>
</bean>
</beans>
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>SpringMVC_Parameter</display-name>
<!-- 配置前端控制器:负责接收请求,响应结果 -->
<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:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<!-- 过滤器(字符集编码格式) -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class> org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
导入jar包(文章中有jar包链接)
我们写一个用来测试的实体类,emp和dept
Emp:
package com.RequestMapping.entity;
import java.util.Date;
import java.util.List;
/**
* 映射Emp表的实体类
*/
public class Emp {
/**雇员编号*/
private int empno;
/**雇员姓名*/
private String ename;
/**职位*/
private String job;
/**上级经理*/
private int mgr;
/**入职日期*/
private Date hiredate;
/**薪水*/
private double salary;
/**奖金*/
private double comm;
/**部门*/
private Dept dept;//一对一,一个员工属于一个部门
/**管理的下属员工信息*/
private List<Emp> subEmpList;//一对多,一个员工可以管理多个下属
/**构造函数*/
public Emp() {
super();
}
public Emp(int empno, String ename, String job, int mgr, Date hiredate, double salary, double comm, Dept dept,
List<Emp> subEmpList) {
super();
this.empno = empno;
this.ename = ename;
this.job = job;
this.mgr = mgr;
this.hiredate = hiredate;
this.salary = salary;
this.comm = comm;
this.dept = dept;
this.subEmpList = subEmpList;
}
/**访问器*/
public int getEmpno() {
return empno;
}
public void setEmpno(int empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public int getMgr() {
return mgr;
}
public void setMgr(int mgr) {
this.mgr = mgr;
}
public Date getHiredate() {
return hiredate;
}
public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public double getComm() {
return comm;
}
public void setComm(double comm) {
this.comm = comm;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
public List<Emp> getSubEmpList() {
return subEmpList;
}
public void setSubEmpList(List<Emp> subEmpList) {
this.subEmpList = subEmpList;
}
@Override
public String toString() {
return "Emp [empno=" + empno + ", ename=" + ename + ", job=" + job + ", mgr=" + mgr + ", hiredate=" + hiredate
+ ", salary=" + salary + ", comm=" + comm + ", dept=" + dept + "]";
}
}
Dept:
package com.RequestMapping.entity;
/**
* 映射Dept表的实体类
*/
public class Dept {
/**部门编号*/
private int deptno;
/**部门名称*/
private String dname;
/**部门所在地址*/
private String location;
/**构造函数*/
public Dept() {
super();
}
public Dept(int deptno, String dname, String location) {
super();
this.deptno = deptno;
this.dname = dname;
this.location = location;
}
/**访问器*/
public int getDeptno() {
return deptno;
}
public void setDeptno(int deptno) {
this.deptno = deptno;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
@Override
public String toString() {
return "Dept [deptno=" + deptno + ", dname=" + dname + ", location=" + location + "]";
}
}
实体类写完后,当然是Dao、service、Controller我就依次将代码放到下方。。
Dao的接口 IEmpDao:
package com.RequestMapping.Dao;
import java.util.List;
import com.RequestMapping.entity.Emp;
public interface IEmpDao {
/*
* 查询所有雇员信息
*/
public List<Emp> selectEmp();
/*
* 查询单个雇员信息
*/
public Emp selectEmpByEmpno(int empno);
}
Dao的实现类 EmpDaoImpl:
这里是写死的
package com.RequestMapping.Dao.Impl;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.RequestMapping.Dao.IEmpDao;
import com.RequestMapping.entity.Dept;
import com.RequestMapping.entity.Emp;
/**
* 数据访问层-接口实现类
*/
@Repository("empDao")//相当于 IEmpDao empDao = new EmpDaoImpl();
public class EmpDaoImpl implements IEmpDao {
private static List<Emp> empList = new ArrayList<Emp>();
static{
//下属列表
List<Emp> subEmpList = new ArrayList<Emp>();
subEmpList.add(new Emp(100001, "下属一号", "需求工程师", 9999, new Date(), 8000, 500, new Dept(10, "需求部", "东软大厦125C"),null));
subEmpList.add(new Emp(100002, "下属二号", "前端工程师", 9999, new Date(), 8000, 500, new Dept(20, "前端部", "东软大厦125C"),null));
subEmpList.add(new Emp(100003, "下属三号", "开发工程师", 9999, new Date(), 8000, 500, new Dept(30, "开发部", "东软大厦125C"),null));
subEmpList.add(new Emp(100004, "下属四号", "测试工程师", 9999, new Date(), 8000, 500, new Dept(40, "测试部", "东软大厦125C"),null));
subEmpList.add(new Emp(100005, "下属五号", "实施工程师", 9999, new Date(), 8000, 500, new Dept(50, "实施部", "东软大厦125C"),null));
//员工列表
empList.add(new Emp(1111, "张三", "需求工程师", 9999, new Date(), 8000, 500, new Dept(10, "需求部", "东软大厦125C"),subEmpList));
empList.add(new Emp(2222, "李四", "前端工程师", 9999, new Date(), 8000, 500, new Dept(20, "前端部", "东软大厦125C"),subEmpList));
empList.add(new Emp(3333, "王五", "开发工程师", 9999, new Date(), 8000, 500, new Dept(30, "开发部", "东软大厦125C"),subEmpList));
empList.add(new Emp(4444, "赵六", "测试工程师", 9999, new Date(), 8000, 500, new Dept(40, "测试部", "东软大厦125C"),subEmpList));
empList.add(new Emp(5555, "田七", "实施工程师", 9999, new Date(), 8000, 500, new Dept(50, "实施部", "东软大厦125C"),subEmpList));
}
@Override
public List<Emp> selectEmp() {
return empList;
}
@Override
public Emp selectEmpByEmpno(int empno) {
return empList.get(empno);
}
}
Service的接口 IEmpService:
package com.RequestMapping.Service;
import java.util.List;
import com.RequestMapping.entity.Emp;
public interface IEmpService {
/*
* 查询所有雇员信息
*/
public List<Emp> selectEmp();
/*
* 查询单个雇员信息
*/
public Emp selectEmpByEmpno(int empno);
}
Service的实现类 EmpServiceImpl:
package com.RequestMapping.Service.Impl;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.RequestMapping.Dao.IEmpDao;
import com.RequestMapping.entity.Emp;
import com.RequestMapping.Service.IEmpService;
/**
* 业务逻辑层-接口实现类
*/
@Service("empService")//相当于IEmpService empService = new EmpServiceImpl();
public class EmpServiceImpl implements IEmpService {
//@Autowired @Qualifier("empDao") //默认根据类型匹配,通常结合@Qualifier指定引用名称使用
@Resource(name="empDao") //默认根据名称匹配
private IEmpDao empDao;
@Override
public List<Emp> selectEmp() {
List<Emp> empList = empDao.selectEmp();
return empList;
}
@Override
public Emp selectEmpByEmpno(int empno) {
Emp emp= empDao.selectEmpByEmpno(empno);
return emp;
}
}
Controller层 EmpController:
这里就是主要的核心代码了7中参数的绑定:
package com.RequestMapping.Controller;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.ModelAndView;
import com.RequestMapping.Service.IEmpService;
import com.RequestMapping.entity.Emp;
/**
* 控制器:处理器请求,响应结果
*/
@Controller
@RequestMapping("/emp")
public class EmpController {
@Resource(name="empService")
private IEmpService empService;
/*
* 1、默认支持类型的五种类型:查询、存值等
* HttpServletRequest、HttpServletResponse、HttpSession、Model(存数据)、ModelMap(继承了LinkedHashMap也是用来存数据的)
* 用法:与servlet中的一样,改怎么使用就这么使用
*/
@RequestMapping("/get")
public String getEmps2(HttpServletRequest request,HttpServletResponse response,HttpSession session,Model model,ModelMap modelMap)
{
//1.调用service方法,查询所有雇员信息
List<Emp> empList= empService.selectEmp();
//request
System.out.println("参数:"+request.getParameter("ename"));
//response
System.out.println("response:国家:"+response.getLocale().getCountry()+" 语言"+response.getLocale().getLanguage());
//2.保存到作用域
request.setAttribute("empList", empList);
//session
System.out.println("session:"+session.getId());
/*
* http://localhost:8080/SpringMVC_Parameter/emp/get.action?ename=king
* http://localhost:8080/empUpdate.jsp
*/
//model
model.addAttribute("empList", empList);
//modelMap
modelMap.addAttribute("title", "雇员信息表");
//3.指定跳转的路径
return "empQuery";
}
/*
* 2、简单类型:查询、单个删除等
* ① 支持字符串、整型、单精度/双精度、布尔类型
*
* ② 建议所有的基本类型使用包装类:
* 原因:比如这里使用int,如果你在jsp页面忘记加参数了也就是路径?后面的那个(实际参数),它默认为null,int是不能存null的
* 例如:Integer、Float/Double、Boolean类型
*
* ③ 形式参数的参数名称与实际参数(请求发送时的参数)的参数名称保持大小写一致
* 形参:public String getEmpByEmpno(Model model, Integer empno)中的empno
* 实际参数:<a href="${pageContext.request.contextPath}/emp/getEmpByEmpno.action?empno=${status.index}">编辑</a>
* 中的empno
* 他们两个的参数名(empno)必须一致
*
* ④:当形参与实际参数不一致时,我们可以使用@RequestParam()来解决
* public String getEmpByEmpno(Model model,@RequestParam(value = "index",defaultValue = "0",required = true) Integer empno)
*
* value:实现参数的名称
* defaultVlaue:当值为null时,默认值为什么
* required:是否必须带改参数
*
*/
@RequestMapping("/getEmpByEmpno")
public String getEmpByEmpno(Model model,Integer empno) {
//1、调用service方法获取当前emp的信息
Emp emp= empService.selectEmpByEmpno(empno);
//2、保存到作用域(这里使使用springMVC带的model)
model.addAttribute("emp", emp);
//发送到修改页面
return "empUpdate";
}
/*
3、简单的pojo类型:更新数据
将表单域中的name属性值与pojo对象的属性名称保持大小写一致即:
雇员编号:<input type="text" name="empno" value="${emp.empno}"><br/><br/>
中的name属性值与
public String updateEmp(Emp emp)方法中emp对象的setName后的ename名称保持大小写一致
4、复杂(包装)pojo类型:dept.deptno dept.dname
5、自定义参数:需要自定义转换器(用于Date转换)
*/
@RequestMapping("/updateEmp")
public String updateEmp(HttpServletResponse response,HttpServletRequest request, Emp emp) throws UnsupportedEncodingException {
System.out.println("修改用户的信息"+emp);
return "success";
}
/*
6、数组类型:批量删除、单个删除
① 实际参数的名称与形式参数名称保持大小写一致即:
<form action="${pageContext.request.contextPath}/emp/deleterEmps.action">中的name属性值
与
public String deleterEmps(Integer[] empnos)中的empnos保持大小写一致
*/
@RequestMapping("/deleterEmps")
public String deleterEmps(Integer[] empnos) {
System.out.println("删除用户empno:"+Arrays.toString(empnos));
return "success";
}
/**
* 7.List集合
*/
@RequestMapping("/getSubEmpByEmpno")
public String getSubEmpByEmpno(Model model,Integer empno)
{
System.out.println("查看当前用户的empno:" + empno);
//当前员工的信息
Emp emp = empService.selectEmpByEmpno(empno);
System.out.println("查看当前用户的empno:" + empno + "得到的员工信息:" + emp);
//当前员工的,下属信息
List<Emp> subEmpList = emp.getSubEmpList();
//保存数据
model.addAttribute("emp", emp);
model.addAttribute("subEmpList", subEmpList);
return "subEmQuery";
}
@RequestMapping("/updateSubEmps")
public String updateSubEmps(Emp emp)
{
List<Emp> subEmpList = emp.getSubEmpList();
for (Emp subEmp : subEmpList) {
System.out.println(subEmp);
}
return "success";
}
}
下面是测试时用的jspy页面:
index.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="emp/get.action?ename=king">点击获取所有雇员信息-------</a><br><br>
</body>
</html>
empQuery.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>${title}</h1>
<form action="${pageContext.request.contextPath}/emp/deleterEmps.action">
<table border="1px" width="80%" style="border-collapse: collapse;">
<tr height="50px">
<th>序号</th>
<th>雇员编号</th>
<th>雇员姓名</th>
<th>职位</th>
<th>上级经理</th>
<th>入职日期</th>
<th>薪水</th>
<th>奖金</th>
<th>部门</th>
<th>操作</th>
</tr>
<c:forEach items="${empList}" var="emp" varStatus="status">
<tr height="35px">
<td>${status.index+1}
<input type="checkbox" name="empnos" value="${emp.empno}">
</td>
<td>${emp.empno}</td>
<td>${emp.ename}</td>
<td>${emp.job}</td>
<td>${emp.mgr}</td>
<td> <fmt:formatDate value="${emp.hiredate}" pattern="yyyy-MM-dd"/></td>
<td>${emp.salary}</td>
<td>${emp.comm}</td>
<td>${emp.dept.dname}</td>
<td>
<a href="${pageContext.request.contextPath}/emp/getEmpByEmpno.action?empno=${status.index}">编辑</a>
<a href="${pageContext.request.contextPath}/emp/deleterEmps.action?empnos=${emp.empno}">修改</a>
<a href="${pageContext.request.contextPath}/emp/getSubEmpByEmpno.action?empno=${status.index}">查看下属员工</a>
</td>
</tr>
</c:forEach>
</table><br><br>
<input type="submit" value="批量删除">
</form>
</body>
</html>
empUpdate.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/emp/updateEmp.action" method="post">
雇员编号:<input type="text" name="empno" value="${emp.empno}" disabled="false" >
<!-- 当想让内容不可修改,同时在后台还能拿到参数,除了使用 readonly外,可以使用disable,然后在使用隐藏域-->
<input type="hidden" name="empno" value="${emp.empno}">
<br/><br/>
雇员姓名:<input type="text" name="ename" value="${emp.ename}" ><br/><br/>
职位:<input type="text" name="job" value="${emp.job}"><br/><br/>
上级经理:<input type="text" name="mgr" value="${emp.mgr}"><br/><br/>
入职日期:<input type="text" name="hiredate" value='<fmt:formatDate value="${emp.hiredate}" pattern="yyyy-MM-dd"/>'><br/><br/>
薪水:<input type="text" name="salary" value="${emp.salary}"><br/><br/>
奖金:<input type="text" name="comm" value="${emp.comm}"><br/><br/>
所在部门:<select name="dept.deptno">
<option value="10" ${emp.dept.deptno eq 10 ? "selected" : ""}>需求部</option>
<option value="20" ${emp.dept.deptno eq 20 ? "selected" : ""}>前端部</option>
<option value="30" ${emp.dept.deptno eq 30 ? "selected" : ""}>开发部</option>
<option value="40" ${emp.dept.deptno eq 40 ? "selected" : ""}>测试部</option>
<option value="50" ${emp.dept.deptno eq 50 ? "selected" : ""}>实施部</option>
</select><br/><br/>
<input type="submit" value="提交修改">
</form>
</body>
</html>
subEmQuery.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>【${emp.ename}】的下属员工列表</h1>
<form action="${pageContext.request.contextPath}/emp/updateSubEmps.action">
<table border="1px" width="80%" style="border-collapse: collapse;">
<tr height="50px">
<th>序号</th>
<th>雇员编号</th>
<th>雇员姓名</th>
<th>职位</th>
<th>上级经理</th>
<th>入职日期</th>
<th>薪水</th>
<th>奖金</th>
<th>部门</th>
</tr>
<!--
subEmpList.get(0).getEname();
subEmpList.get(0).setEname(ename);
-->
<c:forEach items="${subEmpList}" var="subEmp" varStatus="status">
<tr height="35px">
<td>${status.index+1}</td>
<td>
<input type="text" size="10" value="${subEmp.empno}" disabled="disabled">
<input type="hidden" name="subEmpList[${status.index}].empno" value="${subEmp.empno}">
</td>
<td><input type="text" size="10" name="subEmpList[${status.index}].ename" value="${subEmp.ename}"></td>
<td><input type="text" size="10" name="subEmpList[${status.index}].job" value="${subEmp.job}"></td>
<td><input type="text" size="10" name="subEmpList[${status.index}].mgr" value="${subEmp.mgr}"></td>
<td><input type="text" size="10" name="subEmpList[${status.index}].hiredate" value='<fmt:formatDate value="${subEmp.hiredate}" pattern="yyyy-MM-dd"/>'></td>
<td><input type="text" size="10" name="subEmpList[${status.index}].salary" value="${subEmp.salary}"></td>
<td><input type="text" size="10" name="subEmpList[${status.index}].comm" value="${subEmp.comm}"></td>
<td>
<select name="subEmpList[${status.index}].dept.deptno">
<option value="10" ${subEmp.dept.deptno eq 10 ? "selected" : "" }>需求部</option>
<option value="20" ${subEmp.dept.deptno eq 20 ? "selected" : "" }>前端部</option>
<option value="30" ${subEmp.dept.deptno eq 30 ? "selected" : "" }>开发部</option>
<option value="40" ${subEmp.dept.deptno eq 40 ? "selected" : "" }>测试部</option>
<option value="50" ${subEmp.dept.deptno eq 50 ? "selected" : "" }>实施部</option>
</select>
</td>
</tr>
</c:forEach>
</table><br/><br/>
<input type="submit" value="批量修改">
</form>
</body>
</html>
success.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
恭喜你!!测试成功、、、、
</body>
</html>
我的结构目录: