Struts扫盲

Struts扫盲

这里的struts是struts1。以本文记录我的那些复习JavaEE的痛苦并快乐的晚上

Struts是什么

框架的概念想必大家都清楚,框架即“半成品代码”,是为了简化开发而设计的。一个项目有许多分层,拿一个MVC架构的Web应用来说,有表示层、控制层、业务逻辑层、数据访问层 多层(这是一种分法),用于各层的框架就可以被冠以对应的前缀。

在讲Struts之前,可以先了解一下Spring框架,这是一个开源的全面开发框架,见下图。可以看到Spring包含了相当多的模块。在Web模块中可以看到Struts的身影,没错,Struts就是一个Web层的框架,用来帮助我们完成Servlet的编码。

Web层通常指的是应用程序中负责处理用户请求、展示数据以及与用户交互的部分。按照上面的分法,Web层功能集中于表示层和控制层,不涉及数据的访问与处理。

img


Struts开发基本原理

以往基本的Web应用开发关注的都是Servlet该怎么写、该怎么处理请求。具体模型(Bean)调用的语句和请求处理的相关语句都在Servlet中。

而Struts帮我们简化了这个过程,我们不再编写Servlet代码,而是转而编写我们自己的Action类,Action类可以有很多个,分别实现不同的业务逻辑,负责处理请求,最好以功能来命名前缀,比如LoginAction

Struts1提供了一个ActionServlet类来负责请求的调度(对请求进行预处理,并转发给对应的Action类),而不负责具体请求的处理,起到的是(中央)控制器的功能。

另外还有ActionForm、ActionForward、ActionMapping等组件,分别负责了之前Servlet的一小部分逻辑,这样的设计思路层次分明,各部分之前耦合度低,也易于修改。


Struts工作流程

  1. 用户发送一个请求到 ActionServlet

  2. ActionServlet 根据请求URL查找 struts-config.xml 中的相应 Action 类。

  3. Action 类的 execute 方法处理请求逻辑,并返回一个 ActionForward,确定哪个视图(JSP页面)用于显示响应。

  4. 请求被转发到相应的JSP页面,JSP页面通过标签库(如Struts标签库)来展示数据。

    url请求(.do结尾的)
    —>ServletMapping截获请求,交给ActionServlet处理
    —>struts-config.xml根据url指派具体的Action(相当于Controller层)
    —>Action的execute()方法执行具体的业务逻辑(Controller–>Model层)
    —>返回ActionForward对象,struts-config.xml分发具体转向的jsp页面(Controller–>View层)

    原文链接:https://blog.csdn.net/u014256815/article/details/80766990

    这里写图片描述


Struts核心组件

为了更好的理解Struts的核心组件,让我们一起来完成一个基于Struts1框架的猜数游戏,并在这个过程中学习相关的知识点。

猜数游戏

原先的猜数游戏主要代码如下:

NumberGuessBean
package com.niko.bean;import java.io.Serializable;
import java.util.Random;public class NumberGuessBean implements Serializable {private static final long serialVersionUID = 1L;private int answer;private String hint;private int numGuesses;private boolean success;private final Random random = new Random();public NumberGuessBean() {reset();}public int getAnswer() {return answer;}public void setAnswer(int answer) {this.answer = answer;}public String getHint() {return "" + hint;}public void setHint(String hint) {this.hint = hint;}public void setNumGuesses(int numGuesses) {this.numGuesses = numGuesses;}public int getNumGuesses() {return numGuesses;}public boolean getSuccess() {return success;}public void setSuccess(boolean success) {this.success = success;}public void setGuess(String guess) {numGuesses++;int g;try {g = Integer.parseInt(guess);} catch (NumberFormatException e) {g = -1;}if (g == answer) {success = true;} else if (g == -1) {hint = "a number next time";} else if (g < answer) {hint = "higher";} else if (g > answer) {hint = "lower";}}public void reset() {answer = Math.abs(random.nextInt() % 100) + 1;success = false;numGuesses = 0;}
}
numguess.jsp
<%@ page import = "com.niko.bean.NumberGuessBean" %><jsp:useBean id="numguess" class="com.niko.bean.NumberGuessBean" scope="session"/>
<jsp:setProperty name="numguess" property="*"/><html>
<head><title>Number Guess</title></head>
<body bgcolor="white">
<font size=4><% if (numguess.getSuccess()) { %>Congratulations!  You got it.And after just <%= numguess.getNumGuesses() %> tries.<p><% numguess.reset(); %>Care to <a href="numguess.jsp">try again</a>?<% } else if (numguess.getNumGuesses() == 0) { %>Welcome to the Number Guess game.<p>I'm thinking of a number between 1 and 100.<p><form method=get>What's your guess? <input type=text name=guess><input type=submit value="Submit"></form><% } else { %>Good guess, but nope.  Try <b><%= numguess.getHint() %></b>.You have made <%= numguess.getNumGuesses() %> guesses.<p>I'm thinking of a number between 1 and 100.<p><form method=get>What's your guess? <input type=text name=guess><input type=submit value="Submit"></form><% } %></font>
</body>
</html>

这样虽然可以实现功能,但是显示逻辑和具体业务逻辑混杂在一起了,不适合当我们接下来Struts应用的框架。


改进版猜数游戏

为了分离显示逻辑和业务逻辑,根据猜数的流程设计了三个jsp界面,并且在servlet中使用request.getRequestDispatcher("welcome.jsp").forward(req, resp);的方式来跳转的JSP页面。

getRequestDispatcher()ServletRequest 接口中的方法,用于获取 RequestDispatcher 对象。

RequestDispatcher 对象的 forward() 方法会将请求从一个Servlet转发到另一个Servlet或JSP页面,以便处理或显示响应数据。

image-20241107145852331

NumberGuessBean

同上

NumberGuessServlet
package com.niko.controller;import com.niko.bean.NumberGuessBean;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Random;public class NumberGuessServlet extends HttpServlet {private static final long serialVersionUID = 1L;@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/html;charset=utf-8");req.setCharacterEncoding("utf-8");//创建Bean对象(错误),这个方法是每次请求都会被调用的,这样的创建方式会导致Bean的状态不会被保存,//每次请求都new了一个新对象。/*NumberGuessBean numberGuessBean = new NumberGuessBean();String guess=req.getParameter("guess");if(guess!=null){numberGuessBean.setGuess(guess);}*///创建Bean对象(正确),使用session,这样可以在多次猜测尝试过程中保存状态数据HttpSession session = req.getSession();NumberGuessBean numberGuessBean = (NumberGuessBean) session.getAttribute("numberGuessBean");if (numberGuessBean == null) {numberGuessBean = new NumberGuessBean();session.setAttribute("numberGuessBean", numberGuessBean);}String guess = req.getParameter("guess");//去空格if (guess==null || guess.length()==0) {guess="";}guess=guess.trim();if(guess!=null){numberGuessBean.setGuess(guess);}if(numberGuessBean.getSuccess()){req.getRequestDispatcher("success.jsp").forward(req, resp);numberGuessBean.reset();session.setAttribute("numberGuessBean", numberGuessBean);} else if (numberGuessBean.getNumGuesses()==0) {req.getRequestDispatcher("welcome.jsp").forward(req, resp);} else {req.getRequestDispatcher("continue.jsp").forward(req, resp);session.setAttribute("numberGuessBean", numberGuessBean);}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}
}
continue.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>number guess game</title>
</head>
<body>
Good guess, but nope.  Try <b> ${sessionScope.numberGuessBean.hint} </b>.You have made ${sessionScope.numberGuessBean.numGuesses}  guesses.<p>I'm thinking of a number between 1 and 100.<p><form method="get" action="/NumGuess/numguess">What's your guess? <input type=text name=guess><input type=submit value="Submit">
</form>
</body>
</html>
success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>number guess game</title>
</head>
<body>
Congratulations!  You got it.
And after just ${sessionScope.numGuessBean.numGuesses}  tries.<p>Care to <a href="/NumGuess/welcome.jsp">try again</a>?
</body>
</html>
welcome.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>number guess game</title>
</head>
<body>
Welcome to the Number Guess game.<p>I'm thinking of a number between 1 and 100.<p><form method="get" action="/NumGuess/numguess">What's your guess? <input type=text name=guess><input type=submit value="Submit">
</form></body>
</html>

好了,在上面的猜数游戏基础上,来进行基于Struts的改造。

ActionServlet

  • 功能:控制请求的调度,转发请求到相应的Action类。

Action

  • 功能:执行业务逻辑,处理请求,返回结果。

首先肯定需要砍掉原来的Servlet,转而用Action来代替,我们想要设计一个Action类,来接受传入的猜测数字,并根据猜测结果将请求转发到对应的jsp文件来显示。

设计一个NumGuessAction,继承自Action

package com.niko.action;import com.niko.bean.NumberGuessBean;
import org.apache.struts.action.*;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;public class NumGuessAction extends Action {@Overridepublic ActionForward execute(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) throws Exception {// 获取表单数据:并将其转换为DynaActionForm类型,方便动态获取和设置表单参数DynaActionForm daForm = (DynaActionForm) form;String guess = request.getParameter("guess");// 获取用户输入的猜测数字(从表单中取值)// 获取会话对象,如果会话不存在则创建一个新的会话.从会话中获取当前的NumberGuessBean对象(用于存储游戏数据)HttpSession session = request.getSession();//session中存的是多次请求需要用到的持久对象,比如这里的BeanNumberGuessBean numberGuessBean=(NumberGuessBean)session.getAttribute("numberGuessBean");//创建Bean对象:如果这是第一次请求,Bean还没有创建,则new一个,并且我们需要在多次请求中使用这个Bean,因此要存到session中去(用setter来设置)if (numberGuessBean==null){numberGuessBean=new NumberGuessBean();session.setAttribute("numberGuessBean",numberGuessBean);}//开始处理转发逻辑:1.把本次猜的数传给BeannumberGuessBean.setGuess(guess);//处理转发逻辑:2.利用Bean的方法来判断是否猜测成功,并转发到对应的界面//有一个改进的想法,这部分其实也算是猜数逻辑(业务),可以把是否猜成功的逻辑给放到Bean里头,有兴趣的可以试一试if(numberGuessBean.getSuccess()){//表示猜数成功。至于为什么要set表单数据,先按下不表,设计后面JSP读取数据逻辑的更改(因为开始使用ActionForm)daForm.set("NumGuesses",numberGuessBean.getNumGuesses());numberGuessBean.reset();//重启游戏以便重玩//!!这里也很重要,用到了四个形参之一mapping,这个forward方法类似request的getRequestDispatcher的forward方法,负责的是页面跳转//!!另外括号里的叫做逻辑名称,不再是JSP文件名字,逻辑名称和JSP文件之间的映射关系在strusts-config.xml中配置return mapping.findForward("success");}else if(numberGuessBean.getNumGuesses()==0){//表示第一次猜数。转到欢迎视图return mapping.findForward("welcome");}else{//表示继续猜数。转到继续视图,相比成功猜数,需要多存一个hintdaForm.set("NumGuesses",numberGuessBean.getNumGuesses());daForm.set("Hint",numberGuessBean.getHint());return mapping.findForward("continue");}}
}

ActionForm

  • ActionForm:封装表单数据。

可以看到在上面的NumGuessAction的实现中使用到了ActionForm(实际用到的是继承版本的DynaActionForm)。

也许有人会问actionForm这个东西怎么这么多余呢,之前的猜数游戏是在JSP页面里直接用类似

Congratulations!  You got it.  And after just <%= numGuessBean.getNumGuesses() %> tries.<p>

这样的语句直接读取bean里面的数据,

现在使用form表单反而需要先存入form,再在jsp文件里读取property属性:

Congratulations! You got it. And after just <bean:write name="guessBean" property="numGuesses" /> tries.

这样做到底有什么意义?

这也是我的心路历程,现在可以回答这个问题,通过略繁琐的步骤:将用户输入的数据(表单数据)从前端提交到后端进行处理→通过 Action 类封装成一个 JavaBean→再转交给 JSP 页面。 完成了显示逻辑和业务逻辑之间的解耦,实际充当视图的JSP文件不应当知晓内部类有什么方法,而是直接和表单(实际也是一个JavaBean)进行交互。为什么要解耦呢,举一个例子来说,如果猜数Bean的逻辑有所更改,那么所有的JSP文件都要修改,这显然是我们不希望看到的。


ActionForward

  • ActionForward:用于指定请求处理完成后,跳转到哪个视图(JSP)。

ActionMapping

  • ActionMapping: 主要用于将用户请求映射到相应的 Action 类。

在上面的NumGuessAction类中最后的转发逻辑使用到了这两个类,首先NumGuessAction类的execute方法的返回值就是ActionForward对象(有namepath属性),显然mapping.findForward的返回值也是ActionForward对象,findForward需要传入一个转发页面的逻辑名称(在struts-config.xml中配置映射关系)

image-20241107194908645

还有一个返回值为String数组的findForwards方法用于返回当前模块的所有ActionForward名称,详见API。


struts-config.xml

  • struts-config.xml:配置文件,定义请求和响应之间的映射关系。

默认情况下,struts-config.xml 的名称是固定的,通常就是 struts-config.xml。它一般位于项目的 WEB-INF 目录下,即 WEB-INF/struts-config.xml。这是 Struts 框架默认会加载的配置文件路径。

好了!现在让我们来配置这个xml文件。首先是文件的一些常规的版本声明信息。

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE struts-config PUBLIC"-//Apache Software Foundation//DTD Struts Configuration 1.3//EN""http://struts.apache.org/dtds/struts-config_1_3.dtd">

接着使用标签<struts-config>,其中有两个元素<form-beans> <action-mappings>

<struts-config><form-beans><form-bean name="" type=""><!--具体看下面--></form-bean></form-beans><action-mappings><action path="" type="" name=""><!--具体看下面--></action></action-mappings>
</struts-config>

先看<form-beans>,之前讲到ActionForm最终会被转化成JavaBean,那这里就是来设置JavaBean的属性的,下面的语句块

name属性指定的在JSP中访问的form名称;

type指定的是使用DynaActionForm的全类名;

<form-bean name="guessBean" type="org.apache.struts.action.DynaActionForm"><form-property name="guess" type="java.lang.String" /><form-property name="hint" type="java.lang.String" /><form-property name="numGuesses" type="java.lang.String"/>
</form-bean>

可以暂且理解作(当然实现是截然不同的)

Class guessBean{String guess;String hint;String numGuesses
}

接着看<action-mappings>标签,

pathtype指定的是表示请求的 URL 路径,也就是说当URL中是/guess时,就会把请求交给后面type指定的Action类来处理;

name属性指定的是要和这个Action类所绑定的ActionForm类的名字,一般在上面的<form-bean>标签那里就声明了。

<action-mappings><action path="/guess" type="com.niko.struts.NumGuessAction" name="guessBean"><forward name="welcome" path="/jsp/welcome.jsp" /><forward name="continue" path="/jsp/continue.jsp" /><forward name="success" path="/jsp/success.jsp" /></action>
</action-mappings>

JSP界面

最后附上三个改动的jsp文件

welcome.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><%@ taglib uri="http://struts.apache.org/tags-html" prefix="html"%><!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>Number Guess game</title>
</head>
<body>
Welcome to the Number Guess game.
<p>I'm thinking of a number between 1 and 100.</p>
<p><html:form action="/guess" method="get">What's your guess?<html:text property="guess"/><html:submit value="Submit"/></html:form>
</p>
</body>
</html>
continue.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html"%><!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Number Guess game</title>
</head>
<body>      <!--可以看到使用自定义标签获取键值对并输出的方法-->Good guess, but nope. Try <b> <bean:write name="guessBean" property="hint" /></b>. You have made <bean:write name="guessBean" property="numGuesses" />guesses.<p>I'm thinking of a number between 1 and 100.<p><html:form action="/guess" method="get">What's your guess? 	<html:text property="guess" /><html:submit value="Submit" /></html:form>
</body>
</html>
success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html"%><!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Number Guess game</title>
</head>
<body>Congratulations! You got it. And after just<bean:write name="guessBean" property="numGuesses" />tries.<p>Care to <html:link page="/guess">try again?</html:link>
</body>
</html>

接下来会阅读一下和负责转发的控制器有关的源码,搞清struts是怎么简化Action类的请求分发的。

html>

#### success.jsp```jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html"%><!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Number Guess game</title>
</head>
<body>Congratulations! You got it. And after just<bean:write name="guessBean" property="numGuesses" />tries.<p>Care to <html:link page="/guess">try again?</html:link>
</body>
</html>

接下来会阅读一下和负责转发的控制器有关的源码,搞清struts是怎么简化Action类的请求分发的。

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

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

相关文章

【AiPPT-注册/登录安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被机器执行自动化程序攻击&#xff0c;存在如下风险&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露&#xff0c;不符合国家等级保护的要求。短信盗刷带来的拒绝服务风险 &#xff0c;造成用户无法登陆、注册&#xff0c;大量收到垃圾短信的…

自动驾驶系列—从数据采集到存储:解密自动驾驶传感器数据采集盒子的关键技术

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

【月之暗面kimi-注册/登录安全分析报告】

前言 由于网站注册入口容易被机器执行自动化程序攻击&#xff0c;存在如下风险&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露&#xff0c;不符合国家等级保护的要求。短信盗刷带来的拒绝服务风险 &#xff0c;造成用户无法登陆、注册&#xff0c;大量收到垃圾短信的…

时序预测 | 改进图卷积+informer时间序列预测,pytorch架构

时序预测 | 改进图卷积informer时间序列预测&#xff0c;pytorch架构 目录 时序预测 | 改进图卷积informer时间序列预测&#xff0c;pytorch架构预测效果基本介绍参考资料 预测效果 基本介绍 改进图卷积informer时间序列预测代码 CTR-GC卷积,informer&#xff0c;CTR-GC 图卷积…

从入门到精通:一文掌握 Dockerfile 的用法!(多阶段构建与缓存优化)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 Dockerfile基础用法 📒📝 什么是 Dockerfile?📝 Dockerfile 的常见指令🔖 构建指令🔖 命令指令🎈 完整示例:构建一个 Python Flask 应用🔖 1. 项目结构🔖 2. 编写 Dockerfile🔖 3. 构建和运行 Docker 镜像�…

Go语言开发基于SQLite数据库实现用户表修改接口(四)

背景 上一章 Go语言开发基于SQLite数据库实现用户表查询详情接口(三) 这一章我们实现用户表的修改接口 代码实现 mapper层 type UserMapper interface {UpdateById(user *model.User, id uint64) error}type userMapper struct { }func (m *userMapper) UpdateById(user *m…

【C++学习(35)】在Linux中基于ucontext实现C++实现协程(Coroutine),基于C++20的co_await 协程的关键字实现协程

文章目录 为什么使用协程协程的理解协程优势协程的原语操作yield 与 resume 是一个switch操作&#xff08;三种实现方式&#xff09;&#xff1a; 基于 ucontext 的协程基于 XFiber 库的操作1 包装上下文2 XFiber 上下文调度器2.1 CreateFiber2.2 Dispatch 基于C20的co_return …

844.比较含退格的字符串

java用 O&#xff08;1&#xff09;空间这个方法&#xff0c;容易挺多bug的… O&#xff08;1&#xff09;空间 #&#xff1a;删除前一个字符 》 从后面开始判断&#xff08;这样可以用跳过的思想&#xff09;不能使用两次 i- - 来处理 # 的操作&#xff0c;会造成误删了前面…

大数据实训室建设的必要性

一、大数据发展的背景 大数据作为当今信息技术领域的核心驱动力&#xff0c;正在深刻地改变着社会的各个方面。它不仅仅是指数据量庞大&#xff0c;更重要的是指数据的多样性、实时性和复杂性。随着云计算、物联网等技术的迅猛发展&#xff0c;大数据已成为推动经济社会发展的…

MyBatis——增删查改(XML 方式)

1. 查询 1.1. 简单查询 使用注解的方式主要是完成一些简单的增删查改功能&#xff0c;如果要实现复杂的 SQL 功能&#xff0c;还是建议使用 XML 来配置映射语句&#xff0c;将 SQL 语句写在 XML 配置文件中 如果要操作数据库&#xff0c;需要做以下的配置&#xff0c;与注解…

K8S如何基于Istio实现全链路HTTPS

K8S如何基于Istio实现全链路HTTPS Istio 简介Istio 是什么?为什么选择 Istio?Istio 的核心概念Service Mesh(服务网格)Data Plane(数据平面)Sidecar Mode(边车模式)Ambient Mode(环境模式)Control Plane(控制平面)Istio 的架构与组件Envoy ProxyIstiod其他组件Istio 的流量管…

51c大模型~合集44

我自己的原文哦~ https://blog.51cto.com/whaosoft/11884382 #DR4SR 最佳学生论文解读&#xff0c;中科大、华为诺亚&#xff1a;序列推荐新范式DR4SR 本工作由认知智能全国重点实验室 IEEE Fellow 陈恩红团队与华为诺亚方舟实验室完成。陈恩红教授团队深耕数据挖掘、机器学…

HCIP-快速生成树RSTP

一、RSTP是什么 STP&#xff08;Spanning Tree Protocol &#xff09;是生成树协议的英文缩写。该协议可应用于环路网络&#xff0c;通过一定的算法实现路径冗余&#xff0c;同时将环路网络修剪成无环路的树型网络&#xff0c;从而避免报文在环路网络中的增生和无限循环。 RS…

在Element Ui中支持从系统粘贴版中获取图片和PDF,Docx,Doc,PPT等文档

在上一篇中&#xff0c;我们单纯的实现了Ctrl V实现从粘贴版中获取图片信息&#xff0c;但是点击上传的时候会有个bug&#xff0c;就是点击文件上传的时候&#xff0c;会出现一个bug&#xff0c;这篇&#xff0c;我们将在上一篇的基础上进行完善&#xff0c;并支持从粘贴版中获…

《数据可视化技术》上机报告

一、实验目的及要求 掌握pyecharts数据可视化环境搭建以及pyecharts交互式基础图形的绘制。 &#xff08;1&#xff09;掌握pyecharts中初始配置项&#xff0c;系列配置项&#xff0c;全局配置项的配置方法。 &#xff08;2&#xff09;掌握pyecharts中条形图的绘制方法。 …

️虚拟机配置NAT和Bridge模式

虚拟机的网络配置 桥接 通过使用物理机网卡 具有单独ip NAT 把物理机为路由器进行上网 NAT模式&#xff1a; 所谓nat模式&#xff0c;就是虚拟系统会通过宿主机的网络来访问外网&#xff0c;而这里的宿主机相当于有两个网卡&#xff0c;一个是真实网卡&#xff0c;一个是虚拟…

2023年值得关注的9大零售趋势

图片来源&#xff1a;Photo by Heidi Fin on Unsplash 随着经济衰退的威胁日益迫近&#xff0c;新的一年带给零售商一系列挑战&#xff0c;而后者刚从一年的供应链瓶颈和库存过剩中恢复过来。当然&#xff0c;2023年并非一直悲观。随着越来越多的零售商找到新的机会&#xff0c…

杰控通过 OPCproxy 获取数据发送到服务器

把数据从 杰控 取出来发到服务器 前提你在杰控中已经有变量了&#xff08;wincc 也适用&#xff09; 打开你的opcproxy 软件包 opcvarFile 添加变量 写文件就写到 了 opcproxy.ini中 这个文件里就是会读取到的数据 然后 opcproxy.exe发送到桌面快捷方式再考回来 &#…

DVWA靶场通关——SQL Injection篇

一&#xff0c;Low难度下unionget字符串select注入 1&#xff0c;首先手工注入判断是否存在SQL注入漏洞&#xff0c;输入1 这是正常回显的结果&#xff0c;再键入1 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for…

鸿蒙学习生态应用开发能力全景图-三方库(3)

鸿蒙生态三方库&#xff0c;是在鸿蒙系统上可重复使用的软件库&#xff0c;可帮助开发者重用技术资产&#xff0c;快速开发鸿蒙生态应用、元服务&#xff0c;提升开发效率。根据不同的开发语言分为两种&#xff1a;  ArkTS/TS/JS 语言的三方库&#xff0c;可直接导入并使用。…