首页 文章 精选 留言 我的

精选列表

搜索[编写],共10005篇文章
优秀的个人博客,低调大师

SpringBoot2编写第一个Controller,响应你的http请求并返回结果

前言: SpringBoot的Controller模块用的是SpringMvc,所以用法和MVC没有差异。 本文章主要讲解 1.如何接收一个请求 2.如何获取请求的参数 3.常用的两种返回值JSONObject和ModelAndView 4.GET请求和POST请求 5.获取路径参数 6.HttpServletRequest和HttpServletResponse对象 编码 1.新建一个HelloController.java类,代码和结构如图所示 java 复制代码 package org.xujun.springboot.controller; import org.springframe...

优秀的个人博客,低调大师

编写hive udf函数

大写转小写 package com.afan; import org.apache.hadoop.hive.ql.exec.UDF; import org.apache.hadoop.io.Text; public class UDFLower extends UDF{ public Text evaluate(final Text s){ if (null == s){ return null; } return new Text(s.toString().toLowerCase()); } } 1、加载udf jar包 afan@ubuntu:/usr/local/hadoop/hive$ bin/hive Hive history file=/tmp/afan/hive_job_log_afan_201105150623_175667077.txt hive> add jar udf_hive.jar; Added udf_hive.jar to class path Added resource: udf_hive.jar 2、创建udf函数 hive> create temporary function my_lower as 'com.afan.UDFLower'; OK Time taken: 0.253 seconds 3、创建测试数据 hive> create table dual (info string); OK Time taken: 0.178 seconds hive> load data local inpath 'data.txt' into table dual; Copying data from file:/usr/local/hadoop/hive/data.txt Copying file: file:/usr/local/hadoop/hive/data.txt Loading data to table default.dual OK Time taken: 0.377 seconds hive> select info from dual; Total MapReduce jobs = 1 Launching Job 1 out of 1 Number of reduce tasks is set to 0 since there's no reduce operator Starting Job = job_201105150525_0003, Tracking URL = http://localhost:50030/jobdetails.jsp?jobid=job_201105150525_0003 Kill Command = /usr/local/hadoop/bin/../bin/hadoop job-Dmapred.job.tracker=localhost:9001 -kill job_201105150525_0003 2011-05-15 06:46:05,459 Stage-1 map = 0%,reduce = 0% 2011-05-15 06:46:10,905 Stage-1 map = 100%,reduce = 0% 2011-05-15 06:46:13,963 Stage-1 map = 100%,reduce = 100% Ended Job = job_201105150525_0003 OK WHO AM I HELLO worLd Time taken: 14.874 seconds 4、使用udf函数 hive> select my_lower(info) from dual; Total MapReduce jobs = 1 Launching Job 1 out of 1 Number of reduce tasks is set to 0 since there's no reduce operator Starting Job = job_201105150525_0002, Tracking URL = http://localhost:50030/jobdetails.jsp?jobid=job_201105150525_0002 Kill Command = /usr/local/hadoop/bin/../bin/hadoop job-Dmapred.job.tracker=localhost:9001 -kill job_201105150525_0002 2011-05-15 06:43:26,100 Stage-1 map = 0%,reduce = 0% 2011-05-15 06:43:34,364 Stage-1 map = 100%,reduce = 0% 2011-05-15 06:43:37,484 Stage-1 map = 100%,reduce = 100% Ended Job = job_201105150525_0002 OK who am i hello world Time taken: 20.834 seconds 本文转自 SimplePoint 51CTO博客,原文链接:http://blog.51cto.com/2226894115/1897838,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android makefile编写基础

首先来看一个简单的Android makefile,这个是我上篇文章写的,重新摘出来: LOCAL_PATH:=$(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := eng LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog LOCAL_SRC_FILES:= \ ppp.c LOCAL_MODULE:= PPPreboot include $(BUILD_EXECUTABLE) #include $(BUILD_SHARED_LIBRARY)下面对上面的语法进行解析: LOCAL_PATH:=$(call my-dir) 定义了当前模块的相对路径 include $(CLEAR_VARS) 清空了当前的环境变量 LOCAL_MODULE_TAGS := eng 指定模块在eng模式下才进行编译 LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog 编译需要的库 LOCAL_SRC_FILES:ppp.c 编译所需要的目标源文件,一般都是当前目录下,或者依赖于其它目录下 LOCAL_MODULE:= PPPreboot 编译生成该目标的名称,也就是最终的可执行文件 include $(BUILD_EXECUTABLE) 编译所生成的目标的文件格式 其中: my-dir在build/core/definitions.mk定义CLEAR_VARS在build/core/config.mk定义 以下就是我之前写的源码,简单的功能。 #include <stdio.h> #include <stdlib.h> #include <android/log.h> #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "keymatch", __VA_ARGS__) int main(void) { int i ; freopen("/dev/ttyMT0", "a", stdout);setbuf(stdout, NULL); freopen("/dev/ttyMT0", "a", stderr);setbuf(stderr, NULL); LOGD("YYX---->reboot system!!!!!!--->201612.1\n"); system("reboot"); return 0 ; } makefile还有源码写完以后我们就可以进行编译了。首先,需要source build/envsetup.sh 初始化参数和环境变量接着lunch 对应的平台的版本接着就拥有了mm、mmm等编译工具然后对刚刚写的程序进行手动编译: mmm external/test/ 编译成功信息如下: PLATFORM_VERSION_CODENAME=REL PLATFORM_VERSION=6.0 TARGET_PRODUCT=em_t8350_emmc TARGET_BUILD_VARIANT=eng TARGET_BUILD_TYPE=release TARGET_BUILD_APPS= TARGET_ARCH=arm TARGET_ARCH_VARIANT=armv7-a-neon TARGET_CPU_VARIANT=cortex-a7 TARGET_2ND_ARCH= TARGET_2ND_ARCH_VARIANT= TARGET_2ND_CPU_VARIANT= HOST_ARCH=x86_64 HOST_OS=linux HOST_OS_EXTRA=Linux-3.13.0-32-generic-x86_64-with-Ubuntu-14.04-trusty HOST_BUILD_TYPE=release BUILD_ID=MRA58K OUT_DIR=out ============================================ PRODUCT_COPY_FILES frameworks/base/data/sounds/effects/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg ignored. PRODUCT_COPY_FILES device/pskyed/em_t8350_emmc/init.recovery.mt8127.rc:root/init.recovery.mt8127.rc ignored. PRODUCT_COPY_FILES device/pskyed/em_t8350_emmc/handheld_core_hardware.xml:system/etc/permissions/handheld_core_hardware.xml ignored. make: Entering directory `/mnt/sdb1/yangyx/MT8127_t8350_Debug_Camera_20161121/MT8127_M0_MP8_0407_for_t8350' build/core/Makefile:614: warning: overriding commands for target `out/target/product/em_t8350_emmc/boot.img' build/core/Makefile:39: warning: ignoring old commands for target `out/target/product/em_t8350_emmc/boot.img' No private recovery resources for TARGET_DEVICE em_t8350_emmc Import includes file: out/target/product/em_t8350_emmc/obj/EXECUTABLES/PPPreboot_intermediates/import_includes target thumb C: PPPreboot <= external/test/ppp.c target Executable: PPPreboot (out/target/product/em_t8350_emmc/obj/EXECUTABLES/PPPreboot_intermediates/LINKED/PPPreboot) target Unpacked: PPPreboot (out/target/product/em_t8350_emmc/obj/EXECUTABLES/PPPreboot_intermediates/PACKED/PPPreboot) target Symbolic: PPPreboot (out/target/product/em_t8350_emmc/symbols/system/bin/PPPreboot) Export includes file: external/test/Android.mk -- out/target/product/em_t8350_emmc/obj/EXECUTABLES/PPPreboot_intermediates/export_includes target Strip: PPPreboot (out/target/product/em_t8350_emmc/obj/EXECUTABLES/PPPreboot_intermediates/PPPreboot) Install: out/target/product/em_t8350_emmc/system/bin/PPPreboot make: Leaving directory `/mnt/sdb1/yangyx/MT8127_t8350_Debug_Camera_20161121/MT8127_M0_MP8_0407_for_t8350' #### make completed successfully (2 seconds) #### 输出的文件就在out/target/product/em_t8350_emmc/system/bin/PPPreboot,用什么手段执行它都可以,就看你的需求了。

优秀的个人博客,低调大师

如果编写 if 时不带 else

来源:翻译自:Nicklas Millard的文章《Better Software Without If-Else》 注:本文并不肯定或者否定哪一种写法,仅仅为大家提供一些其他的编码思路或者一些值得借鉴的点子,希望大家能在公众号的每一篇文章中都能有所收获,同时欢迎探讨! 设计更好的软件,替换If-Else的5种方法。入门到高级示例 让我直接说这句话:If-Else通常是一个糟糕的选择。 它导致设计复杂,代码可读性差,并且可能导致重构困难。 但是,If-Else已成为事实上的代码分支解决方案,这确实是有道理的。这是向所有有抱负的开发人员讲授的第一件事。不幸的是,许多开发人员从来没有前进到更合适的分支策略。 有些人的口头禅是:If-Else是一把锤子,一切都是钉子。 无法区分何时使用更合适的方法是区分大三学生和大三学生的原因之一。 我将向您展示一些技巧和模式,这些技巧和模式将终结这种可怕的做法。 每个示例的难度都会增加。 Tips:历史文章 PDF,关注微信公众号 Java后端 回复 666 下载。 1.完全不必要的Else块 这也许是那些初级开发人员最负罪的之一。下面的示例很好地说明了当您被认为If-Else很棒时会发生什么。 > Simple if-else 只需删除else`块即可简化此过程。 > Removed else 看起来更专业吧? 您会经常发现,实际上根本不需要其他块。像在这种情况下一样,您想要在满足特定条件的情况下执行某些操作并立即返回。 2. 价值分配 如果您要根据提供的某些输入为变量分配新值,请停止If-Else废话-一种更具可读性的方法。 > Value assignment with if-else 尽管很简单,但它却很糟糕。首先,If-Else很容易在这里被开关取代。但是,我们可以通过完全删除else来进一步简化此代码。 > If statements with fast return 如果不使用else,则我们将剩下干净的可读代码。请注意,我也将样式更改为快速返回而不是单返回语句-如果已经找到正确的值,继续测试一个值根本没有意义。 3. 前提条件检查 通常,我发现,如果方法提供了无效的值,则继续执行是没有意义的。假设我们从以前就有了DefineGender方法,要求提供的输入值必须始终为0或1。 > Method without value checks 在没有价值验证的情况下执行该方法没有任何意义。因此,在允许方法继续执行之前,我们需要检查一些先决条件。 应用保护子句防御性编码技术,您将检查方法的输入值,然后继续执行方法。 > Check preconditions with guard clauses 至此,我们确保仅在值落在预期范围内时才执行主逻辑。 现在,IF也已被三元代替,因为不再需要在结尾处默认返回"未知"。 4. 将If-Else转换为字典—完全避免If-Else 假设您需要执行一些操作,这些操作将根据某些条件进行选择,我们知道以后必须添加更多操作。 也许有人倾向于使用久经考验的If-Else。如果添加新操作,则只需简单地添加其他内容即可。很简单 但是,就维护而言,这种方法不是一个好的设计。 知道我们以后需要添加新的操作后,我们可以将If-Else重构为字典。 可读性已大大提高,并且可以更轻松地推断出该代码。 注意,仅出于说明目的将字典放置在方法内部。您可能希望从其他地方提供它。 5. 扩展应用程序—完全避免使用If-Else 这是一个稍微高级的示例。 通过用对象替换它们,知道何时甚至完全消除If。 通常,您会发现自己不得不扩展应用程序的某些部分。作为初级开发人员,您可能会倾向于通过添加额外的If-Else(即else-if)语句来做到这一点。 举这个说明性的例子。在这里,我们需要将Order实例显示为字符串。首先,我们只有两种字符串表示形式:JSON和纯文本。在此阶段使用If-Else并不是什么大问题,如果我们可以轻松替换其他,只要如前所述即可。 知道我们需要扩展应用程序的这一部分,这种方法绝对是不可接受的。 上面的代码不仅违反了"打开/关闭"原则,而且阅读得不好,还会引起可维护性方面的麻烦。 正确的方法是遵循SOLID原则的方法-我们通过实施动态类型发现过程(在本例中为策略模式)来做到这一点。 重构这个混乱的过程的过程如下: 使用公共接口将每个分支提取到单独的策略类中 动态查找实现通用接口的所有类 根据输入决定执行哪种策略 替换上面示例的代码如下所示。是的,这是更多代码的方式。它要求您了解类型发现的工作原理。但是动态扩展应用程序是一个高级主题。 我只显示将替换If-Else示例的确切部分。如果要查看所有涉及的对象,请查看此要点。 让我们快速浏览一下代码。 方法签名保持不变,因为调用者不需要了解我们的重构。 首先,获取实现通用接口IOrderOutputStrategy的程序集中的所有类型。然后,我们建立一个字典,格式化程序的displayName的名称为key,类型为value。 然后从字典中选择格式化程序类型,然后尝试实例化策略对象。 最后,调用策略对象的ConvertOrderToString。 作者介绍 Nicklas Millard在丹麦的四大咨询公司之一中担任高级技术顾问。他主要担任客户项目的首席开发人员和解决方案架构师。 他一直在为商业客户和政府机构开发软件,例如国防部,教育部,丹麦环境与食品部,国家警察,丹麦劳动力市场和招聘局以及rstad。 在LinkedIn上连接 (本文翻译自Nicklas Millard的文章《Better Software Without If-Else》,参考:https://medium.com/swlh/5-ways-to-replace-if-else-statements-857c0ff19357) —————END————— 推荐阅读: 简单、易用的 MySQL 官方压测工具 “12306”的架构到底有多牛逼? 新技能 MyBatis 千万数据表,快速分页! 一文搞懂 Redis 缓存穿透、击穿、雪崩! MyBatis-Plus常用 API 全套教程 腾讯,干掉 Redis 项目,正式开源、太牛逼啦! IntelliJ IDEA 更新后,电脑卡成球,该如何优化? 最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复666领取,更多内容陆续奉上。 明天见(。・ω・。)ノ♡ 本文分享自微信公众号 - 程序IT圈(DeveloperIT)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

如何编写优雅的Dockerfile

导读 Kubernetes要从容器化开始,而容器又需要从Dockerfile开始,本文将介绍如何写出一个优雅的Dockerfile文件。 文章主要内容包括: Docker容器 Dockerfile 使用多阶构建 感谢公司提供大量机器资源及时间让我们可以实践,感谢在此专题上不断实践的部分项目及人员的支持。 一、Docker容器 1.1 容器的特点 我们都知道容器就是一个标准的软件单元,它有以下特点: 随处运行:容器可以将代码与配置文件和相关依赖库进行打包,从而确保在任何环境下的运行都是一致的。 高资源利用率:容器提供进程级的隔离,因此可以更加精细地设置CPU和内存的使用率,进而更好地利用服务器的计算资源。 快速扩展:每个容器都可作为单独的进程予以运行,并且可以共享底层操作系统的系统资源,这样一来可以加快容器的启动和停止效率。 1.2 Docker容器 目前市面

优秀的个人博客,低调大师

Android笔记:代码编写布局

自己在shareSdk原有布局中添加一个加载过程的布局: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 /** 新增:分享发送进度 **/ private void setLoadingUI() { // 页面父布局 pageLayout = new RelativeLayout(getContext()); pageLayout.setLayoutParams( new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); // 填充编辑页布局 RelativeLayout.LayoutParams llPageLp = new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); llPage.setLayoutParams(llPageLp); pageLayout.addView(llPage); // 加载过程布局 loadReLayout = new RelativeLayout(getContext()); loadReLayout.setLayoutParams( new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); loadReLayout.setClickable( true ); loadReLayout.setBackgroundColor( 0x50000000 ); loadReLayout.setVisibility(View.GONE); pageLayout.addView(loadReLayout); // 加载过程中间布局 LinearLayout loadCenterLayout = new LinearLayout(getContext()); RelativeLayout.LayoutParams lcLp = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); lcLp.addRule(RelativeLayout.CENTER_IN_PARENT); loadCenterLayout.setPadding( 8 , 8 , 8 , 8 ); loadCenterLayout.setBackgroundColor( 0xa5000000 ); loadCenterLayout.setOrientation(LinearLayout.HORIZONTAL); loadReLayout.addView(loadCenterLayout, lcLp); // 进度条和加载中说明 ProgressBar pb = new ProgressBar(getContext()); pb.setLayoutParams( new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); loadCenterLayout.addView(pb); TextView loadText = new TextView(getContext()); LayoutParams ltlp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); ltlp.setMargins( 8 , 0 , 0 , 0 ); ltlp.gravity = Gravity.CENTER_VERTICAL; loadText.setLayoutParams(ltlp); loadText.setText( "正在转发分享..." ); loadText.setTextSize( 16 ); loadText.setTextColor(Color.WHITE); loadCenterLayout.addView(loadText); } 原布局部分代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 private void initPageView() { // 编辑页父布局 llPage = new LinearLayout(getContext()); llPage.setBackgroundColor( 0xff323232 ); llPage.setOrientation(LinearLayout.VERTICAL); // 新增:分享发送过程 setLoadingUI(); // 标题栏 llTitle = new TitleLayout(getContext()); llTitle.setBackgroundResource(R.drawable.title_back); llTitle.getBtnBack().setOnClickListener( this ); llTitle.getTvTitle().setText(R.string.multi_share); llTitle.getBtnRight().setVisibility(View.VISIBLE); llTitle.getBtnRight().setText(R.string.share); llTitle.getBtnRight().setOnClickListener( this ); llTitle.setLayoutParams( new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); llPage.addView(llTitle); FrameLayout flPage = new FrameLayout(getContext()); LinearLayout.LayoutParams lpFl = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); lpFl.weight = 1 ; flPage.setLayoutParams(lpFl); llPage.addView(flPage); // 页面主体 LinearLayout llBody = new LinearLayout(getContext()); llBody.setOrientation(LinearLayout.VERTICAL); FrameLayout.LayoutParams lpLl = new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); lpLl.gravity = Gravity.LEFT | Gravity.TOP; llBody.setLayoutParams(lpLl); flPage.addView(llBody); // 别针图片 ivPin = new ImageView(getContext()); ivPin.setImageResource(R.drawable.pin); int dp_80 = cn.sharesdk.framework.utils.R.dipToPx(getContext(), 80 ); int dp_36 = cn.sharesdk.framework.utils.R.dipToPx(getContext(), 36 ); FrameLayout.LayoutParams lpPin = new FrameLayout.LayoutParams(dp_80, dp_36); lpPin.topMargin = cn.sharesdk.framework.utils.R.dipToPx(getContext(), 6 ); lpPin.gravity = Gravity.RIGHT | Gravity.TOP; ivPin.setLayoutParams(lpPin); flPage.addView(ivPin); ImageView ivShadow = new ImageView(getContext()); ivShadow.setBackgroundResource(R.drawable.title_shadow); ivShadow.setImageResource(R.drawable.title_shadow); FrameLayout.LayoutParams lpSd = new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); ivShadow.setLayoutParams(lpSd); flPage.addView(ivShadow); LinearLayout llInput = new LinearLayout(getContext()); llInput.setMinimumHeight(cn.sharesdk.framework.utils.R.dipToPx(getContext(), 150 )); llInput.setBackgroundResource(R.drawable.edittext_back); LinearLayout.LayoutParams lpInput = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); int dp_3 = cn.sharesdk.framework.utils.R.dipToPx(getContext(), 3 ); lpInput.setMargins(dp_3, dp_3, dp_3, dp_3); lpInput.weight = 1 ; llInput.setLayoutParams(lpInput); llBody.addView(llInput); //... } 效果图: 本文转自 glblong 51CTO博客,原文链接:http://blog.51cto.com/glblong/1370018,如需转载请自行联系原作者

优秀的个人博客,低调大师

如何优雅地编写缓存代码

在日常的编码实践中,经常会用到缓存来解决高并发问题,缓存可以说是解决流量洪峰的不二利器。虽然集团中间件团队已经构建了缓存的基础设施,已经帮助我们解决了绝大部分问题,但是在实际的编码使用过程中,应用端调用缓存API时还是存在下述几类问题: 使用缓存的逻辑非常通用,基本都是先查缓存,有直接返回,没有查DB,再放入缓存中。这段通用逻辑散落在系统的各个地方,违反了高内聚低耦合的原则。 缓存代码和业务逻辑代码深度耦合在一起,不仅降低了代码的可读性,还额外增加了系统复杂度。 如果要切换缓存(MDB->LDB)或者API升级时,所有涉及代码都需要改动。 如果要解决缓存击穿、缓存穿透、级联缓存等类似通用问题时,都需要通过框架去解决。 因此,缓存是什么,如何选择某一种缓存,都不是本文重点,今天就写写实际编码过程中,如何将缓存代码从业务代码中剥离出来,促使代码更简洁,更便于阅读。 实践分析 先读取缓存数据,如果有数据则直接返回,如果没有读取到数据,则读取DB数据,等数据返回后,再更新缓存。 这种场景,在日常编码中,很常见,太简单,但是实际的代码确实很不一样,列举如下几种: ▐传统写法 使用什么缓存,就直接使用,嵌入到业务代码中。这种代码不管是code review,还是后人学习业务代码时,都不想看,道理很简单,跟实际的业务功能无关,我不想知道你用什么缓存,你是怎么编码缓存代码的。 ▐高级一点的写法 相比传统的写法,为了解决缓存各种数据格式(List、Map等),各种对象序列化问题(java、json),团队内可以针对缓存这块,封装成简单的API,方便大家使用。使用简单了,但代码依然嵌入在业务代码中,没有剥离出来。 ▐注解写法 最后是注解写法,相对前两种写法,代码已从业务代码中剥离出来,阅读代码的人,只会关心业务功能是如何实现的,使用哪个缓存,如何实现的,完全可以忽略。 spring cache方案分析 spring cache利用动态代理的方式,在代理类中处理缓存的相关操作,同时调用被代理类中的方法,从而可以使操作缓存的代码和业务代码分离,并且后期需要强化缓存能力时,也只需要修改代理类中的方法即可。 以上就是Spring Cache的原理。Spring Cache是Spring提供的通用缓存框架。它利用了AOP,实现了基于注解的缓存功能,使开发者不用关心底层使用了什么缓存框架,只需要在方法上简单地加一个注解,就能实现缓存功能了。用户使用Spring Cache,可以快速开发一个很不错的缓存功能。 ▐代码目录 ▐注解导图 ▐注解使用示例 @Cacheable(value = "user_cache", unless = "#result == null")public User getUserById(Long id) { return userMapper.getUserById(id);}@CachePut(value = "user_cache", key = "#user.id", unless = "#result == null")public User updateUser(User user) { userMapper.updateUser(user); return user;}@CacheEvict(value = "user_cache", key = "#id")public void deleteUserById(Long id) { userMapper.deleteUserById(id);} ▐方案分析 Spring Cache的功能很强大,设计也非常优雅。特别适合缓存控制没有那么细致的场景,比如偏静态展示页面,点赞数、排名等等,这些场景的特点是对数据实时性没有那么严格的要求,只需要将数据源缓存下来,过期之后自动刷新即可。这些场景下,Spring Cache就是神器,能大幅度提升研发效率。 但在高并发大数据量的场景下,精细的缓存颗粒度的控制上,还是需要做功能扩展。 多级缓存; 缓存定期刷新; 列表缓存; 缓存cpp保护机制; 缓存计数。  例如:Spring Cache并没有二级缓存的实现 自定义cache方案 学习spring cache框架方案,实现自定义cache框架,不仅保留spring cache框架的优点,同时实现spring cache很多缺失的能力,例如缓存击穿、缓存穿透保护,多级缓存等。 ▐注解代码示例 ▐方案架构 写在最后面 借助spring cache实现方式,构建自定义缓存框架,扩展了很多注解,例如计数、缓存刷新、列表缓存、分布式锁、多级缓存等,不仅实现了缓存代码和业务代码的分离,同时拓展了spring缓存的能力,极大的提升了代码的可读性,降低了缓存代码维护的效率。 团队介绍 天猫汽车技术团队的使命是极致体验的人车生活,重塑汽车行业,做你身边的贴心汽车管家,皆在打造消费者线上看车买车养车心智,数字化并垂直整合汽车行业,通过模式突破撬动品效合一,提升行业效率,创造行业红利。 ¤ 拓展阅读 ¤ 3DXR技术| 终端技术| 音视频技术 服务端技术|技术质量|数据算法 本文分享自微信公众号 - 大淘宝技术(AlibabaMTT)。 如有侵权,请联系 support@oschina.cn 删除。 本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

scala入门之编写scala脚本

尽管scala的设计目的是帮助程序员建造大型系统。但它也能适应于制造小型的脚本。例如把以下代码放在hello.scala文件中: println("Hello,world,form ascript!") 然后运行: scala脚本的命令行参数保存在名为args的scala数组中。scala里,数组以0开始,可以通过在括号里指定索引值来访问数组元素。scala里数组 args 的第一个元素是:args(0),而不是像Java那样的:args[0]。现在,把以下内容写到新文件:helloarg.scala中测试一下: //向第一个参数打问好 println("Hello, " + args(0) + "!") 然后运行: 这条命令里,命令行参数“FHD”被传递给脚本,并通过访问args(0)获得。请注意这个脚本包含了一条注释。scala编译器忽略从//开始到行尾截止的以及在/* 和 */之间的字符。下面再举一个例子,如创建一个名为test.scala的脚本文件: var i = 0; while(i < args.length){ if(i != 0) print(" ") print(args(i)) i += 1; } println() 运行结果: 注意: Java的++i 和 i++ 在scala里不能使用的,要在scala里得到同样效果,必须要么写成: i = i + 1,要么写成: i += 1。 scala和Java一样,必须把while或if的布尔表达式放在括号里。 scala和Java一样,如果代码块仅有一行语句,就像上例中的 if 语句,那么花括号就可以不写。 尽管scala也和Java一样用分号分隔语句,但是scala的分号经常是可选的。 本文来自云栖社区合作伙伴“开源中国” 本文作者:柳哥 原文链接

优秀的个人博客,低调大师

高效编写Dockerfile的几条准则

概述 Dockerfile 是专门用来进行自动化构建镜像的编排文件(就像Jenkins 2.0时代的Jenkinsfile是对Jenkins的Job和Stage的编排一样),我们可以通过 docker build 命令来自动化地从 Dockerfile 所描述的步骤来构建自定义的 Docker镜像,这比我们去命令行一条条指令执行的方式构建高效得多。 另一方面,由于 Dockerfile 提供了统一的配置语法,因此通过这样一份配置文件,我们可以在各种不同的平台上进行分发,需要时通过 Dockerfile 构建一下就能得到所需的镜像。 最后一个必须提的优点便是:Dockerfile 通过与镜像配合使用,使得 Docker镜像构建之时可以充分利用 “镜像的缓存功能”,因此也提效不少! 然而写 Dockerfile 也像写代码一样,一份精心设计、Clean Code 的 Dockerfile 能在提高可读性的同时也大大提升Docker的使用效率 因此下面就结合实践来讲几条 Dockerfile 的实践心得! 基础镜像的选择有讲究 在我的文章 《利用K8S技术栈打造个人私有云(连载之:基础镜像制作与实验)》 中,我们是基于某个Linux基础镜像作为底包,然后打包进我需要的功能从而形成自己的镜像。 这里选择基础镜像时是有讲究的: 一是 应当尽量选择官方镜像库里的基础镜像; 二是 应当选择轻量级的镜像做底包 就典型的Linux基础镜像来说,大小关系如下: Ubuntu > CentOS > Debian 因此相比 Ubuntu,其实更推荐使用最轻量级的 Debian镜像,而且它也是一个完整的Release版,可以放心使用 多使用标签Tag 有好处 构建镜像时,给其打上一个易读的镜像标签有助于帮助了解镜像的功能,比如: docker build -t=“centos:wordpress" . 例如上面的这个centos镜像是用来做wordpress用的,所以已经集成了wordpress功能,这一看就很清晰明了 再者,我们也应该在 Dockerfile 的 FROM 指令中明确指明标签 Tag,不要再让 Docker daemon 去猜,如 FROM debian:codesheep 充分利用镜像缓存 什么是镜像缓存? 由 Dockerfile 最终构建出来的镜像是在基础镜像之上一层层叠加而得,因此在过程中会产生一个个新的 镜像层。Docker daemon 在构建镜像的过程中会缓存一系列中间镜像。 docker build镜像时,会顺序执行Dockerfile中的指令,并同时比较当前指令和其基础镜像的所有子镜像,若发现有一个子镜像也是由相同的指令生成,则 命中缓存,同时可以直接使用该子镜像而避免再去重新生成了。 为了有效地使用缓存,需要保证 Dockerfile 中指令的 连续一致,尽量将相同指令的部分放在前面,而将有差异性的指令放在后面 举例:假如我想用 Dockerfile方式 基于最基本的 CentOS 镜像来构建两个不同的镜像时,两个Dockerfile的开头可以相同: FROM centos:latest # 下面安装两个常用的工具 RUN yum install -y net-tools.x86_64 RUN yum install lrzsz ######## 上面为两个Dockerfile文件中相同的部分###### ######## 下面为两个Dockerfile文件中不同的部分###### ...... ADD 与 COPY 指令的正确使用 虽然两者都可以添加文件到镜像中,但在一般用法中,还是推荐以COPY指令为首选,原因在于ADD指令并没有COPY指令来的纯粹,ADD会添加一些额外功能,典型的如下 ADD 一个压缩包时,其不仅会复制,还会自动解压,而有时我们并不需要这种额外的功能。 ADD codesheep.tar.gz /path 除此之外,在需要添加多个文件到镜像中的时候,不要一次性集中添加,而是选择 按需 在必要时 逐个 添加即可,因为这样有利于利用镜像缓存 尽量使用docker volume 虽然上面一条原则说推荐通过 COPY 命令来向镜像中添加多个文件,然而实际情况中,若文件 大而多 的时候还是应该优先用 docker -v 命令来挂载文件,而不是依赖于 ADD 或者 COPY CMD 和 ENTRYPOINT指令 的正确理解使用 Dockerfile 制作镜像时,会组合 CMD 和 ENTRYPOINT 指令来作为容器运行时的默认命令:即 CMD + ENTRYPOINT。此时的默认命令组成中: ENTRYPOINT 指令部分固定不变,容器运行时是无法修改的 而 CMD 部分的指令也可以改变,表现在运行容器时,docker run 命令中提供的参数会覆盖CMD的指令内容。 举个例子: FROM debian:latest MAINTAINER codesheep@163.com ENTRYPOINT [ "ls", "-l"] CMD ["-a"] 若以默认命令运行容器,可以发现,执行的是 ls -a -l 命令: 若 docker run 中增加参数 -t docker run -it --rm --name test debian:codesheep -t 也可以发现执行的是 ls -l -t,即 Dockerfile 中的 CMD 原参数被覆盖了: 因此推荐的使用方式是: 使用exec格式的 ENTRYPOINT指令 设置固定的默认命令和参数 使用 CMD指令 设置可变的参数 不推荐在 Dockerfile中 做端口映射 Dockerfile 可以通过 EXPOSE指令 将容器端口映射到主机端口上,但这样会导致镜像在一台主机上仅能启动一个容器! 所以应该在 docker run 命令中来用 -p 参数来指定端口映射,而不要将该工作置于 Dockerfile 之中: #尽量避免这种方式 EXPOSE 8080:8899 #选择仅仅暴露端口即可,端口映射的任务交给 docker run 去做 EXPOSE 8080 使用 Dockerfile 来共享镜像 推荐通过共享 Dockerfile 的方式来共享镜像,优点多多: 通过 Dockerfile 构建的镜像用户可以清楚地看到构建的过程 就像 Jenkinsfile 可以加入版本控制从而追踪CI系统的变迁和步骤的回滚一样,Dockerfile 作为一个编排文件同样可以入库做版本控制,这样也可以回溯 使用 Dockerfile 构建的镜像具有确定性,没有玄学的成分 后记 作者更多的原创文章在此,欢迎观赏 如果有兴趣,也可以抽点时间看看作者一些关于容器化、微服务化方面的文章: 从一份配置清单详解Nginx服务器配置 利用K8S技术栈打造个人私有云 连载文章 Docker容器可视化监控中心搭建 利用ELK搭建Docker容器化应用日志中心 利用TICK搭建Docker容器可视化监控中心 RPC框架实践之:Apache Thrift RPC框架实践之:Google gRPC 微服务调用链追踪中心搭建 Docker容器跨主机通信 Docker Swarm集群初探 作者相关的SpringBt实践文章在此: SpringBoot应用部署于外置Tomcat容器 ElasticSearch搜索引擎在SpringBt中的实践 初探Kotlin+SpringBoot联合编程 Spring Boot日志框架实践 SpringBoot优雅编码之:Lombok加持

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Oracle

Oracle

Oracle Database,又名Oracle RDBMS,或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是目前世界上流行的关系数据库管理系统,系统可移植性好、使用方便、功能强,适用于各类大、中、小、微机环境。它是一种高效率、可靠性好的、适应高吞吐量的数据库方案。

Eclipse

Eclipse

Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括Java开发工具(Java Development Kit,JDK)。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。