【JavaEE初阶系列】——Servlet运行原理以及Servlet API详解

目录

🚩Servlet运行原理

🚩Servlet API 详解

🎓HttpServlet核心方法

🎓HttpServletRequest核心方法

🎈核心方法的使用

🔴获取请求中的参数

💻query string

💻直接通过form表单

💻json

🎓HttpServletResponse核心方法

🎈setStatus(设置状态码)

🎈sendError发送错误码同时提示信息

🎈setHeader刷新浏览器时间戳

🎈setHeader和sendRedirect构造重定向响应


🚩Servlet运行原理

我们上一篇完成了一篇用servlet来写hello world程序,我们会发现 Servlet 的代码中我们并没有写 main 方法, 那么对应的 doGet 代码是如何被调用的呢? 响应又是如何 返回给浏览器的

servlet是一组api,操作http协议的,tomcat提供的。一组api也可以理解为一个HTTP服务器框架的。 


我们给一个程序,比喻成一辆汽车,main方法就是发动机,是否存在一辆车,没有发动机也可以跑呢?——火车车厢,只有火车头上面有发动机,后面的车厢都是不需要的,车头拉着跑就行了。

我们servlet的代码中没有写main方法,但是也是执行的,是因为我们的程序不是直接执行的,而是在tomcat上运行的,tomcat这个程序里面有main方法,tomcat是火车头,写的webapp就是车厢。

继承HttpServlet这个类,重写里面的方法,目的就是为了把咱们自己定义的代码,插入到“tomcat”中。

在tomcat中,就有形如以下这样的代码:

  • 先通过tcp socket读取请求,http也是基于tcp的 String requestString=readRequest();
  • 然后解析这个请求 HttpServletRequest req=parse(requestString)
  • 构造空的响应对象HttpServletResponse resp=new HttpServletResponse();
  • 创建一个HttpServlet对象 HttpServlet s=new HelloServlet(); s.doGet(req,resp);
  • 把得到的响应返回给浏览器
  • 。。。。。。

tomcat能够自动的new出一个咱们写的子类。执行到s.doGet(req,resp),就相当于把上述代码嵌套到tomcat代码中了。

相当于在处理一次请求的过程中,tomcat完成了99%的工作,剩余的1%由咱们写的代码来完成,(99%的工作是通用的公共的工作,剩下的1%是结合具体的业务,具有差异化的工作)。为了解决不同的业务场景,使用tomcat写网站,不同网站,涉及的业务是不同的。

我们写网站的时候,1.接收前端传来的http请求,并解析。2.根据http请求计算出一个http响应。3.把http响应返回到浏览器这边。  


🚩Servlet API 详解

🎓HttpServlet核心方法

我们写 Servlet 代码的时候, 首先第一步就是先创建类, 继承自 HttpServlet, 并重写其中的某些方法。

  • 方法名称:init     调用时机:在HttpServlet实例化之后被调用一次   (完成初始化操作)
  • 方法名称:destroy     调用时机:在HttpServlet实例不再使用的时候调用一次(用来释放资源)
  • 方法名称:service     每次收到http请求,就会自动执行的方法,处理请求,计算响应(服务器的主逻辑)
  • 方法名称:doGet       收到GET请求的时候调用(由service方法调用)
  • 方法名称:doPost      收到POST请求的时候调用(由service方法调用)
  • 方法名称:doPut/doDelete/doOptions/.....  收到其他请求的时候调用(由service方法调用)

这几个方法,都是可以在子类中重写的。在子类重写这些方法之后,这些方法也都是不需要咱们手动调用的,都是tomcat在合适的时机自行调用。

如果不重写service,在父类(HttpServlet中),自己的service,就会根据请求的方法,来分别调用下面的doGet,doPost,doPut....

  • init
  • destroy
  • service   这三个方法是根据servlet的生命周期来进行的。

所谓生命周期 就是 什么时间点做什么事情。

我们在幼年/青年阶段我们需要学习,成年的时候我们需要工作,老年的时候我们需要养老和退休休息(当我们在执行servlet的时候,我们会根据特定的时间使用上述方法,如果在初始化的时候用init,在释放资源的时候我们用destroy,在收到http请求的时候我们用service方法。)

一个servlet不用了,说明tomcat要关闭了。

tomcat关闭有两种方式:

  • 1.直接干掉tomcat进程(比如直接在任务管理器中结束进程,或者直接点x)这时候我们完全用不到destroy方法
  • 2.通过8005管理端口,给tomcat发送一个“停机”指令,这个时候是能够执行destroy的。 

以往写的代码,都是自己定义方法,自己来调用,或者是直接调用别人定义好的方法。

从今往后,经常会涉及到咱们自己写好的代码,让别人帮咱们调用。所谓“框架”就是如此~~

一个程序的主体部分,都已经被其他大佬们写完了,有些细节内容,允许咱们插入咱们自己写的自定义的逻辑。

一台电脑整体的框架:cpu,主板,内存,硬盘,散热器,电源,机箱,显卡。大的框架确定,还可以有一些自定义的空间。

为什么写代码的时候,要使用框架呢?使用框架,就是不让我们随便写代码。就是限制程序员的自由,不能让程序员代码胡写。“设计模式”属于一种“软性的限制”,“使用框架”属于一种“硬性的限制”,所以框架就出现了。

使用postman,发起一个post请求,让我们看看效果。理解前端和后端之间的交互的对应关系,是一个很重要的事情,也是进入web开发的入门难点。


🎓HttpServletRequest核心方法

Tomcat 通过 Socket API 读取 HTTP 请求 ( 字符串 ), 并且按照 HTTP 协议的格式把字符串解析成
HttpServletRequest 对象 .

表示了一个HTTP请求。

类似于,第一行我们需要得到版本号,第二行我们需要知道uri......

方法 描述
  • String getProtocol() 返回请求协议的名称和版本。
  • String getMethod() 返回请求的 HTTP 方法的名称,例如,GETPOST PUT
  • String getRequestURI() 从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该请求的 URL 的一部分。
  • String getContextPath() 返回指示请求上下文的请求 URI 部分。
  • String getQueryString() 返回包含在路径后的请求 URL 中的查询字符串。

  • Enumeration getParameterNames() 返回一个 String 对象的枚举,包含在该请求中包含的参数的名称。
  • String getParameter(String name) 以字符串形式返回请求参数的值,或者如果参数不存在则返回 null。
  • String[] getParameterValues(String name) 返回一个字符串对象的数组,包含所有给定的请求参数的值,如果参数不存在则返回 null。key存在重复的请求一个key有多个值?a=10&a=20;

query String本质上是键值对,一般都是根据key获取value,很少把queryString作为整体来使用。

  • Enumeration getHeaderNames() 返回一个枚举,包含在该请求中包含的所有的头名。
  • String getHeader(String name) 以字符串形式返回指定的请求头的值。

上述两个方法http请求header部分,也是键值对,也需要根据key获取到value

  • String getCharacterEncoding() 返回请求主体中使用的字符编码的名称。
  • String getContentType() 返回请求主体的 MIME 类型,如果不知道类型则返回 null
  • int getContentLength() 以字节为单位返回请求主体的长度,并提供输入流,或者如果长 度未知则返回 -1

上述三个方法,都是对getHeader进行了简单的封装。?a=10&b=20&c=30 query string更多的是希望能够知道a是几,b是几,c是几.

  • InputStream getInputStream() 用于读取请求的 body 内容. 返回一个 InputStream 对象

通过这个方法,得到一个流对象,读取这个对象,救恩那个得到整个请求的body。(很多时候,在代码中表示一个“不定长”的数据的时候,经常就会想到使用“流对象”)使用流对象,就允许咱们不必一次性处理完,一次处理一点,也可以一次全都处理,更好的兼容二进制数据。

上述介绍的方法,都是get系列的方法(都是读方法),没有set系列(没有写方法)。当前拿到的HttpServletRequest,这里的数据都是来自于客户端发来的,这些数据的内容已经确定下来了,程序员是不需要更改的。

🎈核心方法的使用

调用api,把得到的结果构造成一个字符串,同意返回给客户端。
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 java.io.IOException;
import java.util.Enumeration;

@WebServlet("/show")
public class ShowRequestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //调用api,把得到的结果构造成一个字符串,统一返回给客户端
        StringBuilder stringBuilder=new StringBuilder();
        stringBuilder.append(req.getProtocol());//得到版本号
        stringBuilder.append("<br>");
        stringBuilder.append(req.getMethod());//获取方法 get还是post还是delete
        stringBuilder.append("<br>");
        stringBuilder.append(req.getRequestURI());//地址
        stringBuilder.append("<br>");
        stringBuilder.append(req.getContextPath());//上下文路径
        stringBuilder.append("<br>");
        stringBuilder.append(req.getQueryString());//请求字符串
        stringBuilder.append("<br>");

        //获取所有的header
        Enumeration<String> headerName=req.getHeaderNames();//头部信息
        while (headerName.hasMoreElements()){
            String key=headerName.nextElement();
            String value=req.getHeader(key);
            stringBuilder.append(key+":"+"  "+value);
            stringBuilder.append("<br>");

        }
        //告诉浏览器,咱们的数据返回的是什么类型,任何一次服务器返回,都要做的事情
        resp.setContentType("text/html; charset=utf8");
        //把上述内容整体返回到客户端这边
        resp.getWriter().write(stringBuilder.toString());
    }
}

🔴获取请求中的参数
💻query string

在服务器这边获取到请求中的参数(Query String)

query string中的键值对,都是程序员自定义的,实际开发中都会非常广泛的使用到query string这样的机制。

请求就是相当于输入网址,后面写上 ?username=zyf&password=0528 那么就获取,然后打印出来,如果输入的地址(请求)没有username和passeord那么就是null
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 java.io.IOException;

//从query string中获取用户名和密码
@WebServlet("/getParameter")
public class QueryStringGetParameterServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //此处约定,请求中给定的query string 是形如:username=zyf&password=0528
        //上述query string就会被tomcat给自动解析成一个Map结构
        //getParameter就会在map中查找
        //请求就是相当于输入网址,后面写上 ?username=zyf&password=0528 那么就获取,然后打印出来,如果输入的地址(请求)没有username和passeord那么就是null
        String username=req.getParameter("username");
        String password=req.getParameter("password");
        //对两者内容,进行处理,这里就是简单的打印出来
        System.out.println("username="+username);
        System.out.println("password="+password);
        resp.getWriter().write("ok");
    }
}

如果请求中没有加上query string,此时得到的结果是null null


💻直接通过form表单

body的格式就是query string格式,Content-Type:application/x-www-form-urlencoded

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 java.io.IOException;

@WebServlet("/postParameter")
public class FormPostParameterServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //约定 前端形如这样的请求:
        //POST: /postParameter
        //Content-Type: x-www-form-urlencoded

        //username=zyf&password=528
        //就需要在后端代码中,把body中的 值 给拿到,获取值的方法还是getParameter
        String username=req.getParameter("username");
        String password=req.getParameter("password");
        System.out.println("username="+username);
        System.out.println("password="+password);
        resp.getWriter().write("ok");
    }
}


💻json
        请求格式如下req(json字符串)——>得到Request对象
        Post /json
        Content-type: application/json
        {
           username:zhangsan",
           password:"123"


        }  此处约定响应的格式(也按照json来组织;
        {
          ok:true
        }

body的格式是json,Content-Type:application/json

json本质上也是键值对,规则和form表单截然不同,解析方式自然也变了,由于json支持“嵌套”,因此自己手写解析json的代码,并不容易。


用json处理请求中的参数,有些麻烦。

  • 1.下载导入jackson到项目中,通过maven

  • 2.使用jackson,一个类两个方法

ObjectMapper

把json字符串,映射成一个java对象——readValue

把一个java对象,映射成json字符串——writeValueAsString

网络传输,使用json字符串,java代码中各种逻辑,java对象。

站在服务器的角度,收到的请求,就是json字符串,就需要把json字符串,先映射到java对象中,再进行一系列的业务逻辑处理。

处理完了之后,可能还需要把得到的java对象,映射回json字符串,并且通过响应来返回。


把json字符串,映射成一个java对象——readValue

jackson从json字符串映射到java对象的过程:

  • 1.参数就是json字符串(json字符串是在http的body中的,就需要通过HttpServletRequest中的getInputStream来获取到)此处把这个流对象,直接传给readValue,readValue内部就会读取到InputStram中的所有数据(http请求的body,上面的json字符串)
  • 2.按照json的格式,进行解析,把json字符串,解析成Map(键值对)
  • 3.把Map转换成java对象,在方法的第二个参数,传入了Request.class告诉readValue方法内部,要把当前Map转成啥样的java对象。readValue内部,就可以通过反射api,创建出Request.class实例,并且,根据Request.class提供的属性的名字,来查询上述Map,把得到的结果,赋值给对应的属性。

举个例子:当前Request.class中就知道了,Request两个属性,分别叫做username和password。

拿到username去刚才的Map里查询~~查到的结果叫做cl,于是把cl赋值给username

拿着password去刚才的Map里查询~查到的结果叫做1005,于是把1005赋值给password属性

最终就得到了一个完整的Request对象,username就是cl,password是1005


把一个java对象,映射成json字符串——writeValueAsString

这个代码就是readValue的反向操作能把java对象映射成json字符串。writeValueAsString

  • 1.通过传入的参数,获取到类对象,通过反射拿到哪些属性,就有一个属性 ok
  • 2.根据属性的名字拿到属性的值 拿到了值 true
  • 3.把上述 属性名字 和 属性值 按照json格式构造成字符串就是返回值 形如"{ok:true}"

postman对于json格式要求比较严格,key必须加上引号(但是比如前端的ajax这里就可以不加引号)


🎓HttpServletResponse核心方法

类似于这种响应报文。

方法 描述
  • void setStatus(int sc) 为该响应设置状态码。
  • void setHeader(String name, String value) 设置一个带有给定的名称和值的 header. 如果 name 已经存在, 则覆盖旧的值.
  • void addHeader(String name, String value) 添加一个带有给定的名称和值的 header. 如果 name 已经存在, 不覆盖旧的值, 并列添加新的键值对
  • void setContentType(String type) 设置被发送到客户端的响应的内容类型。
  • void setCharacterEncoding(String charset) 设置被发送到客户端的响应的字符编码(MIME 字符集)例如, UTF-8。
  • void sendRedirect(String location) 使用指定的重定向位置 URL 发送临时重定向响应到客户端。
  • PrintWriter getWriter() 用于往 body 中写入文本格式数据.
  • OutputStream getOutputStream() 用于往 body 中写入二进制格式数据.

🎈setStatus(设置状态码)

@WebServlet("/status")
public class StatusServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //不显示设置。默认是200
       // resp.setStatus(404);
        resp.sendError(404,"这个页面是个错误的页面");
    }
}

🎈sendError发送错误码同时提示信息


🎈setHeader刷新浏览器时间戳


通过setHeader给响应中设置一些特殊的header,比如,可以设置refresh:1,让浏览器,每秒钟自动刷新一次。

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 java.io.IOException;
@WebServlet("/refresh")
public class RefreshServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //通过setHeader给响应设置一些特殊的header
        //refresh使浏览器每隔xs都刷新一次
        resp.setHeader("refresh","1");
        resp.getWriter().write(" "+System.currentTimeMillis());//时间戳(当前时间)
    }
}

虽然是每秒刷新一次,但是也不是精准的1000ms,略多一点,浏览器发请求,到服务器返回响应,也需要时间。


🎈setHeader和sendRedirect构造重定向响应

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 java.io.IOException;

@WebServlet("/Redirect")
public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.设置状态码302(重定向)
        //2.header里面设置一个属性location描述跳转到哪个页面中去
//        resp.setStatus(302);
      // resp.setHeader("Location","https://www.baidu.com");
        resp.sendRedirect("https://www.baidu.com");
    }
}
  • 重定向状态码是3xx(比如302)
  • header需要有一个Location属性,描述要跳转到哪里
resp.setHeader("Location","https://www.baidu.com");
  • 直接跳转到所写字符串ip地址
resp.sendRedirect("https://www.baidu.com");


知不足而奋进,望远山而前行。

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

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

相关文章

免费思维13招之二:第三方思维

思维02:第三方思维 第三方思维又叫第三方资费思维。是一种可以使你的产品免费但是你却依然赚钱的思维。 大家还记得之前讲的“餐厅免费吃饭却年赚百万”的案例吗?这个案例运用了多种免费思维的子思维,其中也用到了第三方资费思维,怎么运用的呢?韩女士,与各行各业合作,…

电脑上如何设置闹钟提醒 电脑闹钟提醒设置方法

在这个信息爆炸的时代&#xff0c;我们每个人每天都面临着无数的任务和约定。繁杂的工作与生活&#xff0c;让我时常感到应接不暇&#xff0c;一不小心就会遗漏某些重要事务&#xff0c;这给我带来不小的困扰。我相信&#xff0c;很多人都有过这样的经历&#xff0c;面对一堆待…

Jmeter性能测试(四)

一、遇到问题解决思路 1、检查请求头是否正确 2、检查请求参数是否正确 3、检查鉴权信息是否正确 4、检查变量作用域 5、检查数据提取是否正确(正则/json提取器) 二、请求头检查 1、在Http信息头管理器查看 2、注意这里的变量作用域是全局的 三、请求参数检查 1、在查看结…

演唱会新风:允许部分歌手闭麦,让观众先唱

演唱会市场的热度从2023年延续至今&#xff0c;出现了一些“倒反天罡”的现象。 例如&#xff1a;让歌迷在台下给歌手唱歌。 5月6日抖音娱乐榜第一的消息是“第一次见辟谣观众没假唱的”。原因是凤凰传奇在常州和北京鸟巢先后举办两场演唱会&#xff0c;其中鸟巢演唱会被认为…

2024年开抖音小店需要多少钱?你真的知道吗?最新入驻条件及费用

大家好&#xff0c;我是电商花花。 现在仍然有很多想开抖店&#xff0c;想做抖音小店&#xff0c;但是很多人都不知道投资一家抖音小店需要多少钱&#xff0c;今天花花就给大家讲一下做一家抖音小店需要投入多少资金&#xff0c;以及具体投入到哪些方面。 我们就说一下个体店…

信息安全-古典密码学简介

目录 C. D. Shannon: 一、置换密码 二、单表代替密码 ① 加法密码 ② 乘法密码 ③密钥词组代替密码 三、多表代替密码 代数密码 四、古典密码的穷举分析 1、单表代替密码分析 五、古典密码的统计分析 1、密钥词组单表代替密码的统计分析 2、英语的统计规…

2024年数维杯数学建模A题思路

文章目录 1 赛题思路2 比赛日期和时间3 竞赛信息4 建模常见问题类型4.1 分类问题4.2 优化问题4.3 预测问题4.4 评价问题 5 建模资料 1 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 2 比赛日期和时间 报名截止时间&#xff1a;2024…

无人机+垂直起降:微型共轴双旋翼无人机技术详解

微型共轴双旋翼无人机技术是一种独特的无人机设计&#xff0c;它结合了垂直起降&#xff08;VTOL&#xff09;能力和微型无人机的灵活性。这种设计允许无人机在无需跑道的情况下垂直起降&#xff0c;并具备在空中悬停和执行各种飞行动作的能力。 适用于集群控制&#xff0c;荷载…

vitis 2020.1 Up date XSA文件后,编译不通过

原来是可以编译通过的&#xff0c;升级XSA文件后&#xff0c;出现各种问题&#xff0c;pmufw没法编译通过 xpfw_config.h:14:10: fatal error: xparameters.h: No such file or directory Vitis 2020.2 - fatal error: xparameters.h: No such file or directory (xilinx.com)…

网络安全之DHCP详解

DHCP&#xff1a;Dynamic Host Configration Protocol 动态主机配置协议 某一协议的数据是基于UDP封装的&#xff0c;当它想确保自己的可靠性时&#xff0c;这个协议要么选确认重传机制&#xff0c;要么选周期性传输。 DHCP是确认重传&#xff0c;【UDP|DHCP】,当DHCP分配完地…

【Vue】Vue packages version mismatch(vue 和 vue-template-compiler)

报错&#xff1a;Vue packages version mismatch 原因&#xff1a;vue和vue-template-compiler版本不一样解决&#xff1a;如上vue版本为 2.6.14&#xff0c;vue-template-comiler版本为2.7.16。将vue-template-comiler版本设置为和vue版本一致即可。 npm install vue-templat…

神经网络极简入门

神经网络是深度学习的基础&#xff0c;正是深度学习的兴起&#xff0c;让停滞不前的人工智能再一次的取得飞速的发展。 其实神经网络的理论由来已久&#xff0c;灵感来自仿生智能计算&#xff0c;只是以前限于硬件的计算能力&#xff0c;没有突出的表现&#xff0c;直至谷歌的A…

响应式编程Spring Reactor探索

一&#xff0c;介绍 响应式编程&#xff08;Reactive Programming&#xff09;&#xff0c;简单来说是一种生产者只负责生成并发出数据/事件&#xff0c;消费者来监听并负责定义如何处理数据/事件的变化传递方式的编程思想。 响应式编程借鉴了Reactor设计模式&#xff0c;我们…

神器:jQuery一键转换为纯净JavaScript代码

我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版&#xff0c;欢迎购买。点击进入详情 该工具将 jQuery 代码转换为现代、高效的 JavaScript。它允许您用纯 JavaScript 替换 jQuery&#xff0c;同时保持原始代码不变。 虽然 jQuery 一直是 Web 开发中…

防火墙技术基础篇:什么是包过滤技术

什么是防火墙包过滤技术 当数据在网络中传输时&#xff0c;它们被分割成小的单元&#xff0c;称为数据包。防火墙的包过滤是一种基本的网络安全技术&#xff0c;用于检查这些数据包并根据预定义的规则决定是否允许它们通过防火墙。 防火墙包过滤是一种关键的网络安全技术&am…

在下游市场需求带动下 轮胎模具市场规模逐渐扩大

在下游市场需求带动下 轮胎模具市场规模逐渐扩大 轮胎模具是通过硫化、成型等工序生产各种轮胎的一种工具。轮胎模具是生产轮胎的关键设备之一&#xff0c;其性能直接影响到轮胎的耐用性和安全性。根据花纹加工工艺不同&#xff0c;轮胎模具加工工艺可分为精密铸造工艺、数控雕…

炒美股怎么开户?

近年来&#xff0c;随着国内投资者对境外投资需求的不断增长&#xff0c;炒美股逐渐成为许多投资者的选择。然而&#xff0c;随着监管政策的不断完善&#xff0c;传统的互联网券商开户方式已经不再适用。那么&#xff0c;对于想要入场美股市场的投资者来说&#xff0c;该如何开…

实现左上角的固定视口但是网格以图片中心放大缩小

仅仅修改了showbk&#xff08;&#xff09; 函数部分&#xff0c;增加bkv4 直接采样&#xff0c;然后粘贴到左上角&#xff0c;实现多余部分裁剪&#xff0c;形成视口内放大缩小 // 程序&#xff1a;2D RPG 地图编辑器与摄像机追随 // 作者&#xff1a;bilibili 民用级脑的研发…

windows10打印机共享完美解决方案

提到文件共享大家并不陌生,相关的还有打印机共享,这个多见于单位、复印部,在一个区域网里多台电脑共用一台打印机,打印资料非常方便,就包括在家里,我们现在一般都会有多台电脑或设备,通过家庭网络联接,如果共享一台打印机的话也是件便捷的事。 但是随着操作系统的更新…

Win11任务栏通知很不明显的解决方案

Win11流行起来后&#xff0c;不少用户抱怨Win11的任务栏通知闪烁的颜色很不明显&#xff0c;经常微信来消息了看不到。虽然有右下角的微信图标会闪烁&#xff0c;但是提醒舒适度还是觉得不如Win10舒服显眼。 默认的颜色是这样子的&#xff0c;可以看得出Win11的任务栏提醒颜…