让Struts 1焕发青春----小议对Struts的改造.

转载自:http://www.iteye.com/topic/93984

目前流行的新型的MVC框架 几乎都在"增强单元测试能力"上做了很多文章.
目的就是让 Controller 可以脱离web容器单独进行单元测试.

大多数采用的方法都是使 Controller 中的方法的参数 和 返回值 与 j2ee特有的类(如HttpXXXX)无关.

例如 传入的 是若干个 HashMap ,返回的是纯的字符串.
这样在单元测试的时候,只要new一个Controller,准备一些装有测试数据的Map,
然后执行相关的Controller方法,再然后看看返回值,就可以了.

显然,流行的Struts 1.X (>=1.2) 是不具备这样的特性的.
如果您现在有一个遗留的使用Struts 1.X(>=1.2) 的系统,或者因为人力资源的原因,不能立即引入ww或Struts2.x,
那么如何让您的Action类似的可测性了呢(其实除了可测试性,这样的改进往往也会大大的减少代码的开发量).

下面我就把我的一些心得写出来,和大家一起分享,欢迎大家拍砖.
注意:以下讨论均以改造 DispatchAction 为例(不使用DispatchAction的情况应该比较少吧 呵呵).

先说明一下改造的目的:
简化开发,减少代码量
增强单元测试能力
全面兼容现有DispatchAction(这点很重要)

一个传统的 DispatchAction 代码结构

代码
  1. public class MyClassicAction extends DispatchAction {   
  2.     // 一个添加user的操作   
  3.     public ActionForward addUser(ActionMapping mapping, ActionForm form,   
  4.             HttpServletRequest request, HttpServletResponse response)   
  5.             throws Exception {   
  6.         // 1 取得参数 ...   
  7.         // 2 执行相关的BO DAO方法   
  8.         // 3 取得执行后的返回值   
  9.            
  10.          if (添加成功) {   
  11.             return mapping.findForward("Succee");   
  12.          }   
  13.            
  14.         return mapping.findForward("Fail");   
  15.         // 还有一种情况是 返回 new ActionForward(....).   
  16.     }   
  17. }   
<script>render_code();</script>
对于上面的代码大家再熟悉不过了,我们现在要做的事情就是通过改造,让上面的action方法变成下面的样子.

 

 

代码
  1. // 一个添加user的操作   
  2. public String addUser( 传入与web容器无关的参数 )   
  3.         throws Exception {   
  4.     // 1 取得参数 ...   
  5.     // 2 执行相关的BO DAO方法   
  6.     // 3 取得执行后的返回值   
  7.        
  8.      if (添加成功) {   
  9.         return "Succee";   
  10.      }   
  11.        
  12.     return "Fail";   
  13.     // 对于action中返回 new ActionForward(....).的情况,我们可以设定另外一种特殊的前缀.   
  14.     // 例如  return "NEW:/addUserFail.jsp";   
  15. }   
<script>render_code();</script>

 

上面的代码是一个最终的目标,我们先一步一步来.
首先这种改造我们一定要从 Struts 提供的 DispatchAction 做起.
总体思路很明显:
自己写个类 继承 DispatchAction ,并且添加或修改一些方法, 然后项目中的其他的DispatchAction都继承这个自己写的DispatchAction类.

简单说一下Struts 的 DispatchAction的工作流程.

首先 它也是一个action,所以请求来了 他会自动去执行execute方法
execute方法内部做一些操作后,会调用 dispatchMethod 方法
dispatchMethod方法会,调用getMethod方法 来取得欲执行的方法,并且执行之

改造的核心就是围绕最关键的dispatchMethod方法.
而由于我们要"全面兼容现有DispatchAction",所以对于getMethod我们不做修改,而是选择增加一个类似的方法 getMethodTD

首先写一个新的 .DispatchAction基类,他继承org.apache.struts.actions.DispatchAction.

 

代码
  1.   
  2. public class TDDispatchAction extends DispatchAction {   
  3.        
  4.     protected Class[] typesTD = {TDServletWrapper.class };   
  5.        
  6.     protected final static String TD_METHODCACHE_PREFIX="TD_METHODCACHE_PREFIX";   
  7.     protected final static String TD_DEFAULT_METHOD="defaultMethod";   
  8.     protected final static String TD_NEW_FORWARD_PREFIX="NEW:";   
  9.        
  10.   
  11.        
  12.     protected Method getMethodTD(String name)   
  13.         throws NoSuchMethodException {   
  14.   
  15.         String cacheKey=TD_METHODCACHE_PREFIX+name;   
  16.            
  17.         synchronized (methods) {   
  18.             Method method = (Method) methods.get(cacheKey);   
  19.   
  20.             if (method == null) {   
  21.                 method = clazz.getMethod(name, typesTD);   
  22.                 methods.put(cacheKey, method);   
  23.             }   
  24.   
  25.             return (method);   
  26.         }   
  27.     }   
  28.   
  29. // 对unspecified这个方法的扩展实际上与本次讨论无关,只是为我们框架加的一个小功能   
  30. // 提供一个默认的方法.也就是说,如果您的action中,有名为 defaultMethod 的方法,那么无需在请求的参数中加入相关信息,就会自动执行.   
  31.     protected ActionForward unspecified(ActionMapping mapping, ActionForm form,   
  32.             HttpServletRequest request, HttpServletResponse response)   
  33.             throws Exception {   
  34.         return dispatchMethod(mapping, form, request, response, TD_DEFAULT_METHOD);   
  35.     }   
  36.   
  37.    protected ActionForward dispatchMethod(ActionMapping mapping,   
  38.         ActionForm form, HttpServletRequest request,   
  39.         HttpServletResponse response, String name)   
  40.         throws Exception {   
  41.     // 后面再说   
  42.   
  43.     }   
  44.  }   
  45.   
<script>render_code();</script>

 

首先来看一下这个 getMethodTD 方法.
原getMethod(String name)方法的作用是:
取得一个名字为name,参数类型为 ActionMapping , ActionForm , HttpServletRequest ,HttpServletResponse 的一个方法.

而这个新写的 getMethodTD 方法的作用是:
取得一个名字为name,参数类型为 TDServletWrapper 的一个方法.
这个大家和 原getMethod一对比就能看出来作用和差异了,注意那个cacheKey=TD_METHODCACHE_PREFIX+name,这个是必须的,不能直接用name做key.

其中这个 TDServletWrapper 就是封装的一个"可以"与web容器脱离的对象,注意,这里用的是可以.而不是直接就脱离了.
对此后面再做解释.

要添加的 getMethodTD 方法添加完了.
下面来看一下重点 改造 dispatchMethod方法:

原dispatchMethod方法的流程其实很简单:
1 调用 getMethod(name)方法,取得真正要执行的方法 并执行(传入的参数类型为 ActionMapping , ActionForm , HttpServletRequest ,HttpServletResponse ).
2 返回 方法执行的 结果 (返回结果类型为 ActionForward ).

改造后的目的是:

1 调用 getMethodTD(name)方法,取得真正要执行的方法并执行(传入的参数类型为TDServletWrapper ).
2 取得 方法执行的 结果 (返回结果类型为 String ).
3 根据这个返回的String, 创建ActionForward对象,并返回

但是我们还有一个目的是"兼容",所以最后确定的流程是

1 调用 getMethod(name)方法,取得真正要执行的方法,如果取得,则按原方式继续.
2 如果没有取得 调用 getMethodTD(name)方法,取得真正要执行的方法,若取得则执行.
3 根据返回结果的类型不同 做不同的操作.

 

代码
  1. // 改造的 dispatchMethod方法,重点看 //TD...字样所在的代码段   
  2.     protected ActionForward dispatchMethod(ActionMapping mapping,   
  3.         ActionForm form, HttpServletRequest request,   
  4.         HttpServletResponse response, String name)   
  5.         throws Exception {   
  6.   
  7.         if (name == null) {   
  8.             return this.unspecified(mapping, form, request, response);   
  9.         }   
  10.   
  11.         Method method = null;   
  12.            
  13. //TD:   Modified by Wei Zijun   
  14.         boolean useTDMethod=false;   
  15.         try {   
  16.             // 按传统方式取 action方法   
  17.             method = getMethod(name);   
  18.         } catch (NoSuchMethodException ex) {   
  19.             try {   
  20.             // 没取到的话 再按新方式取 action方法   
  21.              method = getMethodTD(name);   
  22.              // 标识一下到底是使用的原始方法 还是新方法   
  23.              useTDMethod=true;   
  24.             } catch (NoSuchMethodException e) {   
  25.                 String message =   
  26.                     messages.getMessage("dispatch.method", mapping.getPath(), name);   
  27.        
  28.                 log.error(message, e);   
  29.        
  30.                 String userMsg =   
  31.                     messages.getMessage("dispatch.method.user", mapping.getPath());   
  32.                 throw new NoSuchMethodException(userMsg);   
  33.             }   
  34.                
  35.         }   
  36.   
  37.         ActionForward forward = null;   
  38.   
  39.         try {   
  40. //TD:   Modified by Wei Zijun   
  41.             Object forwardR=null;   
  42.                
  43.             if (!useTDMethod){   
  44.             // 如果是原始 action 方法   
  45.                 Object[] args = { mapping, form, request, response };   
  46.                 forwardR= method.invoke(this, args);   
  47.                    
  48.             }else{   
  49.             // 如果是新 action 方法   
  50.                 // TDServletWrapperFactory.getInstance创建一个封装的对象   
  51.                 Object[] args = { TDServletWrapperFactory.getInstance(mapping, form,request, response) };   
  52.                 forwardR= method.invoke(this, args);   
  53.             }   
  54.                
  55.             if (forwardR!=null){   
  56.                 if (forwardR instanceof ActionForward){   
  57.                     forward=(ActionForward)forwardR;   
  58.                 }else{   
  59.                     // 如果返回值是字符串:   
  60.                     // 以 TD_NEW_FORWARD_PREFIX("NEW:")开头,则new ActionForward;   
  61.                     // 否则 调用 mapping.findForward.   
  62.                     if ( (String.valueOf(forwardR) ).toUpperCase().startsWith(TD_NEW_FORWARD_PREFIX)){   
  63.                         forward = new ActionForward(String.valueOf(forwardR).substring(TD_NEW_FORWARD_PREFIX.length()) );   
  64.                     }else{   
  65.                         forward = mapping.findForward(String.valueOf(forwardR));   
  66.                     }   
  67.                 }   
  68.             }   
  69.                
  70.         } catch (ClassCastException e) {   
  71.             String message =   
  72.                 messages.getMessage("dispatch.return", mapping.getPath(), name);   
  73.   
  74.             log.error(message, e);   
  75.             throw e;   
  76.         } catch (IllegalAccessException e) {   
  77.             String message =   
  78.                 messages.getMessage("dispatch.error", mapping.getPath(), name);   
  79.   
  80.             log.error(message, e);   
  81.             throw e;   
  82.         } catch (InvocationTargetException e) {   
  83.   
  84.             Throwable t = e.getTargetException();   
  85.   
  86.             if (t instanceof Exception) {   
  87.                 throw ((Exception) t);   
  88.             } else {   
  89.                 String message =   
  90.                     messages.getMessage("dispatch.error", mapping.getPath(),   
  91.                         name);   
  92.   
  93.                 log.error(message, e);   
  94.                 throw new ServletException(t);   
  95.             }   
  96.         }   
  97.   
  98.         return (forward);   
  99.     }   
  100.   
<script>render_code();</script>

 

这样做完后,我们的action就可以支持下面的任何一种类型的方法了:

 

代码
  1. // 传统方式   
  2. public ActionForward testSubmit(ActionMapping mapping,ActionForm form, HttpServletRequest request,HttpServletResponse response) throws Exception;   
  3.   
  4. // 新方式   
  5. public String testSubmit1(TDServletWrapper servletWrapper) throws Exception;   
  6.   
  7. // 以下两者是则中方式.   
  8. public String testSubmit2(ActionMapping mapping,ActionForm form, HttpServletRequest request,HttpServletResponse response) throws Exception;   
  9.   
  10. public ActionForward testSubmit3(TDServletWrapper servletWrapper) throws Exception;   
  11.   
<script>render_code();</script>

 

下面简单说一下 TDServletWrapper .
这个类实际上就是封装了 ActionMapping mapping,ActionForm form, HttpServletRequest request,HttpServletResponse response,
同时提供了一些用来简化开发的方法,例如

public String getParameter(String key,String ifNull);
public Integer getIntegerParameter(String key);
public void bind(Object bean);
等等.
不再具体叙述,看一下代码就应该都明白了.

TDServletWrapper 是一接口,他有两个实现:
TDServletWrapperTestImpl 为测试专用的,是一个与web容器无关的实现.
他主要就是利用"模拟"request response的方式来处理
目前他的实现还不是很完全(例如没有模拟session等),大家可以根据自己的需求来补充.

大家也可以根据这个原理 不用TDServletWrapper ,而完全的使用Map等,
总之,通过dispatchMethod的改造,几乎可以实现你全部的合理的想法(别超出Struts1的能力极限就行),我在这里提供的只是一个思路而已.

经过上面的改造,一个可测试的action就诞生了:

 

代码
  1. public class MyClassicAction extends TDDispatchAction {   
  2.     // 一个添加user的操作   
  3.     public ActionForward addUser(TDServletWrapper servletWrapper) throws Exception {   
  4.            
  5.         UserInfoVO vo=new UserInfoVO();   
  6.         servletWrapper.bind(vo);   
  7.        
  8.         // 校验,并执行相关DAO操作   
  9.   
  10.          if ( 添加成功 ) {   
  11.             return "Succee";   
  12.          }   
  13.            
  14.         return "Fail";   
  15.   
  16.     }   
  17. }   
<script>render_code();</script>

 

测试的可以这样写:

代码
  1. public static void testAddUser() throws Exception {   
  2.     // 创建一个action对象   
  3.   
  4.     TestAction test=new TestAction();   
  5.   
  6.     //准备一些测试数据 放入map里,模仿request的Parameters   
  7.     Map requestParameters=new HashMap();   
  8.        
  9.     requestParameters.put("userName", "asdqe");   
  10.     requestParameters.put("password", "mghjghj");   
  11.     requestParameters.put("userRole", "1");   
  12.     requestParameters.put("email", "ut_email");   
  13.     requestParameters.put("gender", "1");   
  14.     requestParameters.put("age", "22");   
  15.        
  16.     // TDServletWrapperFactory.getTestInstance 方法用来取得一个脱离web容器的 TDServletWrapper对象 )   
  17.     Assert.isTrue("Sucess".equals(test.testSubmit1(TDServletWrapperFactory.getTestInstance(requestParameters))) );   
  18.   
  19. }   
<script>render_code();</script>

 

至此全部改造完毕.
这样的改造对于大家来说也许没什么意义,不过多少也是一种尝试.
由于Struts1.X(>=1.2)的 局限性,以及我个人能力的局限性,很多改造还不够彻底 不够合理,
对于前者我无能为力,对于后者,还恳请大家多多指正我的不足 谢谢了先.

上次发了一篇对 spring jdbctemplate的改造,这次又发了一篇对 struts 1 的改造,
(其实我那个ecside 也是对老版extremecomponents的改造)
也许很多人会觉得我太落伍了,这些老掉牙的东西还改造个什么劲儿啊 呵呵
其实,很多时候,对于我来说,改造就是一个智力游戏,在一些现有的东西实现一些自己的新的想法,这样的感觉总是快乐的.

而且,我们的项目就是用的老技术 老东西,在不能换新东西的情况下,努力的挖掘现有东西的剩余价值,也许更具有实际意义.

AFSim 2.9中文手册实战指南:从安装到高级功能详解(附B站视频链接) 本文详细介绍了AFSim 2.9的安装配置、基础功能操作和高级功能应用,帮助用户快速掌握这款强大的仿真平台。从系统要求检查到多体动力学仿真,再到性能优化技巧,全面解析AFSim 2.9的使用方法,适合初学者和进阶用户参考。 阅读详情

相关推荐

在MCUXpresso上通过J-link烧写KW45

背景: KW45B是nxp 的ble npi产品,其使用的IDE为MCUXpressso或者IAR,MCUXpressso有一些bug,但是由于MCUXpressso阅读查找代码相对IAR容易,所以我经常使用的MCUXpressso进行开发。当然IAR也有其优势,比如编译、烧写速度相对MCUXpressso更快,是比较通用的一个平台,而MCUXpressso是NXP独有的。 Kw45有两种烧写方式,一是使用MCUlink,二是使用Jlink。NXP ble 产品线提供的IDE资料里有最新的15.0版本的

weixin_41889258的博客 2859

struts 1的优缺点

优点: 1)、实现了MVC模式,结构清晰,是开发者只关注业务逻辑的实现,实现了表现与逻辑分离 2)、提供了一套丰富的标签,能大大提高开发效率 3)、页面导航:使系统的脉络更加清晰。通过struts-config.xml配置文件,即可把握整个系统各部分之间的联系,这对后期的维护有利。尤其是对接手遗留项目,优势更明显 4)、维护扩展方便 5)、便于团队开发 6)、提供Exception处理

lixin032190的专栏 862

自动驾驶车辆纵向控制实战:基于CARLA与ROS2的PID算法实现

本文详细介绍了基于CARLA与ROS2的自动驾驶车辆纵向控制实战,重点讲解了PID算法的实现与应用。通过搭建仿真环境、实现定速巡航和自适应巡航功能,帮助开发者掌握自动驾驶核心技术。文章还分享了系统集成、仿真验证及常见问题的调试技巧,为自动驾驶开发提供实用指导。

weixin_33728708的博客 354

struts2 对外实现接口步骤

1.在struts.xml文件中配置路径映射的Action 图中 1 为定义的url,2为要映射的Action,result标签可以控制执行完Action中的逻辑后跳转的页面, 例如:“http://localhost:8080/imCroePlatform/get/custOrderLogistics?commonRegionId=123&extCustOrderId=12...

weixin_42095871的博客 6341

实战避坑:泛微E9流程接口与单点登录(SSO)开发全解析(含自定义Action、Restful API与免密登录)

本文深入解析泛微E9流程接口与单点登录(SSO)开发中的关键技术与避坑指南,涵盖自定义Action开发、Restful API鉴权机制及跨系统单点登录实现。通过实战案例与代码示例,帮助开发者高效完成二次开发,解决系统集成中的数据孤岛问题,提升企业信息化建设效率。

weixin_42549451的博客 672

高速PCB设计中的信号完整性挑战:回损、插损与串扰的深度解析

本文深入探讨了高速PCB设计中的信号完整性挑战,重点分析了回损、插损与串扰三大关键问题。通过实际案例和专业技术解析,提供了降低回损的阻抗控制技巧、减少插损的板材选择建议以及抑制串扰的布线策略,帮助工程师应对5G和AI芯片时代的高速信号传输需求。

websocket5live的博客 262

struts restful风格http接口

新需求告一段落,总结一下前阶段问题。在这之前不得不吐槽一下,明明之前说好的远程接口服务用dubbo+zookeeper,都tm写完了又说不用了,换http接口,换就换吧还什么restful,记得上次接触restful还是n年前在的mvc下面用过,现在乍一听在struts下面用这个,差点愁坏了某些人,因为组里没一个人用过,无奈花了大半天各种论坛各种逛,但是这方面的资料寥寥无几,而且各个帖子的最终来源

linge-的博客 5302

中小团队必看!用Harbor+Jenkins打造轻量级K3S交付流水线(含成本优化方案)

本文为中小团队详细介绍了如何利用K3S、Harbor与Jenkins构建一套高性价比的自动化交付流水线。内容涵盖单节点K3S集群的效能配置、Harbor镜像仓库的磁盘空间优化、Jenkins共享库实现流水线复用,并提供了完整的实战案例与成本优化方案,帮助团队以较低成本快速落地高效的自动化部署。

weixin_29223603的博客 503

再改struts1.2.*,让struts1再焕发表春,支持Annotation和POJO风格的Action

最近有个项目改造,经过一番讨论后决定使用struts1,为什么不是struts2,呵呵,不是由我一个人说了算了,因为使用习惯了struts2,回头再来用struts1,感觉非常不爽,所以打 算对其进行改造一番,在网上也搜了一些关于struts1改造,都只是支持了Annotation配置和从IOC中取Action,我的改造目标在此之上加了一些,主要有: 1.支持基于Annotation的...

sam1982 191

某易将军令口令纯算法还原py篇

算法开源,将军令口令算法。希望官方多多更新吧

任雪飘的博客 3909

100天精通Oracle-实战系列(第14天)Veritas NetBackup(NBU) 配合 RMAN 脚本备份 Oracle 数据库

100天精通Oracle-实战系列(第14天)Veritas NetBackup(NBU) 配合 RMAN 脚本备份 Oracle 数据库

LuciferLiu_DBA 1992

Struts 1简介及存在的问题_转载自李刚《Struts 2.1权威指南》

1.2.1 Struts 1简介及存在的问题 从过去的岁月来看,Struts 1是所有MVC框架中不容辩驳的胜利者,不管是市场占有率,还是所拥有的开发人群,Struts 1都拥有其他MVC框架不可比拟的优势。Struts 1的成功得益于它丰富的文档、活跃的开发群体。当然,Struts 1是世界上第一个发布的MVC框架:Struts 1.0在2001年6月发布,这一点可能是使它得...

190

Jetson AGX Orin平台Jetpack5升级Jetpack6,自研载板USB不工作问题调试

1. 问题描述将AGX Orin工业的新板放到定制的载板上当在ubuntu 22和Jetpack 6.1上使用SDKManager刷机时,lsusb为空,并且在引导时没有发现/注册xusb设备。USB C端口正常打开并供电,但似乎没有数据传输。在Ubuntu 18 Jetpack 5.1.4 SDKManager上同样刷机之后,usb C集线器在引导时被正确地发现,显示在lsusb中,并识别/挂载外部驱动器。

jzwjzw19900922的博客 1509

struts模拟struts2改造

看了论坛里一篇文章, 很有些启发: [url]http://www.iteye.com/topic/93984[/url]让Struts 1焕发青春----小议对Struts的改造 经过改造之后,struts也变成跟struts2有些类似: 1. action的方法参数中没有了request, response, 剥离了跟web容器的耦合, 于是变得能够方便测试。 2. action方法能...

spiritfrog 208

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

【Unity知识速递】第7期 Unity 6.6 深度解析:WebGPU 正式版与 WebAssembly64,Web 游戏的内存革命

如何启用 Wasm64?需要注意的是,Wasm64 需要浏览器支持 Memory64 提案,目前主流浏览器的最新版本都已支持。一句话总结: Unity 6.6 用 WebGPU 转正、Wasm64 16GB 内存和渐进式加载三连击,将 Web 游戏的体验天花板又往上推了一大截——Web 平台不再是"妥协版",正在成为真正的一等公民。Unity 6.6 新增了 WebAssembly64(Wasm64) 构建选项,这可能是本次更新中对 Web 游戏开发者影响最大的一项升级。

crystal_WJT的博客 412

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

我给 DeepSeek 大肥鱼做了一款马里奥式闯关游戏

当全网都在把大肥鱼做成直播虚拟主播的时候,我决定给她做一款横版闯关游戏。

2301_79028807的博客 886

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

节日小游戏活动快速落地与高效转化技巧

比如,把平时验证过的消消乐、大转盘、接红包等经典玩法作为底子,只需要针对特定的节日(像春节、中秋)或者门店庆典,快速替换一套应景的UI素材和文案,再植入专属的节日福利。这种“旧瓶装新酒”的方式,能把开发周期压缩到极致,确保活动能踩着流量的爆发点准时上线。特别是对于实体门店,游戏通关后的奖励最好能直接引导到线下核销,比如“满100减20”的优惠券,这样能把线上的流量精准地拽进店里,形成实实在在的成交。这种基于熟人关系的推荐,比硬广有效得多,能迅速在朋友圈和社群里铺开,实现低成本的流量获取。

Hxzyxkf2016的博客 180

Claude Code做Unity项目移植(三): 使用SKILL进行自动化Git提交

本文介绍基于ClaudeCodeSkill的Unity自动化Git提交助手——unity-git-commit,通过四大模块实现提交前的智能检查与安全修复:1)校验Git仓库状态;2)验证Unity项目文件(如排除Library/Temp,确保.meta文件完整);3)依据可配置的C#命名规范自动识别并安全修复命名问题(避免序列化、反射等高危场景误改);4)生成精准提交注释,但强制人工确认。全程仅本地commit,不自动推送或破坏性操作,保障安全。核心思想为“机械化工作交AI,关键决策留人工”,显著提升提交

Aman1984的博客 639

PyBox有什么好用的数织小游戏吗?

我们今天梳理了市面上主流的数织解谜工具,包括在线网站和本地应用,并且重点介绍了 PyBox 中的数织 Nonogram。在线解谜网站**适合快速上手、免费体验,但不适合长时间离线使用。本地解谜工具**提供了离线体验,方便专注解题,但可能会涉及安装和广告问题。PyBox的数织 Nonogram**则在绿色无广告、免安装、完全开源、及DIY定制等方面有着明显优势,非常适合想要一个干净、可控、无干扰的游戏环境的用户。如果你追求极致无干扰、注重隐私、喜欢轻量应用,那么数织 Nonogram 绝对值得一试。

Sanmsung的博客 123

Windows远程桌面实现之二十:实现 moonlight 游戏串流协议之(一)

本文介绍在xdisp_virt中集成Moonlight游戏串流协议的实现过程。基于开源项目Sunshine逆向的GameStream协议,作者从零研究并自研实现Moonlight服务端网络通信部分,涵盖mDNS设备发现、nvhttp/nvhttps加密通信、nvrtsp协议交互及三类UDP数据通道(视频、音频、控制)。重点解决多客户端并发问题,采用串行处理机制确保连接一致性。通过自定义证书验证与密钥管理,完成配对与安全通信,最终实现低延迟、高性能的游戏串流体验,显著优于WebRTC浏览器方案。

fanxiushu的专栏 164

二进制木马免杀-游戏嵌套与图标替换

二进制木马免杀-游戏嵌套与图标替换

2503_94637398的博客 232

Unity UGUI布局组件(LayoutGroup、ContentSizeFitter、LayoutElement)详解

UGUI 自动布局是一套的 C# 排版系统,和 Transform 原生锚点完全隔离;整套布局;最终输出只有对 RectTransform 的sizeDelta的赋值。

m0_63330263的博客 257

mark 黑塞《玻璃球游戏》

【最近在读黑塞的《玻璃球游戏》-“ai预言“ - 优快云 App】

2502_94306440的博客 187

Cocos Creator3.8.8 + Xcode27.2 编译运行失败

Cocos Creator3.8构建IOS工程后,使用最新的Xcode27.2版本进行真机调试,遇到C++标准库冲突,如is_floating_point、is_arithmetic和is_signed失败,以及webp对齐失败等问题。

HYNN12的博客 351

Kubernetes 的资源开销、适用边界,以及游戏与有状态负载的工程现实

Kubernetes 不是成本优化工具,而是一把运维规模化的杠杆。它的核心价值在于:当工作负载足够异构、运维复杂度足够高、团队能力足够匹配时,将原本指数级增长的运维成本拉回线性甚至常数级。它通过精细调度回收资源碎片,通过声明式 API 消除手工操作的边际成本,通过生态工具链实现跨环境的一致性。但杠杆的前提是——你得有足够重的负载来撬动。游戏服务端:外围服务放心上 K8s,对局制专用服用 Agones,持久世界要慎之又慎,核心战斗别把容器当虚拟机用。数据库。

神棍之路 264

【Unity基础】Unity输入与Time.deltaTime的使用原则

本文从 mousePositionDelta 与 GetAxis 出发,分析了Time.deltaTime的使用原则

Tealc 360
上一篇: Java相对路径/绝对路径总结
下一篇: 揭秘jbpm流程引擎内核设计思想及构架
wang20051
博客等级 码龄22年 2粉丝 3原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值