网站注册登录模块

news/2024/5/20 21:20:58/文章来源:https://blog.csdn.net/qq_21923867/article/details/60881103

需求: 利用MVC模式实现网站注册登录模块
- (1)实现一个注册页面,可以将用户使用表单提交的注册信息保存到MySql数据库中,用户可以上传头像
- (2)实现一个登陆页面,当用户输入用户名和密码之后.去之前保存数据库里看看是否有该用户信息,如果有,就提示登陆成功,显示用户头像和用户名.没有的话,提示用户名密码错误,重新登陆
- (3)登录成功的用户可以通过注销退出登录

(一)注册界面前后端

1.注册界面JSP

  • 需要实现前端对注册信息的验证,需要实现上传头像的功能
register.jsp
<form enctype="multipart/form-data" method="post" action="${pageContext.request.contextPath}/servlet/RegisterServlet" onsubmit="return validate()"><table border="1" width="600"><tr><td>*用户名:</td><td><input type="text" name="username" /> </td></tr><tr><td>*密码:</td><td><input type="password" name="password"  /> </td></tr><tr><td>*确认密码:</td><td><input type="password" name="repass"  /> </td></tr><tr><td>*Email:</td><td><input type="text" name="email"  /> </td></tr><tr><td>*头像:</td><td><input type="file" name="headimg"  /> </td></tr><tr><td><input type="submit"  value="提交注册信息" /></td><td></td></tr></table></form>
前端Js验证:
function validate()
{var name = document.getElementById("name");var email = document.getElementById("email");var password = document.getElementById("password");var repass = document.getElementById("repass");//判断姓名格式var regName = /^([\u4e00-\u9fa5]+|([a-z]+\s?)+)$/;if(name.value.match(regName) == null){alert("姓名格式有误,请输入中文名或英文名!");return false;}//判断电子邮箱格式var regEmail=/^\w+@\w+\.(com|edu|org|gov|cn)$/g ;if(email.value.match(regEmail)==null){alert("电子邮箱格式错误!");return false;}//判断密码var regPassword =/(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[^a-zA-Z0-9]).{3,8}/if(password.value.length<3||password.value.length>8){alert("请设置长度位3~8的密码!");return false;}else if(password.value.match(regPassword)==null){alert("密码必须包含字符、数字和特殊字符!");return false;}//验证确认密码if(repss.value != password.value){alert("确认密码不一致!");return false;}return true;
}

2.注册控制器RegisterServlet

业务逻辑:
(1)对拿到的客户端请求数据进行验证和保存,切割表单数据和文件数据(函数一)
(2)验证通过,显示注册成功,跳到登陆界面,服务器端验证不通过,则回到注册界面,比如用户名已注册(函数二)
(3)把实现功能的函数封装进service中,把获取到的表单用户数据保存到数据库中

RegisterServlet.java
    UserService service = new UserService();private String uploadImgDirString;public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException{System.out.println("开始验证");request.setCharacterEncoding("utf-8");//请求编码response.setContentType("text/html;charset=utf-8");//告诉浏览器编码格式response.setCharacterEncoding("utf-8");//正文编码//common切割表单数据和文件User user = parseUser(request);//判断用户名是否已经注册过boolean isUserNameExists = service.isUserNameExists(user.getUsername());if (!isUserNameExists){//没有重名,先保存用户数据到数据库中,然后跳转到登陆界面int result = service.registerUser(user);System.out.println("result="+result);if (result==1){response.getWriter().println("注册成功,1s后跳转到登陆界面");response.setHeader("refresh","1;url='"+request.getContextPath()+"/login.jsp'");}}else{response.getWriter().println("此用户名已被注册,请重新注册");response.setHeader("refresh","1;url='"+request.getContextPath()+"/register.jsp'");}}//解析数据包方法不可分离出去private User parseUser(HttpServletRequest request){//利用common-bin组件来解析数据包,拿到表单数据和图片地址User user = new User();String username = null;String password = null;String email = null;String heading_path = null;//拿到存放上传图像的路径uploadImgDirString = request.getRealPath("/img");//System.out.println("uploadImgDirString=" + uploadImgDirString);//开始解析数据包DiskFileItemFactory factory = new DiskFileItemFactory();ServletContext servletContext=this.getServletConfig().getServletContext();File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir");factory.setRepository(repository);// 拿到上传组件ServletFileUpload upload = new ServletFileUpload(factory);// 开始解析请求的数据包try{List<FileItem> filelist = upload.parseRequest(request);// 处理apache已经分割好的数据集合Iterator<FileItem> iter = filelist.iterator();while (iter.hasNext()){FileItem item = iter.next();if (item.isFormField()){// 如果是表表单数据if (item.getFieldName().equals("username")){username = item.getString();}else if (item.getFieldName().equals("password")){password = item.getString();}else if (item.getFieldName().equals("email")){email = item.getString();}}else{// 如果是文件数据,交给service处理,接收返回的图片路径heading_path=service.getFilePath(item, uploadImgDirString);}}}catch (FileUploadException e){e.printStackTrace();}//封装一条记录到JavaBeanuser.setUsername(username);user.setPassword(password);user.setEmail(email);user.setHeading_path(heading_path);return user;}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException{doGet(request, response);}

3.JavaBean和Mysql数据库

  • 包含username,password,email和图片路径heading_path
  • 数据库需要新建并加载配置数据
User.java
public class User
{private String username;private String email;private String password;private String heading_path;@Overridepublic String toString(){return "User [username=" + username + ", email=" + email+ ", password=" + password + ", heading_path=" + heading_path+ "]";}public User(String username, String email, String password,String heading_path){super();this.username = username;this.email = email;this.password = password;this.heading_path = heading_path;}public String getUsername(){return username;}public void setUsername(String username){this.username = username;}public String getEmail(){return email;}public void setEmail(String email){this.email = email;}public String getPassword(){return password;}public void setPassword(String password){this.password = password;}public String getHeading_path(){return heading_path;}public void setHeading_path(String heading_path){this.heading_path = heading_path;}
}
DBUtils.java
package com.cskaoyan.db.utils;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;//数据仓库
public class DBUtils
{static Properties properties;static Connection con;static String host;static String port;static String db;static{try{//加载配置文件Class.forName("com.mysql.jdbc.Driver").newInstance();String path = DBUtils.class.getClassLoader().getResource("conn.prop").getPath();properties = new Properties();properties.load(new FileInputStream(path));host = properties.getProperty("host");port = properties.getProperty("port");db = properties.getProperty("db");}catch (FileNotFoundException e){e.printStackTrace();}catch (IOException e){e.printStackTrace();}catch (InstantiationException e){e.printStackTrace();}catch (IllegalAccessException e){e.printStackTrace();}catch (ClassNotFoundException e){e.printStackTrace();}}//连接数据库public static Connection getConnection() throws SQLException{String url = "jdbc:mysql://" + host + ":" + port + "/" + db;return DriverManager.getConnection(url, properties);}//关闭连接,释放资源public static void realeseResourse(Connection con, Statement st,ResultSet rs){if (con != null){try{con.close();}catch (SQLException e){e.printStackTrace();}}if (st != null){try{con.close();}catch (SQLException e){e.printStackTrace();}}if (rs != null){try{con.close();}catch (SQLException e){e.printStackTrace();}}}}
数据库配置conn.prop放在src目录下
host=localhost
port=3306
db=mysj
user=root
password=123456

4.数据接口访问层

  • 封装不同类型的数据库存取方法,比如将数据存入mysql数据库中,或者将数据存入xml文件中打印输出
    需要不同的方法来实现
package com.cskaoyan.dataInterface;
import com.cskaoyan.model.User;public interface UserData
{//用戶数据访问接口,由具体的数据存储仓库去实现public boolean findUserNameInXml(String username);public int saveUserToXml(User user);public boolean findUserNameInXml(String username, String password);public String findHeadingPathInXml(String username);
}

5.写model实现对数据库的增删改查来保存user信息

  • 需要实现数据接口层的具体方法
MySql.java
package com.cskaoyan.model;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.cskaoyan.dataInterface.UserData;
import com.cskaoyan.db.utils.DBUtils;//实现对具体类型的数据仓库的操作
public class MySql implements UserData
{
//判断注册用户名是否已经存在public boolean findUserNameInXml(String username){boolean ret = false;Connection conn = null;ResultSet rs = null;PreparedStatement st = null;try{conn = DBUtils.getConnection();st = conn.prepareStatement("select * from user where username = ?;");st.setString(1, username);rs = st.executeQuery();if (rs.next()){// 结果集不为空,说明对应用户名和密码有ret = true;}}catch (SQLException e){e.printStackTrace();}return ret;}//把用户信息记录插入到数据库中public int saveUserToXml(User user){int ret = -1;Connection conn = null;ResultSet rs = null;PreparedStatement st = null;try{conn = DBUtils.getConnection();st = conn.prepareStatement("insert into user values(?,?,?,?); ");st.setString(1, user.getUsername());st.setString(2, user.getPassword());st.setString(3, user.getEmail());st.setString(4, user.getHeading_path());ret = st.executeUpdate();}catch (SQLException e){e.printStackTrace();}return ret;}
}

6.写service封装对数据库的具体操作(多态)

UserService.java
package com.cskaoyan.service;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;import org.apache.commons.fileupload.FileItem;
import com.cskaoyan.dataInterface.UserData;
import com.cskaoyan.model.MySql;
import com.cskaoyan.model.User;//service层,实现业务逻辑,分派业务给具体的数据仓库去实现
public class UserService
{
//判读注册用户名是否存在public boolean isUserNameExists(String username){UserData userData = new MySql();boolean isExists = userData.findUserNameInXml(username);return isExists;}//保存用户信息到数据库中public int registerUser(User user){UserData userData = new MySql();userData.saveUserToXml(user);return 1;}public String getPicturePath(String username){UserData userData = new MySql();String picturePath = userData.findHeadingPathInXml(username);return picturePath;}//处理文件数据,得到图片路径public String getFilePath(FileItem item, String uploadImgDirString){//拼接获取图片的绝对路径String ret = null;String fileName = item.getName(); // 文件名try{InputStream inputStream = item.getInputStream();// UUID文件重名的问题UUID randomUUID = UUID.randomUUID();String finalImageFilename = randomUUID.toString() + fileName;// 文件目录规划的问题// 在指定文件夹下创建一个新的文件File file = new File(uploadImgDirString, finalImageFilename);FileOutputStream fos = new FileOutputStream(file);//输出流保存图片到服务器硬盘的指定位置byte[] b = new byte[1024];int len = 0;while ((len = inputStream.read(b)) != -1){fos.write(b, 0, len);}fos.close();ret = file.getAbsolutePath();}catch (IOException e){e.printStackTrace();}return ret;}}

(二)登陆界面前后端

1.写注册成功后跳转到的登陆界面JSP

login.jsp
<form action="${pageContext.request.contextPath}/servlet/LoginServlet" method="get">用户名 <input type="text" name="username" /><br>密 &nbsp;码 <input type="password" name="password" /><br><input type="submit" value="登录" />
</form>

2.写处理登录成功的servlet控制器

  • 需要把用户登陆的用户名,密码保存到session中,通服务器后台数据库中的数据进行验证
  • 需要利用得到的用户名去数据库中查找保存的图片路径,取出来保存到session中
  • 以上两个方法的实现需要到service中写

LoginServlet.java

package com.cskaoyan.servlet;public class LoginServlet extends HttpServlet
{public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException{//验证用户名密码是否与服务器上一致response.setContentType("text/html;charset=utf-8");String username = request.getParameter("username");String password = request.getParameter("password");UserService service = new UserService();//找到图片路径String path = service.getPicturePath(username);//验证用户名和密码与数据库已保存的注册信息是否一致boolean flag  = service.checkUserInfo(username, password);if (flag){// 把用户信息和图片路径放到Session里HttpSession session = request.getSession(true);session.setAttribute("username", username);session.setAttribute("password", password);session.setAttribute("heading_path", path);response.getWriter().println("登陆成功,即将跳转到主页");response.setHeader("refresh", "1;url='" + request.getContextPath() + "/index.jsp'");}else{response.getWriter().println("用户名密码输入错误,请重新登陆");response.setHeader("refresh", "1;url='"+request.getContextPath()+"/login.jsp'");}}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException{doGet(request, response);}}

3.写登陆界面的service和数据访问接口

  • //验证用户名密码是否与服务器上一致
  • //找到图片路径
UserService.java
public String getPicturePath(String username){UserData userData = new MySql();String picturePath = userData.findHeadingPathInXml(username);return picturePath;}public boolean checkUserInfo(String username, String password){UserData userData = new MySql();boolean isUserInfOk = userData.findUserNameInXml(username, password);return isUserInfOk;}
MySql.java
public boolean findUserNameInXml(String username, String password)
{boolean ret = false;Connection conn = null;ResultSet rs = null;PreparedStatement st = null;try{conn = DBUtils.getConnection();st = conn.prepareStatement("select * from user where username = ? and password =? ; ");st.setString(1, username);st.setString(2, password);rs = st.executeQuery();if (rs.next()){ret = true; // 结果集不为空,说明存在对应用户名}}catch (SQLException e){e.printStackTrace();}return ret;
}public String findHeadingPathInXml(String username)
{String headimg_path = null;Connection conn = null;ResultSet rs = null;PreparedStatement ps;try{conn = DBUtils.getConnection();ps = conn.prepareStatement("select * from user where username = ?;");System.out.println(username);ps.setString(1, username);rs = ps.executeQuery();//查while (rs.next()){headimg_path = rs.getString("headimg_path");}}catch (SQLException e){e.printStackTrace();}return headimg_path;}

(三)写登陆成功的主页

1.登陆成功的界面JSP

  • 需要显示用户图像和用户名
  • 需要有注销按钮
<body><h1>个人主页</h1> <br><hr><%response.setContentType("text/html;charset=utf-8");String username = (String)  pageContext.getAttribute("username", PageContext.SESSION_SCOPE);String headPath = (String) pageContext.getAttribute("heading_path", PageContext.SESSION_SCOPE);if (username ==null){%><a href = "${pageContext.request.contextPath}/login.jsp">登录&nbsp;&nbsp;</a><a href = "${pageContext.request.contextPath }/register.jsp">注册</a><%}else{%><img src='<%=headPath%>'/>${username},&nbsp;欢迎您!<br><a href = '${pageContext.request.contextPath}/servlet/LogoutServlet'>注销</a><%}%></body>

2.注销Servlet

public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException{response.setContentType("text/html;charset=utf-8");HttpSession session = request.getSession(false);if (session != null){session.invalidate();response.getWriter().println("正在注销,1秒后跳转到主页");response.setHeader("refresh", "1;url='"+request.getContextPath()+"/index.jsp'");}}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException{doGet(request, response);}

(四)利用Junit测试各函数是否正常运行

package com.cskaoyan.test;
import junit.framework.Assert;
import org.junit.Test;
import com.cskaoyan.model.MySql;
import com.cskaoyan.model.User;@SuppressWarnings("deprecation")
public class FunctionTest
{@Testpublic void testMySaveUser(){MySql model = new MySql();User user = new User("aa", "aa", "aa", "bb");int saveUserToXml = model.saveUserToXml(user);System.out.println(saveUserToXml);}@Testpublic void testMyFindUser(){MySql model = new MySql();boolean ret = model.findUserNameInXml("aa", "bb");// 断言Assert.assertEquals(false, ret);}@Testpublic void testMyFindUsername(){MySql model = new MySql();boolean ret = model.findUserNameInXml("cc");Assert.assertEquals(true, ret);}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_834122.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网站发展历程九大阶段,及知识体系梳理

网站发展历程用思维导图学习java真的是一个不错的方式&#xff01;今天&#xff0c;我们用导图的方式来梳理一下一个网站从0到1流量逐渐增加的过程中会涉及到的技术与知识体系。讲讲一个网站在用户量越来越多&#xff0c;并发越来越高的情况下&#xff0c;它的架构是如何演变的…

程序员 6 个接私活的网站,你有技术就有钱!

2020年最新的常问企业面试题大全以及答案作者 | 发哥来源 | GitHubDaily本篇文章会向大家推荐国内外几个接外包比较靠谱的平台&#xff0c;主旨是贵精不贵多。因此&#xff0c;像「猪xx」这种会让程序员自贬身价&#xff0c;扰乱市场规则的网站&#xff0c;便不会出现在下方推荐…

实现网站二维码扫描登录

在尝试使用网页版微信时&#xff0c;发现微信的登录方式比较酷。区别与常用的用户名和密码的登录方式&#xff0c;网页微信登录只需要轻轻一扫&#xff0c;即可方便的实现登录功能。 下面尝试根据个人的理解对其可能的架构猜测一番。总体来看&#xff0c;扫描二维码实现网站的登…

太全了!学Java项目,上这个网站就够了

你有多久没好好学习一个开源项目了&#xff1f;你是否经常为找不到好的开源项目而烦恼&#xff1f;你是否为不知道怎么入手去看一个开源项目&#xff1f;你是否想看别人的项目学习笔记&#xff1f;你是否想跟着别人的项目搭建过程一步一步跟着做项目&#xff1f;今天给大家介绍…

tp5更改在标签栏显示的网站图标

随便选择一张你喜欢的图片&#xff0c;然后命名改为favicon.ico。将其替换原来tp5框架public下的favicon.ico。然后在网页html文件head标签内加上 上图href表示favicon.ico的路径。更改后如下图&#xff1a;

洛克王国服务器维护中,帮助中心-洛克王国-Roco Kingdom-官方网站-腾讯儿童-点亮魔法,放飞童年...

社区加载很慢或停止不动1、网络波动造成加载终止&#xff0c;请尝试重新登录(按F5刷新页面)。2、网络连接方式过慢导致&#xff0c;更换更快的互联网连接方式。3、网络高峰期造成的网络拥堵现象&#xff0c;请稍候再尝试登录。4、运行了其他网络程序&#xff0c;如&#xff1a;…

为什么WordPress是构建您的业务或创业网站的最佳平台

许多企业和创业公司不知道WordPress有多棒&#xff0c;或者为什么他们应该使用它&#xff0c;所以在本文中&#xff0c;我将告诉您为什么您应该为您的企业或创业网站使用WordPress。除了WordPress是免费的&#xff0c;WordPres真棒。它很容易使用&#xff0c;超强大&#xff0c…

WordPress Gutenberg编辑器官方信息网站已更新为交互式前端演示

Gutenberg官方信息网站已经更新&#xff0c;其特色是可以在前端操作的交互式演示。它基于Frontenberg&#xff0c;一个由Automattic的VIP牛仔Tom Nowell创建的网站。它在前端加载了一个带有Gutenberg的WordPress实例&#xff0c;因此访问者无需登录或创建测试站点来尝试它。 Gu…

内容和商品如何在一个网站內有效共存

本文的原始文章来自未经整理的WordPress&#xff0c;这里对内容进行了部分修改和升级。 这几天对原文经过进一步思考&#xff0c;考虑到国外那些使用WordPress建站的成功案例&#xff0c;认为内容和商品&#xff08;数字化商品和实物商品&#xff09;是小微企业和内容创作者从事…

初学者的图片SEO指南 - 为搜索引擎优化图片

您是否希望在您的网站上改进图片SEO&#xff1f;如果正确优化&#xff0c;图片搜索可以为您的网站带来许多新访问者。 要从图片SEO中受益&#xff0c;您需要帮助搜索引擎找到您的图片并为正确的关键词编制索引。 在本初学者指南中&#xff0c;我们将向您展示如何通过遵循最佳…

WordPress SEO教程:让你的网站排名更高和速度更快的101个优化技巧(2019年完整指南)

阅读其他WordPress SEO教程后&#xff0c;你可能已经听说过要一直保持Yoast绿灯&#xff08;这几乎不起作用&#xff09;。 点击创建免费网站。 所以&#xff0c;让我们减少无关紧要的事&#xff0c;让我告诉你是什么让我每天有2500个访客。 这是您在搜索引擎中将WordPress网…

如何在WordPress网站上轻松显示代码

您想在WordPress博客文章中显示代码吗&#xff1f;如果您尝试添加常规文本代码&#xff0c;则WordPress将无法正确显示。 每次保存文章时&#xff0c;WordPress都会通过几个清理过滤器运行您的内容。这些过滤器用于确保有人不通过文章编辑器注入代码来破解您的网站。 在本文中…

5个最佳拖放式WordPress网站页面生成器比较(2018)

你想要一个简单的方法来建立和定制你的WordPress网站&#xff1f;这就是拖放WordPress页面生成器插件派上用场的地方。这些WordPress页面构建器允许您在不编写任何代码的情况下创建、编辑和自定义您的站点布局。在本文中&#xff0c;我们将比较和回顾5个最好的WordPress拖放页面…

7个最好的拖放式WordPress网站页面构建器比较(2019)

您想在不雇用开发人员的情况下建立网站吗&#xff1f;拖放WordPress网站页面构建器使您可以轻松地创建和自定义漂亮的网站。 在本文中&#xff0c;我们将比较最流行的拖放WordPress网站页面构建器&#xff0c;以便您可以根据需要选择最佳的WordPress页面构建器并开始创建您的站…

SEO基础知识:如何在您的网站上使用标题

标题可以帮助用户和搜索引擎阅读和理解文本。它们充当读者的路标&#xff0c;使人们更容易理解文章或页面的内容。标题还定义了内容的哪些部分很重要&#xff0c;并说明它们是如何相互关联的。在这里&#xff0c;我们将为您提供有关如何思考和使用标题来改进内容的指示。 目录…

Yoast SEO 11.0:非常棒的结构化数据

结构化数据非常重要。许多当前和未来的搜索增强功能都是 – 或将来 – 由结构化数据提供支持。虽然搜索引擎擅长阅读内容&#xff0c;但结构化数据可以帮助他们理解。Yoast SEO已经支持一些基本的架构标记有些时间了&#xff0c;但现在更进一步了。Yoast SEO 11.0具有完全重写的…

SEO基础:什么是结构化数据?

您可能听说过结构化数据&#xff0c;Schema.org和JSON-LD。但这些术语到底意味着什么呢&#xff1f;什么是结构化数据&#xff1f;结构化数据有什么作用&#xff1f;它与SEO有什么关系&#xff1f;对于那些不知道结构化数据是什么的人&#xff1a;这篇文章将告诉你&#xff01;…

WordPress 5.2 网站健康检查

WordPress 5.2将在管理界面中包含两个新页面&#xff0c;以帮助最终用户通过常见配置问题和其他具有健康在线状态的元素自助服务他们的网站。它还为开发人员提供了添加调试信息的标准化位置。 新页面可以在“ 工具”菜单下找到&#xff0c;名为“网站健康”&#xff0c;并为用…

介绍一个很好的网站店铺推广优惠券插件 - URL Coupons

优惠券是促进店铺访问者转化购买的很好手段。 今天我介绍一个很好的店铺优惠券插件 - URL Coupons&#xff0c;它允许你给店铺的优惠券添加一个URL。当这个URL被点击的时候&#xff0c;它会自动使用优惠券&#xff0c;并将商品加入到顾客的购物车中。 这种方式允许你以更方便的…

SEO基础:什么是结构化数据?- WP站长

您可能听说过结构化数据&#xff0c;Schema.org和JSON-LD。但这些术语到底意味着什么呢&#xff1f;什么是结构化数据&#xff1f;结构化数据有什么作用&#xff1f;它与SEO有什么关系&#xff1f;对于那些不知道结构化数据是什么的人&#xff1a;这篇文章将告诉你&#xff01;…