第四十七章 Spring之假如让你来写MVC——闪存管理器篇

Spring源码阅读目录

第一部分——IOC篇

第一章 Spring之最熟悉的陌生人——IOC
第二章 Spring之假如让你来写IOC容器——加载资源篇
第三章 Spring之假如让你来写IOC容器——解析配置文件篇
第四章 Spring之假如让你来写IOC容器——XML配置文件篇
第五章 Spring之假如让你来写IOC容器——BeanFactory和FactoryBean
第六章 Spring之假如让你来写IOC容器——Scope和属性填充
第七章 Spring之假如让你来写IOC容器——属性填充特别篇:SpEL表达式
第八章 Spring之假如让你来写IOC容器——拓展篇
第九章 Spring之源码阅读——环境搭建篇
第十章 Spring之源码阅读——IOC篇

第二部分——AOP篇

第十一章 Spring之不太熟的熟人——AOP
第十二章 Spring之不得不了解的内容——概念篇
第十三章 Spring之假如让你来写AOP——AOP联盟篇
第十四章 Spring之假如让你来写AOP——雏形篇
第十五章 Spring之假如让你来写AOP——Joinpoint(连接点)篇
第十六章 Spring之假如让你来写AOP——Pointcut(切点)篇
第十七章 Spring之假如让你来写AOP——Advice(通知)上篇
第十八章 Spring之假如让你来写AOP——Advice(通知)下篇
第十九章 Spring之假如让你来写AOP——番外篇:Spring早期设计
第二十章 Spring之假如让你来写AOP——Aspect(切面)篇
第二十一章 Spring之假如让你来写AOP——Weaver(织入器)篇
第二十二章 Spring之假如让你来写AOP——Target Object(目标对象)篇
第二十三章 Spring之假如让你来写AOP——融入IOC容器篇
第二十四章 Spring之源码阅读——AOP篇

第三部分——事务篇

第二十五章 Spring之曾经的老朋友——事务
第二十六章 Spring之假如让你来写事务——初稿篇
第二十七章 Spring之假如让你来写事务——铁三角篇
第二十八章 Spring之假如让你来写事务——属性篇
第二十九章 Spring之假如让你来写事务——状态篇
第三十章 Spring之假如让你来写事务——管理篇
第三十一章 Spring之假如让你来写事务——融入IOC容器篇
第三十二章 Spring之源码阅读——事务篇

第四部分——MVC篇

第三十三章 Spring之梦开始的地方——MVC
第三十四章 Spring之假如让你来写MVC——草图篇
第三十五章 Spring之假如让你来写MVC——映射器篇
第三十六章 Spring之假如让你来写MVC——拦截器篇
第三十七章 Spring之假如让你来写MVC——控制器篇
第三十八章 Spring之假如让你来写MVC——适配器篇
第三十九章 Spring之假如让你来写MVC——番外篇:类型转换
第四十章 Spring之假如让你来写MVC——ModelAndView篇
第四十一章 Spring之假如让你来写MVC——番外篇:数据绑定
第四十二章 Spring之假如让你来写MVC——视图篇
第四十三章 Spring之假如让你来写MVC——上传文件篇
第四十四章 Spring之假如让你来写MVC——异常处理器篇
第四十五章 Spring之假如让你来写MVC——国际化篇
第四十六章 Spring之假如让你来写MVC——主题解析器篇
第四十七章 Spring之假如让你来写MVC——闪存管理器篇
第四十八章 Spring之假如让你来写MVC——请求映射视图篇
第四十九章 Spring之假如让你来写MVC——番外篇:属性操作
第五十章 Spring之假如让你来写MVC——融入IOC容器篇
第五十一章 Spring之源码阅读——MVC篇


文章目录

  • Spring源码阅读目录
    • 第一部分——IOC篇
    • 第二部分——AOP篇
    • 第三部分——事务篇
    • 第四部分——MVC篇
  • 前言
  • 尝试动手写IOC容器
      • 第四十一版 闪存管理器
        • 闪存数据结构
        • 闪存管理器
        • 重定向视图
        • 修改`UrlBasedViewResolver`
        • 修改`DispatcherServlet`
        • 测试
  • 总结


前言

    对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了
在这里插入图片描述

    所以决定花点时间研究研究Spring的源码。主要参考的书籍是:《Spring源码深度解析(第2版)》、《Spring揭秘》、《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》


     书接上回,在上篇 第四十六章 Spring之假如让你来写MVC——主题解析器篇 中,A君 已经完成了 主题解析器 部分的功能了。接下来看看 A君 会有什么骚操作吧

尝试动手写IOC容器

    出场人物:A君(苦逼的开发)、老大(项目经理)

    背景:老大 要求 A君在一周内开发个简单的 IOC容器

    前情提要:A君 已经完成了 主题解析器 部分的功能了 。。。

第四十一版 闪存管理器

    “A君,就快结束了,今天在完成一个组件吧。” 老大 说道

    “啊,还没玩啊,还有啥组件?” A君 都快崩溃了

    “哈哈哈,不多了,就剩两个组件了。” 老大 笑道

    “那今天是什么?” A君 问道

    “就是重定向和转发,转发过程中需要存储的数据,去渲染视图。” A君 问道

    “那要如何实现呢?” A君 疑惑道

    “其实也不难,保留在内存中就行了,等过期后在失效掉。不过,看了下代码,你之前似乎并未实现 重定向视图,这部分内容你也补充下吧!” 老大 解释道

    “好吧。” A君 无奈应到,只能默默回去干活了

闪存数据结构

    走出办公室后,A君 径直回到了自己的工位上,开始琢磨 闪存 的事情了。按照 老大 的说法,闪存 存在着过期时间,那就不能是简单的Map能解决的,除了过期时间,闪存 也可能存在着一个key,多个value的情况,如:复选框。同时也可以根据目标请求路径存储数据。基于上述这么多情况,自然就得定义个单独的类来进行处理了。A君 新增FlashMap类,代码如下:

/*** 闪存*/
public final class FlashMap extends HashMap<String, Object> implements Comparable<FlashMap> {private final MultiValueMap<String, String> targetRequestParams = new LinkedMultiValueMap<>(3);/*** 目标请求路径*/private String targetRequestPath;/*** 过期时间*/private long expirationTime = -1;public void startExpirationPeriod(int timeToLive) {this.expirationTime = System.currentTimeMillis() + timeToLive * 1000;}public FlashMap addTargetRequestParams(MultiValueMap<String, String> params) {if (params != null) {params.forEach((key, values) -> {for (String value : values) {addTargetRequestParam(key, value);}});}return this;}public FlashMap addTargetRequestParam(String name, String value) {if (StringUtils.hasText(name) && StringUtils.hasText(value)) {this.targetRequestParams.add(name, value);}return this;}public MultiValueMap<String, String> getTargetRequestParams() {return this.targetRequestParams;}public boolean isExpired() {return (this.expirationTime != -1 && System.currentTimeMillis() > this.expirationTime);}@Overridepublic int compareTo(FlashMap other) {int thisUrlPath = (this.targetRequestPath != null ? 1 : 0);int otherUrlPath = (other.targetRequestPath != null ? 1 : 0);if (thisUrlPath != otherUrlPath) {return otherUrlPath - thisUrlPath;} else {return other.targetRequestParams.size() - this.targetRequestParams.size();}}//省略其他代码。。。
}
闪存管理器

    闪存数据结构 定义好之后,接着就是如何管理它了,直接操作FlashMap对象也不是不行,但是毕竟还是太过麻烦了,也不好管理。与其如此,不如直接定义个管理者,让它进行统一操作。管理器也简单,就干两件事:增加、更新。A君 新增FlashMapManager接口,代码如下:

/*** 闪存管理器*/
public interface FlashMapManager {/*** 清除闪存** @param request* @param response* @return*/FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response);/*** 保存闪存** @param flashMap* @param request* @param response*/void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response);
}

接下来就是抽象类了,哪些可以提取出来呢?A君 开动脑瓜开始思考:清理要怎么清理?保存要怎么保存?

  • 清理:首当其冲的自然是过期数据了,这个不用怀疑。还有就是每个url需要保留多少数据?对于一次请求来说,请求结束也意味着这些数据已经失效,其生命周期很短,这就意味着 闪存 不可能存储很多数据。那就定一个规则:每次都拉出一个倒霉鬼进行清理,目标url为空的优先,参数多的优先谁叫他占内存呢(笑)?

  • 保存:这就更简单了,获取已有数据和现有数据合并即可了

思绪整理完毕后,A君 新增AbstractFlashMapManager类,代码如下:

public abstract class AbstractFlashMapManager implements FlashMapManager {private static final Object DEFAULT_FLASH_MAPS_MUTEX = new Object();/*** 180s*/private int flashMapTimeout = 180;private UrlPathHelper urlPathHelper = UrlPathHelper.defaultInstance;@Overridepublic final FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response) {List<FlashMap> allFlashMaps = retrieveFlashMaps(request);if (CollectionUtils.isEmpty(allFlashMaps)) {return null;}/***获取过期数据*/List<FlashMap> mapsToRemove = getExpiredFlashMaps(allFlashMaps);/*** targetPath、参数多的会优先被清理*/FlashMap match = getMatchingFlashMap(allFlashMaps, request);if (match != null) {mapsToRemove.add(match);}if (!mapsToRemove.isEmpty()) {Object mutex = getFlashMapsMutex(request);if (mutex != null) {synchronized (mutex) {//清除数据allFlashMaps = retrieveFlashMaps(request);if (allFlashMaps != null) {allFlashMaps.removeAll(mapsToRemove);updateFlashMaps(allFlashMaps, request, response);}}} else {allFlashMaps.removeAll(mapsToRemove);updateFlashMaps(allFlashMaps, request, response);}}return match;}@Overridepublic final void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response) {if (CollectionUtils.isEmpty(flashMap)) {return;}/*** 修正地址*/String path = decodeAndNormalizePath(flashMap.getTargetRequestPath(), request);flashMap.setTargetRequestPath(path);//设置开始时间flashMap.startExpirationPeriod(getFlashMapTimeout());/*** 获取对象锁*/Object mutex = getFlashMapsMutex(request);if (mutex != null) {synchronized (mutex) {/*** 获取老数据*/List<FlashMap> allFlashMaps = retrieveFlashMaps(request);allFlashMaps = (allFlashMaps != null ? allFlashMaps : new CopyOnWriteArrayList<>());/*** 添加并更新数据*/allFlashMaps.add(flashMap);updateFlashMaps(allFlashMaps, request, response);}} else {List<FlashMap> allFlashMaps = retrieveFlashMaps(request);allFlashMaps = (allFlashMaps != null ? allFlashMaps : new ArrayList<>(1));allFlashMaps.add(flashMap);updateFlashMaps(allFlashMaps, request, response);}}protected abstract List<FlashMap> retrieveFlashMaps(HttpServletRequest request);protected abstract void updateFlashMaps(List<FlashMap> flashMaps, HttpServletRequest request, HttpServletResponse response);//省略其他代码。。。
}

抽象类已经把大部分逻辑提取出来了,剩下的部分就是从哪里获取数据?从哪里更新数据?这个有可能是Cookie,也有可能是Session。老样子,这里还是以Session为例,这个很简单,直接从Session中的获取值就行了。A君 新增SessionFlashMapManager类,代码如下:

public class SessionFlashMapManager extends AbstractFlashMapManager {private static final String FLASH_MAPS_SESSION_ATTRIBUTE = SessionFlashMapManager.class.getName() + ".FLASH_MAPS";@Overrideprotected List<FlashMap> retrieveFlashMaps(HttpServletRequest request) {HttpSession session = request.getSession(false);return (session != null ? (List<FlashMap>) session.getAttribute(FLASH_MAPS_SESSION_ATTRIBUTE) : null);}@Overrideprotected void updateFlashMaps(List<FlashMap> flashMaps, HttpServletRequest request, HttpServletResponse response) {WebUtils.setSessionAttribute(request, FLASH_MAPS_SESSION_ATTRIBUTE, (!flashMaps.isEmpty() ? flashMaps : null));}@Overrideprotected Object getFlashMapsMutex(HttpServletRequest request) {return WebUtils.getSessionMutex(request.getSession());}
}
重定向视图

    闪存管理器 是定义好了,可是在哪里用呢?A君 不经一阵挠头。总不能在DispatcherServlet中弄吧。又思索了一阵,A君 忽然发现,不论 重定向 还是 转发,其实都是在返回值里边定义,转发 还好说,直接通过分发器就行了,接着就是 重定向 了,不如定一个 视图 来进行处理。实现是解决了,但是还有个问题:这个 视图 要干什么?A君 又想了下,所谓 重定向 无非就是uri的定义罢了。重定向 到哪里?要不要带参数?要带哪些参数?这个就是 重定向视图 要处理的重点了。好啦,一切都敲定了。A君 新增RedirectView类,代码如下:

/*** 重定向视图*/
public class RedirectView extends AbstractUrlBasedView implements SmartView {/*** 是否是绝对路径*/private boolean contextRelative = false;/*** 是否兼容 HTTP/1.0*/private boolean http10Compatible = true;/*** 暴露模型属性*/private boolean exposeModelAttributes = true;/*** URL 编码*/private String encodingScheme;/*** 响应码*/private HttpStatus statusCode;/*** 是否将当前请求的查询参数传递到目标 URL*/private boolean propagateQueryParams = false;/*** 主机名*/private String[] hosts;@Overrideprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,HttpServletResponse response) throws IOException {String targetUrl = createTargetUrl(model, request);targetUrl = updateTargetUrl(targetUrl, model, request, response);//保存到闪存中RequestContextUtils.saveOutputFlashMap(targetUrl, request, response);// 重定向sendRedirect(request, response, targetUrl, this.http10Compatible);}/*** 构建重定向url** @param model* @param request* @return* @throws UnsupportedEncodingException*/protected final String createTargetUrl(Map<String, Object> model, HttpServletRequest request)throws UnsupportedEncodingException {StringBuilder targetUrl = new StringBuilder();if (this.contextRelative && getUrl().startsWith("/")) {targetUrl.append(getContextPath(request));}targetUrl.append(getUrl());String enc = this.encodingScheme;/*** 编码为空,使用请求编码*/if (enc == null) {enc = request.getCharacterEncoding();}/*** 请求编码还是为空,使用默认编码*/if (enc == null) {enc = WebUtils.DEFAULT_CHARACTER_ENCODING;}/*** 是否追加参数*/if (isPropagateQueryProperties()) {appendCurrentQueryParams(targetUrl, request);}/*** 是否暴露模型数据*/if (this.exposeModelAttributes) {appendQueryProperties(targetUrl, model, enc);}return targetUrl.toString();}//省略其他方法。。。
}

这个类基本上就是在构建url,拼接参数

修改UrlBasedViewResolver

    之前在写UrlBasedViewResolver时候,A君 并没有支持 重定向转发,这回就可以加上了。改动的点也比较简单,在渲染的时候,做个判断就行了。A君 修改createView方法,改动如下:

	@Overrideprotected View createView(String viewName, Locale locale) throws Exception {/*** 是否能够处理*/if (!canHandle(viewName, locale)) {return null;}/*** 重定向*/if (viewName.startsWith(REDIRECT_URL_PREFIX)) {String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());RedirectView view = new RedirectView(redirectUrl,isRedirectContextRelative(), isRedirectHttp10Compatible());String[] hosts = getRedirectHosts();if (hosts != null) {view.setHosts(hosts);}return applyLifecycleMethods(REDIRECT_URL_PREFIX, view);}/*** 转发*/if (viewName.startsWith(FORWARD_URL_PREFIX)) {String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());InternalResourceView view = new InternalResourceView(forwardUrl);return applyLifecycleMethods(FORWARD_URL_PREFIX, view);}return super.createView(viewName, locale);}
修改DispatcherServlet

    现在组件基本准备好了,既然是组件,那么DispatcherServlet就当之无愧了。首先需要的是初始化该组件,A君 新增initFlashMapManager方法,代码如下:

    protected void initStrategies() {/***1.初始化文件上传*/initMultipartResolver();/*** 2.初始化国际化*/initLocaleResolver();/*** 3.初始化主题*/initThemeResolver();/*** 4.初始化url处理器*/initHandlerMappings();/*** 5.初始化适配器*/initHandlerAdapters();/*** 6.初始化异常处理*/initHandlerExceptionResolvers();/*** 初始化视图*/initViewResolvers();/*** 初始化闪存管理器*/initFlashMapManager();}private void initFlashMapManager() {this.flashMapManager = new SessionFlashMapManager();}

还有一个问题:谁去清理 闪存?谁去创建 闪存 对象?当下 A君doService方法进行修改,改动如下:

    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {Map<String, Object> attributesSnapshot = null;if (WebUtils.isIncludeRequest(request)) {//清理includeattributesSnapshot = new HashMap<>();Enumeration<?> attrNames = request.getAttributeNames();while (attrNames.hasMoreElements()) {String attrName = (String) attrNames.nextElement();if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {attributesSnapshot.put(attrName, request.getAttribute(attrName));}}}//设置国际化处理器request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);//设置主题处理器request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);/*** 存在闪存管理器*/if (this.flashMapManager != null) {FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);if (inputFlashMap != null) {/*** 已存在,转成不可修改的*/request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));}//不存在,建立新的缓存request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);}doDispatch(request, response);}

就新增了对于 闪存 的处理,并无太多东西

测试

    好了,现在一切都完成了,又可以开始愉快地测试了。这次测试主要就集中在 控制器 上,其他地方基本就不用改动。A君 新增HelloController代码:

@Controller
public class HelloController {// 首页视图@RequestMapping("/index")public String home(Model model) {// 将 Flash 属性添加到重定向的请求中model.addAttribute("message", "Form submitted successfully!");return "redirect:hello";}@RequestMapping("/hello")public String hello(String message, Model model) {model.addAttribute("message", message);return "hello";}
}

添加一个hello.jsp,代码如下:


<%@page isELIgnored="false"%>
<html>
<body>
<h2>This message is: ${requestScope.message}</h2>
</body>
</html>

然后就可以添加测试代码,如下:

	@Testpublic void v41() throws LifecycleException {System.out.println("############# 第四十一版: 闪存管理器篇 #############");Tomcat tomcat = new Tomcat();//设置端口tomcat.setPort(8082);//设置静态资源路径String webApp = new File("src/main/resources/v41").getAbsolutePath();Context context = tomcat.addWebapp("/test/", webApp);tomcat.start();//挂起tomcat.getServer().await();}

测试结果如下:

在这里插入图片描述

可以看到,经过 重定向 后,url后边也拼上参数。OK,现在 A君 也可以下班了

在这里插入图片描述


总结

    正所谓树欲静而风不止,欲知后事如何,请看下回分解(✪ω✪)

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

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

相关文章

基于微信小程序高校订餐系统的设计与开发ssm+论文源码调试讲解

第4章 系统设计 一个成功设计的系统在内容上必定是丰富的&#xff0c;在系统外观或系统功能上必定是对用户友好的。所以为了提升系统的价值&#xff0c;吸引更多的访问者访问系统&#xff0c;以及让来访用户可以花费更多时间停留在系统上&#xff0c;则表明该系统设计得比较专…

使用批处理文件清除系统垃圾

第一步&#xff1a;打开记事本&#xff0c;里面的命令如下 echo off echo 正在清理临时文件&#xff0c;请稍候...:: 清理系统临时文件 echo 清理系统临时文件... del /q /f /s "%TEMP%\*.*" del /q /f /s "%WINDIR%\Temp\*.*" rd /s /q "%WINDIR%\T…

Linux——信号量和(环形队列消费者模型)

Linux——线程条件变量&#xff08;同步&#xff09;-CSDN博客 文章目录 目录 文章目录 前言 一、信号量是什么&#xff1f; 二、信号量 1、主要类型 2、操作 3、应用场景 三、信号量函数 1、sem_init 函数 2、sem_wait 函数 3、sem_post 函数 4、sem_destroy 函数 ​​​​​​…

垂直供排水抢险车:守护城市,抗击洪涝|深圳鼎跃

我国面积幅员辽阔&#xff0c;其灾害种类多样&#xff0c;而洪涝灾害是其中最常见的灾害&#xff0c;其容易受强降雨的影响&#xff0c;严重影响人民群众的日常生活。而在洪水肆虐的场景中&#xff0c;快速、高效地排涝和供水是防止次生灾害、保护人民生命财产安全的关键环节。…

Social LSTM:Human Trajectory Prediction in Crowded Spaces | 文献翻译

概要 行人遵循不同轨迹以避免障碍物和容纳同行者。任何在这种场景中巡航的自动驾驶车辆都需要能够遇见行人的未来位置并相应地调整其路线以避免碰撞。轨迹预测问题能够被看作一个顺序生成任务&#xff0c;其中我们对基于行人过去的位置预测其未来的轨迹感兴趣。根据最近RNN&am…

React+AntDesign实现类似Chatgpt交互界面

以下是一个基于React和Ant Design搭建的简单ChatGPT风格前端交互界面代码框架示例&#xff0c;该示例实现了基本的用户输入、发送请求以及展示回复的功能。假设后端有一个模拟接口来处理请求并返回回复。 1. 项目初始化&#xff1a; 确保你已经安装了Node.js和npm。通过以下命…

与“神”对话:Swift 语言在 2025 中的云霓之望

0. 引子 夜深人静&#xff0c;是一片极度沉醉的黑&#xff0c;这便于我与深沉的 macbook 悄悄隐秘于其中。一股异香袭来&#xff0c;恍惚着&#xff0c;撸码中身心极度疲惫、头脑昏沉的我仿佛感觉到了一束淡淡的微光轻洒在窗边。 我的对面若隐若现逐渐浮现出一个熟悉的身影。他…

iOS 网络请求: Alamofire 结合 ObjectMapper 实现自动解析

引言 在 iOS 开发中&#xff0c;网络请求是常见且致其重要的功能之一。从获取资料到上传数据&#xff0c;出色的网络请求框架能夠大大提升开发效率。 Alamofire 是一个极具人气的 Swift 网络请求框架&#xff0c;提供了便据的 API 以完成网络请求和响应处理。它支持多种请求类…

68,[8] BUUCTF WEB [RoarCTF 2019]Simple Upload(未写完)

<?php // 声明命名空间&#xff0c;遵循 PSR-4 自动加载规范&#xff0c;命名空间为 Home\Controller namespace Home\Controller;// 导入 Think\Controller 类&#xff0c;以便扩展该类 use Think\Controller;// 定义 IndexController 类&#xff0c;继承自 Think\Control…

【C++】引用(上)

1、引用的基本使用 作用&#xff1a;给变量起别名 语法&#xff1a;数据类型&#xff08;该数据类型要与原名的数据类型一致&#xff09; &别名原名&#xff1b; 示例&#xff1a; #include<iostream> using namespace std; int main() {int a 10;int& …

JDBC实验测试

一、语言和环境 实现语言&#xff1a;Java。 环境要求&#xff1a;IDEA2023.3、JDK 17 、MySQL8.0、Navicat 16 for MySQL。 二、技术要求 该系统采用 SWING 技术配合 JDBC 使用 JAVA 编程语言完成桌面应用开发。 三、功能要求 某电商公司为了方便客服查看用户的订单信…

外包公司名单一览表(成都)

大家好&#xff0c;我是苍何。 之前写了一篇武汉的外包公司名单&#xff0c;评论区做了个简单统计&#xff0c;很多人说&#xff0c;在外包的日子很煎熬&#xff0c;不再想去了。 有小伙伴留言说有些外包会强制离职&#xff0c;不行就转岗&#xff0c;让人极度没有安全感。 这…

一些硬件知识【2025/1/21】

小米2K摄像头拆解&#xff1a; 这种小转接板空余部分一般全部打上过孔&#xff1a; 摄像头下面的SENSOR,这一部分才是摄像头的核心&#xff1a; 干硬件多久跳槽一次比较合适&#xff1f;3年跳一次可以吗&#xff1f; eMCC存储芯片外面那一圈焊盘是做什么用的&#xff1f; 没有…

WPF实战案例 | C# WPF实现计算器源码

WPF实战案例 | C# WPF实现计算器源码 一、设计来源计算器应用程序讲解1.1 主界面1.2 计算界面 二、效果和源码2.1 界面设计&#xff08;XAML&#xff09;2.2 代码逻辑&#xff08;C#&#xff09;2.3 实现步骤总结 源码下载更多优质源码分享 作者&#xff1a;xcLeigh 文章地址&a…

【HBuilderX 中 Git 的使用】

目录&#xff1a; 一&#xff1a;安装必要的版本控制工具二&#xff1a;把Github上的项目克隆到本地三&#xff1a;将本地的项目上传到Github上 一&#xff1a;安装必要的版本控制工具 1️⃣ 安装 TortoiseGit 工具&#xff0c;下载地址&#xff1a;https://tortoisegit.org/do…

浅谈 JVM

JVM 内存划分 JVM 内存划分为 四个区域&#xff0c;分别为 程序计数器、元数据区、栈、堆 程序计数器是记录当前指令执行到哪个地址 元数据区存储存储的是当前类加载好的数据&#xff0c;包括常量池和类对象的信息&#xff0c;.java 编译之后产生 .class 文件&#xff0c;运…

OpenCV简介、OpenCV安装

OpenCV简介、OpenCV安装 本文目录&#xff1a; 零、时光宝盒 一、OpenCV简介 二、OpenCV图像处理基础知识 三、OpenCV-Python环境安装 2.1、纯python环境下安装OpenCV 2.2、Anaconda管理环境下安装 OpenCV 四、如何用OpenCV 中进行读取展示图像 五、OpenCV读取图像、显…

使用Chrome和Selenium实现对Superset等私域网站的截图

最近遇到了一个问题&#xff0c;因为一些原因&#xff0c;我搭建的一个 Superset 的 Report 功能由于节假日期间不好控制邮件的发送&#xff0c;所以急需一个方案来替换掉 Superset 的 Report 功能 首先我们需要 Chrome 浏览器和 Chrome Driver&#xff0c;这是执行数据抓取的…

算法专题(三):二分查找

本篇还是像之前一样&#xff0c;以举例子的形式向大家讲解&#xff01;每道题的题目均是传送门&#xff01;点击跳转对应题&#xff01; 目录 一、二分查找 1.1 题目 1.2 思路 1.3 代码实现 总结&#xff08;模版&#xff09; 朴素版&#xff1a; 二、在排序数组中查找…

在k8s中部署一个可外部访问的Redis Sentinel

1.前提条件&#xff1a; 1.部署了multus 想要k8s外部能访问k8s内部的redis&#xff0c;redis-server启动时必须使用multus的IP 2.helm客户端安装 2.开始安装 准备3个multus ip 10.10.10.130 10.10.10.131 10.10.10.132 apiVersion: k8s.cni.cncf.io/v1 kind: NetworkAttac…