手动实现Tomcat底层机制+自己设计Servlet

news2024/11/20 2:43:02

文章目录

    • 1.Tomcat整体架构分析
      • 自己理解
    • 2.第一阶段
        • 1.实现功能
        • 2.代码
          • 1.TomcatV1.java
        • 3.调试阶段
          • 1.阻塞在readLine导致无法返回结果
        • 4.结果演示
    • 3.第二阶段
        • 1.实现功能
        • 2.代码
          • 1.RequestHander.java
          • 2.TomcatV2.java
        • 3.调试阶段
          • 1.发现每次按回车会接受到两次请求
        • 4.结果演示
    • 4.第三阶段
        • 1.实现功能
        • 2.总体框架
        • 3.代码实现
          • ===1.封装Request
          • 1.Request.java
          • 2.RequestHander.java
            • 单元测试报错Sock is closed
            • 原因
            • 修改之后
          • ===2.封装Response
          • 3.Response.java
          • 4.RequestHander.java
            • 单元测试无误
          • ===3.设计servlet规范
          • 5.Servlet.java
          • 6.HttpServlet.java
          • 7.CalServlet.java
          • 8.WebUtils.java
          • 9.RequestHander.java
            • 单元测试无误
          • ===4.xml + 反射初始化容器
          • 10.web.xml
          • 11.TomcatV3.java
          • 12.RequestHander.java
        • 4.总体调试阶段
          • 1.空指针异常
        • 5.结果演示
    • 5.课后作业
        • 1.更新WebUtils.java
          • 添加方法
        • 2.修改RequestHander.java
        • 3.cal.html
        • 4.调试阶段
        • 5.结果展示

1.Tomcat整体架构分析

image-20240126204326055

image-20240126204409213

自己理解

pdf下载

image-20240128100508870

2.第一阶段

1.实现功能

编写自己的Tomcat能接受浏览器的请求并返回结果

2.代码
1.TomcatV1.java
package Tomcat;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author 孙显圣
 * @version 1.0
 * 第一个版本的,可以接收浏览器的请求并返回信息
 */
public class TomcatV1 {
    public static void main(String[] args) throws Exception {
        //在8080端口监听
        ServerSocket serverSocket = new ServerSocket(8081);
        System.out.println("Tomcat在8080端口监听");


        //只要不是手动关闭服务,则循环获取连接
        while (!serverSocket.isClosed()) {

            //获取连接
            Socket accept = serverSocket.accept();
            //获取输入流
            InputStream inputStream = accept.getInputStream();
            //使用转换流转为bufferedreader可以读取一行
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
            //字符串缓冲区
            String buff = null;
            while ((buff = bufferedReader.readLine()) != null) {
                //readline()在网络编程中,只有客户端的连接关闭了才会返回null,所以如果不设置别的判定条件退出,则会阻塞
                //由于请求信息最后有一个\r\n,读到这个\r\n会返回一个"",就可以退出了
                if (buff.equals("")) {
                    break;
                }
                System.out.println(buff);
            }

            //tomcat向浏览器发送http响应,\r\n是换行
            String respHead =
                    "HTTP/1.1 200 OK\r\n" +
                    "Content-Type: text/html;charset=utf-8\r\n\r\n"; //响应头下面要空一行才能写响应体所以两个换行
            String resp = respHead + "<h1>hello world!</h1>";

            //获取输出流
            OutputStream outputStream = accept.getOutputStream();
            //输出
            outputStream.write(resp.getBytes());

            //关闭
            outputStream.flush();
            outputStream.close();
            inputStream.close();
            accept.close();
        }



    }
}

3.调试阶段
1.阻塞在readLine导致无法返回结果
  1. 最开始我并没有添加,如果readLine()读到""就退出循环的逻辑
  2. 后来发现readLine()在网络编程中,只有浏览器端关闭连接才会返回null
  3. 当读到了请求的\r\n的时候就会返回一个""字符串,然后阻塞在这里等待输入
  4. 所以在循环中添加当读到\r\n即返回""的时候退出即可
4.结果演示

image-20240127133045568

image-20240127133105064

3.第二阶段

1.实现功能

BIO线程模型支持多线程

image-20240127133413354

2.代码
1.RequestHander.java
package Tomcat.handler;

import java.io.*;
import java.net.Socket;

/**
 * @author 孙显圣
 * @version 1.0
 * 处理http请求的线程类
 */
public class RequestHander implements Runnable{
    //定义一个socket属性
    private Socket socket = null;

    public RequestHander(Socket socket) {
        this.socket = socket;
    }

    public void run() {
        //可以对客户端或浏览器进行交互
        try {
            //获取输入流
            InputStream inputStream = socket.getInputStream();
            //转换成BufferedReader,方便按行读取
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
            System.out.println("tomcatV2接受到浏览器的数据如下:");
            //接受数据
            //设置缓冲
            String buff = null;
            //循环读取
            while ((buff = bufferedReader.readLine()) != null) {
                //判断是否读取到了\r\n
                if (buff.equals("")) {
                    break; //读取完毕则退出循环,避免readLine阻塞
                }
                System.out.println(buff);
            }

            //获取输出流,返回信息到浏览器
            OutputStream outputStream = socket.getOutputStream();
            String respHead =
                    "HTTP/1.1 200 OK\r\n" +
                            "Content-Type: text/html;charset=utf-8\r\n\r\n"; //响应头下面要空一行才能写响应体所以两个换行
            String resp = respHead + "<h1>hello 孙显圣!</h1>";
            outputStream.write(resp.getBytes());

            //关闭
            outputStream.flush();
            outputStream.close();
            inputStream.close();
            socket.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            //确保关闭
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }

    }
}

2.TomcatV2.java
package Tomcat;

import Tomcat.handler.RequestHander;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author 孙显圣
 * @version 1.0
 */
public class TomcatV2 {
    public static void main(String[] args) throws IOException {
        //在8080端口监听
        ServerSocket serverSocket = new ServerSocket(8080);
        System.out.println("TomcatV2在8080端口监听");

        //只要没有手动关闭则服务一直开启,循环监听
        while (!serverSocket.isClosed()) {

            Socket socket = serverSocket.accept();

            //将获取的socket交给线程类来处理
            RequestHander requestHander = new RequestHander(socket);
            Thread thread = new Thread(requestHander);
            thread.start(); //启动线程


        }
    }
}

3.调试阶段
1.发现每次按回车会接受到两次请求

image-20240127141739775

原因就是每次请求还要请求一下页面的小图标

image-20240127141931309

4.结果演示

image-20240127141956236

4.第三阶段

1.实现功能

image-20240127142230563

image-20240127142544134

2.总体框架

image-20240127215734459

3.代码实现
===1.封装Request
1.Request.java
package Tomcat.http;

/**
 * @author 孙显圣
 * @version 1.0
 */

import java.io.*;
import java.util.HashMap;

/**
 * 1.Request的作用就是封装http请求的数据 GET /tomcatv2?a=9&b=3 HTTP/1.1
 * 2.比如method(get), uri(/tomcat/cal),还有参数列表(num1&num2)
 * 3.相当于原生的HttpServletRequest
 */
public class Request {
    private String method;
    private String uri;
    //存放参数列表
    private HashMap<String, String> parametersMapping = new HashMap<String, String>();

    //构造方法获取inputStream来封装信息
    public Request(InputStream inputStream) throws IOException {
        //转换成BufferedReader
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
                inputStream, "utf-8"));
        //读取第一行
        String s = bufferedReader.readLine();
        //将内容封装到属性
        String[] split = s.split(" ");
        method = split[0];
        //判断是否有参数列表
        int index = split[1].indexOf("?");

        if (index == -1) { //没有参数列表
            uri = split[1];
        }
        else { //有参数列表
            uri = split[1].substring(0,index);
            String parameters = split[1].substring(index + 1, split[1].length());
            String[] split1 = parameters.split("&"); //分割参数
            //判断?后面是否有东西
            if (split1 != null && !"".equals(split1)) {
                //遍历参数
                for (String parameterPair : split1) {
                    String[] split2 = parameterPair.split("=");
                    parametersMapping.put(split2[0],split2[1]);
                }
            }

        }
        //不能关闭,否则socket也就关闭了
//        inputStream.close();
    }

    public String getMethod() {
        return method;
    }

    public String getUri() {
        return uri;
    }
    public String getParameter(String name) {
        if (parametersMapping.containsKey(name)) {
            return parametersMapping.get(name);
        }
        else {
            return null;
        }
    }

}

2.RequestHander.java
package Tomcat.handler;

import Tomcat.http.Request;

import java.io.*;
import java.net.Socket;

/**
 * @author 孙显圣
 * @version 1.0
 * 处理http请求的线程类
 */
public class RequestHander implements Runnable {
    //定义一个socket属性
    private Socket socket = null;

    public RequestHander(Socket socket) {
        this.socket = socket;
    }

    public void run() {
        //可以对客户端或浏览器进行交互
        try {
            //获取输入流
            InputStream inputStream = socket.getInputStream();
            //封装到Request里
            Request request = new Request(inputStream);
            //获取数据
            System.out.println(request.getUri() + " " + request.getMethod());
            System.out.println(request.getParameter("num1") + " " + request.getParameter("num2")
            + " " + request.getParameter("num3"));

            //获取输出流,返回信息到浏览器
            OutputStream outputStream = socket.getOutputStream();
            String respHead =
                    "HTTP/1.1 200 OK\r\n" +
                            "Content-Type: text/html;charset=utf-8\r\n\r\n"; //响应头下面要空一行才能写响应体所以两个换行
            String resp = respHead + "<h1>hello 孙显圣!</h1>";
            outputStream.write(resp.getBytes());

            //关闭
            outputStream.flush();
            outputStream.close();
            inputStream.close();
            socket.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            //确保关闭
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }

    }
}

单元测试报错Sock is closed

image-20240127153426609

原因

在Request.java中获取完参数顺便把inputStream关闭了,导致了Socket也一起关闭了,使得主线程在使用socket的时候出现了已经关闭的错误

image-20240127153538511

修改之后

image-20240127153742457

image-20240127153750759

===2.封装Response
3.Response.java
package Tomcat.http;

/**
 * @author 孙显圣
 * @version 1.0
 */

import java.io.OutputStream;

/**
 * 1.这个response对象可以封装OutputSream
 * 2.即可以通过这个对象返回http响应给浏览器
 * 3.相当于原生的HttpServletResponse
 */
public class Response {
    private OutputStream outputStream = null;
    //封装一个响应头
    public static final String respHeader = "HTTP/1.1 200 OK\r\n" +
            "Content-Type: text/html;charset=utf-8\r\n\r\n"; //响应头下面要空一行才能写响应体所以两个换行

    public Response(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    //以后再Servlet里面获取输出流
    public OutputStream getOutputStream() {
        return outputStream;
    }
}

4.RequestHander.java
package Tomcat.handler;

import Tomcat.http.Request;
import Tomcat.http.Response;

import java.io.*;
import java.net.Socket;

/**
 * @author 孙显圣
 * @version 1.0
 * 处理http请求的线程类
 */
public class RequestHander implements Runnable {
    //定义一个socket属性
    private Socket socket = null;

    public RequestHander(Socket socket) {
        this.socket = socket;
    }

    public void run() {
        //可以对客户端或浏览器进行交互
        try {
            //获取输入流
            InputStream inputStream = socket.getInputStream();
            //封装到Request里
            Request request = new Request(inputStream);
            //获取数据
            System.out.println(request.getUri() + " " + request.getMethod());
            System.out.println(request.getParameter("num1") + " " + request.getParameter("num2")
            + " " + request.getParameter("num3"));

            //获取输出流,返回信息到浏览器
            OutputStream outputStream = socket.getOutputStream();
            //封装到Response对象中
            Response response = new Response(outputStream);

            //获取输出流输出信息
            OutputStream outputStream1 = response.getOutputStream();
            outputStream1.write((response.respHeader + "<h1>response响应:你好,孙显圣</h1>").getBytes());


            //关闭
            outputStream.flush();
            outputStream.close();
            inputStream.close();
            socket.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            //确保关闭
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }

    }
}

单元测试无误

image-20240127161328146

image-20240127161334862

===3.设计servlet规范
5.Servlet.java
package Tomcat.servlet;

import Tomcat.http.Request;
import Tomcat.http.Response;

import java.io.IOException;

/**
 * @author 孙显圣
 * @version 1.0
 * 保留三个核心方法
 */
public interface Servlet {
    void init() throws Exception;
    void service(Request request, Response response) throws IOException;
    void destroy();
}

6.HttpServlet.java
package Tomcat.servlet;

import Tomcat.http.Request;
import Tomcat.http.Response;

import java.io.IOException;

/**
 * @author 孙显圣
 * @version 1.0
 */
public abstract class HttpServlet implements Servlet {
    public void service(Request request, Response response) throws IOException {
        //抽象模板设计模式,判断类型来决定调用什么方法
        if ("GET".equalsIgnoreCase(request.getMethod())) {
            //以后会反射创建子类实例,调用子类的service方法,子类没有,就从父类找,然后再使用动态绑定到子类的doGet方法
            this.doGet(request, response);
        } else if ("POST".equalsIgnoreCase(request.getMethod())) {
            this.doPost(request, response);
        }
    }

    public abstract void doGet(Request request, Response response);

    public abstract void doPost(Request request, Response response);

}

7.CalServlet.java
package Tomcat.servlet;

import Tomcat.http.Request;
import Tomcat.http.Response;
import Tomcat.utils.WebUtils;

import java.io.IOException;
import java.io.OutputStream;

/**
 * @author 孙显圣
 * @version 1.0
 */
public class CalServlet extends HttpServlet {

    public void doGet(Request request, Response response) {
        //完成计算任务
        int num1 = WebUtils.parseInt(request.getParameter("num1"), 0);
        int num2 = WebUtils.parseInt(request.getParameter("num2"), 0);
        int sum = num1 + num2;
        //返回计算结果给浏览器
        OutputStream outputStream = response.getOutputStream();
        String resp = Response.respHeader + "<h1>" + num1 + " + " + num2 + " = " + sum + "</h1>";
        try {
            outputStream.write(resp.getBytes());
            //关闭
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void doPost(Request request, Response response) {
        this.doGet(request, response);
    }

    public void init() throws Exception {

    }

    public void destroy() {

    }
}

8.WebUtils.java
package Tomcat.utils;

/**
 * @author 孙显圣
 * @version 1.0
 */
public class WebUtils {
    //把String类型转换成int类型,如果是非整数则返回默认值
    public static int parseInt(String str, int defaultVal) {
        try {
            return Integer.parseInt(str);
        } catch (NumberFormatException e) {
            System.out.println("转换失败");
        }
        return defaultVal;
    }

}

9.RequestHander.java
package Tomcat.handler;

import Tomcat.http.Request;
import Tomcat.http.Response;
import Tomcat.servlet.CalServlet;

import java.io.*;
import java.net.Socket;

/**
 * @author 孙显圣
 * @version 1.0
 * 处理http请求的线程类
 */
public class RequestHander implements Runnable {
    //定义一个socket属性
    private Socket socket = null;

    public RequestHander(Socket socket) {
        this.socket = socket;
    }

    public void run() {
        //可以对客户端或浏览器进行交互
        try {
            //获取输入流
            InputStream inputStream = socket.getInputStream();
            //封装到Request里
            Request request = new Request(inputStream);

            //获取输出流,返回信息到浏览器
            OutputStream outputStream = socket.getOutputStream();
            //封装到Response对象中
            Response response = new Response(outputStream);

            CalServlet calServlet = new CalServlet();
            calServlet.service(request,response); //这个会调用他抽象父类的service方法

            inputStream.close();
            socket.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            //确保关闭
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }

    }
}

单元测试无误

image-20240127170622206

===4.xml + 反射初始化容器

image-20240127172419183

10.web.xml

image-20240127200603422

image-20240127200517652

11.TomcatV3.java
package Tomcat;

import Tomcat.handler.RequestHander;
import Tomcat.servlet.HttpServlet;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 孙显圣
 * @version 1.0
 */
public class TomcatV3 {
    //设置两个hashmap容器,在启动的Tomcat的时候就初始化
    //存放名字和实例
    public static final ConcurrentHashMap<String, HttpServlet>
            servletMapping = new ConcurrentHashMap<String, HttpServlet>();
    //存放路径和名字
    public static final ConcurrentHashMap<String, String>
            servletUrlMapping = new ConcurrentHashMap<String, String>();

    public static void main(String[] args) throws MalformedURLException, DocumentException, ClassNotFoundException, InstantiationException, IllegalAccessException {
        TomcatV3 tomcatV3 = new TomcatV3();
        tomcatV3.init();
        tomcatV3.run();
    }

    //启动TomcatV3容器
    public void run() {
        try {

            ServerSocket serverSocket = new ServerSocket(8080);
            System.out.println("TomcatV3在8080端口监听");

            while (!serverSocket.isClosed()) { //只要没有手动关闭服务,就循环获取连接
                Socket socket = serverSocket.accept();
                //将socket交给线程处理
                RequestHander requestHander = new RequestHander(socket);
                new Thread(requestHander).start(); //启动线程
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void init() throws MalformedURLException, DocumentException, ClassNotFoundException, InstantiationException, IllegalAccessException {
        //获取该类的路径
        String path = TomcatV3.class.getResource("/").getPath();

        //使用dom4j读取web.xml文件
        //获取解析器
        SAXReader saxReader = new SAXReader();
        //读取文件
        Document read = saxReader.read(new File(path + "web.xml"));
        //获取根元素
        Element rootElement = read.getRootElement();
        //获取所有二级元素
        List<Element> servlet = rootElement.elements();

        //遍历所有二级元素,根据不同类型做处理
        for (Element element : servlet) {
            if ("servlet".equalsIgnoreCase(element.getName())) {
                //获取名字和全类名
                Element servletName = element.element("servlet-name");
                Element servletClass = element.element("servlet-class");

                //通过反射创建实例
                Class<?> aClass = Class.forName(servletClass.getText().trim()); //trim是清除空格
                HttpServlet httpServlet = (HttpServlet) aClass.newInstance();

                //将其放到容器中去
                servletMapping.put(servletName.getText(), httpServlet);

            } else if ("servlet-mapping".equalsIgnoreCase(element.getName())) {
                //获取名字和url
                Element servletName = element.element("servlet-name");
                Element servletUrl = element.element("url-pattern");

                //将其放到容器中去
                servletUrlMapping.put("/tomcat" + servletUrl.getText(), servletName.getText());
            }
        }
    }


}

12.RequestHander.java
package Tomcat.handler;

import Tomcat.TomcatV3;
import Tomcat.http.Request;
import Tomcat.http.Response;
import Tomcat.servlet.CalServlet;
import Tomcat.servlet.HttpServlet;

import java.io.*;
import java.net.Socket;

/**
 * @author 孙显圣
 * @version 1.0
 * 处理http请求的线程类
 */
public class RequestHander implements Runnable {
    //定义一个socket属性
    private Socket socket = null;

    public RequestHander(Socket socket) {
        this.socket = socket;
    }

    public void run() {
        //可以对客户端或浏览器进行交互
        try {
            //获取输入流
            InputStream inputStream = socket.getInputStream();
            //封装到Request里
            Request request = new Request(inputStream);

            //获取输出流,返回信息到浏览器
            OutputStream outputStream = socket.getOutputStream();
            //封装到Response对象中
            Response response = new Response(outputStream);
            String uri = request.getUri();
            String servletName = TomcatV3.servletUrlMapping.get(uri);
            if (servletName == null) {
                servletName = "";
            }
            HttpServlet httpServlet = TomcatV3.servletMapping.get(servletName);
            //判断是否得到了这个对象
            if (httpServlet != null) {
                httpServlet.service(request, response);
            } else {
                //没有得到则返回404
                String resp = Response.respHeader + "<h1>404 not found</h1>";
                response.getOutputStream().write(resp.getBytes());
            }

            inputStream.close();
            socket.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            //确保关闭
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }

    }
}

4.总体调试阶段
1.空指针异常

image-20240127195609800

image-20240127200135939

5.结果演示

image-20240127200406441

image-20240127200413877

image-20240127200437664

5.课后作业

image-20240127201804124

image-20240127201735945

1.更新WebUtils.java
添加方法
    //判断是不是html格式的文件,如果是就直接读取文件内容并且返回true
    public static boolean isHtml(String uri, Response response) {
        //使用正则表达式匹配html文件
        String regStr = "(/.*)*/(.*\\.html)";
        Pattern compile = Pattern.compile(regStr);
        Matcher matcher = compile.matcher(uri);
        if (!matcher.find()) { //没匹配到就直接返回false
            return false;
        }
        //得到html文件的路径
        String path = "D:\\Intelij IDEA Project\\java_web\\tomcat\\target\\classes\\" + matcher.group(2);
        System.out.println(path);
        //根据路径读取文件并存放到StringBuilder中
        StringBuilder stringBuilder = new StringBuilder();
        try {
            BufferedReader bufferedReader = new BufferedReader(new FileReader(path));
            String buf = null;
            while ((buf = bufferedReader.readLine()) != null) {
                stringBuilder.append(buf); //存放到stringBuilder中
            }
            //将stringBuilder的内容响应给浏览器
            String resp = Response.respHeader + stringBuilder.toString();
            response.getOutputStream().write(resp.getBytes());
        }catch (Exception e) {
            System.out.println("文件找不到!");
            return false; //返回false之后就会继续进行原来的逻辑,弹出404
        }

        //如果不出异常则说明响应成功
        return true;
    }
2.修改RequestHander.java

image-20240127215107134

3.cal.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/tomcat/CalServlet" method="get">
    num1:<input type="text" name="num1">
    num2:<input type="text" name="num2">
    <input type="submit" value="提交">
</form>
</body>
</html>
4.调试阶段
  1. 一直显示我的cal.html文件找不到,调了半个小时
  2. 原因是String path = TomcatV3.class.getResource("/").getPath();使用这个来获取的路径没有空格,而我的资源路径是这个D:\\Intelij IDEA Project\\java_web\\tomcat\\target\\classes\\,中间带了空格,真的是醉了,深刻体会到文件夹起名不要带空格的重要性了
5.结果展示

image-20240127215627956

image-20240127215633496

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

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

相关文章

基于Spring Boot的简历系统设计与开发

基于Spring Boot的简历系统设计与开发 开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/idea 部分系统展示 前台首页界面 简历模板管理界面 用户管理界面 管理员登录界…

C++设计模式:策略模式(二)

1、定义与动机 定义一系列算法&#xff0c;把它们一个个封装起来&#xff0c;并且使它们可互相替换&#xff08;变化&#xff09;&#xff0c;该模式使得算法可独立于使用它的客户程序&#xff08;稳定&#xff09;而变化&#xff08;扩展&#xff0c;子类化&#xff09; 在软…

pinia 的介绍和使用

pinia是vue2,vue2 尤其是vue3官方推荐的状态管理器&#xff0c;和vuex类似&#xff0c;但使用起来更为简单&#xff0c; 概念&#xff1a; state:定义响应式共享变量 getter&#xff1a;相当于计算属性 actions&#xff1a;相当于方法 npm安装 npm install pinia创建pinia ,注…

【Python】常见容器

Python容器 列表元组字符串集合字典 列表 定义方法&#xff1a;[元素1, 元素2, …] 列表一次可以存储多个不同数据类型的数据&#xff0c;支持嵌套。 例如&#xff1a; list1 ["张三", 33, True] print(list1) print(type(list1))list2 [list, "李四", …

企业数据资产评估的财报显性化及数据资产入表的主要应用场景

一、背景 近年来&#xff0c;企业“数据资产”的概念不断受政府/企业重视和建设.根据《数据资产评估指导意见》规定&#xff0c;数据资产评估是指资产评估机构及其资产评估专业人员遵守法律、行政法规和资产评估准则&#xff0c;根据委托对评估基准日特定目的下的数据资产价值…

Halcon的HWindowControl控件在C#WinForm中的使用介绍(包括绘制ROI)

Halcon的HSmartWindowControl控件在C#WinForm中的使用介绍&#xff08;包括绘制ROI&#xff09; 文章目录 Halcon的HSmartWindowControl控件在C#WinForm中的使用介绍&#xff08;包括绘制ROI&#xff09;一、 引入hSmartWindowControl控件二、 编写打开图像功能三、 编写绘制RO…

【热门话题】Stable Diffusion:本地部署教程

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 Stable Diffusion&#xff1a;本地部署教程一、引言二、环境准备1. 硬件配置2. …

Java多态练习2

设计金融产品类Financial&#xff0c;属性包括产品名称、产品介绍、起投金额、产品期限&#xff08;int&#xff09;、年化收益&#xff08;百分数&#xff09;&#xff1b;方法包括发布、截止、投资。 设计金融产品类子类&#xff1a; 基金产品Fund&#xff0c;继承金融产品类…

【JAVASE】带你了解面向对象三大特性之一(继承)

✅作者简介&#xff1a;大家好&#xff0c;我是橘橙黄又青&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;再无B&#xff5e;U&#xff5e;G-CSDN博客 1.继承 1.1 为什么需要继承 Java 中使用类对现实世界中实体来…

牛客网BC-125 序列中整数去重复(难题讲解)

题目如下 --------------------------------------------------------------------------------------------------------------------------------- 题目讲解&#xff08;思路&#xff09; -------------------------------------------------------------------------------…

爱上数据结构:二叉树的基本概念

​ ​ &#x1f525;个人主页&#xff1a;guoguoqiang. &#x1f525;专栏&#xff1a;数据结构 ​ 一、树的基本概念 1.概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因 为它看起…

机器学习 - multi-class 数据集训练 (含代码)

直接上代码 # Multi-class datasetimport numpy as np RANDOM_SEED 42 np.random.seed(RANDOM_SEED) N 100 # number of points per class D 2 # dimensionality K 3 # number of classes X np.zeros((N*K, D)) y np.zeros(N*K, dtypeuint8) for j in range(K):ix rang…

cJSON(API的详细使用教程)

我们今天来学习一般嵌入式的必备库&#xff0c;JSON库 1&#xff0c;json和cJSON 那什么是JSON什么是cJSON&#xff0c;他们之间有什么样的关联呢&#xff0c;让我们一起来探究一下吧。 JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&…

tomcat 结构目录

bin 启动&#xff0c;关闭和其他脚本。这些 .sh文件&#xff08;对于Unix系统&#xff09;是这些.bat文件的功能副本&#xff08;对于Windows系统&#xff09;。由于Win32命令行缺少某些功能&#xff0c;因此此处包含一些其他文件。比如说&#xff1a;windows下启动tomcat用的是…

物理层习题及其相关知识(谁看谁不迷糊呢)

1. 对于带宽为50k Hz的信道&#xff0c;若有4种不同的物理状态来表示数据&#xff0c;信噪比为20dB 。&#xff08;1&#xff09; 按奈奎斯特定理&#xff0c;信道的最大传输数据速率是多少&#xff1f;&#xff08;2&#xff09; 按香农定理&#xff0c;信道的最大传输数据速度…

JAVAEE之Spring Boot日志

1. 日志概述 1.1 学习日志的原因 ⽇志对我们来说并不陌生, 从JavaSE部分, 我们就在使用 System.out.print 来打印日志了. 通过打印日志来发现和定位问题, 或者根据日志来分析程序的运行过程. 在Spring的学习中, 也经常根据控制台的日志来分析和定位问题. 随着项⽬的复杂…

记录Linux系统中vim同时开多个窗口编辑文件

在使用Linux进行文本编辑的时候&#xff0c;通常使用vim编辑器编辑文件&#xff0c;当然啦&#xff0c;vim也可以创建文件&#xff0c;如果只是一个一个创建&#xff0c;只需要vim创建即可&#xff0c;但是如何一次性打开多个窗口编辑呢&#xff1f; 目录 1、目标&#xff1a;…

微信小程序uniapp+vue.js旅游攻略系统9krxx

实现了一个完整的旅游攻略小程序系统&#xff0c;其中主要有用户模块、用户表模块、token表模块、收藏表模块、视频信息模块、视频类型模块、景点资讯模块、门票购买模块、旅游攻略模块、景点信息模块、论坛表模块、视频信息评论表模块、旅游攻略评论表模块、景点信息评论表模块…

python 02字符串

字符串可能是用到最多的数据类型了&#xff0c;所有标准序列操作&#xff08;索引、切片、乘法、成员资格检查、长度、最小值和最大值&#xff09;都适用于字符串 但别忘了字符串是不可变的&#xff0c;因此所有的元素赋值和切片赋值都是非法的。 1.居中效果 默认为空格 可…

搭建电商购物独立站抓取主流电商产品数据的方法:工具+电商数据采集API接口

分享一个抓取数据产品的方法&#xff0c;也是别人给我说的。 想做一个联盟产品相关的网站&#xff0c;然后需要采集电商网站的产品。咨询大佬告诉我&#xff0c;大量级电商商品数据的采集可以接入专业的电商数据采集API接口&#xff0c;也可以用webscrsper&#xff0c;于是乎就…