Servlet搭建博客系统

现在我们可以使用Servlet来搭建一个动态(前后端可以交互)的博客系统了(使用Hexo只能实现一个纯静态的网页,即只能在后台自己上传博客)。有一种"多年媳妇熬成婆"的感觉。

一、准备工作

首先创建好项目,引入相关依赖。具体过程在"Servlet的创建"中介绍了。

在这我们要引入servlet,mysql,jackson的相关依赖。

然后将相关web.xml配置好,将网站前端的代码也引入webapp中。

二、业务逻辑的实现

由于数据要进行持久化保存,在这我们使用mysql数据来存储。

首先我们先进行数据库的设计。

在这个博客系统中,会涉及到写博客和登陆的简单操作,因此需要创建两个表:用户表和博客表

因为数据库需要创建,当们换了一台机器的时候需要再一次创建,为了简便,可以将创建的sql语句保存下来,下次直接调用即可。

然后将上述代码复制到mysql的命令行执行即可。


封装数据库

为了简化后续对数据库的crud操作,在这对JDBC进行封装,后续代码就轻松许多了。

在这创建一个dao的文件夹,表示Data Access Object, 即数据访问对象,通过写一些类,然后通过类中的封装好的方法来间接访问数据库。

在dao文件下创建一个DBUtil的类,将连接数据库和释放资源操作进行封装。

package dao;

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class DBUtil {
    //使用单例模式中的饿汉模式创建实例
    private static volatile DataSource dataSource = null;

    private static 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("root");
                }
            }
        }
        return dataSource;
    }

    //获取数据库连接
    public static Connection getConnection() {
        try {
            return getDataSource().getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    //释放资源
    public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet){
        //一个一个释放,防止一个抛出异常,后续就不释放连接了
        if(resultSet != null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(statement != null){
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        
        if(connection != null){
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

 创建实体类

创建实体类的Dao类,进一步封装数据库操作

package dao;

import java.net.ConnectException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

//通过这个类,封装针对 blog 表的增删改查操作
public class BlogDao {
    //新增一个博客
    //使用try catch捕获异常,finally释放资源
    public void insert(Blog blog)  {
        Connection connection = null;
        PreparedStatement statement = null;
        try{
            connection = DBUtil.getConnection();
            String sql = "insert into blog values(null, ?, ?, ?, now())";
            statement = connection.prepareStatement(sql);
            statement.setString(1, blog.getTitle());
            statement.setString(2, blog.getContent());
            statement.setInt(3, blog.getUserId());
            statement.executeUpdate();
        }catch (SQLException e){
            e.printStackTrace();
        }finally {
            DBUtil.close(connection, statement, null);
        }
    }

    public List<Blog> getBlogs(){
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        List<Blog> blogs = new ArrayList<>();
        try{
            connection = DBUtil.getConnection();
            String sql = "select * from blog";
            statement = connection.prepareStatement(sql);
            resultSet = statement.executeQuery();
            while(resultSet.next()){
                Blog blog = new Blog();
                blog.setBlogId(resultSet.getInt("blogId"));
                blog.setTitle(resultSet.getString("title"));
                blog.setContent(resultSet.getString("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 getBlog(){
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        Blog blog = null;
        try{
            connection = DBUtil.getConnection();
            String sql = "select * from blog";
            statement = connection.prepareStatement(sql);
            resultSet = statement.executeQuery();
            if(resultSet.next()){
                blog = new Blog();
                blog.setBlogId(resultSet.getInt("blogId"));
                blog.setTitle(resultSet.getString("title"));
                blog.setContent(resultSet.getString("content"));
                blog.setUserId(resultSet.getInt("userId"));
                blog.setPostTime(resultSet.getTimestamp("postTime"));
            }
        } catch (SQLException e){
            e.printStackTrace();
        }finally {
            DBUtil.close(connection, statement, resultSet);
        }

        return blog;
    }

    //根据博客ID指定博客删除
    public void delete(int blogId){
        Connection connection = null;
        PreparedStatement statement = null;
        try{
            connection = DBUtil.getConnection();
            String sql = "delete from blog where blogId = ?";
            statement = connection.prepareStatement(sql);
            statement.setInt(1, blogId);
            statement.executeUpdate();
        }catch (SQLException e){
            e.printStackTrace();
        }finally {
            DBUtil.close(connection, statement, null);
        }
    }
}

后面再处理数据库操作的时候就可以直接使用这些代码了。

通过观察这些代码,我们会发现非常多重复的东西,后期通过学习了一些高级的框架后就能将代码的结构再优化一下.

同理,UserDao类也是如此完成。

至此,数据方面的东西我们都已经写完了,后续只需要调用即可。

接来下就可以进行一些前后端交互逻辑的实现了。

在这以功能点为维度进行展开,针对每个功能点,进行"设计前后端交互接口","开发后端代码","开发前端代码","调试"

实现博客列表页

让博客列表页能够加载博客列表。

大致流程如下:

  1. 前端发起一个HTTP请求,向后端所要博客列表数据

  2. 后端收到请求之后查询数据库获取数据库中的 博客列表,将数据返回给前端

  3. 前端拿到响应后,根据内容构造出html片段,并显示。

在写代码前,需要进行约定,即规范双方发什么样的数据,发什么请求,如何解析数据等。

假设双方约定按照如下格式发送数据。

后端代码:

@WebServlet("/html/blog")
public class BlogServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //从数据库中获取数据
        BlogDao blogDao = new BlogDao();
        List<Blog> blogs = blogDao.getBlogs();
        //将数组转换成对象字符串
        String respJson = objectMapper.writeValueAsString(blogs);
        resp.setContentType("application/json; charset=utf8");
        //写回到响应中
        resp.getWriter().write(respJson);
    }
}

前端代码:

让页面通过js的ajax的方式发起http请求。

 function getBlogs(){
            $.ajax({
                type: 'get',
                url: 'blog',
                success: function(body){
                    let container = document.querySelector('.container-right');
                    for(let blog of body){
                        let blogDiv = document.createElement('div');
                        blogDiv.className='blog';
                        //构造标题
                        let titleDiv = document.createElement('div');
                        titleDiv.className = 'title';
                        titleDiv.innerHTML = blog.title;
                        blogDiv.appendChild(titleDiv);
                        //构造发布时间
                        let dateDiv = document.createElement('div');
                        dateDiv.className = 'date';
                        dateDiv.innerHTML = blog.postTime;
                        blogDiv.appendChild(dateDiv);
                        //构造博客摘要
                        let descDiv = document.createElement('div');
                        descDiv.className = 'desc';
                        descDiv.innerHTML = blog.content;
                        blogDiv.appendChild(descDiv);
                        //构造查看全文按钮
                        let a = document.createElement('a');
                        a.href = 'blog_content.html?blogId=' + blog.blogId;
                        a.innerHTML = '查看全文 &gt;&gt';
                        blogDiv.appendChild(a);

                        container.appendChild(blogDiv);
                    }
                }
            });
        }
        getBlogs();

由于数据库中的数据为标明啥数据,我们还需要手动指定.

效果如下:

博客详情页

1.约定前后端交互接口

和前面博客列表页类似,不同的是我们只需要在请求中带上blogId的属性,以及后端代码的稍作修改即可。

后端代码:

我们可以通过对之前的后端代码稍作修改,就可以完成上述操作。

前端代码:

 <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>

    <script>
        function getBlog(){
            $.ajax({
                type: 'get',
                url: 'blog'+location.search,
                success: function(body){
                    let h3 = document.querySelector('.container-right h3');
                    h3.innerHTML = body.title;
                    let dateDiv = document.querySelector('.container-right .date');
                    dateDiv.innerHTML = body.postTime;
                    editormd.markdownToHTML('content', { markdown: body.content });
                }
            });
        }

        getBlog();
    </script>

登录功能

1.约定前后端交互接口

此处提交用户名和密码,可以使用form也可以使用ajax。在这使用form的形式(更简单一些)。

2.后端代码

@WebServlet("/html/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //首先获取请求中的查询字符串中的用户名和密码
        //需要手动告诉Servlet,使用什么样的编码方式来读取请求
        req.setCharacterEncoding("utf8");
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if(username == null || password == null || username.equals("") || password.equals("")){
            //用户提交的数据非法
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前的用户名或密码非法");
            return;
        }
        //再去数据库中比对
        UserDao userDao = new UserDao();
        User user = userDao.getUserByName(username);
        if(user == null){
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前的用户名或密码错误");
            return;
        }
        if(!user.getPassword().equals(password)){
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("当前的用户名或密码错误");
        }
        //创建会话关系
        HttpSession session = req.getSession(true);
        session.setAttribute("user", user);
        //发送重定向网页,跳转到列表页
        resp.sendRedirect("blog_list.html");
    }
}

注意:

由于请求中可能带有中文字符,我们需要手动指定一下字符集utf8,防止读取请求的时候出现乱码。

使用一个会话,让服务器保存当前用户的一些数据。

3.前端代码

            <form action="login" method="post">
                <div class="row">
                    <span>用户名</span>
                    <input type="text" id="username">
                </div>
                <div class="row">
                    <span>密码</span>
                    <input type="password" id="password">
                </div>
                <div class="row">
                    <input type="submit" id="submit" value="登录">
                </div>
            </form>

检查用户登录状态

强制用户登录,当用户直接去访问博客列表页或者其他页面的时候,如果是未登录过的状态,会强制跳转到登录页要求用户登录。

如何实现?

在其他页面中的前端代码,写一个ajax请求,通过这个请求,访问服务器来获取当前的登录状态。

如果当前未登录,则跳转登录页面,如果已经登录,就不进行操作。

1.约定前后端交互接口

2.后端代码

   protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession(false);
        if(session == null){
            resp.setStatus(403);
            return;
        }

        User user = (User) session.getAttribute("user");
        if(user == null){
            resp.setStatus(403);
            return;
        }
        
        resp.setStatus(200);
    }

3.前端代码

<div class="login-container">
        <!-- 登录对话框 -->
        <div class="login-dialog">
            <h3>登录</h3>
            <!-- 使用 form 包裹一下下列内容, 便于后续给服务器提交数据 -->
            <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" id="submit" value="登录">
                </div>
            </form>
        </div>
    </div>

form表单中的action为请求中的url,method为请求中的方法类型,id属性时针对html获取元素,name属性则是针对form表单构造http请求。

显示用户信息

当我们进入博客列表页的时候,用户显示的内容应该是登录用户的信息,一旦我们进入到博客详情页的时候,显示的就应该是该博客作者的信息。

首先是博客列表页

1.约定前后端交互接口

2.后端代码

由于我们进入博客列表页,首先会去检查是否已经登录过,如果登录过就可以拿到用户的数据,此时我们可以将用户数据返回给前端,然后修改用户姓名的属性。此时我们也只要在前面代码的基础上稍加修改。

        //防止将密码传输回去
        user.setPassword("");
        String respJson = objectMapper.writeValueAsString(user);
        resp.setContentType("application/json; charset=utf8");
        resp.getWriter().write(respJson);

3.前端代码

前端代码也只需要在之前的基础上稍加修改就行

function checkLogin(){
   $.ajax({
    type: 'get',
    url: 'login',
    success: function(body){
        let h3 = document.querySelector('h3');
        h3.innerHTML = body.username;
    },
    
    error: function(body){
        location.assign('blog_login.html');
    }
   }) ;
}

然后是博客详情页

详情页这里显示的是当前文章的作者信息,由于我们知道blogId,就可以查询到userId然后就能查询到user的信息。最后再将信息显示出来即可。

1.约定前后端交互接口

2.后端代码

public class UserServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String blogId = req.getParameter("blogId");
        //如果用户直接访问博客详情页
        if(blogId == null){
            //从session中拿到user对象
            HttpSession session = req.getSession(false);
            if(session == null){
                //为了方便后续统一处理,返回空对象
                User user = new User();
                String respJson = objectMapper.writeValueAsString(user);
                resp.setContentType("application/json; charset=utf8");
                resp.getWriter().write(respJson);
                return;
            }
            User user = (User)session.getAttribute("user");
            String respJson = objectMapper.writeValueAsString(user);
            resp.setContentType("application/json; charset=utf8");
            resp.getWriter().write(respJson);
        }else{
            BlogDao blogDao = new BlogDao();
            Blog blog = blogDao.getBlog(Integer.parseInt(blogId));

            if(blog == null){
                User user = new User();
                String respJson = objectMapper.writeValueAsString(user);
                resp.setContentType("application/json; charset=utf8");
                resp.getWriter().write(respJson);
                return;
            }

            UserDao userDao = new UserDao();
            User user = userDao.getUserById(blog.getUserId());
            
            if(user == null){
                String respJson = objectMapper.writeValueAsString(user);
                resp.setContentType("application/json; charset=utf8");
                resp.getWriter().write(respJson);
                return;
            }

            String respJson = objectMapper.writeValueAsString(user);
            resp.setContentType("application/json; charset=utf8");
            resp.getWriter().write(respJson);
        }
    }
}

3.前端代码

前端代码前面博客列表页类似

        function getUser(){
            $.ajax({
                type:'get',
                url:'user'+location.search,
                success: function(body){
                    let h3 = document.querySelector('.card h3');
                    h3.innerHTML = body.username;
                }
            });
        }

        getUser();

用户退出功能

当用户点击注销的时候,即点击了a标签,此时会触发一个get请求,服务器收到这个get请求,就可以把当前用户会话中的user对象删除。即通过代码删除之前的session对象(最好是删除映射关系,但是Servlet没有提供相应简单的API).

1.约定前后端交互接口

2.后端代码

@WebServlet("/html/logout")
public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession(false);
        if(session == null){
            resp.sendRedirect("blog_login.html");
            return;
        }

        session.removeAttribute("user");
        resp.sendRedirect("blog_login.html");
    }
}

3.前端代码

只需要给a元素写个href即可。

发布博客

在写博客页中,用户可以写博客标题,正文,然后点击发布即可上传数据。

1.约定前后端交互接口

2.后端代码

 @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession(false);
        if(session == null){
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("用户未登录! 无法发布博客!");
            return;
        }
        User user = (User)session.getAttribute("user");
        if (user == null) {
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("用户未登录! 无法发布博客!");
            return;
        }

        resp.setCharacterEncoding("utf8");
        String title = req.getParameter("title");
        String content = req.getParameter("content");
        if (title == null || content == null || "".equals(title) || "".equals(content)) {
            resp.setContentType("text/html; charset=utf8");
            resp.getWriter().write("标题或者正文为空");
            return;
        }

        Blog blog = new Blog();
        blog.setTitle(title);
        blog.setContent(content);
        blog.setUserId(user.getUserId());
        BlogDao blogDao = new BlogDao();
        blogDao.insert(blog);
        resp.sendRedirect("blog_list.html");
    }

3.前端代码

    <div class="blog-edit-container">
        <form action="blog" method="post">
            <!-- 标题编辑区 -->
            <div class="title">
                <input type="text" id="title-input" name="title">
                <input type="submit" id="submit">
            </div>
            <!-- 博客编辑器 -->
            <!-- 把 md 编辑器放到这个 div 中 -->
            <div id="editor">
                <textarea name="content" style="display: none;"></textarea>
            </div>
        </form>
    </div>

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

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

相关文章

【Vue】单页应用程序介绍

通常基于Vue去开发一整个网站&#xff0c;开发出来的这整个网站应用&#xff0c;我们都会叫做单页应用程序 概念 单页应用程序&#xff1a;SPA【Single Page Application】是指所有的功能都在一个html页面上实现 我们可以将页面共用的部分封装成组件&#xff0c;底下要切换的也…

A6500-LC LVDT 前置器,用于A6500-UM, 导轨安装

电源 22.5V to 32VDC <30mA <0.1%/V <60V( 使用SELV/PELV 供电电源) 约2.2Vrms,5kHz IP20 IEC 60529 -35C to 75C(-31F to 167F) -35C to 85C(-31F to 185F) 电流损耗 供电电压对 运行温度 存储温度 0.35mm(0.014 in ),10 to 55Hz 15g 根据 EN 60068-2-27 根据IEC 613…

nginx配置WebSocket参数wss连接

目录 一、原文连接 二、 配置参数 三、实践 四、重启nginx 五、连接websocket 一、原文连接 nginx配置websocket支持wss-腾讯云开发者社区-腾讯云 二、 配置参数 map $http_upgrade $connection_upgrade { default upgrade; close; } upstream websocket { se…

大数据处理学习笔记

sudo tar -zxvf hadoop-1.1.2.tar.gz -C / #解压到/usr/local目录下 sudo mv hadoop-1.1.2 hadoop #重命名为hadoop sudo chown -R python ./hadoop #修改文件权限 //java安装同上给hadoop配置环境变量&#xff0c;将下面代…

Thinkphp使用Elasticsearch查询

在Thinkphp中调用ES&#xff0c;如果自己手写json格式的query肯定是很麻烦的。我这里使用的是ONGR ElasticsearchDSL 构建 ES 查询。ongr ElasticsearchDSL 的开源项目地址&#xff1a;GitHub - ongr-io/ElasticsearchDSL: Query DSL library for Elasticsearch。ONGR Elastics…

分布式数据库架构:从单实例到分布式,开发人员需及早掌握?

现在互联网应用已经普及,数据量不断增大。对淘宝、美团、百度等互联网业务来说,传统单实例数据库很难支撑其性能和存储的要求,所以分布式架构得到了很大发展。而开发人员、项目经理,一定要认识到数据库技术正在经历一场较大的变革,及早掌握好分布式架构设计,帮助公司从古…

DSP28335模块配置模板系列——定时器中断配置模板

一、配置步骤&#xff1a; 1.使能定时器时钟 EALLOW;SysCtrlRegs.PCLKCR3.bit.CPUTIMER2ENCLK 1; // CPU Timer 2EDIS; 2.设置定时器的中断向量 EALLOW;PieVectTable.TINT2 &TIM2_IRQn;EDIS;其中TIM2_IRQn时定时器中断服务程序的名称 &#xff0c;将中断服务函数的地址…

【回溯算法】N皇后问题·构建多叉决策树,遍历决策节点,做出决策(边),收集答案

0、前言 在由树形解空间入手&#xff0c;深入分析回溯、动态规划、分治算法的共同点和不同点这篇博客&#xff0c;其实已经对回溯算法的思想、做题框架做出了详细的阐述。这篇文章我们再从N皇后问题&#xff0c;加深我们对其理解。 这里在简单再次对其进行概述&#xff1a; …

dataphin是什么及其简单使用示例

1.1dataphin是什么&#xff1f; Dataphin是由阿里研发的智能大数据建设平台&#xff0c;提供一站式数据中台&#xff08;大数据平台&#xff09;建设服务。Dataphin通过沙箱&#xff08;项目&#xff09;实现业务及作业资源隔离&#xff0c;运行更快&#xff0c;且数据同步到D…

代码随想录算法训练营第四十八 | ● 121. 买卖股票的最佳时机 ● 122.买卖股票的最佳时机II

121. 买卖股票的最佳时机 买卖股票的最佳时机 视频讲解&#xff1a;https://www.bilibili.com/video/BV1Xe4y1u77q https://programmercarl.com/0121.%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E7%9A%84%E6%9C%80%E4%BD%B3%E6%97%B6%E6%9C%BA.html class Solution { public:int ma…

因你而变 共赴新程 | AidLux全新版本震撼发布!

历经400多个日夜&#xff0c;AidLux 2.0&#xff08;基础版&#xff09;终于要与大家见面了。 开发者们问过无数次&#xff0c;新版本何时发布&#xff0c;期待的功能何时上线……在此&#xff0c;让我先真诚地感谢大家长期以来的期待与关心&#xff01; 一年多以来&#xff…

如何从官网下载 mysql 二进制安装包

一.下载二进行包 1. 官网网址: https://www.mysql.com/ 如图所示进入官网 2. 点击 DOWNLOADS ,进入如下图 在该页面找到 MySQL Community (GPL) Downloads 点进去 如上图页面&#xff0c;找到 MySQL Community Server 在点进去 下载 linux 通用版 点击最下面 Compressed …

服务监控-微服务小白入门(5)

背景 什么是服务监控 监视当前系统应用状态、内存、线程、堆栈、日志等等相关信息&#xff0c;主要目的在服务出现问题或者快要出现问题时能够准确快速地发现以减小影响范围。 为什么要使用服务监控 服务监控在微服务改造过程中的重要性不言而喻&#xff0c;没有强大的监控…

kafka-生产者拦截器(SpringBoot整合Kafka)

文章目录 1、生产者拦截器1.1、创建生产者拦截器1.2、KafkaTemplate配置生产者拦截器1.3、使用Java代码创建主题分区副本1.4、application.yml配置----v1版1.5、屏蔽 kafka debug 日志 logback.xml1.6、引入spring-kafka依赖1.7、控制台日志 1、生产者拦截器 1.1、创建生产者拦…

SkyWalking之P0核心业务场景输出调用链路应用

延伸扩展&#xff1a;XX核心业务场景 路由标签打标、传播、检索 链路标签染色与传播 SW: SkyWalking的简写 用户请求携带HTTP头信息X-sw8-correlation “X-sw8-correlation: key1value1,key2value2,key3value3” 网关侧读取解析HTTP头信息X-sw8-correlation&#xff0c;然后通…

Dokcer 基础使用 (4) 网络管理

文章目录 Docker 网络管理需求Docker 网络架构认识Docker 常见网络类型1. bridge 网络2. host 网络3. container 网络4. none 网络5. overlay 网络 Docker 网路基础指令Docker 网络管理实操 其他相关链接 Docker 基础使用(0&#xff09;基础认识 Docker 基础使用(1&#xff09;…

【HarmonyOS4学习笔记】《HarmonyOS4+NEXT星河版入门到企业级实战教程》课程学习笔记(十三)

课程地址&#xff1a; 黑马程序员HarmonyOS4NEXT星河版入门到企业级实战教程&#xff0c;一套精通鸿蒙应用开发 &#xff08;本篇笔记对应课程第 20 - 21节&#xff09; P20《19.ArkUI-属性动画和显式动画》 本节先来学习属性动画和显式动画&#xff1a; 在代码中定义动画&am…

使用difflib实现文件差异比较用html显示

1.默认方式&#xff0c;其中加入文本过长&#xff0c;需要换行&#xff0c;因此做 contenthtml_output.replace(</style>,table.diff td {word-wrap: break-word;white-space: pre-wrap;max-width: 100%;}</style>)&#xff0c;添加换行操作 ps&#xff1a;当前te…

BGP汇总+认证

一、BGP 的宣告问题 1、在 BGP 协议中每台运行 BGP 的设备上&#xff0c;宣告本地直连路由 2、在 BGP 协议中运行 BGP 协议的设备来宣告.通过 IGP 学习到的&#xff0c;未运行 BGP 协议设备产2、生的路由&#xff1b; 在 BGP 协议中宣告本地路由表中路由条目时,将携带本地到达这…

PostgreSQL基础(九):PostgreSQL的事务介绍

文章目录 PostgreSQL的事务介绍 一、什么是ACID&#xff08;常识&#xff09; 二、事务的基本使用 三、保存点&#xff08;了解&#xff09; PostgreSQL的事务介绍 一、什么是ACID&#xff08;常识&#xff09; 在日常操作中&#xff0c;对于一组相关操作&#xff0c;通常…