博客网站/部署服务器---继上篇前端页面接入后端

目录

准备工作

创建用户类博客类与连接数据库

创建博客类

创建用户类

创建连接数据库工具类

实现对数据库数据博客的操作

实现对数据库用户的操作

创建数据库语句

登录页面

前端

后端

博客列表

前端

注销登录

写入数据

判断用户是否登录

替换页面用户昵称

后端

处理注销登录 LogoutServlet/doGet

处理获取所有博客 BlogServlet/doGet

处理判断用户是否登录 loginServlet/doGet

处理替换页面用户昵称 UserinfoServlet/doGet

博客详情页

前端

注销登录

查询博客

判断用户是否登录

将博客详情页昵称替换为当前博客作者昵称/是否显示删除博客

后端

处理注销登录 LogoutServlet/doGet

处理查询博客 BlogServlet/doGet

处理判断用户是否登录 LoginServlet/doGet

处理将博客详情页昵称替换为当前博客作者昵称/是否显示删除博客 UserinfoServlet/doGet

博客编辑页

前端

后端 BlogServlet/doPost

部署

准备工作

下载jdk

下载tomcat

加权限

安装mysql

使用maven打包

访问


准备工作

在main下创建webapp文件夹下再创建WEB-INF文件夹下再创建web.xml文件

web.xml

<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app><display-name>Archetype Created Web Application</display-name>
</web-app>

需要用到

  1. javaservlet3.1
  2. jackson 版本随意
  3. mysql 根据你的mysql版本

这些均在Maven Repository: Search/Browse/Explore (mvnrepository.com)网址内下载

之后复制代码粘贴到pom.xml下的 <dependecies>标签内,并且设置打包格式为war这是tomcat要求的(之后部署需要使用),再设置包名

<dependencies><!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --><!--jQuery库--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --><!--jackSon库--><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.13.5</version></dependency><!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j --><!--Mysql库--><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.0.33</version></dependency></dependencies><!--    打包格式--><packaging>war</packaging><build>
<!--        包名--><finalName>blog_system</finalName></build>

创建用户类博客类与连接数据库

创建博客类

博客包含

  1. 博客ID
  2. 博客标题
  3. 博客内容
  4. 创建它的作者ID
  5. 发布博客的时间

之后ALT+Insert生成Get和Set方法(时间的get方法有所改变,需要格式化为日期格式)

package model;import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.SimpleTimeZone;//表示博客
public class Blog {private int blogId;private String title;private String content;private int userId;private Timestamp postTime;public int getBlogId() {return blogId;}public void setBlogId(int blogId) {this.blogId = blogId;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public int getUserId() {return userId;}public void setUserId(int userId) {this.userId = userId;}public String getPostTime() {//把时间戳构造成日期结构SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return simpleDateFormat.format(this.postTime);}public void setPostTime(Timestamp postTime) {this.postTime = postTime;}
}

创建用户类

  1. 用户包含
  2. 用户ID
  3. 用户名称
  4. 用户密码
  5. 判定该博客是否是登录用户的博客(后续删除博客功能需要用到、此变量不会写入数据库)

生成Get、Set方法

package model;//表示用户
public class User {private int userId;private String username;private String password;private int isYourBlog=0;public int getIsYourBlog() {return isYourBlog;}public void setIsYourBlog(int isYourBlog) {this.isYourBlog = isYourBlog;}public int getUserId() {return userId;}public void setUserId(int userId) {this.userId = userId;}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 model;import com.mysql.cj.jdbc.MysqlDataSource;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class DBUtil {//定义私有静态的DataSource对象,用于存储数据库连接信息。//volatile确保了对这个变量的读写操作都是原子性的,即使再多线程环境下//对这个变量访问也是线程安全的private volatile static DataSource dataSource=null;//获取数据库连接 管理数据库的创建、配置和管理private static javax.sql.DataSource getDataSource() {//如果为空时 加锁if(dataSource==null){synchronized (DBUtil.class){if (dataSource == null) {dataSource = new MysqlDataSource();((MysqlDataSource)dataSource).setURL("jdbc:mysql://127.0.0.1:3306/blog_system?characterEncoding=utf8&useSSL=false");((MysqlDataSource)dataSource).setUser("root");((MysqlDataSource)dataSource).setPassword("monan1946");}}}return dataSource;}//这个方法是从DataSource获取一个具体的数据库连接//它通常在需要执行数据库操作时调用,比如执行SQL查询或更新public static Connection getConnection() throws SQLException {return getDataSource().getConnection();}//关闭数据库连接public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet){if (resultSet!=null){try {resultSet.close();} catch (SQLException e) {throw new RuntimeException(e);}}if(statement!=null){try {statement.close();} catch (SQLException e) {throw new RuntimeException(e);}}if(connection!=null){try {connection.close();} catch (SQLException e) {throw new RuntimeException(e);}}}}

实现对数据库数据博客的操作

与数据库连接后,查询数据库,插入数据,删除数据操作

package model;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;//import static jdk.nashorn.internal.objects.NativeString.substring;//Dao Data Access Object 用来访问数据的对象
//针对博客的增删改查
public class BlogDao {//插入博客public void insert(Blog blog){Connection connection=null;PreparedStatement statement=null;try {//与数据库建立连接connection = DBUtil.getConnection();//构造SQL语句String sql = "insert into blog values(null,?,?,?,now())";//写入sql语句statement = connection.prepareStatement(sql);//写入问号对应的数据statement.setString(1, blog.getTitle());statement.setString(2, blog.getContent());statement.setInt(3, blog.getUserId());//执行sqlint ret = statement.executeUpdate();if (ret == 1) {System.out.println("插入成功");} else {System.out.println("插入失败");}}catch (SQLException e) {e.printStackTrace();}finally {//关闭连接DBUtil.close(connection,statement,null);}}//查询所有博客public List<Blog> selectAll(){Connection connection=null;PreparedStatement statement=null;ResultSet resultSet=null;List<Blog> blogs=new ArrayList<>();try {//与数据库建立连接connection=DBUtil.getConnection();//构造sql//让新的先读取 页面新的在最上面String sql="select * from blog order by postTime desc";//写入sqlstatement=connection.prepareStatement(sql);//执行SQLresultSet=statement.executeQuery();//遍历结果集合while (resultSet.next()){Blog blog=new Blog();blog.setBlogId(resultSet.getInt("blogId"));blog.setTitle(resultSet.getString("title"));String content=resultSet.getString("content");
//                //如果长度大于100时,只截取100个字符作为摘要if(content.length()>100){content=content.substring(0,100);}//如果不大于100 不截取blog.setContent(content);blog.setUserId(resultSet.getInt("userId"));blog.setPostTime(resultSet.getTimestamp("postTime"));blogs.add(blog);}} catch (SQLException e) {e.printStackTrace();}finally {//关闭连接DBUtil.close(connection,statement,resultSet);}return blogs;}//查询一篇博客public Blog selectOne(int blogId) {Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {// 1. 和数据库建立连接connection = DBUtil.getConnection();// 2. 构造 SQLString sql = "select * from blog where blogId = ?";statement = connection.prepareStatement(sql);statement.setInt(1, blogId);// 3. 执行 SQLresultSet = statement.executeQuery();// 4. 遍历结果集. 由于是按照 blogId 来查询. blogId 是自增主键, 不能重复.//    此处的查询结果不可能是多条记录. 只能是 1 条或者 0 条.if (resultSet.next()) {Blog blog = new Blog();blog.setBlogId(resultSet.getInt("blogId"));blog.setTitle(resultSet.getString("title"));blog.setContent(resultSet.getString("content"));blog.setPostTime(resultSet.getTimestamp("postTime"));blog.setUserId(resultSet.getInt("userId"));return blog;}} catch (SQLException throwables) {throwables.printStackTrace();} finally {// 5. 关闭资源DBUtil.close(connection, statement, resultSet);}return null;}//删除博客public void delete(int blogId){Connection connection=null;PreparedStatement statement=null;ResultSet resultSet=null;try {//建立连接connection =DBUtil.getConnection();//构造SQL语句String sql="delete from blog where blogId=?";//写入sqlstatement=connection.prepareStatement(sql);//替换?statement.setInt(1,blogId);//执行sqlint ret=statement.executeUpdate();if(ret==1){System.out.println("删除成功");}else{System.out.println("删除失败");}} catch (SQLException e) {e.printStackTrace();}finally {DBUtil.close(connection,statement,null);}}
}

实现对数据库用户的操作

对数据库内的数据进行操作

package model;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;//Dao Data Access Object 用来访问数据的对象
//针对用户的增删改查
public class UserDao {//查询用户public User selectByName(String username){Connection connection=null;PreparedStatement statement=null;ResultSet resultSet=null;try {//建立连接connection=DBUtil.getConnection();//构造sql语句String sql="select * from user where username=?";//写入sqlstatement=connection.prepareStatement(sql);//填入问号statement.setString(1,username);//执行sqlresultSet=statement.executeQuery();if(resultSet.next()){User user=new User();user.setUserId(resultSet.getInt("userId"));user.setUsername(resultSet.getString("username"));user.setPassword(resultSet.getString("password"));return user;}} catch (SQLException e) {e.printStackTrace();}finally {DBUtil.close(connection,statement,resultSet);}return null;}public User selectById(int userId){Connection connection=null;PreparedStatement statement=null;ResultSet resultSet=null;try {//建立连接connection=DBUtil.getConnection();//构造sql语句String sql="select * from user where userId=?";//写入sqlstatement=connection.prepareStatement(sql);//填入问号statement.setInt(1,userId);//执行sqlresultSet=statement.executeQuery();if(resultSet.next()){User user=new User();user.setUserId(resultSet.getInt("userId"));user.setUsername(resultSet.getString("username"));user.setPassword(resultSet.getString("password"));return user;}} catch (SQLException e) {e.printStackTrace();}finally {DBUtil.close(connection,statement,resultSet);}return null;}
}

创建数据库语句

需要把创建数据库的语句放入db.sql中,方便后续更换数据库使用

create database blog_system;use blog_system;create table blog(blogId int primary key auto_increment, --主键 自增 博客IDtitle varchar(1024), --博客标题content mediumtext, --博客正文  mediutext类型很长userId int, --作者idpostTime datetime --发布事件
);create table blog(blogId int primary key auto_increment,title varchar(1024),content mediumtext,userId int,postTime datetime
);create table user(userId int primary key auto_increment, --用户idusername varchar(128) unique , --用户名password varchar(128) --密码
);
create table user(userId int primary key auto_increment,username varchar(128) unique ,password varchar(128)
);

登录页面

前端

login/loginServlet

前端将用户写入的数据已post方法提交给后端

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>登录页</title><link rel="stylesheet" href="css/common.css"><link rel="stylesheet" href="css/login.css">
</head>
<body><!-- 导航栏 --><div class="nav"><!-- logo图片 --><img src="image/logo.jpg" alt="" width="50px"><span class="title">我的博客系统</span><span class="spacer"></span><!-- 导航栏 --><a href="blog_list.html">主页</a><a href="blog_edit.html">写博客</a></div><!-- 登录板块 --><div class="login-container"><div class="login-dialog"><p>登录</p><form action="login" method="post"><div class="row"><span>用户名</span><input type="text" id="username" name="username"></div><div class="row"><span>密码</span><input type="password" id="password" name="password"></div><div class="row"><input type="submit" value="提交"id="submit"></div></form></div></div>
</body>
</html>

后端

  1. 后端调用doPost方法
  2. 使用resp.getParameter方法获取数据
  3. 去数据库内查询数据。
  4. 查询不到则响应登录失败
  5. 若查询到时,创建session HttpSession session= req.getSession()
  6. 也就是浏览器内的cookie session.setAttribute把此用户存储进去,方便后续使用之后登录成功跳转至博客列表
package controller;import model.User;
import 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 javax.servlet.http.HttpSession;
import java.io.IOException;@WebServlet("/login")
public class loginServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//告诉servlet(服务器)按照utf格式解析请求中的body//如果不使用这个 中文格式的会乱码req.setCharacterEncoding("utf8");//告诉浏览器 按照utf8格式解析响应
//        resp.setCharacterEncoding("utf8");resp.setContentType("text/html;charset=utf8");String username=req.getParameter("username");String password=req.getParameter("password");//        System.out.println(username);
//        System.out.println(password);//        if(username.equals("user")&&password.equals("123")){
//            resp.getWriter().write("登录成功");
//        }
//        else
//        {
//            resp.getWriter().write("用户名或密码错误");
//        }UserDao userDao=new UserDao();User user=userDao.selectByName(username);if(user==null){resp.getWriter().write("用户名或密码错误");return;}if(!user.getPassword().equals(password)){resp.getWriter().write("用户名或密码错误");return;}//到这里就是登录成功了//构造会话 cookieHttpSession session= req.getSession();//把信息存到cookie内,方便后续使用session.setAttribute("user",user);//登录成功后 跳转页面resp.sendRedirect("blog_list.html");}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//验证登录状态//如果用户未登录,强制重定向到登录页面//如果用户已登录,不做动作//取查询sessionHttpSession session=req.getSession(false);if(session==null){//如果连session都没有 返回403状态码//403状态码就是代表用户未登录的状态码resp.setStatus(403);return;}//取cookie查找是否有user这个用户//这个user不是用户名 而是user内的用户名User user= (User) session.getAttribute("user");if(user==null){//没有这个用户,说明未登录resp.setStatus(403);return;}//到这说明用户已经登录成功resp.setStatus(200);}
}

博客列表

前端

前端使用ajax方法,所以需要引入jquery

jquery需要从jQuery CDN  链接内点击minified 复制

https://code.jquery.com/jquery-3.7.1.min.js

之后引入即可使用

前端整体代码

<!DOCTYPE html><html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>墨轩博客页</title><link rel="stylesheet" href="css/common.css"><link rel="stylesheet" href="css/blog.css">
</head>
<body><!-- 导航栏 --><div class="nav"><!-- logo图片 --><img src="image/logo.jpg" alt="" width="50px"><span class="title">我的博客系统</span><span class="spacer"></span><!-- 导航栏 --><a href="blog_list.html">主页</a><a href="blog_edit.html">写博客</a><a href="logout">注销</a>
<!--        a会直接向对应路径发起get请求--></div><!-- 页面主体 --><div class="container"><!-- 左侧个人信息 --><div class="container-left"><div class="card"><img src="image/蕾姆.jpg" alt=""><h2>墨轩111</h2><a href="#">github链接</a><div class="counter"><span>文章</span><span>分类</span></div><div class="counter"><span>2</span><span>3</span></div></div><!-- 右侧个人信息 --></div><div class="container-right"><!-- <div class="blog">这是我的第一篇文章</div><div class="date">2024-3-27 20:20</div><div class="desc">文章内容Lorem ipsum dolor sit amet consectetur, adipisicing elit. At laboriosam quis quos, numquam tenetur cum ipsa architecto nihil, a asperiores error fugit. Praesentium eaque animi esse nemo ut nobis atque.</div><a href="blog_detail.html">查看全文 &gt;&gt; </a> --><!-- &gt;是html的> 正常的>无法之间写入 --></div></div><!-- 引入jquery --><script src="https://code.jquery.com/jquery-3.7.1.min.js"></script><script src="js/app.js"></script><script>function getBlogs(){//构造ajax$.ajax({type:'get',url:'blog',success: function(body){let container=document.querySelector('.container-right');//后端返回的是json数据//如果Content-Type是application/json,jquery ajax会把body转成js对象for(let blog of body){//构造最外层的divlet blogDiv=document.createElement('div');blogDiv.className='blog';//创建博客标题let titleDiv=document.createElement('div');titleDiv.className='title';//把后端传来的json,被jackson解析成blog对象,对象内的标题属性赋值titleDiv.innerHTML=blog.title;//把titleDiv添加在blogDiv内blogDiv.appendChild(titleDiv);//创建日期let dateDiv=document.createElement('div');dateDiv.className='date';//把后端传来的json,被jackson解析成blog对象 对象内的时间属性赋值dateDiv.innerHTML=blog.postTime;//把dateDiv添加到blogDiv内blogDiv.appendChild(dateDiv);//创建内容let descDiv=document.createElement('div');descDiv.className='desc';//把把后端传来的json,被jackson解析成blog对象 对象内的内容属性赋值descDiv.innerHTML=blog.content;//把descDiv添加到blogDiv内blogDiv.appendChild(descDiv);//创建按钮let a=document.createElement('a');a.innerHTML='查看全文 >>';//传递这篇博客的ID是多少 后端才能取查询这篇博客的详细页a.href='blog_detail.html?blogId='+blog.blogId;blogDiv.appendChild(a);//把blogDiv添加到container内container.appendChild(blogDiv);}}});}getBlogs();//使用ajax构造get方法。判断当前是否用户// 如果没登陆,强制跳转到登录页面checkLogin();//将博客列表的昵称替换为当前登录用户的昵称function GetUser(){$.ajax({type: 'get',url:'userInfo',//后端查询失败时,发送状态码为403 403不会触发回调函数success:function (body){//将登录的用户名替换上去let user=document.querySelector('.card h2');user.innerHTML=body.username;}});}GetUser();</script>
</body>
</html>

注销登录

注意:注销登录和注销账号是不同的,注销登录即退出登录,而注销账户是删除账号

<a href="logout">注销</a>
<!--        a会直接向对应路径发起get请求-->

写入数据

  1. 前端构造ajax构造get方法,发送给后端。
  2. 调用回调函数。
  3. 遍历后端传来的数据(想要用遍历,那么后端传来的数据一定是像List这种可遍历的,如何只传来的对象,那就无法遍历)
  4. 创建div 分别把博客的标题,时间,内容创建,把后端数据都写入。并且创建查询博客详情页的按键。当按下这个按键时,会把博客ID传递给博客详情页,详情页就能拿着这个ID去后端查询此博客
  5. 把整体div放入原先的div内
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script><script src="js/app.js"></script><script>function getBlogs(){//构造ajax$.ajax({type:'get',url:'blog',success: function(body){let container=document.querySelector('.container-right');//后端返回的是json数据//如果Content-Type是application/json,jquery ajax会把body转成js对象for(let blog of body){//构造最外层的divlet blogDiv=document.createElement('div');blogDiv.className='blog';//创建博客标题let titleDiv=document.createElement('div');titleDiv.className='title';//把后端传来的json,被jackson解析成blog对象,对象内的标题属性赋值titleDiv.innerHTML=blog.title;//把titleDiv添加在blogDiv内blogDiv.appendChild(titleDiv);//创建日期let dateDiv=document.createElement('div');dateDiv.className='date';//把后端传来的json,被jackson解析成blog对象 对象内的时间属性赋值dateDiv.innerHTML=blog.postTime;//把dateDiv添加到blogDiv内blogDiv.appendChild(dateDiv);//创建内容let descDiv=document.createElement('div');descDiv.className='desc';//把把后端传来的json,被jackson解析成blog对象 对象内的内容属性赋值descDiv.innerHTML=blog.content;//把descDiv添加到blogDiv内blogDiv.appendChild(descDiv);//创建按钮let a=document.createElement('a');a.innerHTML='查看全文 >>';//传递这篇博客的ID是多少 后端才能取查询这篇博客的详细页a.href='blog_detail.html?blogId='+blog.blogId;blogDiv.appendChild(a);//把blogDiv添加到container内container.appendChild(blogDiv);}}});}getBlogs();

判断用户是否登录

因为这个功能是多个页面都会使用,所以写入js文件内,在前面引入js即可调用这个方法

//使用ajax构造get方法。判断当前是否用户// 如果没登陆,强制跳转到登录页面checkLogin();这个在js内的app.js文件function checkLogin(){$.ajax({type: 'get',url:'login',success:function (){// 当客户端响应200状态码时,说明用户已登录,不做处理},error:function (){//发客户端响应403时 就会触发error//强制跳转登录页面location.assign('login.html');}})
}

替换页面用户昵称

向后端发起get方法,把页面昵称替换为当前登录用户的昵称

function GetUser(){$.ajax({type: 'get',url:'userInfo',//后端查询失败时,发送状态码为403 403不会触发回调函数success:function (body){//将登录的用户名替换上去let user=document.querySelector('.card h2');user.innerHTML=body.username;}});}GetUser();

后端

处理注销登录 LogoutServlet/doGet

  1. req.getSession查询是否创建了session
  2. 使用session.removeAttribute删除session的当前的用户
  3. resp.sendRedirect强制跳转到登录页面
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//注销功能//删除session中的对象 就相当于取消状态//查找是否有sessionHttpSession session=req.getSession(false);if(session==null) {resp.setStatus(403);return;}//删除session中的usersession.removeAttribute("user");//重定向到登录页面resp.sendRedirect("login.html");}
}

处理获取所有博客 BlogServlet/doGet

  1. 想要构造JSON格式,就得创建ObjectMapper
  2. 因为前端使用的是ajax,后端需要响应json格式。所以需要用resp.setContentType设置响应格式 (application/json;charset=utf8)
  3. 创建操作博客内容的数据库类
  4. 查看req.getParameter获取前端是否传来了博客ID,如果传来了,说明前端想要查询单个博客(这里是博客详情页,后续讲)
  5. 如果没传博客ID,说明前端想要查询所有博客,博客列表是查询所有博客
  6. 调用blogDo查询博客类,查询所有博客,并且把这些数据放入List,保证所有博客可以遍历。
  7. 使用objectMapper.writeValueAsString将数据构造成json格式
  8. 响应数据
@Override//发送博客列表或博客详情页的内容protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//想要构造JSON格式,就得用到此类private ObjectMapper objectMapper=new ObjectMapper();resp.setContentType("application/json;charset=utf8");//要拿到写数据库操作的类BlogDao blogDao=new BlogDao();//获取url中的博客IDString blogId=req.getParameter("blogId");System.out.println(blogId);//为空时 说明时获取博客列表,而不是获取博客详情if(blogId==null){//查询所有 返回的是list 每个类型是一个博客类型List<Blog> blogs=blogDao.selectAll();//转成json格式String jsonString=objectMapper.writeValueAsString(blogs);//响应resp.getWriter().write(jsonString);}else{//如果获取到博客ID时,说明时获取博客详情页//使用数据库查找这个博客IDBlog blog=blogDao.selectOne(Integer.parseInt(blogId));//记住,传json格式,如果你要用for循环 那么你的对象一定要是可迭代的对象//如果你单构造个类过去,你用for是肯定循环不了的,你必须使用List这种可以迭代的对象,才能使用这个迭代循环List<Blog> blogs=new ArrayList<>();blogs.add(blog);//你也可以单传个对象 到前端那不用循环直接使用也可以
//            List<Blog> blogs=new ArrayList<>();
//            String jsonString=objectMapper.writeValueAsString(blog);//转为json格式String jsonString=objectMapper.writeValueAsString(blogs);//响应resp.getWriter().write(jsonString);}}

处理判断用户是否登录 loginServlet/doGet

  1. req.getSession查询是否创建了session(这里可以直接判断用户是否登录了,因为只要登录,那么就一定创建了session
  2. 为空时,resp.setStatus响应状态码403
  3. 如果不为空时,session.getAttribute查询session是否有该用户
  4. 如果为空,则有创建session,但没有此用户,setStatus响应状态码403
  5. 如果也不为空,说明该用户以及登录,setStatus响应状态码200
  6. 响应状态码403是为了不让前端调用回调函数,当返回200时,才调用
@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//验证登录状态//如果用户未登录,强制重定向到登录页面//如果用户已登录,不做动作//取查询sessionHttpSession session=req.getSession(false);if(session==null){//如果连session都没有 返回403状态码//403状态码就是代表用户未登录的状态码resp.setStatus(403);return;}//取cookie查找是否有user这个用户//这个user不是用户名 而是user内的用户名User user= (User) session.getAttribute("user");if(user==null){//没有这个用户,说明未登录resp.setStatus(403);return;}//到这说明用户已经登录成功resp.setStatus(200);}

处理替换页面用户昵称 UserinfoServlet/doGet

  1. 想要构造JSON格式,就得创建此类objectMapper
  2. req.getParameter获取blogId(如果为空,则是博客列表发来的,想要获取此博客的用户名,如果不为空,是博客详情页发来的,判断此博客是否是当前登录用户的博客)
  3. req.getSession判断是否有session
  4. session.getAttribute 查询session是否有该用户,获取此用户
  5. 当blogId为空时,博客列表发来的
  6. 首先用户的密码设为空串 (不是修改数据库内的数据)
  7. resp.setContentType设置响应格式
  8. objectMapper.writeValueAsString数据构造为JSON格式
  9. 响应数据
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//想要构造json 就要创建这个类ObjectMapper objectMapper=new ObjectMapper();String blogid=req.getParameter("blogId");//直接取查找是否有cookieHttpSession session=req.getSession(false);if(session==null){//后端查询失败时,发送状态码为403 403不会触发回调函数resp.setStatus(403);resp.setContentType("text/html;charset=utf8");resp.getWriter().write("没有该用户");return;}//获取当前登录用户User user= (User) session.getAttribute("user");if(user==null){resp.setStatus(403);resp.setContentType("text/html;charset=utf8");resp.getWriter().write("没有该用户");return;}if(blogid==null){//查询登录的用户//当url中的查询字符串中没有blogId时,说明是博客列表发来的请求//不用让密码在body中明文显示user.setPassword("");resp.setContentType("application/json;charset=utf8");//构造成json格式//返回登录的用户String jsonString=objectMapper.writeValueAsString(user);//写入resp.getWriter().write(jsonString);

博客详情页

前端

总体

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>博客详情页</title><link rel="stylesheet" href="css/common.css"><link rel="stylesheet" href="css/blog.css"><link rel="stylesheet" href="css/blog_detail.css"><!-- 引入 editor.md 的依赖 --><link rel="stylesheet" href="editor.md/css/editormd.min.css" /><script src="js/jquery.min.js"></script><script src="editor.md/lib/marked.min.js"></script><script src="editor.md/lib/prettify.min.js"></script><script src="editor.md/editormd.js"></script>
</head>
<body><!-- 导航栏 --><div class="nav"><!-- logo图片 --><img src="image/logo.jpg" alt="" width="50px"><span class="title">我的博客系统</span><span class="spacer"></span><!-- 导航栏 --><a href="blog_list.html">主页</a><a href="blog_edit.html">写博客</a><a href="logout">注销</a><!--        a会直接向对应路径发起get请求--><div class="dete"></div>
<!--            删除博客--></div><!-- 页面主体 --><div class="container"><!-- 左侧个人信息 --><div class="container-left"><div class="card"><img src="image/蕾姆.jpg" alt=""><h2>墨轩111</h2><a href="#">github链接</a><div class="counter"><span>文章</span><span>分类</span></div><div class="counter"><span>2</span><span>3</span></div></div></div><!-- 右侧个人信息 -->
<!--        <div class="container-right">-->
<!--        </div>--><div class="container-right"><!-- 这个 div 里来放博客的内容 --><div class="blog-content"></div></div></div><script src="https://code.jquery.com/jquery-3.6.1.min.js"></script><script src="js/app.js"></script><script>function getBlogs() {//构造ajax$.ajax({type: 'get',//博客列表传来的url: 'blog' + location.search,success: function (body) {let container = document.querySelector('.container-right');//后端返回的是json数据//如果Content-Type是application/json,jquery ajax会把body转成js对象for (let blog of body) {//构造最外层的divlet blogDiv = document.createElement('div');blogDiv.className = 'blog';//创建博客标题let titleDiv = document.createElement('div');titleDiv.className = 'title';//把后端传来的json,被jackson解析成blog对象,对象内的标题属性赋值titleDiv.innerHTML = blog.title;//把titleDiv添加在blogDiv内blogDiv.appendChild(titleDiv);//创建日期let dateDiv = document.createElement('div');dateDiv.className = 'date';//把后端传来的json,被jackson解析成blog对象 对象内的时间属性赋值dateDiv.innerHTML = blog.postTime;//把dateDiv添加到blogDiv内blogDiv.appendChild(dateDiv);//创建内容let descDiv = document.createElement('div');descDiv.className = 'desc';//把把后端传来的json,被jackson解析成blog对象 对象内的内容属性赋值descDiv.innerHTML = blog.content;//把descDiv添加到blogDiv内blogDiv.appendChild(descDiv);//把blogDiv添加到container内container.appendChild(blogDiv);}}});}getBlogs();checkLogin();function GetUser(){$.ajax({type: 'get',url:'userInfo'+location.search,//加个博客ID 让后端查找这篇博客success:function (body){//将这篇作者的用户名替换上去let user=document.querySelector('.card h2');user.innerHTML=body.username;//当这篇博客是当前用户的 那么就显示删除按钮if(body.isYourBlog){let delet=document.createElement('a');delet.className="delete";delet.innerHTML="删除博客"delet.href='blogdelete'+location.search;let de=document.querySelector('.dete');de.appendChild(delet);}}});}GetUser();</script>
</body>
</html>

注销登录

与博客列表一致这里不再赘述

查询博客

  1. 构造ajax的get方法,url是博客列表传来的博客ID,传给后端,后端才能根据这个ID查询博客
  2. 后端传来了数据,只要是json格式(application/json;charset=utf8)那么ajax就会把body转成js的对象。
  3. 创建div,分别把博客的标题、时间、内容写入
<script src="https://code.jquery.com/jquery-3.6.1.min.js"></script><script src="js/app.js"></script><script>function getBlogs() {//构造ajax$.ajax({type: 'get',//博客列表传来的url: 'blog' + location.search,success: function (body) {let container = document.querySelector('.container-right');//后端返回的是json数据//如果Content-Type是application/json,jquery ajax会把body转成js对象for (let blog of body) {//构造最外层的divlet blogDiv = document.createElement('div');blogDiv.className = 'blog';//创建博客标题let titleDiv = document.createElement('div');titleDiv.className = 'title';//把后端传来的json,被jackson解析成blog对象,对象内的标题属性赋值titleDiv.innerHTML = blog.title;//把titleDiv添加在blogDiv内blogDiv.appendChild(titleDiv);//创建日期let dateDiv = document.createElement('div');dateDiv.className = 'date';//把后端传来的json,被jackson解析成blog对象 对象内的时间属性赋值dateDiv.innerHTML = blog.postTime;//把dateDiv添加到blogDiv内blogDiv.appendChild(dateDiv);//创建内容let descDiv = document.createElement('div');descDiv.className = 'desc';//把把后端传来的json,被jackson解析成blog对象 对象内的内容属性赋值descDiv.innerHTML = blog.content;//把descDiv添加到blogDiv内blogDiv.appendChild(descDiv);//把blogDiv添加到container内container.appendChild(blogDiv);}}});}getBlogs();

判断用户是否登录

这个与博客列表页一样,这里不再赘述

将博客详情页昵称替换为当前博客作者昵称/是否显示删除博客

  1. 构造ajax的get方法,url传入博客列表传来的博客ID
  2. 获取数据后,将后端的数据写入昵称
  3. 判断isYourBlog是否为1 (1是说明当前登录用户的ID与博客ID相同(此篇博客是当前用户写的),说明当前用户有支配此篇博客的权利,显示删除博客 0说明当前登录用户的ID与博客ID不同(此篇博客不是当前用户写的,没有支配全,不显示删除博客)
  4. 为1时,创建a标签,命名为删除博客,链接到删除博客的后端,并且传入博客ID
  5. 再放入原先的div
function GetUser(){$.ajax({type: 'get',url:'userInfo'+location.search,//加个博客ID 让后端查找这篇博客success:function (body){//将这篇作者的用户名替换上去let user=document.querySelector('.card h2');user.innerHTML=body.username;//当这篇博客是当前用户的 那么就显示删除按钮if(body.isYourBlog){let delet=document.createElement('a');delet.className="delete";delet.innerHTML="删除博客"delet.href='blogdelete'+location.search;let de=document.querySelector('.dete');de.appendChild(delet);}}});}GetUser();

后端

处理注销登录 LogoutServlet/doGet

这里与博客列表一致,不再赘述

处理查询博客 BlogServlet/doGet

  1. resp.setContentTyoe设置响应格式 (application/json;charset=utf8)
  2. 创建操作博客类
  3. req.getParameter获取前端传来的博客ID
  4. 这里是查询单个博客,所以是不为空的
  5. 将博客ID转为int 并且从数据库内查询
  6. 将博客放入List(这里最好不用放入List,因为只是单个博客,不需要迭代,我只是演示)
  7. objectMapper.writeVlueAsString构造成JSON格式
  8. 响应数据
else{resp.setContentType("application/json;charset=utf8");//如果获取到博客ID时,说明时获取博客详情页//使用数据库查找这个博客IDString blogId=req.getParameter("blogId");Blog blog=blogDao.selectOne(Integer.parseInt(blogId));//记住,传json格式,如果你要用for循环 那么你的对象一定要是可迭代的对象//如果你单构造个类过去,你用for是肯定循环不了的,你必须使用List这种可以迭代的对象,才能使用这个迭代循环List<Blog> blogs=new ArrayList<>();blogs.add(blog);//你也可以单传个对象 到前端那不用循环直接使用也可以
//            List<Blog> blogs=new ArrayList<>();
//            String jsonString=objectMapper.writeValueAsString(blog);//转为json格式String jsonString=objectMapper.writeValueAsString(blogs);//响应resp.getWriter().write(jsonString);}

处理判断用户是否登录 LoginServlet/doGet

这里是与博客列表是一样的,就不再赘述

处理将博客详情页昵称替换为当前博客作者昵称/是否显示删除博客 UserinfoServlet/doGet

  1. 创建要构造JSON的类
  2. resp.getParameter获取blogId
  3. req.GetSession查询是否有session
  4. session.getAttribute获取session是否有该用户
  5. 数据库中查询此篇博客
  6. 数据中查询用户类是否有此篇博客的作者
  7. 当都有时,判断当前session的用户ID与此篇博客的ID是否相同(相同时,用户类的isyourblog设置1反之设置0)
  8. 响应时,把用户密码设置为0不要名为显示,不修改数据库的数据
  9. resp.setContentType设置响应格式
  10. objectMapper.writeValueAsString构造为JSON格式
  11. 响应
//想要构造json 就要创建这个类ObjectMapper objectMapper=new ObjectMapper();String blogid=req.getParameter("blogId");//直接取查找是否有cookieHttpSession session=req.getSession(false);if(session==null){//后端查询失败时,发送状态码为403 403不会触发回调函数resp.setStatus(403);resp.setContentType("text/html;charset=utf8");resp.getWriter().write("没有该用户");return;}//获取当前登录用户User user= (User) session.getAttribute("user");if(user==null){resp.setStatus(403);resp.setContentType("text/html;charset=utf8");resp.getWriter().write("没有该用户");return;}else{//查询这篇博客的作者//如果有blogId时,说明是博客详情页发来的请求BlogDao blogDao=new BlogDao();Blog blog=blogDao.selectOne(Integer.parseInt(blogid));if(blog==null){resp.setStatus(403);resp.setContentType("text/html;charset=utf8");resp.getWriter().write("没有该用户");return;}UserDao userDao=new UserDao();//用博客类里的用户id 去用户类去查询  查询用户是否有这个博客作者User author=userDao.selectById(blog.getUserId());if(author==null){resp.setStatus(403);resp.setContentType("text/html;charset=utf8");resp.getWriter().write("没有该用户");return;}if(author.getUserId()== user.getUserId()){author.setIsYourBlog(1);}else {author.setIsYourBlog(0);}//不用让密码在body中明文显示user.setPassword("");resp.setContentType("application/json;charset=utf8");String jsonString=objectMapper.writeValueAsString(author);//返回这篇博客的作者resp.getWriter().write(jsonString);}

博客编辑页

前端

通过from表单提交标题,内容

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>博客编辑页</title><link rel="stylesheet" href="css/common.css"><link rel="stylesheet" href="css/blog-edit-container.css"><!-- 引入 editor.md 的依赖 --><link rel="stylesheet" href="editor.md/css/editormd.min.css" /><script src="js/jquery.min.js"></script><script src="editor.md/lib/marked.min.js"></script><script src="editor.md/lib/prettify.min.js"></script><script src="editor.md/editormd.js"></script>
</head>
<body><!-- 导航栏 --><div class="nav"><!-- logo图片 --><img src="image/logo.jpg" alt="" width="50px"><span class="title">我的博客系统</span><span class="spacer"></span><!-- 导航栏 --><a href="blog_list.html">主页</a><a href="blog_edit.html">写博客</a><a href="logout">注销</a><!--        a会直接向对应路径发起get请求--></div><div class="blog-edit-container"><form action="blog" method="post" style="height: 100%"><!-- 标题编辑区 --><div class="title"><input type="text" id="title" placeholder="请输入文章标题" name="title"><input type="submit" id="submit" value="发布文章"></div><!-- 博客编辑器标签 --><div id="editor"><!-- 需要在这里加上一个隐藏的 textarea --><!-- 属于 editor.md 这个库要求的. --><textarea name="content" style="display: none;" ></textarea></div></form></div><script>// 初始化编辑器var editor = editormd("editor", {// 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉.width: "100%",// 设定编辑器高度height: "calc(100% - 50px)",// 编辑器中的初始内容markdown: "# 在这里写下一篇博客",// 指定 editor.md 依赖的插件路径path: "editor.md/lib/",// 加上这个属性, 效果就是把编辑器里的内容给自动保存到 textarea 里.saveHTMLToTextArea: true,});</script>
</body>
</html>

后端 BlogServlet/doPost

  1. req.setCharacterEncoding设置后端读取的格式
  2. req.getSession判断session是否存在
  3. session.getAttribute判断用户是否在session内
  4. 使用req.getParameter获取标题、内容
  5. 如果标题内容为空或者为空串,则响应400状态码
  6. 创建一个博客类
  7. 将数据写入博客类中
  8. 博客的作者ID就是当前登录用户的ID
  9. 最后将这个类写入数据库(实际就是将类中的数据写入)
  10. 写入后,resp.sendRedirect跳转到博客列表
//提交博客@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setCharacterEncoding("utf8");//判断会话是否存在和会话中的用户是否存在HttpSession session=req.getSession(false);if (session == null) {resp.setStatus(400);return;}User user= (User) session.getAttribute("user");
//        System.out.println(user.getUsername());
//        System.out.println(user.getUserId());if(user==null){resp.setStatus(400);return;}//2.获取博客的标题和正文String title=req.getParameter("title");String content=req.getParameter("content");
//        System.out.println(title);
//        System.out.println(content);if(title==null||content==null||title.equals("")||content.equals("")){resp.setStatus(400);resp.setContentType("text/html;charset=utf8");resp.getWriter().write("标题或正文不符合");return;}//把数据写入一个对象Blog blog=new Blog();blog.setTitle(title);blog.setContent(content);//博客作者的ID就是当前登录用户的ID  所以直接从session拿出当前用户 再拿出当前用户的idblog.setUserId(user.getUserId());//创建博客写入数据库的对象BlogDao blogDao=new BlogDao();//把这个博客对象写入数据库blogDao.insert(blog);//响应resp.sendRedirect("blog_list.html");}

部署

准备工作

均在Linux系统下

下载jdk

yum install java-1.8.0-openjdk-devel.x86_64

下载tomcat

把window下的tomcat8.0拖拽到linux上

解压 

unzip apache-tomcat-8.5.100.zip

加权限

cd到tomcat的bin目录下

给所有.sh加上权限

chmod +x *.sh

./即可启动

startup.sh是linux启动脚本startup.bat是window启动脚本

但是大概率启动不了,因为tomcat8080端口,服务器是默认不开放的,所有要进入你的服务器控制台开放端口

安装mysql

之后把db.sql里的sql语句复制到数据库内,重新建库建表

使用maven打包

记得改包格式和包名

把打好的包从window拖拽到Linux的Tomcat的webappsxia

访问

你服务器的地址:端口:包名:你也访问的页面

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

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

相关文章

永恒之蓝上线CS

该文介绍了在内网环境下&#xff0c;利用永恒之蓝漏洞&#xff08;EternalBlue&#xff09;将攻击者控制台&#xff08;CS&#xff09;上线的方法。前提条件是目标机器可上网、无防火墙和杀毒软件&#xff0c;并且存在永恒之蓝漏洞。使用Erebus插件的eterblue功能&#xff0c;通…

xpath的使用以及原理-元素定位

# 查找文本框输入文本 driver.find_element(By.CLASS_NAME,"nav-search-input").send_keys("i_cecream查找到了") #查找到之后点击 driver.find_element(By.CLASS_NAME,"nav-search-btn").click()time.sleep(30)selenium4的解析。 client调用se…

2024团体程序设计天梯赛L1-103 整数的持续性

题目链接L1-103 整数的持续性 #include<iostream> #include<stdio.h> #include<algorithm> using namespace std; struct node{int x;int d; }p[2000]; bool cmp(node a, node b) {if (a.d b.d) return a.x < b.x;return a.d>b.d; } int cnt, cntt; v…

信号----硬件中断

硬件中断 键盘组合键是向进程发送信号&#xff0c;那么键盘数据是如何输入到内核中&#xff0c;然后变成信号的&#xff1f;这个信号又是由谁发送给进程的&#xff1f; 当键盘被按下&#xff0c;操作系统先知道用户用键盘输入了内容 那么操作系统是如何知道键盘上有内容了&…

【Camera Sensor Driver笔记】二、点亮指南之Sensor Module XML

Camera Sensor module XML详解&#xff1a; cameraId 与 slot id 一一对应 &#xff08;即&#xff1a;dtsi中相对应的sensor的 cell-index &#xff09; moduleName 模组厂名称 sensorName sensor 名称 actuatorName 马达名称 oisName …

学习亚马逊云科技AWS云计算技术的三款官方免费3A游戏大作

玩3A大作免费电脑游戏&#xff0c;就能成为AWS云架构师、云开发大&#x1f42e;&#xff1f;这么好的事尊的假的&#xff1f;小李哥今天就来给大家介绍&#xff0c;如何通过玩AWS官方的定制版虚拟人生、炉石传说和密室逃脱游戏学习AWS。这三个游戏完全免费&#xff0c;没有任何…

如何利用pg_dump和pg_restore迁移从一个PostgreSQL服务器到另一个服务器,同时保持一致性与高效性?

文章目录 解决方案1. 使用pg_dump导出数据2. 将导出的数据复制到目标服务器3. 使用pg_restore导入数据保持一致性与高效性的策略一致性高效性 示例代码导出数据复制数据到目标服务器在目标服务器上解压并导入数据 PostgreSQL数据库的迁移是一个常见的任务&#xff0c;特别是在升…

如何在PostgreSQL中使用CTE(公共表表达式)来简化复杂的查询逻辑?

文章目录 解决方案步骤示例代码 结论 在处理复杂的SQL查询时&#xff0c;我们经常会遇到需要多次引用子查询或中间结果的情况。这可能会使得查询变得冗长且难以理解。为了解决这个问题&#xff0c;PostgreSQL&#xff08;以及其他一些SQL数据库系统&#xff09;引入了公共表表达…

springdoc-openapi使用

springdoc-openapi使用 一、引入pom二、新增配置类OpenApiConfig四、Controller层示例五、配置文件新增内容六、验证 一、引入pom <dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-ui</artifactId><version>1…

【WSL】单机大模型前的基础环境配置

前言&#xff1a;在上一篇文章中&#xff0c;我们完成了WSL的部署&#xff0c;但是在大模型搭建&#xff08;尤其是Langchain&#xff09;前&#xff0c;还碰到了不少的坑&#xff0c;查找了不少的文章&#xff0c;所以本篇文章就做一个记录&#xff0c;避免以后再走冤枉路。 …

并发编程 可见性、原子性和有序性,如何解决

可见性&#xff0c;原子性和有序性 CPU&#xff0c;内存&#xff0c;I/0 三者在速度上存在很大差异&#xff0c;大概是CPU耗时一天 内存耗时一年&#xff0c;内存耗时一天 /O耗时十年 CPU 增加了缓存&#xff0c;以均衡与内存的速度差异;操作系统增加了进程、线程&#xff0…

oracle一次sql优化笔记

背景&#xff1a;两个百万级数据量表需要连接&#xff0c;加全索引的情况下速度仍不见改善&#xff0c;苦查一下午解决问题未遂。 解决&#xff1a;经大佬指点了解到oracle优化器提示&#xff0c;使用/* USE_HASH(table1 table2) */或者/* USE_MERGE(table1 table2) */来指导优…

P5732 【深基5.习7】杨辉三角

此题可以为杨辉三角&#xff0c;可以看一下这篇文章: 传送门 AC代码&#xff1a; #include<iostream>using namespace std;const int N 30; int arr[N][N];int main() {int n;cin >> n ;arr[1][1] 1;for(int i1;i<n;i){for(int j1;j<i;j){if(j 1 || j …

Callable and FutureTask

Callable 由关系图可知&#xff0c;Callable和Runnable一样&#xff0c;也是一个函数式接口&#xff0c;可以使用Lambda表达式 与之不同的是&#xff0c;其内部的call()方法可以抛出异常且能return一个返回值 Callable<Object> callable new Callable() {Overridepublic…

网上赚钱新姿势:日赚二三十,十大靠谱平台任你选!

互联网时代下&#xff0c;网络兼职已成为许多人追求额外收入的热门选择。互联网的广泛普及与发展&#xff0c;不仅让人们轻松获取海量信息&#xff0c;更为我们提供了多样化的兼职机会。这些兼职工作不仅时间自由&#xff0c;而且种类繁多&#xff0c;适合各种人群参与。接下来…

【AR开发示例】实现AR管线巡检

写在前面的话 这是一篇旧文档&#xff0c;代码仓库见 https://gitee.com/tanyunxiu/AR-pipe 本文档是基于超图移动端SDK的AR模块开发的示例&#xff0c;仅供参考&#xff0c;SDK在持续迭代中&#xff0c;相关描述可能有变化。 示例介绍 这是一个使用AR查看墙内管线的基础示…

Spring Cloud 运维篇1——Jenkins CI/CD 持续集成部署

Jenkins 1、Jenkins是什么&#xff1f; Jenkins 是一款开源 CI/CD 软件&#xff0c;用于自动化各种任务&#xff0c;包括构建、测试和部署软件。 Jenkins 支持各种运行方式&#xff0c;可通过系统包、Docker 或者一个独立的 Java 程序。 Jenkins Docker Compose持续集成流…

k8s安装,linux-ubuntu上面kubernetes详细安装过程

官方文档&#xff1a;https://kubernetes.io/zh-cn/docs/setup/production-environment/container-runtimes/ 环境配置 该部分每个主机都要执行 如果你确定不需要某个特定设置&#xff0c;则可以跳过它。 设置root登录 sudo passwd root sudo vim /etc/ssh/sshd_config Perm…

HarmonyOS NEXT 使用XComponent + Vsync 实现自定义动画

介绍 XComponent 提供了应用在 native 侧调用 OpenGLES 图形接口的能力&#xff0c;本文主要介绍如何配合 Vsync 事件&#xff0c;完成自定义动画。在这种实现方式下&#xff0c;自定义动画的绘制不在 UI 主线程中完成&#xff0c;即使主线程卡顿&#xff0c;动画效果也不会受…

晶圆制造之MPW(多项目晶圆)简介

01、MPW是什么&#xff1f; 在半导体行业中&#xff0c;MPW 是 "Multi Project Wafer" 的缩写&#xff0c;中文意思是多项目晶圆。MPW 的主要思想是将使用相同工艺的多个集成电路设计放在同一晶圆片上进行流片&#xff08;即制造&#xff09;。这种方法允许多个设计共…