回顾2017

  过去的一年里,依然在做一些有趣好玩的事。这一年里写博客少了,拍照也少了,但想分享的东西还是很多。

  工作上作为项目负责人,顺利将Spring Cloud架构引入公司的项目中,并在稳定用于生产环境,说实话踩了不少坑,当然也收获了不少,这里感谢领导同事们的信任与支持。就喜欢这种优雅而又简单的解决方案,对新同事来说学习成本也不高。

  过去的一年里,通过各种共享汽车,车技提升不少,各种复杂路况也算是游刃有余了,安全驾驶上万公里,零事故。感谢TOGO,iGo这些“练车”平台。解锁了蛙泳技能,并在最近成功挑战了2公里,耗时1个半小时。宝体的3米恒温池确实舒服,下一步将解锁自由泳,由此跑步有所减少。

  和好友注册了一家公司,当是个人品牌建设,花图相册没有啥变化,毕竟PC端已不再是主流,“歌词Style”公众号却人气增加不少,现在4000多的粉丝,每天使用700~900不等,明显出现了周末高峰期,计划着升级到服务号以提供更好的服务。摄影器材上出掉了佳能全套,败入Sony A7rII,50F1.4,1635F4,轻装上阵。当然还有一台Fujifilm X70出售中,计划换“Mavic II”,一代的画质不太理想。为了能在离职后不中断社保和更加自由的职业规划,将户口迁入了深圳。加入房奴一族,购买了位于大亚湾碧桂园一处房产,环境优美,宜居大盘,期待明年交房后生活。

  暂且记得的就这么多了,所谓累并快乐着。不忘初心,牢记使命,未来我想更多的是幕后走向台前,多元结合,合作共赢,期待。

岭背汤,不被大众所知的银杏小村

一说到观赏银杏,回答最多的一定是广东省南雄市,这里有密集的银杏种植,位于著名景点帽子峰,也有千年古银杏群位于坪田镇。恰好到了这个季节,于是计划着自驾南雄之旅。显然人满为患的景区是不是这次首要考虑,介于上次揭阳梅海之行得出的重要结论,美景一般都是在深山老林。

一个偶然的机会得知坪田岭背汤这个小村庄也有不少的老银杏,并且居住在村里的人已经基本搬离了,甚至连进村路都没修好,仅有一条半山腰凿出来的泥路,却成了当地摄影发烧友的天堂。在搜索了各大导航App之后发现并没有这个叫【岭背汤】的地方,不过也没关系,到了之后当地人总该清楚。于是长途跋涉500公里入住了镇上一处民宿之后,和老板闲聊了一阵子,得知我们的目的后很大方的介绍了这个地方,还推荐了别的一些拍摄点,于是第二天的计划就顺利的加入岭背汤。

由于路不好走,坐着他们家的摩托车就过去了。后来发现其实不下雨,车也能进去,就是比较考验技巧,一般问题也不大。大体位置是在迳洞村往冯屋方向,过了冯屋景点再往里开大概10分钟路程。其实过了景点就已经看不到人了,沿途也没有指示牌。这个小村看起来都是建筑在山腰上,错落有致,银杏数量也超过我们想象,落在地上还沾着露水,没有踩踏过的痕迹,非常之美。

不过此次行程大部分时间都在跑长途上了,并且天气也不给力,并没有拍的尽兴。最大的收获还是在于找到这样一个地方,有机会明年再来。

本次行程一些照片都在花图相册:https://snapast.com/albums/steve/AFp6PhEHro7hZ5gYko4ydK

初代树莓派上使用ZeroTier LAN

  话说这个初代的Pi闲置也有好些年头了,一直通着电放在网络箱,没怎么用起来。这不最近发现有一个基于P2P网络的Private LAN工具:ZeroTier。正如他所描述的:A virtual networking layer that works the same everywhere。免费版本支持100个设备,基本也够用了。

  按官方的说明配置好Windows,MacOS,iOS都没问题,但最重要的要在Pi上用却遇到了点麻烦,通过apt官方仓库的方式安装,运行时会报段错误(Segmentation fault),可能是不支持debian stretch。想着既然开源的,不如直接编译一个,在官方Github上找到了源代码,make && make install之后一段漫长的等待之后,果然可以了,先用zerotier-one -d启动主程序,再join到创建的私有网络中,zerotier-cli join your_network_id,顺利加入到Lan中。

  注册,创建网络什么的就简单了,官网的操作后台也是既简洁又专业,记得新join的设备勾上Auth,测试了几天,延时还是比较大的,但稳定性不错,强力推荐。

漫长的深户之旅

  入了深户,只是为了更好的离开深圳,一切只是一个开始。

  一晃眼,也来深工作了六年多了,计划离职休息一段时间,一个很偶然的因素决定以个人申报方式将户口迁入深圳,于是开始了长达3个月之久的等待。入户方式有三种:调干、招/调工,在满足积分的情况下依个人情况具体选择,要说现在干部身份和工人身份究竟有多大区别,可能就好听点罢了。

  网上有各种各样关于身份的识别方式,然而汇总到一点就是全日制普通大中专院校毕业生就够了。正常情况下毕业后会有个学籍档案,并附有报到证一式两份,蓝色联贴在档案袋外面,白色联放在档案内。由于毕业后一直没处理档案,联系了学校档案处老师,约了个时间过来顺利拿到尘封6年之久的学籍档案,档案袋都破了,给换了个新袋子,重新贴上封条,顺便看了一眼当年的成绩,哎~~

  大概2014年之前的学籍档案放入人才市场或事业单位一年后会有转正定级的操作,这就是那些年的干部身份获取方式。然而这么多年过去了,随着国家政策的变化,已经在全国范围逐步取消转正定级,还包括档案托管费,以及干部介绍信之类的,很巧我们那就已经没有了。作为政策的衔接,深圳这边在认定干部也不再检查转正定级,然而对外的资料都还是说要这个。

  在了解相关背景后,本着人在哪,档案就在哪,有则全力争取的原则,在去年的11月23日签下代理协议的时候选择了以调干这种困难模式开始了入户之旅,就连办事人员对我这种档案居然还拿在自己手中的,能不能通过都还不确定。第二天便回家,将档案放在我们当地的人才市场。

  后面的步骤和大家就没什么区别了,很快的公示完了之后拿到商调函寄回家办理调档。随后的人社局审核一切顺利,并未要求补充什么资料。拿到调令去公安局办理户口准迁证,寄回家办理户口迁移证,同时原户口我那一页就被收上去了,想想还真有点舍不得。准迁证加迁移证就可以办理入户了,于是新的户口页打印出来。再办理新身份证,为了原证不被剪,可以选择邮寄。到此全部搞定。

  正如开头所说,一切只是一个开始。

Spring MVC之工作流程

  一直以来都是用Spring MVC做业务开发,却很少去了解内部的一些细节,虽然网上与天盖地的源码解读,然而却无多大印象,加上版本变化之快,不妨重读一次。这里以spring-webmvc 4.3.4为例,也正是花图所使用的版本,下面详细来看看。

  DispatcherServlet继承自FrameworkServlet,而FrameworkServlet又继承自HttpServletBean并实现了ApplicationContextAware,通过这样的一个分层结构,可以更清晰的知道处理流程。

  第一部分:初始化

  初始化分两个部分:一个是监听器部分:负责Root WebApplicationContext的初始化,包含通过XmlBeanDefinitionReader获取配置文件,进行加载,通过RequestMappingHandlerMapping扫描出带有RequestMapping注解的Controller,通过RequestMappingHandlerAdapter注册Controller,等等。

  另一部分是FrameworkServlet的初始化:抽象类HttpServletBean继承自HttpServlet,实现了父类的init()方法,获取init-param中的配置,对DispatcherServlet进行数据绑定。然后调用FrameworkServlet.initServletBean(),初始化webApplicationContext,并调用DispatcherServlet.onRefresh(),来完成初始化。

initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);

  从Bean中取出相应的Resolver,若没配置则取默认,默认的设置在DispatcherServlet.properties。

  第二部分:接收请求

  对于Java Web而言,都是通过web.xml中的Servlet来分配请求,一般的都会将”/”根请求配置为DispatcherServlet,这样所有的前端请求都会路由到Spring MVC。

  抽象类FrameworkServlet对Servlet的几个基本Http method方法进行了代理,统一通过processRequest()进行处理,并最终到达DispatcherServlet中的doDispatch()。

  doDispatch()中先检查request是否是multipart request,如果是,则用一个multipartResolver来包装request,判断依据是contentType是否是”multipart/”开头。

  找到HandlerExecutionChain用来处理当前的请求,方法是从已注册的mappingRegistry中找到最匹配请求URL路径的handlerMethod,这个即我们最熟悉的Controller,放在HandlerExecutionChain中。若HandlerExecutionChain为空,则没有匹配到任何处理流程,发送404响应,并报非常熟悉的一个日志:”No mapping found for HTTP request with URI XXX”。

  找到HandlerAdapter用来处理请求,从已加载的handlerAdapters中遍历出一个支持该Handler的HandlerAdapter,本例中的Handler即Controller。

  检查Http method是否为GET或者HEAD,如果是,进行last-modified验证,符合条件的发送304响应,并更新Last-Modified相关参数值。

  检查拦截器配置,遍历执行applyPreHandle(),并触发triggerAfterCompletion(),若有返回false的,则本次请求结束。

  一路顺利到此可以调用Controller中的方法了。

  对于Controller返回时ModelAndView没设置view的,给一个默认view。

  接着倒着遍历执行过滤器applyPostHandle(),执行完一个完整的拦截器链。

  接下来是进行ModelAndView的渲染,将ModelMap转化到request.setAttribute()中,并RequestDispatcher.include() or forward()到JSP中。

  最终完成本次请求。