实验一 Servlet 与会话技术
- 实验目的与要求
- 实验目的:
- 理解 Servlet 的工作原理。
- 掌握 Servlet 的创建及使用。
- 掌握请求与响应对象的使用。
- 掌握 Session 与 Cookie 对象的使用。
- 实验要求:
- 独立完成实验。
- 书写实验报告书。
二、实验环境
- 服务器:Tomcat 10
- IDE:IDEA
- 数据库:MySQL
三、实验步骤与结果
- 用户注册页面(register.html)
<!DOCTYPE html>
<html>
<head>
<title>用户注册</title>
</head>
<body>
<form action="RegisterServlet" method="post">
用户名: <input type="text" name="username"><br>
密码: <input type="password" name="password"><br>
<input type="submit" value="注册">
</form>
</body>
</html>
- 用户登录页面(login.html)
<!DOCTYPE html>
<html>
<head>
<title>用户登录</title>
</head>
<body>
<form action="LoginServlet" method="post">
用户名: <input type="text" name="username"><br>
密码: <input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
3.编写主页面 Servlet(IndexServlet.java),判断用户是否已经登录,如果用户已经登录,提示欢迎用户信息,并给出“退出”链接。如果用户还没有登录,提示用户登录后才能访问主页面,并给出“登录”和“注册”链接。
@WebServlet("/IndexServlet")
public class IndexServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
String username = (String) session.getAttribute("username");
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
if (username != null) {
out.println("欢迎, " + username + "! <a href='LogoutServlet'>退出</a>");
} else {
out.println("请先<a href='login.html'>登录</a>或<a href='register.html'>注册</a>.");
}
}
}
4.编写处理用户注册信息 Servlet(RegisterServlet.java),用于接收注册页面register.html 中的用户注册信息,并把用户注册信息中的用户名和密码保存在 Cookie 中 1 个月,保存成功后提示用户注册成功并跳转到登录页面 login.html。注册时应首先判断用户是否注册过(从 Cookie 中获取用户注册信息),如果注册过,则提示用户已经注册过,不能重复注册,并跳转到登录页面。
@WebServlet("/RegisterServlet")
public class RegisterServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
Cookie[] cookies = request.getCookies();
boolean registered = false;
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("username")) {
registered = true;
break;
}
}
}
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
if (registered) {
out.println("您已注册过,不能重复注册,请<a href='login.html'>登录</a>!");
} else {
Cookie userCookie = new Cookie("username", username);
userCookie.setMaxAge(30*24*60*60);
response.addCookie(userCookie);
Cookie passCookie = new Cookie("password", password);
passCookie.setMaxAge(30*24*60*60);
response.addCookie(passCookie);
out.println("注册成功,请<a href='login.html'>登录</a>.");
}
}
}
5.编写处理用户登录信息Servle(t LoginServlet.java),用于接收登录页面login.html中的用户登录信息,并从 Cookie 中获取用户注册信息,如果从 Cookie 中未找到用户注册信息,提示用户还未注册并跳转到注册页面,否则把从登录页面中接收到的用户信息和从 Cookie 中获取的用户信息进行比较,如果一致,则登录成功,把用户信息保存在 Session 中,并跳转到主页面,否则提示用户所填写登录信息有误, 并跳回到登录页面。
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
Cookie[] cookies = request.getCookies();
String registeredUsername = null;
String registeredPassword = null;
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("username")) {
registeredUsername = cookie.getValue();
} else if (cookie.getName().equals("password")) {
registeredPassword = cookie.getValue();
}
}
}
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
if (registeredUsername == null) {
out.println("您还未注册,请先<a href='register.html'>注册</a>.");
} else if (username.equals(registeredUsername) && password.equals(registeredPassword)) {
HttpSession session = request.getSession();
session.setAttribute("username", username);
response.sendRedirect("IndexServlet");
} else {
out.println("登录信息有误,请<a href='login.html'>重试</a>.");
}
}
}
6.编写退出登录 Servlet(LogoutServlet.java),实现退出登录功能,提示用户成功退出登录,并跳转到主页面。
@WebServlet("/LogoutServlet")
public class LogoutServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
session.invalidate();
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("退出成功,请<a href='IndexServlet'>返回主页</a>.");
}
}
四、讨论、心得
在本次实验中,我遇到了以下问题:
- 在使用 Cookie 保存用户信息时,初期不清楚 Cookie 的生命周期设置,导致用户信息没有被正确保存。通过查阅资料和调试代码,了解了 setMaxAge() 方法的使用。
- 在实现用户登录功能时,最初没有正确处理从 Cookie 中读取用户信息的逻辑,导致验证失败。通过逐步调试,解决了这一问题。
本次实验让我更深入地理解了 Servlet 的工作机制,并熟悉了如何使用 Servlet 处理请求和响应。
实验二 JSP 技术
- 实验目的与要求
- 实验目的:
- 掌握在 JSP 中添加 Java 脚本。
- 掌握 JSP 中常用的指令和动作标签。
- 掌握 JSP 中 9 个隐式对象的使用。
- 实验要求:
- 独立完成实验。
- 书写实验报告书。
二、实验环境
- 服务器:Tomcat 10
- IDE:IDEA
- 数据库:MySQL
三、实验步骤与结果
- 把主页面 IndexServlet 替换为 index.jsp,实现与 IndexServlet 相同的功能。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>主页</title>
</head>
<body>
<jsp:include page="header.jsp"/>
<%
HttpSession session = request.getSession();
String username = (String) session.getAttribute("username");
if (username != null) {
out.println("欢迎, " + username + "! <a href='LogoutServlet'>退出</a>");
} else {
out.println("请先<a href='login.jsp'>登录</a>或<a href='register.jsp'>注册</a>.");
}
%>
<jsp:include page="footer.jsp"/>
</body>
</html>
- 设计并实现页面头信息文件 header.jsp 和页面脚信息文件 footer.jsp。
<!-- header.jsp -->
<div>
<h1>欢迎来到我的网站</h1>
<hr>
</div>
<!-- footer.jsp -->
<div>
<hr>
<p>版权所有 © 2024</p>
</div>
- 修改注册页面为register.jsp,修改登录页面为login.jsp,并在index.jsp,register.jsp和 login.jsp 页面中都包含进 header.jsp 和 footer.jsp。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户注册</title>
</head>
<body>
<jsp:include page="header.jsp"/>
<form action="RegisterServlet" method="post">
用户名: <input type="text" name="username"><br>
密码: <input type="password" name="password"><br>
<input type="submit" value="注册">
</form>
<jsp:include page="footer.jsp"/>
</body>
</html>
- 修改注册页面 register.jsp,当用户访问注册页面时,判断用户是否注册过,如果用户已经注册过,将错误消息“您已注册过,不能重复注册,请登录!”保存在request 范围,再跳转到登录页面,最后修改登录页面 login.jsp,在登录页面获取并显示该错误消息。
- 设计并实现页面头信息文件 header.jsp 和页面脚信息文件 footer.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户登录</title>
</head>
<body>
<jsp:include page="header.jsp"/>
<form action="LoginServlet" method="post">
用户名: <input type="text" name="username"><br>
密码: <input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
<jsp:include page="footer.jsp"/>
</body>
</html>
- 修改注册页面为 register.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户注册</title>
</head>
<body>
<jsp:include page="header.jsp"/>
<form action="RegisterServlet" method="post">
用户名: <input type="text" name="username"><br>
密码: <input type="password" name="password"><br>
<input type="submit" value="注册">
</form>
<jsp:include page="footer.jsp"/>
</body>
</html>
- 修改登录页面 login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户登录</title>
</head>
<body>
<jsp:include page="header.jsp"/>
<%
String errorMsg = (String) request.getAttribute("errorMsg");
if (errorMsg != null) {
out.println("<p style='color:red'>" + errorMsg + "</p>");
}
%>
<form action="LoginServlet" method="post">
用户名: <input type="text" name="username"><br>
密码: <input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
<jsp:include page="footer.jsp"/>
</body>
</html>
- 讨论、心得
在本次实验中,我遇到了以下问题:
- · 初期不清楚如何在 JSP 中包含其他 JSP 文件,通过查阅资料和调试,学习到了 <jsp:include> 标签的使用。
- · 在实现错误消息显示功能时,最初不知道如何在 JSP 页面中获取和显示从 Servlet 设置的属性,经过调试和实践,理解了使用 request.getAttribute() 方法。
- · 本次实验让我更熟悉了 JSP 技术,特别是如何在 JSP 中编写脚本、使用指令和动作标签,以及如何使用隐式对象。
实验三 JDBC 访问数据库
- 实验目的与要求
- 实验目的:
- 掌握 JDBC 常用 API 的使用。
- 掌握 JDBC 访问数据库的步骤。
- 实验要求:
- 独立完成实验。
- 书写实验报告书。
二、实验环境
- 服务器:Tomcat 10
- IDE:IDEA
- 数据库:MySQL
三、实验步骤与结果
- 在数据库中创建用户表 user,至少包含 username, password 等字段,尽量与注册页面内容保持一致。
CREATE TABLE user (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL
);
- 创建工具类 DBUtils.java,至少包含获得数据库连接和释放资源 2 个方法。
public class DBUtils {
private static final String URL = "jdbc:mysql://localhost:3306/yourDatabase";
private static final String USER = "root";
private static final String PASSWORD = "root";
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PASSWORD);
}
public static void close(Connection conn, Statement stmt, ResultSet rs) {
try {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
- 修改 RegisterServlet.java,把用户注册从 Cookie 实现改为由数据库实现。接收注册页面中的用户注册信息,并把用户注册信息保存到数据库中,在注册过程中需要判断用户名是否已经存在,如果已经存在,提示用户该用户名已被占用,并返回注册页面,否则提示用户注册成功并跳转到登陆页面。
@WebServlet("/RegisterServlet")
public class RegisterServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = DBUtils.getConnection();
String sqlCheck = "SELECT * FROM user WHERE username = ?";
pstmt = conn.prepareStatement(sqlCheck);
pstmt.setString(1, username);
rs = pstmt.executeQuery();
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
if (rs.next()) {
request.setAttribute("errorMsg", "您已注册过,不能重复注册,请登录!");
request.getRequestDispatcher("login.jsp").forward(request, response);
} else {
String sqlInsert = "INSERT INTO user (username, password) VALUES (?, ?)";
pstmt = conn.prepareStatement(sqlInsert);
pstmt.setString(1, username);
pstmt.setString(2, password);
pstmt.executeUpdate();
out.println("注册成功,请<a href='login.jsp'>登录</a>.");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtils.close(conn, pstmt, rs);
}
}
}
- 修改 LoginServlet.java,接收登陆页面中的用户登陆信息,并在数据库中查找是否存在和用户登陆信息一致的记录,如果查找成功,把用户信息保存在 Session 中,并跳转到主页面,否则提示用户所填写登陆信息有误,并跳回到登陆页面。
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = DBUtils.getConnection();
String sql = "SELECT * FROM user WHERE username = ? AND password = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);
rs = pstmt.executeQuery();
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
if (rs.next()) {
HttpSession session = request.getSession();
session.setAttribute("username", username);
response.sendRedirect("index.jsp");
} else {
request.setAttribute("errorMsg", "登录信息有误,请重试。");
request.getRequestDispatcher("login.jsp").forward(request, response);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtils.close(conn, pstmt, rs);
}
}
}
- 讨论、心得
在本次实验中,我遇到了以下问题:
- 本次实验让我更深入地理解了 Servlet 的工作机制,并熟悉了如何使用 Servlet 处理请求和响应。初期在配置 JDBC 驱动时遇到了问题,通过查看文档和示例代码,解决了驱动加载的问题。
- 在编写数据库操作代码时,不熟悉PreparedStatement 的使用,经过调试和学习,掌握了 PreparedStatement 的基本用法。
- 本次实验让我熟悉了如何使用 JDBC 访问数据库,并加深了对数据库操作的理解。
实验四 MVC 设计模式
- 实验目的与要求
- 实验目的:
- 理解 MVC 设计模式的思想。
- 掌握 MVC 设计模式的开发方法。
- 培养学生分析问题和解决问题的能力。
- 实验要求:
- 独立完成实验。
- 书写实验报告书。
二、实验环境
- 服务器:Tomcat 10
- IDE:IDEA
- 数据库:MySQL
三、实验步骤与结果
- 创建数据库,并在数据库中创建用户表
CREATE DATABASE user_management;
USE user_management;
CREATE TABLE user (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL
);
- 创建并实现以下 jsp 页面(表示层页面): 用户列表页面(userList.jsp),添加用户页面(addUser.jsp),修改用户信息页面(modifyUser.jsp)。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户列表</title>
</head>
<body>
<jsp:include page="header.jsp"/>
<h2>用户列表</h2>
<table border="1">
<tr>
<th>用户名</th>
<th>操作</th>
</tr>
<%
List<User> userList = (List<User>) request.getAttribute("userList");
if (userList != null) {
for (User user : userList) {
out.println("<tr><td>" + user.getUsername() + "</td><td><a href='ModifyUserServlet?id=" + user.getId() + "'>修改</a> <a href='DeleteUserServlet?id=" + user.getId() + "'>删除</a></td></tr>");
}
}
%>
</table>
<a href="addUser.jsp">添加用户</a>
<jsp:include page="footer.jsp"/>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户列表</title>
</head>
<body>
<jsp:include page="header.jsp"/>
<h2>用户列表</h2>
<table border="1">
<tr>
<th>用户名</th>
<th>操作</th>
</tr>
<%
List<User> userList = (List<User>) request.getAttribute("userList");
if (userList != null) {
for (User user : userList) {
out.println("<tr><td>" + user.getUsername() + "</td><td><a href='ModifyUserServlet?id=" + user.getId() + "'>修改</a> <a href='DeleteUserServlet?id=" + user.getId() + "'>删除</a></td></tr>");
}
}
%>
</table>
<a href="addUser.jsp">添加用户</a>
<jsp:include page="footer.jsp"/>
</body>
</html>
userList.jsp
addUser.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>添加用户</title>
</head>
<body>
<jsp:include page="header.jsp"/>
<h2>添加用户</h2>
<form action="AddUserServlet" method="post">
用户名: <input type="text" name="username"><br>
密码: <input type="password" name="password"><br>
<input type="submit" value="添加">
</form>
<jsp:include page="footer.jsp"/>
</body>
</html>
modifyUser.jsp
- 设计并实现页面头信息文件 header.jsp 和页面脚信息文件 footer.jsp。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户管理系统</title>
<style>
body {
font-family: Arial, sans-serif;
}
header {
background-color: #f8f9fa;
padding: 10px 20px;
text-align: center;
font-size: 24px;
border-bottom: 1px solid #e9ecef;
}
</style>
</head>
<body>
<header>
<h1>用户管理系统</h1>
</header>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<footer>
<hr>
<p style="text-align:center;">© 2024 用户管理系统</p>
</footer>
</body>
</html>
- 创建以下包,并在对应的包中添加必要的类: com.yourName.servlet ( 控 制 层 类 ) com.yourName.service ( 业 务 层 类 ) com.yourName.dao(数据访问层类) com.yourName.entity ( 实 体 类 ) com.yourName.util(工具类)
package com.example.model;
public class User {
private int id;
private String username;
private String password;
public User() {}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public User(int id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public int getId() {
return id;
}
public void setId(int 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;
}
}
package com.example.model;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class UserDAO {
public static List<User> getAllUsers() {
List<User> userList = new ArrayList<>();
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
conn = DBUtils.getConnection();
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT * FROM user");
while (rs.next()) {
int id = rs.getInt("id");
String username = rs.getString("username");
String password = rs.getString("password");
userList.add(new User(id, username, password));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtils.close(rs, stmt, conn);
}
return userList;
}
public static void addUser(User user) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = DBUtils.getConnection();
String sql = "INSERT INTO user (username, password) VALUES (?, ?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, user.getUsername());
pstmt.setString(2, user.getPassword());
pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtils.close(pstmt, conn);
}
}
public static void deleteUser(int id) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = DBUtils.getConnection();
String sql = "DELETE FROM user WHERE id = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, id);
pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtils.close(pstmt, conn);
}
}
public static User getUserById(int id) {
User user = null;
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = DBUtils.getConnection();
String sql = "SELECT * FROM user WHERE id = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, id);
rs = pstmt.executeQuery();
if (rs.next()) {
String username = rs.getString("username");
String password = rs.getString("password");
user = new User(id, username, password);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtils.close(rs, pstmt, conn);
}
return user;
}
}
package com.example.controller;
import com.example.model.User;
import com.example.model.UserDAO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
@WebServlet("/UserController")
public class UserController extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String action = request.getParameter("action");
if (action == null) {
action = "list";
}
switch (action) {
case "list":
listUsers(request, response);
break;
case "add":
showAddUserForm(request, response);
break;
case "insert":
addUser(request, response);
break;
case "delete":
deleteUser(request, response);
break;
default:
listUsers(request, response);
break;
}
}
private void listUsers(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<User> userList = UserDAO.getAllUsers();
request.setAttribute("userList", userList);
request.getRequestDispatcher("view/userList.jsp").forward(request, response);
}
private void showAddUserForm(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher("view/addUser.jsp").forward(request, response);
}
private void addUser(HttpServletRequest request, HttpServletResponse response) throws IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
UserDAO.addUser(new User(username, password));
response.sendRedirect("UserController?action=list");
}
private void deleteUser(HttpServletRequest request, HttpServletResponse response) throws IOException {
int id = Integer.parseInt(request.getParameter("id"));
UserDAO.deleteUser(id);
response.sendRedirect("UserController?action=list");
}
}
package com.example.util;
import java.sql.*;
public class DBUtils {
private static final String URL = "jdbc:mysql://localhost:3306/mvcdb";
private static final String USER = "root";
private static final String PASSWORD = "password";
private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
static {
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PASSWORD);
}
public static void close(ResultSet rs, Statement stmt, Connection conn) {
try {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void close(Statement stmt, Connection conn) {
try {
if (stmt != null) stmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
- 讨论、心得(遇到的问题与解决办法等)
通过本次实验,我掌握了 MVC 设计模式的基本概念,了解了如何在 Web 应用中应用 MVC 模式。通过编写用户管理系统,理解了如何将控制器、模型和视图分离,实现代码的模块化和可维护性。