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

文章目录

    • 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

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

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

相关文章

【Python使用】嘿马头条完整开发md笔记第4篇:数据库,1 方案选择【附代码文档】

嘿马头条项目从到完整开发笔记总结完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;课程简介&#xff0c;ToutiaoWeb虚拟机使用说明1 产品介绍,2 原型图与UI图,3 技术架构,4 开发,1 需求,2 注意事项。数据库&#xff0c;理解ORM1 简介,2 安装,3 数据库连接…

C++要点细细梳理(下)(内存分配、异常处理、template和文件读写)

4. 类动态内存分配 4.1 C语言动态内存分配&#xff1a;malloc和free 4.2 C动态内存分配&#xff1a;new和delete 思考&#xff1a;定义一个对象和定义一个普通变量有何区别? 普通变量:分配足够空间即可存放数据对象:除了需要空间&#xff0c;还要构造/析构 类比&#xff1a;…

关于 VScode, 点击文件右键或者在文件夹中没有 【 在vscode中打开选项】 解决办法

关于 VScode, 点击文件右键或者在文件夹中没有 【 在vscode中打开选项】 解决办法 段子手-168 2024-4-6 1、在任意位置创建一个文本文件。如&#xff1a;a.txt 2、复制以下代码到 a.txt 文本文件中。 &#xff08;注&#xff1a; 以 ; 开头的 , 是备注信息 , 不需要做任何修…

代码随想录第19天

654. 最大二叉树 已解答 中等 相关标签 相关企业 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值。递归地在最大值 左边 的 子数组前缀上 构建左子树。递归地在最大值 右边 的 子数组后缀…

影院座位选择简易实现(uniapp)

界面展示 主要使用到uniap中的movable-area&#xff0c;和movable-view组件实现。 代码逻辑分析 1、使用movable-area和movea-view组件&#xff0c;用于座位展示 <div class"ui-seat__box"><movable-area class"ui-movableArea"><movab…

【QT学习】5.控件

一。控件的了解 1.控件作用 拖拽页面&#xff0c;所见即所得。 2.创建控件 3.向qt项目中添加资源 3.1显示图片 项目右键--》add new 创建结果&#xff1a; 添加资源到文件中 补充&#xff1a;使用代码的方式添加图片 3.2显示动图 1.添加动图资源 添加资源 2.显示动图 3.3显示…

基于单片机电子硬币储存器的设计

**单片机设计介绍&#xff0c;基于单片机电子硬币储存器的设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机电子硬币储存器的设计概要主要涵盖了硬件设计、软件设计、硬币识别、计数与储存等核心功能。以下是对该设…

OWASP TOP10 漏洞详解

前言 该内容是 OWASP TOP 10 的学习笔记&#xff0c;笔记内容来源 B 站龙哥的视频【12.Top漏洞10&#xff1a;服务器请求伪造_哔哩哔哩_bilibili】 一、访问控制崩溃 概念 未对通过身份验证的用户实施恰当的访问控制。攻击者可以利用这些缺陷访问未经授权的功能或数据&#xf…

【Linux】环境基础开发工具使用——gcc/g++使用

Linux编译器-gcc/g使用 1. 背景知识 1. 预处理&#xff08;进行宏替换 ) 2. 编译&#xff08;生成汇编 ) 3. 汇编&#xff08;生成机器可识别代码&#xff09; 4. 连接&#xff08;生成可执行文件或库文件 ) 2. gcc如何完成 格式 gcc [ 选项 ] 要编译的文件 [ 选…

阿德勒、荣格、埃里克森、霍妮、弗洛姆、沙利文的新精神分析理论

新精神分析理论&#xff0c;强调自我的自主性及其整合与调节功能&#xff0c;强调文化和社会因素对人格的重大影响。 一、阿德勒的个体心理学 阿德勒&#xff0c;是一个男人努力克服自卑感的优秀样板。阿德勒写了《超越与自卑》。 阿德勒&#xff0c;向意识层面扩展精神分析…

c++11的重要特性3

目录 1、lambda表达式 C98中的一个例子 lambda表达式的 lambda表达式语法 函数对象与lambda表达式 3、可变参数模板 递归获取 逗号表达式展开参数包 2、包装器 function包装器 bind 1、lambda表达式 C98中的一个例子 在C98中&#xff0c;如果想要对一个数据集合中的元素进…

NetSuite 自定义记录类型的权限控制

在近期的一个定制项目中&#xff0c;遭受了一次用户洗礼。有个好奇宝宝把我们的一个自定义类型的表记录进行了删除&#xff0c;导致一个重要功能失败。算是给我们扎实上了一课。自定义类型的权限也需要重视起来。所以&#xff0c;今朝我们记录下这个设置&#xff0c;同时写给未…

C++ setw() 函数

C setw() 函数 分类 编程技术 C setw() 函数用于设置字段的宽度&#xff0c;语法格式如下&#xff1a; setw(n) n 表示宽度&#xff0c;用数字表示。 setw() 函数只对紧接着的输出产生作用。 当后面紧跟着的输出字段长度小于 n 的时候&#xff0c;在该字段前面用空格补齐&…

《QT实用小工具·十五》多种样式的开关控件

1、概述 源码放在文章末尾 目前实现了三种样式的开关控件按钮&#xff0c;如下所示&#xff1a; 项目部分代码如下所示&#xff1a; #ifndef IMAGESWITCH_H #define IMAGESWITCH_H/*** 图片开关控件 * 1. 自带三种开关按钮样式。* 2. 可自定义开关图片。*/#include <QWid…

SpringBoot新增员工模块开发

需求分析与设计 一&#xff1a;产品原型 一般在做需求分析时&#xff0c;往往都是对照着产品原型进行分析&#xff0c;因为产品原型比较直观&#xff0c;便于我们理解业务。 后台系统中可以管理员工信息&#xff0c;通过新增员工来添加后台系统用户。 新增员工原型&#xf…

Vue中如何使用Tailwind CSS样式?多次引用不成功?具体步骤怎么做?

一、安装Tailwind CSS和依赖 在你的Vue项目中安装Tailwind CSS及其依赖。你可以使用npm或yarn来安装。 npm install tailwindcsslatest postcsslatest autoprefixerlatest # 或者yarn add tailwindcsslatest postcsslatest autoprefixerlatest 二、初始化Tailwind CSS np…

Linux常用命令-网络管理

文章目录 ping基本用法主要选项常见用途和理解输出注意事项 ifconfig基本用法配置网络接口示例高级功能 netstat基本用法常用选项示例注意事项 wget主要特点基本用法常见选项 示例注意事项 curl主要特点基本用法常见选项 示例注意事项 参考资料在线文档和手册图书在线教程和指南…

YOLO-World:实时开放词汇对象检测(论文+代码)

目录 一、YOLO-World摘要以及主要贡献 1.1摘要 1.2主要贡献 二、YOLO-World模型创新点总结 2.1YOLO Detector 2.2Text Encoder 2.3Re-parameterizable Vision-Language PAN 2.4核心创新点总结 三、如何应用 3.1推理预测 3.2自定义词汇推理 3.3自定义词汇类别…

Struts2的入门:新建项目——》导入jar包——》jsp,action,struts.xml,web.xml——》在项目运行

文章目录 配置环境tomcat 新建项目导入jar包新建jsp界面新建action类新建struts.xml,用来配置action文件配置Struts2的核心过滤器&#xff1a;web.xml 启动测试给一个返回界面在struts.xml中配置以实现页面的跳转&#xff1a;result再写个success.jsp最后在项目运行 配置环境 …

C++从入门到精通——类的定义及类的访问限定符和封装

类的定义及类的访问限定符和封装 前言一、类的定义类的两种定义方式成员变量命名规则的建议示例 二、类的访问限定符和封装访问限定符访问限定符说明C为什么要出现访问限定符例题 封装例题 前言 类的定义是面向对象编程中的基本概念&#xff0c;它描述了一类具有相同属性和方法…