关于intellij-idea:IntelliJ-IDEA-中文官方文档

目录意识IntelliJ IDEA IntelliJ IDEA 装置和设置IntelliJ IDEA如何应用IntelliJ IDEA中不容错过的快捷键IntelliJ IDEA业余的应用技巧从IntelliJ IDEA获取帮忙 应用IntelliJ IDEA的帮忙主题应用Tips of the Day和在线资源应用Productivity GuideIntelliJ IDEA问题报告并分享意见keymap 参考IntelliJ IDEA的个别准则 应用IntelliJ IDEA的用户界面进行导览 IntelliJ IDEA欢送界面IntelliJ IDEA的菜单栏与工具栏IntelliJ IDEA导航栏操作IntelliJ IDEA状态栏IntelliJ IDEA的视图模式设置IntelliJ IDEA背景图像设置IntelliJ IDEA我的项目和工作环境 IntelliJ IDEA配置代码款式IntelliJ IDEA如何配置色彩与字体配置IntelliJ IDEA键盘快捷键IntelliJ IDEA如何配置菜单、工具栏与行分隔符配置IntelliJ IDEA的疾速列表IntelliJ IDEA如何应用代码款式IntellJ IDEA文件的类型与色彩IntelliJ IDEA配置文件编码切换启动JDK与共享IDE设置如何导出IDE设置以及配置浏览器IntelliJ IDEA编辑器 IntelliJ IDEA的根本编辑程序 在IntelliJ IDEA编辑器中抉择文本IntelliJ IDEA的剪切、复制和粘贴在IntelliJ IDEA和Explorer / Finder之间复制和粘贴IntelliJ IDEA代码块的正文及勾销正文IntelliJ IDEA如何实现撤销和重做在IntelliJ IDEA编辑器中关上并从新关上文件在IntelliJ IDEA编辑器中敞开文件Lens模式以及Multicursor的应用IntelliJ IDEA收藏夹IntelliJ IDEA保留和还原更改在IntelliJ IDEA编辑器中更改字体大小IntelliJ IDEA高级编辑程序 从新格式化IntelliJ IDEA源代码更改IntelliJ IDEA的代码缩进如何折叠IntelliJ IDEA代码片段查看IntelliJ IDEA插入符IntelliJ IDEA如何让文件可写怎么突出显示IntelliJ IDEA的大括号Scratches文件介绍如何增加,删除和挪动IntelliJ IDEA的代码IntelliJ IDEA如何连贯行与文字IntelliJ IDEA宰割字符串的办法如何以表格格局编辑CSV文件IntelliJ IDEA拖放操作如何在IntelliJ IDEA编辑器中应用Macros治理IntelliJ IDEA的编辑器选项卡 ...

July 18, 2020 · 4 min · jiezi

IntelliJ-的书签Bookmarks

IntelliJ 能够帮助你在 IntelliJ 中快速浏览和定位。 针对一些大型项目,代码之间的调用比较复杂的项目这个是非常方便,有用和快速的。 打书签你可用打数字书签,也可以打文字书签,也可以做无标记书签。 数字书签打数字书签的快捷键是 Ctrl + Shift + <数字>。 例如希望在我们的代码中打上这个标签,选择你需要打标签的行,如果你希望打上书签 1。 那么你就可以使用 Ctrl + Shift + 1 在这行上打上书签 1。 如果你希望撤销打上的书签,你再输入一次 Ctrl + Shift + 1 就可以在这行上撤销了。 字母书签你也可以在这行上打上字母书签。 打字母标签没有和打数字书签一样的快捷键,你需要在你的键盘上输入 Ctrl + F11,在弹出的对话框中进行选择你需要的字母。 如果你不进行字母选择,你会看到前面有个√符号,表示的是这行已经是一个书签了,但是没有任何标识。 无标记书签做无标记书签的快捷键是 F11。 直接在键盘上使用 F11 来标记一个书签。 书签访问只有数字书签能够快速访问,使用 Ctrl + 数字,就可以直接定位到你的数字书签了。 其他书签的访问,你可用使用快捷键 Shift + F11 在弹出来的对话框中进行查看。 书签删除在弹出的书签列表中,选择你希望删除的书签,然后按 Del 键,或者直接在书签列表上面点减号就可以删除了。 https://www.ossez.com/t/intellij-bookmarks/197

June 24, 2020 · 1 min · jiezi

IntelliJ-IDEA创建完整的Spring-Boot项目

使用IntelliJ IDEA创建一个完整的Spring Boot项目流程以IntelliJ IDEA2019.3为例 准备工作 本地安装好JDK电脑已经联网IDEA已经安装IDEA配置好了Maven环境1.打开IDEA开发工具,点击Create New Project创建一个新项目 2.左侧选择Spring Initializr,右侧选择连接https://start.spring.io ,点击next 3.此过程等待加载 4.选择新建项目的相关信息参考如下 Group公司或者组织域名的倒序Artifact 项目或者模块名Type:maven部署方式(一般不更改)packaging:打包方式(一般不更改)Java Version:Java版本(如果你的电脑上有多个JDK版本,请选择)Version:项目或者模块版本(一般不更改)Name:项目名Description:项目描述package:包名(根据需求设置)注意:所有的内容都必须是小写字母,否则IDEA不予通过! 设置完成后,点击Next 5.选择模块和Spring Boot的版本版本建议选择稳定版模块根据需要选择,我这里选择web,Mybatis,MySQL等右边可以看到选择的模块项目 6.填写项目名和项目路径Project name:项目名Project location:存放路径选择好之后,点击Finish. 7.等待Maven部署项目,最好不要乱动项目结构 8.运行项目运行项目方式 左下角的Terminal终端输入命令:mvn spring-boot:run运行运行主程序入口main方法直接运行Spring Boot项目 9.配置数据源运行项目之后,发现有错误,是因为添加了数据库的模块,但是没有配置数据源 数据源配置在项目中依次找到src/main/resources/application.properties文件,将下面的配置信息复制进去,修改为自己的版本 #配置数据库的项目信息(MySQL数据库)#配置数据库的驱动spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver# 旧版使用的驱动#spring.datasource.driver-class-name=com.mysql.jdbc.Driver#配置数据库连接URLspring.datasource.url=jdbc:mysql://127.0.0.1:3306/cms?characterEncoding=utf8&serverTimezone=UTC#配置数据库额度用户名spring.datasource.username=lele#配置数据库的密码spring.datasource.password=lele# 配置服务器的相关信息# 配置服务器的端口server.port=80注:数据库的驱动在不同的版本上不同,注意区分版本,因为还添加了web模块,可能存在服务器端口占用问题,我这里顺便将端口改成了80 10.配置完成后,再次运行项目使用命令:mvn spring-boot:run 运行之后光标闪烁,程序正常启动

November 13, 2019 · 1 min · jiezi

IntelliJ-IDEA-设置Terminal-窗口字体大小

我在Setting中查看了所有和Terminal字样有关的设置,都没有找到设置字体大小的方法,原来Terminal也只需要设置Console的字体大小就可以了。 Settings——>Editor——>Colors Scheme——>Console Font 在这里设置了之后,直接去看Terminal中的字体,没有任何变化,开始还以为失败了,其实只要打开一个新的Terminal窗口就可以了。追求完美的性格,对自己无语。。。不过字体调好了以后使用起来眼睛就轻松多了 个人网站

October 15, 2019 · 1 min · jiezi

讲真我发现这本书有个地方写错了

本文首发于公众号【why技术】,关注公众号,有更加优秀的排版方式,阅读体验更佳。 可恶的标题党首先,我先说一下我发现的《Java并发编程的艺术》写错的地方吧。我手上这本《Java并发编程的艺术》的版次是:2019年3月第1版第14次印刷。 我浏览目录的时候注意到了其中3.6.5小节的标题是:《为什么final引用不能从构造函数内“溢出”》 很明显,作者这里是一个笔误。从作者该小节具体的描述也可以看出来,【溢出】应该是【逸出】。 看到这里,你要说我是一个"可恶的标题党",我也不反驳。因为这个错误,结合上下文来看,确实无伤大雅。 但是,只看标题呢?如果只知道java有内存溢出,不知道java有引用逸出的读者呢? 他们可能抠破脑袋,也想不出"构造函数内的final引用"和"内存溢出"之间有什么联系吧? 好了,这个不重要。 因为本文想要阐述的,不是这个笔误,而是这个笔误,背后隐藏的两大知识点:【引用逸出】和【内存溢出】。 主要是接合《Java并发编程的艺术》、《Java并发编程实战》、《深入理解Java虚拟机》这三本书中的相关内容进行对比,然后展开描述。 同时需要强调的是:我认为,这个小小的笔误,完全不妨碍这本书的优秀性。这是一本提升并发编程能力干货满满的书。 对象&引用逸出在《Java并发编程实战》的3.2小节中是这样定义发布与逸出的: “发布(Publish)”一个对象的意思是指,使对象能够在当前作用域之外的代码中使用。将一个指向该对象的引用保存到其他代码可以访问到的地方,或者在某一个非私有的方法中返回该引用,或者将引用传递到其他类的方法中。 当某个不应该发布的对象被发布时,这种情况就被成为"逸出(Escape)"。 概念读起来总是让人摸不着头脑。 我们直接看书里面给出的程序清单3-5: 如程序清单3-5所示:在initialize方法中实例化一个新的HashSet对象,并将对象的引用保存到knownSecrets中以发布该对象。 这段代码有什么问题? 当发布knownSecrets对象时,间接地发布了Secret对象。因为任何代码都可以遍历这个集合,并获得对这个新Secret对象的引用。所以Secret对象"逸出"了,这是不安全的。 再看书里给出的另外一个程序清单3-6:如果按照上述方式来发布states,就会出现问题,因为任何调用者都能修改这个数组的内容。在程序清单3-6中,数组states已经"逸出"了它所在的作用域,因为这个本应该是私有的变量已经被发布了。 当某个对象逸出后,你必须做最坏的打算,必须假设某个类或者线程可能会误用该对象。 同时书中也说到,这也正是需要使用封装的最主要的原因:封装能够使得对程序的正确性进行分析变得可能,并使得无意中破坏设计约束条件变得更难。 this引用逸出在《Java并发编程实战》里面给出了一个"隐式地使this引用逸出"的例子。如下所示:ThisEscape在发布其内部类EventListener时,因为EventListener这个内部类包含了对ThisEscape实例的引用,所以使ThisEscape实例发生了"this引用逸出"。 不好理解对不对?我们再看看书中的描述:对于不正确构造,作者给了一个备注说明:具体来说,只有当构造函数返回时,this引用才应该从线程中逸出。构造函数可以将this引用保存到某个地方,只要其他线程不会在构造函数完成之前使用它。 也不太好理解对不对?确实是,因为我觉得这个代码片段少了几个关键的引导的地方;而这段话很难提炼出关键词,因为全是关键词。 但是我读到这段话的时候,有一句话直接吸引了我的注意力,仿佛把手举得高高的在喊:看我,看我!即使发布对象的语句位于构造函数的最后一行也是如此 作者为什么要感觉是轻描淡写,实际上是在强调"最后一行"呢? 作者没有明说,但是答案是重排序,因为有了重排序,所以一行代码看起来是在最后一行,实际上不是最后一行。 这里我们接合《Java并发编程的艺术》发生笔误的这一章节里面的例子,来说明【this引用逸出】和【即使发布对象的语句位于构造函数的最后一行也是如此】这两个问题,代码如下: 假设一个线程A执行writer()方法,另一个线程B执行reader()方法。 这里的操作2(obj=this)使得对象还未完成构造前就为线程B可见。即使这里的操作2(obj=this)是构造函数的最后一步。 且在程序中操作2(obj=this)排在操作1(i=1)后面,执行read()方法的线程仍然可能无法看到final域被初始化后的值。 因为这里的操作1(i=1)和操作2(obj=this)之间可能被重排序。实际的执行时序可能如下图所示: 所以《Java并发编程的艺术》里面的示例代码和多线程下代码的执行时序图就很好的说明了【this引用逸出带来的问题(线程不安全)】,解答了【《Java并发编程实战》中没有明说的为什么"即使最后一行"也不行(重排序)】。 这一小节就是我读完《Java并发编程实战》、《Java并发编程的艺术》之后,取出书中部分内容再加上自己对于对象&引用逸出的理解的总结、输出。 其实《深入理解Java虚拟机》里面也有对逃逸描述的相关内容,有兴趣的可以翻阅一下。如下:《深入理解Java虚拟机》目录 内存溢出如果前面说的引用逸出让你云里雾里,快要瞌睡了。那接下我们要谈的内存溢出,大家应该都是耳熟能详的了。 先上一个来自《深入理解Java虚拟机》中第2章【Java内存区域与内存溢出异常】中的一张清晰的、牛逼的、经典的、包罗万象的大图:Java 虚拟机运行时数据区 这个图包含的知识点可以说是非常多,全是"内功心法",我们只讨论其中的一大分支---内存溢出。 所以,本小节内容的目的有两个: 第一,通过代码验证Java虚拟机规范中描述的各个运行时区域存储的内容。 第二,希望读者在工作中遇到实际的内存溢出异常时,能根据异常的信息快速判断是哪个区域的内存溢出,知道什么样的代码可能会导致这些区域内存溢出,以及出现这些异常后该如何处理。 对于每个区域具体的职能,就不铺开讲了,一铺开,又是一个万字长篇。我在保证质量的前提下,尽量精简字数,让大家读起来不要那么耗时(实在耗时的话,说明我真的用心在写,可以收藏起来或者转发朋友圈慢慢看呀),一进来,一看完,半小时过去了。话不多说,精彩继续。 程序计数器此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。 由于没有OutOfMemoryError的情况,所以不做模拟。 虚拟机栈&本地方法栈关于虚拟机栈和本地方法栈,在Java虚拟机规范中描述了两种异常: 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。这里把异常分成两种情况,看似更加严谨,但却存在着一些互相重叠的地方:当栈空间无法继续分配时,到底是内存太小,还是已使用的栈空间太大,其本质上只是对同一件事情的两种描述而已。 常言说的好:Talk is cheap.Show me the code(光说不练假把式)。我们用代码说话: 在《深入理解Java虚拟机》笔者的实验中,将实验范围限制于单线程中的操作,尝试了下面两种方法均无法让虚拟机产生OutOfMemoryError异常,尝试的结果都是获得StackOverflowError异常,测试代码如下所示。 使用-Xss参数减少栈内存容量。结果:抛出StackOverflowError异常,异常出现时输出的堆栈深度相应缩小。 定义了大量的本地变量,增大此方法帧中本地变量表的长度。结果:抛出StackOverflowError异常时输出的堆栈深度相应缩小。 虚拟机栈和本地方法栈OOM测试(仅作为第一点测试程序)运行结果: 在单线程下,无论由于栈帧太大还是虚拟机栈容量太小,当内存无法分配的时候,虚拟机抛出的都是StackOverflowError异常。 那我们怎么去模拟OutOfMemoryError异常呢? 我查阅了一些其他的文章,他们的测试不限于单线程,通过不断地创建线程的方式产生内存溢出异常。举出的例子也是书中的例子,如下: 运行结果:Exception in thread"main"java.lang.OutOfMemoryError:unable to create new native thread ...

October 14, 2019 · 1 min · jiezi

redis-漏洞造成服务器被入侵CPU飙升

前言 前几天在自己服务器上搭了redis,准备想着大展身手一番,昨天使用redis-cli命令的时候,10s后,显示进程已杀死。然后又试了几次,都是一样的结果,10s时间,进程被杀死。这个时候我还没发现事情的严重性。<!--more--> 发现问题 进程莫名被杀死,可能是cpu被占满,赶紧看了一下cpu。 [root@VM_0_13_centos etc]# top 果然如此,cpu被莫名的占满了。简单,根据pid杀死进程就行了。 [root@VM_0_13_centos etc]# kill -9 27882 在我以为事情已经完结的情况下,使用reids-cli的时候又出现同样的问题,进程被杀死,我在用 top 命令查看的时候,cpu又被占满了。这个时候我才意识到问题的严重性,这个command是一串无规则数字,应该不是一个正经的进程,而且我试过了几次,这个进程总是在被杀死后重新启动,不过是以不同的pid和不同的command,这可能是一个定时任务,然后我看了一下本地的定时任务: [root@VM_0_13_centos etc]# crontab -l 果然有一个定时任务,那就删掉他。 [root@VM_0_13_centos etc]# rm -rf /var/spool/cron/root 等我再次杀掉那个异常线程在 top 的时候,发现进程又出现了,我返回看定时任务的时候,发现定时任务也又出现了,直接崩溃.... 问题原因 我从一个技术群里面了解到,这可能是redis漏洞所造成的,造成该现象的原因有如下几种: 将redis暴露在公网上,即把redis绑定在0.0.0.0:6379redis密码过于简单,甚至没有(我就是后种情况..)使用root权限操作redis攻击原理首先在本地产生密钥文件 $ ssh-keygen –t rsa进入~/.ssh/,将公钥写进foo.txt文件 $ (echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n") > foo.txt再连接 Redis 写入文件 $ cat foo.txt | redis-cli -h 192.168.1.11 -x set crackit$ redis-cli -h 192.168.1.11$ 192.168.1.11:6379> config set dir /root/.ssh/OK$ 192.168.1.11:6379> config get dir1) "dir"2) "/root/.ssh"$ 192.168.1.11:6379> config set dbfilename "authorized_keys"OK$ 192.168.1.11:6379> saveOK这样就可以成功的将自己的公钥写入 /root/.ssh 文件夹的 authotrized_keys 文件里,然后攻击者直接执行: ...

October 9, 2019 · 1 min · jiezi

SpringBoot实现上传下载一

最近在学Springboot相关知识,这次用Springboot做了一个上传下载的功能上传一个法律名及其发布的年份等信息,然后还要能上传一个pdf文件(这里限制下上传的后缀名就可以),上传之后,点击操作中的下载,下载对应的pdf文件。 预览: 1.前期准备 编辑器:IDEA/eclipse/myeclipselayui文档的bang助:layui开发使用文档 如何使用IDEA创建Springboot项目 使用使用IDEA进行资源标记 根据项目结构图: 标记java文件夹为Source rootresources为resources root为了迎合SSM框架的习惯 创建web文件夹配置web 详细的pom.xml等配置可以参见我的项目目录 这里不一一展开了 主要说一下各个目录文件夹的作用。 resources目录下的application.properties是spring相关配置文件夹generatorConfig是逆向工程的配置文件webapp目录下 public是JS/json/css等静态资源upload是上传文件存放的文件夹WEB-INF是存放jsp等渲染的网页模板文件的文件夹 2.架构设计(MVC) Model:dao+service+model三个包实现数据库连接以及使用数据库实现持久化存储 View:webapp包下的jsp等网页渲染文件 C:controller包下的多个控制器 3.数据库设计 没什么好说的 想说的都在图里 4.Code首先实现上传功能。实现思路:使用layui提供给我们的upload Element。我们上传这个文件,限制后缀名为pdf,限制上传文件最大2048KB等。后台则将上传的File文件使用java的file.transferTo录入upload包下。 View层实现:layer.jsp: <div class="layui-form-item"> <div class="layui-input-block"> <div class="layui-upload"> <button type="button" class="layui-btn layui-btn-normal" id="test8">选择文件</button> <button type="button" class="layui-btn" id="upload">开始上传</button> <input type="hidden" name="fileName" id="fileName"> </div> </div> </div>以上控件组件 layui.use(['element', 'form', 'table', 'layer', 'vip_table', 'laydate','upload'], function() { var form = layui.form, table = layui.table, layer = layui.layer, vipTable = layui.vip_table, element = layui.element, $ = layui.jquery; var laydate = layui.laydate; var upload = layui.upload; //上传 upload.render({ elem: '#test8' ,url: 'layer/upload' ,auto: false //,multiple: true ,bindAction: '#upload' ,size: 2048 //最大允许上传的文件大小 2M ,accept: 'file' //允许上传的文件类型 ,exts:'pdf'//只上传pdf文档 ,done: function(res){ console.log(res) if(res.code == 1){//成功的回调 //do something (比如将res返回的图片链接保存到表单的隐藏域) $('#set-add-put input[name="fileName"]').val(res.data.fileName); $('#upload').hide(); layer.msg(res.msg, { icon: 6 }); }else if(res.code==2){ layer.msg(res.msg, { icon: 5 }); } } });以上对upload的监听,并且对返回的状态码1(成功),2(失败)做相应处理。 ...

October 8, 2019 · 3 min · jiezi

SpringBoot实现上传下载二

这篇写下载。 1.实现思路上一篇的数据库设计中,我们有一个字段始终没有用到fileName,这是用来给Layer对象存储文件名的,以此来完成文件与对象的对应, 预览: 2.Code View层:首先是加载数据表格异步的时候 我们就获取到了fileName,然后通过获取当前行,来获取当前的fileName文件名。 table.on('tool(test)', function(obj) { var data = obj.data; //获得当前行数据 var layEvent = obj.event; //获得 lay-event 对应的值(也可以是表头的 event 参数对应的值) var tr = obj.tr; //获得当前行 tr 的DOM对象 $ = layui.jquery;if (layEvent === 'download') { //删除 var fileName = data.fileName; window.location="layer/download?fileName="+fileName; } });然后不论是上传下载使用ajax都不推荐(上传下载都属于获取资源请求,a标签等即可实现,而ajax是js异步的封装请求,两者实现目标不一样) 这里使用window.location来实现对文件的请求。 然后是Controller层: //下载 @RequestMapping(value = "Index/layer/download") @ResponseBody public Map<String,Object> downloadOne(HttpServletRequest req,HttpServletResponse response) throws IOException{ String fileName = req.getParameter("fileName");// 设置文件名,根据业务需要替换成要下载的文件名// String layerId = req.getParameter("layerId"); String downloadDir = req.getSession().getServletContext().getRealPath("/") +"upload/";// String savePath = req.getSession().getServletContext().getRealPath("/") +"download/"; downloadDir=downloadDir.substring(0,downloadDir.length()-1); downloadDir=downloadDir+"\\";//下载目录 String realPath=downloadDir+fileName;// File file = new File(realPath);//下载目录加文件名拼接成realpath if(file.exists()){ //判断文件父目录是否存在// response.setContentType("application/force-download"); response.setHeader("Content-Disposition", "attachment;fileName=" + fileName); byte[] buffer = new byte[1024]; FileInputStream fis = null; //文件输入流 BufferedInputStream bis = null; OutputStream os = null; //输出流 try { os = response.getOutputStream(); fis = new FileInputStream(file); bis = new BufferedInputStream(fis); int i = bis.read(buffer); while(i != -1){ os.write(buffer); i = bis.read(buffer); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("----------file download" + fileName); try { bis.close(); fis.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return api.returnJson(2,"fail"+realPath+fileName); }主要就是调IO流,然后下载罢了,拼一下路径和文件名即可 ...

October 8, 2019 · 1 min · jiezi

MAC操作系统下JetBrains下的系统产品如Intellij-IDEA无法启动的解决方法

JetBrains公司下的产品表现稳定,但难免也会出现一些意想不到的情况,比如某个配置信息错误导致软件无法启动。此时,我们需要按照以下步骤来排查错误(以Intellij IDEA为例)。 命令行启动来到产品的安装路径(,比如:/Applications/IntelliJ IDEA.app/Contents/MacOS,然后直接执行./idea。查看错误及配置文件信息。 panjiedeMac-Pro:MacOS panjie$ ./idea2019-10-08 12:25:40.418 idea[28221:1016924] allVms required 1.8*,1.8+2019-10-08 12:25:40.423 idea[28221:1016927] Value of IDEA_VM_OPTIONS is (null)2019-10-08 12:25:40.423 idea[28221:1016927] Processing VMOptions file at /Users/panjie/Library/Preferences/IntelliJIdea2019.2/idea.vmoptions2019-10-08 12:25:40.423 idea[28221:1016927] Done此时会打印出无法启动的错误,然后按图索骥即可。 暴力删除法如果实在找不出解决方法,那么也可以直接删除执行./idea后显示的配置文件夹,比如:/Users/panjie/Library/Preferences/IntelliJIdea2019.2 最后,重新启动Intellij IDEA即可。

October 8, 2019 · 1 min · jiezi

IDEA中执行maveninstall时忽略找不到无关类的错误

IDEA中执行maven:install时忽略找不到无关类的错误package com.sun.org.apache.xml.internal.security.signature does not exist某些项目因找不到无关紧要的类而编译不过(如下图),其实只要在设置以下两步就可让项目顺利运行。 1.设置IDEA的编译器2.设置Maven为no error check

October 4, 2019 · 1 min · jiezi

INTERLLij-IDEA-修改背景颜色护眼

IDEA的默认颜色为黑色,确实很酷,第一眼就被它的界面所惊艳到了! 不过软件的默认字体太小,对于我这个有着500多度近视的人来说简直痛苦,特地整理了一些修改背景颜色的方法,供大家参考。 1.IntelliJ IDEA设置菜单栏字体: File --- Setting --- Appearance & Behavior --- Appearance --- Override default fonts by (not recommended) 2.IntelliJ IDEA设置编码字体 File --- Setting --- Editor --- Colors & Fonts ---Font 先保存再修改 3.IntelliJ IDEA设置编码背景色 setting -- Editor --- Colors Scheme --- General --- Text --- Default text4设置参数如图

October 2, 2019 · 1 min · jiezi

IntelliJ-IDEA快捷键

工欲善其事,必先利其器。故整理了一份快捷键。最后的截图可以保存使用。编辑 Ctrl + Space代码补全提示 (任何类的名称, 方法或变量)Ctrl + Shift + Space智能代码补全(筛选方法列表和变量按预期类型)Ctrl + Shift + Enter补全当前语句Ctrl + P参数信息(在方法调用参数内)Ctrl + Q快速文档查找Shift + F1外部文档Ctrl + mouse over code简要信息Ctrl + F1在插入符号中显示错误或警告的说明Alt + InsertGenerate code(Getters/Setters,Constructors,hashCode/equals,toString)Ctrl + O重写方法Ctrl + I实现方法Ctrl + Alt + TSurround with(if..else,try..catch,for,synchronized,etc.)Ctrl + /注释/取消注释行Ctrl + Shift + /注释/取消注释代码块Ctrl + W选择代码块Ctrl + Shift + W将当前所选内容减少到以前状态Alt + Q上下文信息Alt + Enter显示意向操作和快速修复Ctrl + Alt + L重新格式化代码Ctrl + Alt + O优化导入Ctrl + Alt + I自动缩进行/代码块Tab / Shift + Tab缩进/取消缩进选定行Ctrl + X or Shift + Delete将当前行或选定块剪切到剪贴板Ctrl + C or Ctrl + Insert将当前行或选定块复制到剪贴板Ctrl + V or Shift + Insert从剪贴板粘贴Ctrl + Shift + V从最近的缓冲区粘贴.Ctrl + D重复的当前行或选定的块Ctrl + Y删除插入符号中的行Ctrl + Shift + J智能拼接行Ctrl + Enter智能拆分行Shift + Enter增加新行Ctrl + Shift + U在插入符号或选定块中切换word 的大小写Ctrl + Shift + ] / [选择直到代码块结束/开始Ctrl + Delete从当前位置删除到单词结尾位置Ctrl + Backspace从当前位置删除到单词开始位置Ctrl + NumPad+/-展开/折叠代码块Ctrl + Shift + NumPad+展开/折叠代码块Ctrl + Shift + NumPad-全部折叠Ctrl + F4关闭活动编辑器选项卡搜索/替换 Double Shift全局搜索Ctrl + F查找F3查找下一个Shift + F3查找上一个Ctrl + R替换Ctrl + Shift + F在路径中查找Ctrl + Shift + R替代路径(文件内代码批量替换)Ctrl + Shift + S结构搜索(仅限最终版本)Ctrl + Shift + M结构替换 (仅限最终版本)Alt + F7 / Ctrl + F7在文件中查找用法/查找用法Ctrl + Shift + F7高亮显示Ctrl + Alt + F7显示使用编译/运行 Ctrl + F9制作项目(编译修饰和从属关系)Ctrl + Shift + F9编译选定的文件、包或模块Alt + Shift + F10选择配置并运行Alt + Shift + F9选择配置和调试Shift + F10运行Shift + F9调试Ctrl + Shift + F10从编辑器运行上下文配置调试 F8单步执行(不进入类或方法)F7单步执行(进入类或方法)Shift + F7智能单步执行(步入类或方法)Shift + F8跳出子函数Alt + F9执行到光标所在位置Alt + F8计算表达式F9恢复运行Ctrl + F8设置断点Ctrl + Shift + F8查看断点导航 Ctrl + N跳到类Ctrl + Shift + N跳到文件Ctrl + Alt + Shift + N跳到符号Alt + Right/Left跳到下一/上一个编辑器选项卡F12返回上一个工具窗口Esc跳到编辑器 (从工具窗口)Shift + Esc隐藏活动或上一个活动窗口Ctrl + Shift + F4关闭活动运行/邮件/查找/... 选项卡Ctrl + G跳到指定行Ctrl + E最近的文件弹出菜单Ctrl + Alt + Left/Right后退/前进Ctrl + Shift + Backspace导航到上一个编辑位置Alt + F1在任何视图中选择当前文件或符号Ctrl + B or Ctrl + Click跳到声明类Ctrl + Alt + B跳到实现类Ctrl + Shift + I打开快速定义查找Ctrl + Shift + B跳到类型声明Ctrl + U跳到父类/父类方法Alt + Up/Down跳到上一个/下一个方法Ctrl + ] / [移动到代码块结束/开始Ctrl + F12文件结构弹出菜单Ctrl + H类型层次结构Ctrl + Shift + H方法层次结构Ctrl + Alt + H调用层次结构F2 / Shift + F2下一个/上一个高亮错误F4 / Ctrl + Enter编辑源/视图源Alt + Home显示导航栏F11切换书签Ctrl + F11使用助记键切换书签Ctrl + [0-9]跳到编号书签Shift + F11显示书签重构 F5复制F6移动Alt + Delete安全删除Shift + F6命名Ctrl + F6更改签名Ctrl + Alt + N内联Ctrl + Alt + M提取方法Ctrl + Alt + V提取变量Ctrl + Alt + F提取字段Ctrl + Alt + C提取常量Ctrl + Alt + P提取参数VCS/本地历史记录 Ctrl + K提交项目至VCSCtrl + T从VCS更新项目Alt + Shift + C查看最近修改Alt + `弹出VCS菜单通用 Alt + [0-9]打开相应工具窗口Ctrl + S保存全部Ctrl + Alt + Y同步Ctrl + Shift + F12编辑窗口最大化Alt + Shift + F添加到收藏夹Alt + Shift + I使用当前配置文件检查当前文件Ctrl + `快速切换编码、配色等方案Ctrl + Alt + S打开设置对话框Ctrl + Alt + Shift + S打开项目结构对话框Ctrl + Shift + A查找操作Ctrl + Tab切换选项卡/工具窗口实时模板 Ctrl + Alt + JSurround with Live TemplateCtrl + JInsert Live TemplateITERIteration according to Java SDK 1.5 styleINSTCheck object type with instanceof and downcast itITCOIterate elements of java.util.CollectionITITIterate elements of java.util.IteratorITLIIterate elements of java.util.ListPSFIterate elements of java.util.ListTHRthrow new快捷键截图

October 2, 2019 · 2 min · jiezi

有的线程它死了于是它变成一道面试题

有些线程它活着,但它躺在池中碌碌无为;有的线程它死了,于是它变成一道面试题。 这次的文章,要从一次阿里的面试说起。 我记得那天是周一,刚刚经历过周末过的放松,干劲十足的我正在键盘上疯狂的输出。这时,我的手机响了起来,拿起一看,是来自杭州的电话,心想这次是要给我推荐股票呢还是要让我贷款呢。我接起了电话,准备“调戏一番”。那边响起一个声音:"你好,请问是xxx吗?这边是杭州阿里巴巴,现在有时间进行电话面试吗?"。说实在的,听完这句话后,我感觉我已经身在杭州,干劲十足的在杭州的阿里的工位上"修福报"。但是我现在正在疯狂输出,没有时间,于是我说:"不好意思,现在没有时间,可以约在今天晚上8点钟吗?". 晚上如约接到了电话。我们直奔主题,在你来我往中进行了友好的技术交流。具体的面试过程就不详述了,后面有机会整理一份面试分享。整个面试过程中,有这么一道题给我留下了深刻的印象: 一个线程池中的线程异常了,那么线程池会怎么处理这个线程?需要说明一下,文中讨论的线程池都是Executors线程池。 对于Executors线程池我可以说是烂熟于心,因为工作中用的比较的多,阅读过其源码。也是我作为面试官时必问的几个范围之一,比如以下问题: 了解JDK Executors线程池吗?知道JDK提供了哪些默认的实现吗?看过阿里巴巴java开发手册吗?知道为啥不允许使用默认的实现吗?你们没有用默认的吧?那来介绍一下你们自定义线程池的几个常用参数呗?你这个几个参数的值是怎么得来的呀?算出来的?怎么算出来的?线程池里面的任务是IO密集型的还是计算密集型的呢?好,现在我们有一个自定义线程池了,来说一下你这个线程池的工作流程呗?那你这个线程池满了怎么办呀?拒绝?咋拒绝?有哪些拒绝策略呢?别紧张,随便说两个就行。......回到开始说的阿里巴巴java开发手册不允许使用默认实现,你回答说可能会引起OOM,那我们聊聊JVM吧...... 阿里巴巴java开发手册关于线程池创建的建议 这一系列关于线程池的连环炮,就是我作为面试官时必问的几个问题。别问为什么,因为我们的招聘JD上明确写了:熟悉多线程编程。而这些问题,我觉得是熟悉多线程编程的基础。这里我也不解答了,这种文章网上还是挺多的,可以去了解一下。 这块真的很重要,我也多次给我的小伙伴强调: 来吧,一起分析一波好了现在回到阿里的面试官问我的这道面试题: 一个线程池中的线程异常了,那么线程池会怎么处理这个线程?先说说我当时的回答,因为心里没底,我的回答很犹豫也很烂!如下: 我的回答总结起来三句话: 1.抛出堆栈异常 ---这句话对了一半!2.不影响其他线程任务 ---这句话全对!3.这个线程会被放回线程池 ---这句话全错!测试用例写起来抛出堆栈异常为啥对了一半?先让程序跑起来,我们用事实说话:从执行结果我们看出 当执行方式是execute时,可以看到堆栈异常的输出。当执行方式是submit时,堆栈异常没有输出。那么我们怎么拿到submit执行方式的堆栈异常呢,看图说话:所以,现在知道为什么回答:抛出堆栈异常只对了一半吧。execute方法执行时,会抛出(打印)堆栈异常。submit方法执行时,返回结果封装在future中,如果调用future.get()方法则必须进行异常捕获,从而可以抛出(打印)堆栈异常。你以为这一部分写到这里就完事了?那不行啊,你心里没有一个疑问吗?为啥execute直接抛出异常,submit没有直接抛出异常呢? 源码之下无秘密:当执行方式是executes时:在java.util.concurrent.ThreadPoolExecutor#runWorker中抛出了异常:在_java.lang.ThreadGroup#uncaughtException_进行了异常处理:这个uncaughtException是何许人也,看java doc上咋说的:这个方法是JVM调用的,我们只需要指定我们想要的处理方式即可。那我们怎么指定呢: //直接new Thread()的时候Thread t=newThread();t.setUncaughtExceptionHandler(newThread.UncaughtExceptionHandler(){publicvoiduncaughtException(Thread t, Throwable e){//根据业务场景,做你想做的 }});//线程池的时候:ExecutorService threadPool = Executors.newFixedThreadPool(1, thread -> {Thread t =newThread(thread);t.setUncaughtExceptionHandler((t1, e) ->System.out.println("根据业务场景,做你想做的:"+ e.getMessage()));returnt;}); 当执行方式是submit时: 其本质也是调用了execute方法,所以它还是回到_java.util.concurrent.ThreadPoolExecutor#runWorker_方法:向前,继续跟进去看看:_java.util.concurrent.FutureTask#setException_干啥了啊,瞅一眼:深呼吸,整理好思路,我们马上走向最终的真相:好了,第一个议题【抛出堆栈异常为啥对了一半?】讨论完毕。在源码里面走了一趟,现在我们可以给出这一部分的满分答案了。 不影响其他线程任务,回答正确这一部分我们直接上代码,运行起来看结果吧:代码和运行结果是不会骗人的:线程池中一个线程异常了后,不影响其他线程任务大家注意线程名称这个细节:1,2,3,4,6。魔鬼都在细节里啊,这个点我下面会讲,先在这里把问题抛出来:我就纳闷了,怎么没有5啊?! 这个线程会被放回线程池为啥全错了?我们去源码里面寻找答案:让源码给出答案:5号线程去哪里了?new Worker()方法会告诉你:5去哪里了。再配上这张由我这个灵魂画师亲自操刀画的图,一起食用,味道更佳: 现在知道为啥:我回答这个线程会被放回线程池为啥全错了吧。还附带送你一个线程名称变化的细节,不客气。 总结一下当一个线程池里面的线程异常后:当执行方式是execute时,可以看到堆栈异常的输出。当执行方式是submit时,堆栈异常没有输出。但是调用Future.get()方法时,可以捕获到异常。不会影响线程池里面其他线程的正常执行。线程池会把这个线程移除掉,并创建一个新的线程放到线程池中。不要背答案,要理解,要深入,上面说完后记得在问问面试官,需要我从源码的角度讲一讲吗?这逼装的,礼貌而不失风度。以上,我关于《一个线程池中的线程异常了,那么线程池会怎么处理这个线程?》这个问题的见解就表达完毕,仅代表个人观点,欢迎有不同意见的小伙伴,一起讨论,一起进步。 最后说一点这篇文章是我上周五推完上一篇文章之后就在构思并且着手准备了。大部分内容都是思考于晚上睡觉前的半小时,写于周末和工作日的早上早起的一小时。其实想到写什么内容并不难,难的是你对内容的把控。关于技术性的语言,我是反复推敲,查阅大量文章来进行证伪,总之慎言慎言再慎言,毕竟做技术,我认为是一件非常严谨的事情,我常常想象自己就是在故宫修文物的工匠,在工匠精神的认知上,目前我可能和他们还差的有点远,但是我时常以工匠精神要求自己。就像我在群里表达的:对于技术文章(因为我偶尔也会荒腔走板的聊一聊生活,写一写书评,影评),我尽量保证周推,全力保证质量。最后,再感叹一次: 有些线程它活着,但它躺在池中碌碌无为;有些线程也活着,但它一刻不停忙到飞起;有的线程它死了,被抛弃,被回收,但是它无怨无悔, 因为它是死在执行任务的路上,它凭借自己最后的一声呐喊“为了新兄弟,移除我吧!”最后,变成一道面试题。 我还没答上来。 欢迎关注公众号【why技术】。在这里我会分享一些技术相关的东西,主攻java方向,用匠心敲代码,对每一行代码负责。偶尔也会荒腔走板的聊一聊生活,写一写书评,影评。愿你我共同进步。

September 19, 2019 · 1 min · jiezi

Message-前言中不允许有内容

使用Pycharm创建项目,创建前、后都没有问题,当项目关闭了,在打开之前的项目都打不开,屏幕只是闪一下,但是打不开项目,找到idea的日志(默认的日志文件路径在C盘,当前用户所属目录的根目录下.pytharm/system/log )显示: 点一次闪一次一个报错,那就慢慢恢复自定义的设置吧,最后调试找到了是字符集编码设置的地方出了问题: 图中红色方框中的地方改称NO BOM,然后将项目中已经存在的.idea文件夹删除重新导入项目,以后就无虑了;

September 8, 2019 · 1 min · jiezi

Spring-Boot整合JSP遇到的问题解决思路

虽然Spring Boot默认已经不支持JSP了,但是本着学习至上的原则,在多方查资料的情况下,进行了一番整合操作。 1 Maven依赖<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- JSP 渲染引擎 --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <!-- 如果是外部tomcat部署的话,provided属性需要开启 --> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency> <!-- JSTL --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency></dependencies>2 Controller代码@Controllerpublic class JspOnSpringBootController { // 从 application.yml 中读取配置,如取不到默认值为Hello Jsp @Value("${application.hello:Hello Jsp}") private String hello = "Hello Jsp"; /** * 默认页<br/> * @RequestMapping("/") 和 @RequestMapping 是有区别的 * 如果不写参数,则为全局默认页,加入输入404页面,也会自动访问到这个页面。 * 如果加了参数“/”,则只认为是根页面。 * 可以通过localhost:7070或者localhost:7070/index访问该方法 */ @RequestMapping(value = {"/","/index"}) public String index(Model model) { // 直接返回字符串,框架默认会去 spring.view.prefix 目录下的 (index拼接spring.view.suffix)页面 // 本例为 /WEB-INF/jsp/index.jsp model.addAttribute("name", "Landy"); model.addAttribute("hello", hello); return "index"; }}3 application.properties配置#service端口(Dev)server.port=7070spring.mvc.view.prefix = /WEB-INF/jsp/spring.mvc.view.suffix = .jspapplication.hello = hello jsp on spring boot4 Jsp页面代码<html><head> <title>JSP on Spring Boot</title></head><body> <h1 style="color: red">${name}, ${hello}</h1></body></html>5 运行按理说,以上完成后就可以运行顺利看到页面结果了,但是如果直接运行SpringApplication的main方法是不能看到结果的,报404. ...

September 7, 2019 · 1 min · jiezi

Spring-Framework之再探Core-Container中

特别说明这是一个由simviso团队进行的关于Spring Framework 5.2版本内容分享的翻译文档,分享者是Spring Framework 5.2项目leader。视频第一集:https://www.bilibili.com/vide... 视频第二集:https://www.bilibili.com/vide... 视频翻译文字版权归 simviso所有,未经授权,请勿转载:参与人员名单:顺带推荐一个专业的程序员后端微信群的圈子: 1. GenericApplicationContext出于多种目的,特别是在GenericApplicationContext这里,我们专门提供了Kotlin扩展。你不需要特意引入它们(Kotlin支持),不需要额外的步骤,它们已经成为Spring Framework核心中的一部分。so,无论你使用的是 Spring context 5.0、5.1、5. 2 中任何版本,你都会自动获得带有 Kotlin 扩展的 GenericApplicationContext。如果你选择使用 Kotlin 来进行开发,那么它们就会被 Kotlin 编译器检测编译。来看这个GenericApplicationContext,它的处理方式是不是看起来很熟悉,但它是使用Kotlin来写的。可以看到,图中下面的和上面的版本明显有一些区别。举个例子来讲,我们撇开Bar.class,然后来说这里该如何去创建一个bar实例。我们只需要拥有一个Supplier实例即可,so,我们来看registerBean仅仅需要一个基于构造器调用的supplier实例,没有其他。 理由其实很简单,因为这并不是Java里的Lambda表达式,而是一个Kotlin函数。它实际上调用了一个由Kotlin实现的registerBean重载函数。在我们的 Kotlin 扩展中,Kotlin扩展函数是基于一个元数据模型(T::class.java)一个反射模型(BeanDefinitionCustomizer)进行设计的。当我们来问一个Kotlin函数,你会返回什么,它预先已经知道了,而此时Java 8 lambda表达式是无法进行返回类型检查的。基于此,我们可以很好去使用这个特性,仅需要一个supply实例(注:无须使用T.class进行类型限定)就可以知道所得到的组件类型。即通过suplier 实例创建的bean的类型。 我们可以通过Kotlin中一些其他函数API来提高开发体验。下面的这个版本基本上只是使用了一点语法糖,有一点点的语法差异。这是另外一种应用Kotlin语言特性来实现目标API的方式。这不是一个正式的Gradle风格,只是有一点DSL(领域专用语言)的风格。感觉有一点像Gradle构建工具 ,Kotlin风格的Gradle构建工具。 so,这里通过一种不一样的风格来表达Generic(这里指GenericApplicationContext),但这只是Kotlin语言的一种变体,我们这里用它来实现我们的目的,没有什么特别的。 2. 性能调优与GraalVMok,让我们开始转向另一个重要的话题。对于Spring Framework 5,我们一直致力于不断调整并提高它的性能 当然我们也在努力改善提高开箱即用的性能。虽然和我们的目标相关,但努力的方向有点不一样。我们试图在代码库中避免一些性能很差的东西来减少不必要的开销。同时我们也有尝试为你提供带hook的设施,一种你可以用来调整性能的机制。如果你知道的话,你就可以根据它做出最具体的假设,这些假设无法通过通用的框架代码来实现。在5.2中最明显的例子就是注解处理。最初,在5.1版本中,我们主要通过对现有代码进行修订,但在5.2中我们选择完全重新实现。遗憾的是,在java中注解的处理是一个相当复杂的事情。如果你之前有做过这样的事情,你也许就知道我的意思,这也是框架日常所做的绝大多数事情。在一个应用程序的代码中,你几乎不需要去写如何查找一个注解。也就是说你只需要声明注解,框架会负责对它们进行正确的查找。这个过程其实非常复杂,效率又很低。主要原因在于它们是由Java来实现的。我们为了做到最好,在这个方面我们尽量避免反射,避免通过代理。然而不幸的是,注解实例是通过Java代理实现的,主要也是基于jdk自身。因此,我们竭尽所能,从开始就避免使用反射。 关于AnnotationUtils和AnnotatedElementUtils 这两个API基本上是相同的,你在使用SpringBoot的过程中自然会使用到它们(我们会使用注解,那就会使用到这些工具类)。你很少会亲自去使用它们,但是基本上它们对你来讲是透明的。(也就是你看不到,你只需要关心使用什么注解,不需要关心注解背后的实现)如果你使用了其他的Spring项目例如Spring Integration,Spring Batch,那你就可以从这个透明特性里面得到很明显的好处。 在Spring 5.2中有一个名为MergedAnnotations的API,它对Spring声明过的注解层次结构的内省非常有用。你可能感觉到Spring的注解模型很复杂了。这里有个元数据注解模型,你可以在此之上覆写它的属性。你不需要做很复杂的事情,你可以很轻易地通过这些选项获取到。因此我们引入了一种全新的API,它可以使所有的内部检查变得非常简单直接,并且十分高效。 我们的精力更多放在了对于组件中可存在注解和不可存在注解的注册上面。你可以通过编程规范来告诉容器某些注解类型只能存在于特定的组件类里面。换句话讲,在这种特定的组件类和特定的组件包中,根本就不可能有这些注解类型的使用。我们不需要在这些地方去查找这些注解。在这些地方,你根本就找不到它们的。 这些假设我们很难在程序中自己通过代码来实现。正常情况下,注解可以被应用于任何地方,这是一个常识。可能由于一些规定,你需要在你或团队的代码库中服从一些约定,即特定的地方只能用一些特定的注解类型。如果你告诉我们这些约定,我们就可以在运行代码时减少这些注解产生的性能开销。对此我们已经在5.2中做了大量的工作,也就是通过这些信息在注解查找的时候尽量跳过它。以此来整合出一个Java 标准的索引排列。 我们在启动的时候并没有索引,我们只有这些类文件。在运行时类索引并不指向类文件(指向的是JVM里面的class字节码)。我们只能通过两种途径对注解进行内省。如果我们在构建时想要获取额外信息的话,就可以通过像Jandex的索引或者是一个自定义的索引排列一样来达到目的(通过索引来 存储一些关键信息)。就好像你在其他的基础架构中所使用的索引一样。在启动时如果通过加载这样的一个索引来提取信息,这个信息可能是Spring ApplicationContext相关的内容,通过这个索引我们就能立马获取到这个信息。这些在我们的BootStrap代码中都有提供配置可进行调整。我们也会在SpringBoot中会对引用排列进行重新评估,尤其是这个东西它是不是已经可以被SpringBoot自动使用。如果在使用时,发现了一个Jandex索引,SpringBoot会自动识别评估并应用它。 关于这块,接下来的路还很长,但在七月份我们会将我们这些想法放到SpringBoot中。最重要的是如果你使用了这些功能(索引排列支持),那么它将会是你整个架构的一个热点(很明显会大量的用到,因为解决了很多痛点)。同时,你可以对这些可用功能进行调整以避免不必要的开销。今天我们另一个主题则是GraalVM,它是最近比较火的一个话题。Spring框架对它的支持已经有一段时间了(即将Spring Boot Application封装成一个GraalVM Native Image在上面运行),现在这部分仍然处于实验阶段。目前,我们在github上已经简单创建了一个基于GraalVM 19GA版本的wiki。通过它,人们可以进行有针对性的讨论,今年晚些时候,我们也会对其进行专门的讨论。GraalVM Native Image 它到底是什么东西?它是一个很特别的部署架构。它并不是我们常见的JVM,两者完全不一样。它没有任何动态类加载,也没有任何动态内省。很直接的讲,它并没有你想的那么与众不同。GraalVM同样支持反射。你只需要给Native Image工具提供配置文件,这样它就能预先将正确的信息内置进Native Image。所以,在运行时你不能做任何动态反射,但是你可以将你需要用到反射的地方进行提前配置。在这个基于GraalVM的定制版的Spring Framework application中我们对prototypes 进行了实验,得到了比索引排列更好的效果。 拿我们之前很熟悉的函数式Bean Registration(前面ppt中的例子)。通过内联的Supplier注册Bean的过程可以很自然地在GraalVM上运行,你不需要去做什么。在这里我们需要讨论的是在GraalVM中,基于注解的组件模型需要进行一些额外的工作,你需要提前告诉GraalVM中的Native Image,你所要操作的组件类型以及内省。 ...

August 20, 2019 · 1 min · jiezi

Java-实现与-Word-风格相似的菜单

最后一次更新于 2019/07/09 菜单效果演示图Microsoft Word 本程序菜单 简单的菜单布局设计这个项目是人机交互设计课程的一个小作业。不过该作业的要求是不需要编写任何代码的。但为了体现我们小组的设计和分组理念,我还是决定编写一个菜单小程序,该程序有灵活的布局,是微软的 Word 编辑器和 IntelliJ IDEA 编辑器布局的综合。 我们小组需要处理的所有功能已经预先被分类成如下几种名称: 功能分组原理功能性根据 功能相似的应该放在一起 的原则来布局所属的按钮。 例如,空白文本,加粗文本,斜体文本和下划线文本。时序性根据 功能的分组可以反应执行的顺序 的原则来布局所属的按钮。 例如,先复制后粘贴。使用频率根据 使用频率相似的功能应该放在一起 的原则来布局所属的按钮。通常常用操作都会在工具栏里使用户能直接点击。 例如,保存和打印。菜单设计原理费茨法则按钮的面积越大越好。而用户的鼠标指针和按钮之间的距离越小越好。因为这意味着用户实现功能的时间变少。 人类是会犯错误的设计过程中要尽量避免误导用户。比如说,如果先使用户看到“保存为”功能而不是“保存”功能的话很可能使用户误认为“保存为”即是“保存”功能。 人类的失误设计过程中要尽量避免用户意外点中不相关的功能。比如说,用户可能误点中“删除”而不是“粘贴”如果这两个功能紧挨在一起的话。 Java 编程实现从各类文本编辑器和以上原理的灵感激发下,我决定做出灵活布局的菜单。常见的操作会用工具栏来实现,它们被包裹在子菜单中。子菜单 可以利用 JToolBar 实现,当用户点击其他的子菜单时当前显示的子菜单应从 JFrame 移除。举个例子 public void menuSelected(MenuEvent e) { if(toolbar != null) { toolbar.setVisible(false); frame.remove(toolbar); } if(viewbar == null) { viewbar = new JToolBar(); JMenuBar menuBar = new JMenuBar(); // 页面。 ImageIcon pageIcon = new ImageIcon("src/images/page.png"); JMenu page = new JMenu("View Page"); page.setIcon(pageIcon); menuBar.add(page); // 页面所在位置。 ImageIcon posIcon = new ImageIcon("src/images/position.png"); JMenu position = new JMenu("Position"); position.setIcon(posIcon); menuBar.add(position); // 显示文档。 ImageIcon docIcon = new ImageIcon("src/images/documents.png"); JMenu document= new JMenu("Alternative Document"); document.setIcon(docIcon); menuBar.add(document); viewbar.add(menuBar); } frame.add(viewbar, BorderLayout.PAGE_START); viewbar.setVisible(true); frame.setVisible(true);}对于快捷键的实现而言,我们需要调用 setAccelerator() 函数来设置快捷键。比方说, ...

July 9, 2019 · 1 min · jiezi

Java-实现经典扫雷游戏

最后一次更新于 2019/07/08 效果演示图 Java 实现经典扫雷游戏本扫雷游戏以下 功能: 如果点中炸弹会显示炸弹。玩家左键点击方块能显示该方块周围会出现几个炸弹,如果不存在炸弹的话扫描范围会被放大。满足各种行数,列数和炸弹个数要求。对不同水平的玩家提供不同的游戏难度级别。如果玩家单击鼠标右键会显示红旗。如果玩家双击鼠标右键会显示问号。如果玩家游戏挑战失败显示所有炸弹隐藏的地方以及玩家失误标记的地方。如果玩家挑战成功显示所有的炸弹(原本炸弹的位置有可能已被玩家用小红旗标识了)。源代码包括抽象类和接口。我将程序分为三个部分来介绍:GameDriver,Library,UserInterface。 游戏驱动这部分相当简单(因为只有一个主函数)。Driver 类被用于作为启动游戏的接口。 代码如下所示: package GameDriver;import UserInterface.Menu;/** * @author Hephaest * @since 3/21/2019 8:41 PM * 此类是用来运行扫雷游戏程序的。 */public class Driver{ public static void main(String[] Args) { // 游戏启动的时候会附带一个选项菜单窗口。 new Menu("Minesweeper"); }}工具库包 Library 内只有两个文件。第一个是抽象类 Bomb,它用于存储和有关游戏窗口的物理信息。 代码如下所示: package Library;import UserInterface.GameBoard;/** * @author Hephaest * @since 3/21/2019 8:41 PM * 这个抽象类中的抽象方法会在被继承时实现。 */public abstract class Bomb{ /** 游戏窗口实例 **/ protected GameBoard board; /** 实例的高度 **/ protected int boardHeight; /** 实例的宽度 **/ protected int boardWidth; /** * Create bombs, which can be placed on a GameBoard. * @param board the GameBoard upon which user clicks on. */ public Bomb(GameBoard board) { this.board = board; // 真正加入计算的高和宽去需要减去填充边距的长度。 boardHeight = (board.getHeight() - 20) / 20; boardWidth = (board.getWidth() - 20) / 20; } /** * 该方法将会被用于分布炸弹的位置。 */ protected abstract void reproduceBomb();}第二个工具就是 TimeChecker 接口,它使用将毫秒时间转换成相对应的时间表达,将会被用于 SmartSquare 类。 ...

July 8, 2019 · 11 min · jiezi

IntelliJ-IDEA-输入代码代码提示时输入法自动由英文切换到中文问题

今天遇到个鬼事,webstorm 写着,然后输入法,变成你输入一个英文字母,他自动变成了中文输入法然后没办法,百度了下,然并卯,发现也有个机油遇到这个问题: 后来,我研究了下,可能是输入的问题然后我就右击小图标,点击了“设置”! one step,接下来,follow me,看图@1:</center> two step,高级设置>点击切换搜狗..>更改按键顺序 看图@2: three step,点按启用>下拉选中ctrl+shift>确定 看图@3: four step,game over! 爸爸很欣慰。

June 26, 2019 · 1 min · jiezi

使用IDEA创建一个SpringMVC的项目

写在前面:参照《spring实战》实现 1. 使用idea创建项目 这里使用gradle构建项目依赖 使用自己本地的gradle环境 2. 构建完成项目之后修改gradle的配置文件(使用阿里库) 3. 引入springmvc的相关依赖 版本配置文件,这个信息都在上面的依赖中使用到了 4. 这里我们使用纯Java配置 a. 创建WebInitializer类用于替代web.xml的功能 b. WebConfig.java配置springmvc的相关参数 c. RootConfig.java配置spring的相关参数 d. 创建一个控制器HomeController e. 在下面的目录里面创建home.jsp 5. 使用tomcat启动项目,访问

June 26, 2019 · 1 min · jiezi

Spring-Cloud-上手实战架构解析及实作

Spring简介为什么要使用微服务单体应用:目前为止绝大部分的web应用软件采用单体应用,所有的应用的用户UI、业务逻辑、数据库访问都打包在一个应用程序上。 缺点: 开发相互干扰,随着应用的不断升级沟通协调成本增加 应用上线由于某个功能升级导致需要整体的构建、整体测试、整体发布微服务把单体应用拆分成小的、松藕合分布式服务的形式 每个应用一定是独立构建、独立部署与测试,应用也是独立发布,应用于应用直接通常通过restful API接口的形式进行相互调用。解决了单体应用带来的困扰。 Spring cloud 是什么发展历史 2002,Rod Johonson发表了<<Expert One-on-One J2EE Design and Development>>, 包含了3万行的代码在包com.interface21中 2003,Juerge Hoeller,Yann Caroff 联系Rod,将书中代码开源,Yann提出Spring这个词,冠于书中代码; 并发布0.9,使用Apache 2.0协议;Thomas Risberg负责Spring JDBC;Ben Alex将Acegi Security贡献给Rod和Juergen 2004,1.0发布 2005,<<Professional Java Development with Spring Framework>> <<Pro Spring>>出版;1.2.6发布。 AspectJ Leader Adrian Coyler加入Interface21作为首席科学家; 2006,Security 1.0、Spring webflow 1.0发布;Spring 2.0发布; 2007,Spring Batch、WebService、Integration发布;Spring 2.5发布; 2008,Spring Integration 1.0,Spring 2.5.6,Spring Batch 1.0;买了g2One,一家提供Groovy and Grails的公司; 2009,被VMWare发了42亿美金买下;Spring Python、STS发布、3.0发布(将包拆开,不提供超级包),买了Cloud Foundry; 2010,VMWare买了RabbitMQ公司,获得RabbitMQ和Redis技术; 2011,Spring 3.1、Spring AMQP、Spring Data JPA、Spring-data-common 1.0 、Spring Data Redis、Spring Data Mongodb发布; 2012,Rod Johnson离开VMWare;Spring Android、Mobile发布; 2013,VMWare 和 EMC 合力组建了一家公司,Pivotal。Spring 4.0、Spring Boot发布; 2014,Spring 4.1.3、SpringBoot 1.0发布; 2015,Spring 4.2、4.3发布; 2016,Spring 4.3 GA 2017,Spirng 5.x Spring的出现让EJB等重量级的容器技术逐渐走向末路。Spring 通过对Bean的生命周期的管理,可以快速方便的实现业务的逻辑处理。Spring 可以方便的整合几乎所有的主流的开源项目如JPA,缓存,消息组合等等,方便的进行开发。 ...

June 24, 2019 · 3 min · jiezi

前后端分离ssm配置跨域

前后端分离开发需要跨域,之前只会pringboot跨域,只需要一个配置类即可,下面记录一下ssm的配置 三个文件需要添加配置 SimpleCORSFilter.java实现Filterpublic class SimpleCORSFilter implements Filter { private boolean isCross = false; @Override public void destroy() { isCross = false; } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (isCross) { HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletResponse httpServletResponse = (HttpServletResponse) response; System.out.println("拦截请求: " + httpServletRequest.getServletPath()); httpServletResponse.setHeader("Access-Control-Allow-Origin", "*"); httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); httpServletResponse.setHeader("Access-Control-Max-Age", "0"); httpServletResponse.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token"); httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); httpServletResponse.setHeader("XDomainRequestAllowed", "1"); } chain.doFilter(request, response); } @Override public void init(FilterConfig filterConfig) throws ServletException { String isCrossStr = filterConfig.getInitParameter("IsCross"); isCross = isCrossStr.equals("true") ? true : false; System.out.println(isCrossStr); }dispatcher-servlet.xml(springMVC的配置文件)<!-- 接口跨域配置--> <mvc:cors> <mvc:mapping path="/**" allowed-origins="*" allowed-methods="POST, GET, OPTIONS, DELETE, PUT" allowed-headers="Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With" allow-credentials="true" /> </mvc:cors> ## web.xml ...

June 22, 2019 · 1 min · jiezi

springbootquartzjsoupkafka

最近在学习springboot,光看官方文档比较枯燥,于是想用一个项目把各种框架和技术串联起来,思来想去觉得爬虫是一个不错的idea。 大概思路是这样:固定频率去爬取新浪财经的头条新闻,爬到的标题和链接以json方式推到kafka的topic中,再通过ELK消费,在kibana中查看。 首先通过Spring Initializr下载一个demo工程,选择我们需要的依赖包,jsoup的包需要额外添加。 引入到idea中,修改pom文件,加入jsoup依赖,由于习惯了使用jetty作为web容器,所以把tomcat踢掉,引入jetty的依赖,为了方便处理json,引入fastjson依赖。 quartz的使用可以参考官网文档,这里我们通过mysql来持久化定时任务相关信息,涉及到mysql,顺便把mybatis和druid依赖也一起引入。 quartz相关表的sql在官网的demo里可以找到,这里就略过,假设表已建好。springboot提供了很好的quartz支持,自动配置了一个Scheduler,直接Autowired就可以使用,我们新建一个Service,在系统启动的时候启动爬取新闻的定时任务,代码如下: 假设每30分钟爬取一次,我们还需要一个Job实现类,来完成具体的爬取任务,也可以通过不同的job来分别爬取,这里就不展开了。Job实现类如下: 在爬网页之前先看一下每个页面的结构,以新浪财经为例,地址:https://finance.sina.com.cn/,查看页面结构可以发现,我们需要的头条新闻都在“m-hdline”这个class的a标签下,jsoup的使用比较简单,根据需要查找对应的文档就可以了,直接上代码: 接下来需要将获取到的数据发到kafka的topic中,我的win10是家庭版,天生不带docker,我又懒得折腾toolbox,于是搞了个自带的ubuntu虚拟机,直接下载kafka安装,然后创建一个topic:financenews。这时候可以将kafka的信息配置在我们的工程中,如下: springboot也贴心的为我们准备了KafkaTemplate,Autowired即可。这里我们还没有搭建好elk,可以使用直接监听定时任务发送到的topic中的消息是否正常。 最后在job中添加发送消息到kafka的处理: 代码到这里基本差不多了,下面我们启动应用看看效果: 成功。

June 14, 2019 · 1 min · jiezi

springbootmybatismybatisplus分页查询简单实现

最近在研究mybatis,然后就去找简化mybatis开发的工具,发现就有通用Mapper和mybatis-plus两个比较好的可是使用,可是经过对比发现还是mybatis-plus比较好,个人觉得,勿喷。。。集成还是非常简单的,然后就在研究怎么分页,开始研究通用mapper时发现有个pagehelper的分页工具可以和它搭配。然后反过来看是不是也可以和mybatis-plus搭配使用呢?发现mybatis-plus之前是可以支持的,升级成3.X之后就不再支持了。然后就研究mybatis-plus自带的分页工具吧!今天就简单的写个例子吧! 首先我们肯定要先建一个springboot的项目,这里我就不再多说怎么创建了昂,不会的话请参考Spring Boot 的简单教程(一) Spring Boot 项目的创建创建好项目之后我们就需要配置maven依赖了,这里附上我的pom.xml文件了。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- 这是mybatis-plus依赖 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.1.1</version> </dependency> <!-- 这是mybatis-plus的代码自动生成器 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.1.1</version> </dependency> <!-- 这是模板引擎依赖 --> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.28</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>配置好pom.xml文件之后,理所当然的就需要配置application.yml了。 #端口号 server: port: 8888 #数据库的配置信息 spring: datasource: url: jdbc:mysql://localhost:3306/blog #自己的数据库名称 username: root password: 123456 mybatis: #开启驼峰命名法 configuration: map-underscore-to-camel-case: true mybatis-plus: # xml地址 mapper-locations: classpath:mapper/*Mapper.xml # 实体扫描,多个package用逗号或者分号分隔 type-aliases-package: com.zhouzhaodong.pagination.entity #自己的实体类地址 configuration: # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl接下来就需要配置mybatis-plus的代码生成器了。/** * <p> * 读取控制台内容 * </p> */ public static String scanner(String tip) { Scanner scanner = new Scanner(System.in); StringBuilder help = new StringBuilder(); help.append("请输入" + tip + ":"); System.out.println(help.toString()); if (scanner.hasNext()) { String ipt = scanner.next(); if (StringUtils.isNotEmpty(ipt)) { return ipt; } } throw new MybatisPlusException("请输入正确的" + tip + "!"); } public static void main(String[] args) { // 代码生成器 AutoGenerator mpg = new AutoGenerator(); // 全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); gc.setOutputDir(projectPath + "/src/main/java"); gc.setAuthor("jobob"); gc.setOpen(false); // gc.setSwagger2(true); 实体属性 Swagger2 注解 mpg.setGlobalConfig(gc); // 数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/blog?useUnicode=true&useSSL=false&characterEncoding=utf8"); // dsc.setSchemaName("public"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("123456"); mpg.setDataSource(dsc); // 包配置 PackageConfig pc = new PackageConfig(); //这里有个模块名的配置,可以注释掉不用。// pc.setModuleName(scanner("模块名"));// pc.setParent("com.zhouxiaoxi.www"); pc.setParent(scanner("模块地址")); mpg.setPackageInfo(pc); // 自定义配置 InjectionConfig cfg = new InjectionConfig() { @Override public void initMap() { // to do nothing } }; // 如果模板引擎是 freemarker String templatePath = "/templates/mapper.xml.ftl"; // 如果模板引擎是 velocity// String templatePath = "/templates/mapper.xml.vm"; // 自定义输出配置 List<FileOutConfig> focList = new ArrayList<>(); // 自定义配置会被优先输出 focList.add(new FileOutConfig(templatePath) { @Override public String outputFile(TableInfo tableInfo) { // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! return projectPath + "/src/main/resources/mapper/"// + + pc.getModuleName() + 如果放开上面的模块名,这里就有一个模块名了 + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; } }); /* cfg.setFileCreate(new IFileCreate() { @Override public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) { // 判断自定义文件夹是否需要创建 checkDir("调用默认方法创建的目录"); return false; } }); */ cfg.setFileOutConfigList(focList); mpg.setCfg(cfg); // 配置模板 TemplateConfig templateConfig = new TemplateConfig(); // 配置自定义输出模板 //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别 // templateConfig.setEntity("templates/entity2.java"); // templateConfig.setService(); // templateConfig.setController(); templateConfig.setXml(null); mpg.setTemplate(templateConfig); // 策略配置 StrategyConfig strategy = new StrategyConfig(); //数据库表映射到实体的明明策略 strategy.setNaming(NamingStrategy.underline_to_camel); //数据库表字段映射到实体的命名策略, 未指定按照 naming 执行 strategy.setColumnNaming(NamingStrategy.underline_to_camel); //自定义继承的Entity类全称,带包名// strategy.setSuperEntityClass("***"); strategy.setEntityLombokModel(true); strategy.setRestControllerStyle(true); //自定义继承的Controller类全称,带包名// strategy.setSuperControllerClass("***"); strategy.setInclude(scanner("表名,多个英文逗号分割").split(",")); //自定义基础的Entity类,公共字段(可添加更多)// strategy.setSuperEntityColumns("id"); //驼峰转连字符 strategy.setControllerMappingHyphenStyle(true); //表前缀// strategy.setTablePrefix(pc.getModuleName() + "_"); mpg.setStrategy(strategy); mpg.setTemplateEngine(new FreemarkerTemplateEngine()); mpg.execute(); }运行之后就会出现所有需要的文件了。 ...

June 5, 2019 · 3 min · jiezi

基于springsecurityoauth2实现单点登录持续更新

基于spring-security-实现数据库版文章代码地址:链接描述可以下载直接运行,基于springboot2.1.5,springcloud Greenwich版本实现。前面两篇写了认证oauth2通过内存 还有jdbc实现认证中心。接下来我们采用oauth2实现管理系统的单点登录。 说到这里,需要介绍几个注解: @EnableAuthorizationServer 该注解用来开启认证服务,使用该注解表明自己是一个认证服务。 @EnableResourceServer 该注解要用来开启资源保护,表明自己是资源服务器受认证服务保护。 @EnableOAuth2Sso 该注解表示自己是oauth2客户端,也即单点登录客户端 @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) spring-security默 认禁用注解,使用该注解来判断用户对某个控制层的方法是否具有访问权限 好来,注解介绍完了,闲话少说。我们开始今天的主题“单点登录”。 (1)创建sso-client项目,修改maven依赖: 因为,是web项目需要添加maven依赖。 (2)在启动类加上@EnableOAuth2Sso注解,表明自己是客户端 (3)下面进行最重要的,设置配置文件 因为,前面几个配置在之前章节介绍过,这里只介绍server.servlet.session.cookie.name=OAUTH2SESSION这个配置。 这是个坑,我在没加这个配置之前,授权成功后,还是跳转授权登录页码。认证服务器和浏览器控制台也没有报错信息。只好debug一点点差错。 这里简单介绍下如何查阅源码,首先全局搜索自己的配置 security.oauth2.client.user-authorization-uri=http://localhost:9001/oauth/authorize因为这个地址是认证服务器请求授权的,所以,请求认证的过滤器肯定包含他。搜索的结果如下: 两个结果,一个是我们自己配置的忽略,点开另外一个: ok我们在源码中找到这个类,一直向上找,可以找到OAuth2RestTemplate 同样的,我们可以搜索这个地址,查找在认证服务器中是如何认证的。 跑偏了,还是介绍下这个配置吧,通过这个配置session和认证服务器不一样结局。也可以设置上下文路径 server.servlet.context-path=/sso-client (4)调回来,下来我们创建一个controller文件,用来获取授权用户信息: 在template下创建index.html欢迎页面: (5)启动客户端服务: (6)因为,我们需要请求认证服务器,校验token,因此认证服务器需要开启/oauth/token路径,修改WebSecurityConfig文件添加: (7)启动认证服务,访问客户端首页: http://localhost:9005 如下: 自动跳转到认证服务器登录地址,输入用户名: admin 密码: 123456 登录 你可以把项目修改端口启动试试,登录一个另一个不在需要登录。 未完待续,下一篇介绍资源服务器和认证服务器的集成。 有问题,请留言。

June 2, 2019 · 1 min · jiezi

基于springsecurityoauth2实现oauth2数据库版持续更新

基于spring-security-oauth2实现oauth2数据库版文章代码地址:链接描述可以下载直接运行,基于springboot2.1.5,springcloud Greenwich版本实现 该系列分为两个部分:分为内存实现,数据库实现。其中数据库实现采用RBAC权限角色管理。 上一篇,介绍了oauth2的内存实现,也就是认证服务把客户端和用户信息都存储在内存中,这样不利于拓展,不适合于生产环境。下面,我们开始基于mysql数据库的oauth2实现。 首先,我们创建oauth2数据库,注意编码选择utf-8mb4格式,utf-8是不规范的,mysql也没有进行更改。 好了,现在我们初始化表,sql如下: Drop table if exists oauth_client_details;create table oauth_client_details ( client_id VARCHAR(255) PRIMARY KEY, resource_ids VARCHAR(255), client_secret VARCHAR(255), scope VARCHAR(255), authorized_grant_types VARCHAR(255), web_server_redirect_uri VARCHAR(255), authorities VARCHAR(255), access_token_validity INTEGER, refresh_token_validity INTEGER, additional_information TEXT, autoapprove VARCHAR (255) default 'false') ENGINE=InnoDB DEFAULT CHARSET=utf8; Drop table if exists oauth_access_token;create table oauth_access_token ( token_id VARCHAR(255), token BLOB, authentication_id VARCHAR(255), user_name VARCHAR(255), client_id VARCHAR(255), authentication BLOB, refresh_token VARCHAR(255)) ENGINE=InnoDB DEFAULT CHARSET=utf8; ...

May 31, 2019 · 4 min · jiezi

基于springsecurityoauth2实现oauth2持续更新

基于spring-security-oauth2实现oauth2文章代码地址:链接描述可以下载直接运行,基于springboot2.1.5,springcloud Greenwich版本实现 该系列分为两个部分:分为内存实现,数据库实现。其中数据库实现采用RBAC权限角色管理。 首先声明oauth2是一种协议规范,spring-security-oauth2是对他的一种实现。其次,还有shiro实现,自己根据规范编写代码的实现方式。主流的qq,微信等第三方授权登录方式都是基于oauth2实现的。 oauth2的认证方式有授权码,简单,账户密码,客户端等方式,具体请自行百度不做过多的阐述。 本文基于授权码方式实现 oauth生态设计的范围很大,可以说是一种解决方案,它有“第三方客户端(web服务,APP服务)”、“用户”、“认证服务器”、“资源服务器”等部分。认证流程如下图: (A)用户打开客户端以后,客户端要求用户给予授权。(B)用户同意给予客户端授权。 (C)客户端使用上一步获得的授权,向认证服务器申请令牌。 (D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。 (E)客户端使用令牌,向资源服务器申请获取资源。 (F)资源服务器确认令牌无误,同意向客户端开放资源。 好了,简单介绍后,现在开始实现基于内存的认证服务编写: (1)使用idea在nacos-test项目中创建authserver-memory模块。 目录如下图: (2)创建好module之后,我们开始配置pom文件加载依赖。注意:springcloud的版本1.x和2.x差别很大,有很多不兼容,例如jpa1.x的findOne方法在2.x版本中不能使用。因为我们需要先配置依赖管理 我把spring-cloud-Alibaba一起配置了。好了,现在添加oauth2的依赖,因为我们使用springcloud,并且springcloud-security为我们封装好了oauth2, 因次我们只添加这个依赖就可以 (3)依赖添加完成,下面我们开始写代码,创建config包,因为,我们认证之前需要先校验用户的账户密码是否正确,所以我们先配置WebSecurityConfig拦截: 在config方法里,我们在内存中,配置了两个用户,这里注意密码用了BCryptPasswordEncoder进行加密,在springboot2.x中不加密会报错的。 (4)到这里,用户验证已经完成,我们创建AuthConfig配置认证拦截处理: 需要添加 @EnableAuthorizationServer注解开启认证服务,注入加密用的BCryptPasswordEncoder实例。然后,配置需要认证的客户端, 这里需要细说一下,首先是client_id代表是哪个客户端也就是哪个APP或者web服务需要认证的,然后是客户端的secret秘钥需要加密, authorizedGrantTypes授权方式指的是授权码,简单,客户端,账户密码等,这里使用的是授权码(authorization_code),然后是scopes范围, redirectUris重定向地址,就是你的登录地址,授权后跳转的地址。 (5)配置application.properties文件: 很简单,不在多说,现在,我们启动应用: 成功,我们用这个地址进行授权访问: http://localhost:9000/oauth/authorize?client_id=client&response_type=code 成功后,跳转到登录页面: 输入账户:admin 密码: 123456 点登录 选择approve点击Authorize认证 这个code就是授权码 我们打开postman用post方式获取access_token 这个client就是配置的client_id,secret就是配置的secret,返回access_token ok,基于内存的oauth2实现完成,下一篇基于数据库的实现。有问题请留言。

May 30, 2019 · 1 min · jiezi

Spring-Framework系列教程汇总

最近整理出了之前学习Spring Framework系列,记载了一些学习笔记,特地在此罗列出来。大家也可直接关注笔者github(Spring Framework)获取最新资讯。 TutorialSpring-framework-4.x1. Spring Annotation驱动编程2. Java Beans内省机制以及在Spring中的应用3. Java资源管理以及在Spring中的应用4. Spring自定义XML配置扩展Spring-framework-5.x1. 深入Java之国际化2. JSP在Spring中的应用(Annotation版)3. JSP在Spring中的应用(XML版)4. Java Reactive Web设计与实现5. Servlet在Spring中的应用6. Spring5新特性之测试7. Spring5新特性之Web Flux8. Spring Web自动装配(Annotation)Spring-framework-common1. Spring Aware接口应用

May 28, 2019 · 1 min · jiezi

Java-Reactive-Web设计与实现

注: 本文是由读者观看小马哥公开课视频过程中的笔记整理而成。更多Spring Framework文章可参看笔者个人github: spring-framework-lesson 。0. 编程模型与并发模型Spring 5实现了一部分Reactive Spring WebFlux: Reactive Web(non-blocking servers in general) Spring Web MVC:传统Servlet Web(servlet applications in general) 0.1 编程模型编程模型:阻塞、非阻塞 NIO:同步+非阻塞,基于事件非阻塞 基本上采用Callback方式当时不阻塞,后续再输出(再回调)0.2 并发模型并发模型: 同步(Sync)异步(Async)0.3 比较同步+非阻塞:线程不会改变,不会切换[线程:main] Observable 添加观察者! [线程:main] 通知所有观察者! [线程:main] 3. 收到数据更新:Hello World [线程:main] 2. 收到数据更新:Hello World [线程:main] 1. 收到数据更新:Hello World 异步+非阻塞:线程会被切换[线程:main] 启动一个JFrame窗口! [线程:AWT-EventQueue-0] 销毁当前窗口! [线程:AWT-EventQueue-0] 窗口被关闭,退出程序!使用Jconsole查看改异步非阻塞程序 等待总数一直在增加,说明异步程序一直在等待。NIO就是无限地在处理,无限地在等待。 1. Reactive概念Reactive Programming:响应式编程,异步非阻塞就是响应式编程(Reactive Programming),与之相对应的是命令式编程。 Reactive并不是一种新的技术,不用Reactive照样可以实现非阻塞(同步、异步均可,推拉模式的结合),比如利用观察者模式实现(比如Java Swing GUI技术)。 Reactive的另外一种实现方式就是消息队列。 1.1 标准概念1.1.1 维基百科讲法https://en.wikipedia.org/wiki... In computing, reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change. With this paradigm it is possible to express static (e.g., arrays) or dynamic (e.g., event emitters) data streams with ease, and also communicate that an inferred dependency within the associated execution model exists, which facilitates the automatic propagation of the changed data flow关键点: ...

May 25, 2019 · 8 min · jiezi

小说搜索站快速搭建1架构图

技术栈 模板 thymeleaf框架 Spring Boot 2数据库 Mongodb缓存 Redis搜索 聚合源站搜索框架图 仅展示交流使用:免费小说阅读网

May 25, 2019 · 1 min · jiezi

Android云通信IM系列1集成与配置

前言因为项目中用到了腾讯云通信,网上云通信IM的文章很少,所以我打算详细写一下,希望对大家有所帮助。 什么是云通信IM 云通信 IM(Instant Messaging)承载亿级 QQ 用户即时通讯技术,数十年技术积累,腾讯云为您提供超乎寻常即时通讯聊天服务。针对开发者的不同阶段需求及不同场景,云通信 IM 提供了一系列解决方案,包括: Android/iOS/Windows/Web 的 SDK 组件、服务端集成接口、第三方回调接口等,利用这些组件,可以在应用中构建自己的即时通讯产品,解决开发者面临的高并发、高可用性的一系列问题。首先我们要知道云通信IM登录流程,直接看下面图: 不难看懂,说的简单一点就是我们调后台接口去获取Identifier 与UserSig。我们在测试阶段可以 获取测试 userSig,这点在下面会讲到。 集成一.配置1.创建应用2.配置应用-管理员3.获取UserSig 完成账号管理员配置后,单击下载公私钥的链接,即可获得一个名为 keys.zip 的压缩包。解压后可以得到两个文件,即 public_key 和 private_key,用记事本打开 private_key 文件,并将其中的内容拷贝到开发辅助工具的私钥文本输入框中。其中:identifier 即为您的测试账号(也就是 userId),私钥为 private_key 文件里的文本内容,生成的签名就是userSig。identifier 和 userSig 是一一对应的关系。具体操作可以看下面网址:https://cloud.tencent.com/doc...Android-demo下载地址:https://github.com/tencentyun... 二.集成SDK在项目中添加 IM SDK 的依赖 dependencies { api 'com.tencent.imsdk:imsdk:版本号'}版本号最好用当前最新的版本,我用的是4.3.118 dependencies {api 'com.tencent.imsdk:imsdk:4.3.118'}TUIKit集成TUIKit 是基于腾讯云 IM SDK 的一款 UI 组件库,里面提供了一些通用的 UI 组件,开发者可通过该组件库选取自己所需要的组件快速的搭建一个 IM 应用。IM 软件都具备一些通用的 UI 界面,如会话列表,聊天界面等。TUIKit 提供了这一类的组件,并提供了灵活的 UI 和交互扩展接口,方便用户做个性化开发。TUkit我们不一定要集成,只是说如何有需要的可以去集成集成的方式: 直接依赖它的aar包把aar源码文件拷贝到咱们项目中我建议用第二种方式,因为TUkit视图实在是过于简洁,很大程度上我们都需要修改,而且功能上我们也要去修改,用aar包方式的话我们不能去更改文件。 如果有朋友对aar不了解,可以看我之前的文章:https://mp.weixin.qq.com/s/_m... 基本操作1.初始化可以直接看官网址:https://cloud.tencent.com/doc...或者我们可以下载云通信IM的demo,然后仿照它的初始化配置也可以的。 2.登录// identifier为用户名,userSig 为用户登录凭证TIMManager.getInstance().login(identifier, userSig, new TIMCallBack() { @Override public void onError(int code, String desc) { //错误码 code 和错误描述 desc,可用于定位请求失败原因 //错误码 code 列表请参见错误码表 Log.d(tag, "login failed. code: " + code + " errmsg: " + desc); } @Override public void onSuccess() { Log.d(tag, "login succ"); }});先讲到这里,希望对大家有帮助,下节讲一下具体的功能!想多了解的朋友可以加我的微信,交个朋友:lengquele5311 ...

May 24, 2019 · 1 min · jiezi

关于springboot里面的事务回滚的简单记录

最近自己在写一个小的项目,写的时候才发现自己会的东西太少了,总是遇到各种各样的坑。今天主要记录一下自己在写数据库存储的时候想到要是出现错误,是不是要回滚数据库的操作呀!然后就百度并实践了一下,得出下面的结论: 第一、需要在service方法上添加注解: @Transactional(rollbackFor = Exception.class)第二、如果你没有用try catch去捕获异常的话,那么只需要加上这个注解就可以了,如果你捕获异常了但catch里面只是打印或者返回了异常信息,没有手动抛出RuntimeException异常。那么这个时候你就需要在catch里面添加一个手动回滚的机制了。 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();这样就OK了,当然也可以通过AOP去批量实现这种效果,只是暂时我还没有研究明白,所以就先记录这个最简单的了。后期补上。。。

May 22, 2019 · 1 min · jiezi

springcloud框架的简单搭建消费服务基于feign

上一篇文章主要介绍了如何搭建一个简单的springcloud框架。不过,我们搭建好框架就是为了消费它使用它,那么这篇文章就来看看如何去消费使用我们之前搭建起来的服务吧!首先本文是基于上一篇文章进行的。 一、Feign简介Feign是Netflix开发的声明式、模板化的HTTP客户端, Feign可以帮助我们更快捷、优雅地调用HTTP API。 二、启动程序继续用上一节的工程, 启动eureka-server,端口为8080; 启动eureka-client两次,端口分别为8081 、8082. 三、创建一个feign的服务我们首先要新建一个spring-boot工程,取名为serice-feign。 这次我们要选择三个依赖: 对应的pom.xml文件内容为: <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.zhouxiaoxi</groupId> <artifactId>eureka-feign</artifactId> <version>0.0.1-SNAPSHOT</version> <name>eureka-feign</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>接下来我们就需要配置application.yml文件了: ...

May 22, 2019 · 1 min · jiezi

Intellij-Idea-中进行-Mybatis逆向工程

开篇Mybatis有个实用的功能就是逆向工程,能根据表结构反向生成实体类,这样能避免手工生成出错。市面上的教程大多都很老了,大部分都是针对mysql5的,以下为我执行mysql8时的经验。 引入工程这里使用的是maven包管理工具,在pom.xml添加以下配置,以引入mybatis.generator <build> <finalName>SpringMVCBasic</finalName> <!-- 添加mybatis-generator-maven-plugin插件 --> <plugins> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.2</version> <configuration> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> </plugin> </plugins> </build>配置文件在maven项目下的src/main/resources 目录下新建generatorConfig.xml和generator.properties文件 generator.properties jdbc.driverLocation=F:\\maven-repository\\mysql\\mysql-connector-java\\8.0.16\\mysql-connector-java-8.0.16.jarjdbc.driverClass=com.mysql.cj.jdbc.Driverjdbc.connectionURL=jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8jdbc.userId=testjdbc.password=test123注意:1,generator.properties里面的jdbc.driverLocation指向是你本地maven库对应mysql-connector地址2,与老版本不同,这里driversClass为com.mysql.cj.jdbc.Driver generatorConfig.xml <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"><generatorConfiguration> <!--导入属性配置--> <properties resource="generator.properties"></properties> <!--指定特定数据库的jdbc驱动jar包的位置(绝对路径)--> <classPathEntry location="${jdbc.driverLocation}"/> <context id="default" targetRuntime="MyBatis3"> <!-- optional,旨在创建class时,对注释进行控制 --> <commentGenerator> <!--是否去掉自动生成的注释 true:是--> <property name="suppressDate" value="true"/> <property name="suppressAllComments" value="true"/> </commentGenerator> <!--jdbc的数据库连接:驱动类、链接地址、用户名、密码--> <jdbcConnection driverClass="${jdbc.driverClass}" connectionURL="${jdbc.connectionURL}" userId="${jdbc.userId}" password="${jdbc.password}"> </jdbcConnection> <!-- 非必需,类型处理器,在数据库类型和java类型之间的转换控制--> <javaTypeResolver> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <!-- Model模型生成器,用来生成含有主键key的类,记录类 以及查询Example类 targetPackage 指定生成的model生成所在的包名 targetProject 指定在该项目下所在的路径 --> <javaModelGenerator targetPackage="com.ifly.outsourcing.entity" targetProject="src/main/java"> <property name="enableSubPackages" value="true"/> <property name="trimStrings" value="true"/> </javaModelGenerator> <!--Mapper映射文件生成所在的目录 为每一个数据库的表生成对应的SqlMap文件 --> <sqlMapGenerator targetPackage="mappers" targetProject="src/main/resources"> <property name="enableSubPackages" value="false"/> </sqlMapGenerator> <!-- 客户端代码,生成易于使用的针对Model对象和XML配置文件 的代码 type="ANNOTATEDMAPPER",生成Java Model 和基于注解的Mapper对象 type="MIXEDMAPPER",生成基于注解的Java Model 和相应的Mapper对象 type="XMLMAPPER",生成SQLMap XML文件和独立的Mapper接口 --> <javaClientGenerator type="XMLMAPPER" targetPackage="com.ifly.outsourcing.dao" targetProject="src/main/java"> <property name="enableSubPackages" value="true"/> </javaClientGenerator> <!-- 数据表进行生成操作 tableName:表名; domainObjectName:对应的DO --> <table tableName="user" domainObjectName="user" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table> </context></generatorConfiguration>注意:这里主要注意修改对应的javaModelGenerator ,sqlMapGenerator,javaClientGenerator 为自己的生成路径。以及添加自己的数据表。 ...

May 21, 2019 · 1 min · jiezi

Java设计模式综合运用责任链模式进阶

1 责任链模式现存缺点由于责任链大多数都是不纯的情况,本案例中,只要校验失败就直接返回,不继续处理接下去责任链中的其他校验逻辑了,故而出现如果某个部分逻辑是要由多个校验器组成一个整理的校验逻辑的话,则此责任链模式则显现出了它的不足之处了。(责任链模式的具体运用以及原理请参见笔者github wiki 2 责任链模式) 2 改进方式2.1 引入适配器模式关于接口适配器模式原理以及使用场景请参见笔者github wiki 12 适配器模式 。 2.2 引入接口默认方法事例代码请参见工程 design-patterns-business中的 defaultmethod包下的代码。2.2.1 概念java8引入了一个 default medthod 使用 default 关键字Spring 4.2支持加载在默认方法里声明的bean2.2.2 优点用来扩展已有的接口,在对已有接口的使用不产生任何影响的情况下,添加扩展。 比如我们已经投入使用的接口需要拓展一个新的方法,在Java8以前,如果为一个使用的接口增加一个新方法,则我们必须在所有实现类中添加该方法的实现,否则编译会出现异常。如果实现类数量少并且我们有权限修改,可能会工作量相对较少。如果实现类比较多或者我们没有权限修改实现类源代码,这样可能就比较麻烦。而默认方法则解决了这个问题,它提供了一个实现,当没有显示提供其他实现时就采用这个实现,这样新添加的方法将不会破坏现有代码。默认方法的另一个优势是该方法是可选的,子类可以根据不同的需求Override默认实现。 例如,我们定义一个集合接口,其中有增、删、改等操作。如果我们的实现类90%都是以数组保存数据,那么我们可以定义针对这些方法给出默认实现,而对于其他非数组集合或者有其他类似业务,可以选择性复写接口中默认方法。2.2.3 使用原则”类优先” 原则 若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时选择父类中的方法:如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。接口冲突原则 如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突。4.3 项目演示3.1 逻辑梳理由于系统业务需求的变更,目前有两种业务需求, 一种就是按照原来的文件上传需求,上传过程中需要校验操作,只要校验失败了,就直接返回失败信息,整个文件都不需要做处理了。(参见之前的文章 Java设计模式综合运用(门面+模版方法+责任链+策略))另一种就是校验的逻辑都一样,但是如果校验失败的情况,需要继续往下执行,记录一下失败id即可,即需要把失败的记录也保存到数据库中,比如约束字段不符合约束规则的情况,则把此字段置空,然后继续执行其他校验器逻辑即可。(本节需要讨论的问题)3.2 实现方案根据第2节的改进方式可以知道,我们有两种方式改进以上逻辑。 3.2.1 采用适配器模式代码参见2.1版本的,地址为:https://github.com/landy8530/...若采用适配器模式,此处我们会采用接口适配器模式。 接口需要多增加一个不用链式调用的校验方法,定义如下, /** * 业务校验统一接口,增加了接口的默认方法实现,这样可以更加方便且自由选择实现接口的哪些方法。 * @author landyl * @create 10:32 AM 05/09/2018 * @version 2.0 * @since 1.0 */public interface Validator<R extends RequestDetail,F extends RequestFile> { /** * 需要引入责任链的时候,则采用此方法 * @param detail * @param chain * @return * @throws BusinessValidationException */ String doValidate(R detail, F file, ValidatorChain chain) throws BusinessValidationException; /** * 不需要责任链的时候,则可以直接调用此方法的实现即可 * @param detail * @return * @throws BusinessValidationException */ boolean doValidate(R detail, F file) throws BusinessValidationException;}适配器类定义如下抽象类, ...

May 21, 2019 · 2 min · jiezi

SpringBoot整合MybatisPlus的简单教程简单整合

最近在研究springboot,顺便就会看看数据库连接这一块的知识 ,所以当我发现有通用Mapper和MybatisPlus这两款网络上比较火的简化mybatis开发的优秀软件之后。就都想试一下,看看哪一款比较适合自己。先创建一个springboot的项目,可以参考我之前的文章Spring Boot 的简单教程(一) Spring Boot 项目的创建。创建好springboot之后就需要整合mybatis和mybatis-plus了。 打开pom.xml文件,将最新的mybatis相关的包都引用进来。 <!-- 这是mysql的依赖 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- 这是lombok的依赖 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- 这是mybatis-plus依赖 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.1.1</version> </dependency> <!-- 这是mybatis-plus的代码自动生成器 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.1.1</version> </dependency> <!-- 这是模板引擎依赖 --> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.28</version> </dependency>需要对application.yml进行相关的配置。 #端口号 server: port: 8088 #数据库的配置信息 spring: datasource: url: jdbc:mysql://localhost:3306/*** #自己的数据库名称 username: root password: 123456 mybatis: #开启驼峰命名法 configuration: map-underscore-to-camel-case: true mybatis-plus: # xml地址 mapper-locations: classpath:mapper/*Mapper.xml # 实体扫描,多个package用逗号或者分号分隔 type-aliases-package: *** #自己的实体类地址 configuration: # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl自动生成模块的方法,在相应的位置上添加上自己的一些包名就可以运行生成相应的Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码。public class CodeGenerator { /** * <p> * 读取控制台内容 * </p> */ public static String scanner(String tip) { Scanner scanner = new Scanner(System.in); StringBuilder help = new StringBuilder(); help.append("请输入" + tip + ":"); System.out.println(help.toString()); if (scanner.hasNext()) { String ipt = scanner.next(); if (StringUtils.isNotEmpty(ipt)) { return ipt; } } throw new MybatisPlusException("请输入正确的" + tip + "!"); } public static void main(String[] args) { // 代码生成器 AutoGenerator mpg = new AutoGenerator(); // 全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); gc.setOutputDir(projectPath + "/src/main/java"); gc.setAuthor("jobob"); gc.setOpen(false); // gc.setSwagger2(true); 实体属性 Swagger2 注解 mpg.setGlobalConfig(gc); // 数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/***?useUnicode=true&useSSL=false&characterEncoding=utf8"); // dsc.setSchemaName("public"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("***"); mpg.setDataSource(dsc); // 包配置 PackageConfig pc = new PackageConfig(); //这里有个模块名的配置,可以注释掉不用。// pc.setModuleName(scanner("模块名")); pc.setParent("com.zhouxiaoxi.www"); mpg.setPackageInfo(pc); // 自定义配置 InjectionConfig cfg = new InjectionConfig() { @Override public void initMap() { // to do nothing } }; // 如果模板引擎是 freemarker String templatePath = "/templates/mapper.xml.ftl"; // 如果模板引擎是 velocity// String templatePath = "/templates/mapper.xml.vm"; // 自定义输出配置 List<FileOutConfig> focList = new ArrayList<>(); // 自定义配置会被优先输出 focList.add(new FileOutConfig(templatePath) { @Override public String outputFile(TableInfo tableInfo) { // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! return projectPath + "/src/main/resources/mapper/"// + + pc.getModuleName() + 如果放开上面的模块名,这里就有一个模块名了 + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; } }); /* cfg.setFileCreate(new IFileCreate() { @Override public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) { // 判断自定义文件夹是否需要创建 checkDir("调用默认方法创建的目录"); return false; } }); */ cfg.setFileOutConfigList(focList); mpg.setCfg(cfg); // 配置模板 TemplateConfig templateConfig = new TemplateConfig(); // 配置自定义输出模板 //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别 // templateConfig.setEntity("templates/entity2.java"); // templateConfig.setService(); // templateConfig.setController(); templateConfig.setXml(null); mpg.setTemplate(templateConfig); // 策略配置 StrategyConfig strategy = new StrategyConfig(); //数据库表映射到实体的明明策略 strategy.setNaming(NamingStrategy.underline_to_camel); //数据库表字段映射到实体的命名策略, 未指定按照 naming 执行 strategy.setColumnNaming(NamingStrategy.underline_to_camel); //自定义继承的Entity类全称,带包名// strategy.setSuperEntityClass("***"); strategy.setEntityLombokModel(true); strategy.setRestControllerStyle(true); //自定义继承的Controller类全称,带包名// strategy.setSuperControllerClass("***"); strategy.setInclude(scanner("表名,多个英文逗号分割").split(",")); //自定义基础的Entity类,公共字段(可添加更多)// strategy.setSuperEntityColumns("id"); //驼峰转连字符 strategy.setControllerMappingHyphenStyle(true); //表前缀// strategy.setTablePrefix(pc.getModuleName() + "_"); mpg.setStrategy(strategy); mpg.setTemplateEngine(new FreemarkerTemplateEngine()); mpg.execute(); }}在生成的controller里面添加对应的方法启动就可以正常进行访问了。 ...

May 14, 2019 · 2 min · jiezi

springboot整合quarzt实现动态定时任务

实现定时任务的几种方式:1.使用linux的crontab 优点: 1.使用方式很简单,只要在crontab中写好 2.随时可以修改,不需要重启服务器 缺点: 1.分布式的系统中不好使用,只能一台台机器去修改 2.分是最小的时间单位,秒级的不能使用2.使用spring自带的ScheduledExecutor 优点: cronExpression比crontab的更强大一些支持到秒,性能更好 缺点: 修改了cronExpression的重启服务器,否则不生效3. 使用JDK自带的Timer优点: 轻量级,执行速度快缺点:分布式系统不好使用.而且不能指定时间执行,只能按某个频次来执行4.使用quarzt优点:1.可适用于分布式系统,quartz可支持集群模式2.修改了定时任务无须重启服务器(这只是我个人想到的一些优缺点,网友有其他看法可以留言说下)你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。 整合步骤:我们现在知道了quartz有这么优秀,该怎么整合到项目中呢?笔者接下来将实现一个通过http接口调用来触发动态定时任务的一个小功能.笔者使用的环境:jdk:1.8.0_162;springboot:1.5.10.RELEASE1.引入需要的jar包,在pom文件中加入quartz的jar包和spring支持quartz的jar <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency>2.配置调度器的bean,这里spring实现了三个工厂类,SchedulerFactoryBean,CronTriggerBean,JobDetailBean,使用注解的方式将这三个类交给spring管理.一般看网上的资料都是这三个类,都交给spring管理,可以参考这篇文章[这篇文章]。(https://blog.csdn.net/liuchua...。而我这里定时任务的触发是要通过接口的方式来触发,所以只用实现以下SchedulerFactoryBean的调度器即可。如果读者不是很明白这几个类是干嘛的,可以看下quartz使用的文章。我这里简单说下:scheduler:任务的调度器,job:具体的任务类,trigger:触发器,任务什么时候执行是由它决定的。就是说时间人物做什么,scheduler就是主语的人物,trigger是时间,job是做什么事。 @Configurationpublic class SchedulerConfig { /** * attention: * Details:定义quartz调度工厂 */ @Bean(name = "scheduler") public SchedulerFactoryBean schedulerFactory() { SchedulerFactoryBean bean = new SchedulerFactoryBean(); // 用于quartz集群,QuartzScheduler 启动时更新己存在的Job bean.setOverwriteExistingJobs(true); // 延时启动,应用启动1秒后 bean.setStartupDelay(1); return bean; }}3.具体任务类job,必须实现quartz的job类,这个也可以去实现spring的QuartJobBean(spring对job类的实现)是一样的,或者还有一种方式就是MethodInvokingJobDetailFactoryBean,这个类里面可以设置什么类的什么方法来执行这个任务,会更灵活一些: @Slf4jpublic class ScheduleTaskJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { log.info("任务执行了......"); }}4.http的接口来触发该调度程序: ...

May 12, 2019 · 1 min · jiezi

帧动画内存OOM不存在的-SurfaceView逐帧解析

Android 提供了AnimationDrawable用于实现帧动画。在动画开始之前,所有帧的图片都被解析并占用内存,一旦动画较复杂帧数较多,在低配置手机上容易发生 OOM。即使不发生 OOM,也会对内存造成不小的压力。下面代码展示了一个帧数为4的帧动画: 原生帧动画AnimationDrawable drawable = new AnimationDrawable();drawable.addFrame(getDrawable(R.drawable.frame1), frameDuration);drawable.addFrame(getDrawable(R.drawable.frame2), frameDuration);drawable.addFrame(getDrawable(R.drawable.frame3), frameDuration);drawable.addFrame(getDrawable(R.drawable.frame4), frameDuration);drawable.setOneShot(true);ImageView ivFrameAnim = ((ImageView) findViewById(R.id.frame_anim));ivFrameAnim.setImageDrawable(drawable);drawable.start();有没有什么办法让帧动画的数据逐帧加载,而不是一次性全部加载到内存?SurfaceView就提供了这种能力。 SurfaceView屏幕的显示机制和帧动画类似,也是一帧一帧的连环画,只不过刷新频率很高,感觉像连续的。为了显示一帧,需要经历计算和渲染两个过程,CPU 先计算出这一帧的图像数据并写入内存,然后调用 OpenGL 命令将内存中数据渲染成图像存放在 GPU Buffer 中,显示设备每隔一定时间从 Buffer 中获取图像并显示。 上述过程中的计算,对于View来说,就好比在主线程遍历 View树 以决定视图画多大(measure),画在哪(layout),画些啥(draw),计算结果存放在内存中,SurfaceFlinger 会调用 OpenGL 命令将内存中的数据渲染成图像存放在 GPU Buffer 中。每隔16.6ms,显示器从 Buffer 中取出帧并显示。所以自定义 View 可以通过重载onMeasure()、onLayout()、onDraw()来定义帧内容,但不能定义帧刷新频率。 SurfaceView可以突破这个限制。而且它可以将计算帧数据放到独立的线程中进行。下面是自定义SurfaceView的模版代码: public abstract class BaseSurfaceView extends SurfaceView implements SurfaceHolder.Callback { public static final int DEFAULT_FRAME_DURATION_MILLISECOND = 50; //用于计算帧数据的线程 private HandlerThread handlerThread; private Handler handler; //帧刷新频率 private int frameDuration = DEFAULT_FRAME_DURATION_MILLISECOND; //用于绘制帧的画布 private Canvas canvas; private boolean isAlive; public BaseSurfaceView(Context context) { super(context); init(); } protected void init() { getHolder().addCallback(this); //设置透明背景,否则SurfaceView背景是黑的 setBackgroundTransparent(); } private void setBackgroundTransparent() { getHolder().setFormat(PixelFormat.TRANSLUCENT); setZOrderOnTop(true); } @Override public void surfaceCreated(SurfaceHolder holder) { isAlive = true; startDrawThread(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { stopDrawThread(); isAlive = false; } //停止帧绘制线程 private void stopDrawThread() { handlerThread.quit(); handler = null; } //启动帧绘制线程 private void startDrawThread() { handlerThread = new HandlerThread("SurfaceViewThread"); handlerThread.start(); handler = new Handler(handlerThread.getLooper()); handler.post(new DrawRunnable()); } private class DrawRunnable implements Runnable { @Override public void run() { if (!isAlive) { return; } try { //1.获取画布 canvas = getHolder().lockCanvas(); //2.绘制一帧 onFrameDraw(canvas); } catch (Exception e) { e.printStackTrace(); } finally { //3.将帧数据提交 getHolder().unlockCanvasAndPost(canvas); //4.一帧绘制结束 onFrameDrawFinish(); } //不停的将自己推送到绘制线程的消息队列以实现帧刷新 handler.postDelayed(this, frameDuration); } } protected abstract void onFrameDrawFinish(); protected abstract void onFrameDraw(Canvas canvas);}用HandlerThread作为独立帧绘制线程,好处是可以通过与其绑定的Handler方便地实现“每隔一段时间刷新”,而且在Surface被销毁的时候可以方便的调用HandlerThread.quit()来结束线程执行的逻辑。DrawRunnable.run()运用模版方法模式定义了绘制算法框架,其中帧绘制逻辑的具体实现被定义成两个抽象方法,推迟到子类中实现,因为绘制的东西是多样的,对于本文来说,绘制的就是一张张图片,所以新建BaseSurfaceView的子类FrameSurfaceView:逐帧解析 & 及时回收public class FrameSurfaceView extends BaseSurfaceView { public static final int INVALID_BITMAP_INDEX = Integer.MAX_VALUE; private List<Integer> bitmaps = new ArrayList<>(); //帧图片 private Bitmap frameBitmap; //帧索引 private int bitmapIndex = INVALID_BITMAP_INDEX; private Paint paint = new Paint(); private BitmapFactory.Options options = new BitmapFactory.Options(); //帧图片原始大小 private Rect srcRect; //帧图片目标大小 private Rect dstRect = new Rect(); private int defaultWidth; private int defaultHeight; public void setDuration(int duration) { int frameDuration = duration / bitmaps.size(); setFrameDuration(frameDuration); } public void setBitmaps(List<Integer> bitmaps) { if (bitmaps == null || bitmaps.size() == 0) { return; } this.bitmaps = bitmaps; //默认情况下,计算第一帧图片的原始大小 getBitmapDimension(bitmaps.get(0)); } private void getBitmapDimension(Integer integer) { final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(this.getResources(), integer, options); defaultWidth = options.outWidth; defaultHeight = options.outHeight; srcRect = new Rect(0, 0, defaultWidth, defaultHeight); requestLayout(); } public FrameSurfaceView(Context context) { super(context); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); dstRect.set(0, 0, getWidth(), getHeight()); } @Override protected void onFrameDrawFinish() { //在一帧绘制完后,直接回收它 recycleOneFrame(); } //回收帧 private void recycleOneFrame() { if (frameBitmap != null) { frameBitmap.recycle(); frameBitmap = null; } } @Override protected void onFrameDraw(Canvas canvas) { //绘制一帧前需要先清画布,否则所有帧都叠在一起同时显示 clearCanvas(canvas); if (!isStart()) { return; } if (!isFinish()) { drawOneFrame(canvas); } else { onFrameAnimationEnd(); } } //绘制一帧,是张Bitmap private void drawOneFrame(Canvas canvas) { frameBitmap = BitmapUtil.decodeOriginBitmap(getResources(), bitmaps.get(bitmapIndex), options); canvas.drawBitmap(frameBitmap, srcRect, dstRect, paint); bitmapIndex++; } private void onFrameAnimationEnd() { reset(); } private void reset() { bitmapIndex = INVALID_BITMAP_INDEX; } //帧动画是否结束 private boolean isFinish() { return bitmapIndex >= bitmaps.size(); } //帧动画是否开始 private boolean isStart() { return bitmapIndex != INVALID_BITMAP_INDEX; } //开始播放帧动画 public void start() { bitmapIndex = 0; } private void clearCanvas(Canvas canvas) { paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); canvas.drawPaint(paint); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); }}FrameSurfaceView继承自BaseSurfaceView,所以它复用了基类的绘制框架算法,并且定了自己每一帧的绘制内容:一张Bitmap。Bitmap资源 id 通过setBitmaps()传递进来, 绘制一帧解析一张 ,在每一帧绘制完毕后,调用Bitmap.recycle()释放图片 native 内存并去除 java 堆中图片像素数据的引用。这样当 GC 发生时,图片像素数据可以及时被回收。一切都是这么地能够自圆其说,我迫不及待地运行代码并打开AndroidStudio的Profiler标签页,切换到MEMORY,想用真实内存数据验证下性能。但残酷的事实狠狠地打了下脸。。。多次播放帧动画后,内存占用居然比原生AnimationDrawable还大,而且每播放一次,内存中都会多出 N 个Bitmap对象(N为帧动画总帧数)。唯一令人欣慰的是,手动触发 GC 后帧动画图片能够被回收。(AnimationDrawable中的图片数据不会被 GC) ...

May 10, 2019 · 4 min · jiezi

mapper4与springboot的简单整合

最近自己在网上搜索一些关于mapper4的教程,一直都没有找到简单明了的,所以就只能自己写一篇初级入门的mapper4与当下最火的springboot的整合。1.首先我们需要用IDEA工具新建一个springboot的项目。 Group和Artfact需要自己进行填写,否则就是默认的。 选择Web和MySQL 然后点击下一步完成就好了。 项目建好之后的结构如下所示,需要将application.properties改名为application.yml。 2.需要在maven里面添加相关的依赖。<!-- 添加通用 Mapper 提供的 starter --><dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper-spring-boot-starter</artifactId> <version>2.1.5</version></dependency><!-- 添加lombok插件 --><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.6</version> <scope>provided</scope></dependency>3.application配置文件进行相关设置。#端口号server: port: 8088spring: #数据库连接数据配置 datasource: url: jdbc:mysql://localhost:3306/mapper-test username: root password: 123456mybatis: #驼峰命名法 configuration: map-underscore-to-camel-case: true #配置mybatis的全局配置文件 mapper-locations: classpath:mapping/*.xml#sql语句的打印logging: level: com: mapper4: www: debug4.需要在Spring Boot 的启动类上用@MapperScan 注解进行配置。@tk.mybatis.spring.annotation.MapperScan(basePackages = "扫描包") 5.新建一个Girl的实体类,并将其放到entity包中。 用lombok的@Data注解,这样就可以省略掉get/set等方法。 6.新建一个GirlMapper接口类,并将其放入到mapper包中。 继承BaseMapper<实体类>类。 7.新建一个GirlController类,将其放到controller中。 写一个根据id查询数据的方法。 8.用postman进行接口的调用你就会发现可以成功的查询出相关的数据了。拓展如果你想要自己写一些sql语句进行查询,不想使用mapper4自带的方法的话,那你就需要自己写一个*mapper.xml。这里我们简单的写一个*mapper.xml进行查询。其实我们在application.yml里面已经进行了相关的配置了。 这样程序就会自动的去这个目录下面去扫描相关的xml进行关联了。 我们需要在resources里面新建一个mapping文件夹,里面来存放我们写的*mapper.xml文件 需要在GirlMapper.xml里面添加一个新的查询SQL。 在GirlMapper类中添加这个方法,然后就可以在GirlController里面进行调用了。 在GirlController里面添加相关的方法。 进行测试就可以了,发现也是可以的,至此我们就完成了springboot与mapper4的简单集成。

April 30, 2019 · 1 min · jiezi

使用idea创建SpringBoot项目

首先我们打开idea。这里我是用的版本是2019.1.这边项目名选默认的就可以了,项目地址个人建议用一个专门的文件夹进行存放。

April 18, 2019 · 1 min · jiezi

工具集核心教程 | 第三篇: Thymeleaf模板引擎入门到进阶

thymeleaf介绍简单说, Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP。相较与其他的模板引擎,它有如下三个极吸引人的特点:1.Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。2.Thymeleaf 开箱即用的特性。它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、该jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。3.Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。标准表达式语法它们分为四类:1.变量表达式2.选择或星号表达式3.文字国际化表达式4.URL表达式变量表达式变量表达式即OGNL表达式或Spring EL表达式(在Spring术语中也叫model attributes)。如下所示:${session.user.name}它们将以HTML标签的一个属性来表示:<span th:text="${book.author.name}"> <li th:each=“book : ${books}"> 选择(星号)表达式选择表达式很像变量表达式,不过它们用一个预先选择的对象来代替上下文变量容器(map)来执行,如下:{customer.name}被指定的object由th:object属性定义: <div th:object="${book}"> … <span th:text=”{title}">…</span> … </div> 文字国际化表达式文字国际化表达式允许我们从一个外部文件获取区域文字信息(.properties),用Key索引Value,还可以提供一组参数(可选). #{main.title} #{message.entrycreated(${entryId})} 可以在模板文件中找到这样的表达式代码: <table> … <th th:text="#{header.address.city}">…</th> <th th:text="#{header.address.country}">…</th> … </table> URL表达式URL表达式指的是把一个有用的上下文或回话信息添加到URL,这个过程经常被叫做URL重写。@{/order/list}URL还可以设置参数:@{/order/details(id=${orderId})}相对路径:@{../documents/report}让我们看这些表达式: <form th:action="@{/createOrder}"> <a href=“main.html” th:href="@{/main}">变量表达式和星号表达有什么区别吗?如果不考虑上下文的情况下,两者没有区别;星号语法评估在选定对象上表达,而不是整个上下文什么是选定对象?就是父标签的值,如下: <div th:object="${session.user}"> <p>Name: <span th:text="{firstName}">Sebastian</span>.</p> <p>Surname: <span th:text="{lastName}">Pepper</span>.</p> <p>Nationality: <span th:text="{nationality}">Saturn</span>.</p> </div>这是完全等价于: <div th:object="${session.user}"> <p>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p> <p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p> <p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p> </div>当然,美元符号和星号语法可以混合使用: <div th:object="${session.user}"> <p>Name: <span th:text="{firstName}">Sebastian</span>.</p> <p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p> <p>Nationality: <span th:text="{nationality}">Saturn</span>.</p> </div>表达式支持的语法字面(Literals)文本文字(Text literals): ‘one text’, ‘Another one!’,…数字文本(Number literals): 0, 34, 3.0, 12.3,…布尔文本(Boolean literals): true, false空(Null literal): null文字标记(Literal tokens): one, sometext, main,…文本操作(Text operations)字符串连接(String concatenation): +文本替换(Literal substitutions): |The name is ${name}|算术运算(Arithmetic operations)二元运算符(Binary operators): +, -, , /, %减号(单目运算符)Minus sign (unary operator): -布尔操作(Boolean operations)二元运算符(Binary operators):and, or布尔否定(一元运算符)Boolean negation (unary operator):!, not比较和等价(Comparisons and equality)比较(Comparators): >, <, >=, <= (gt, lt, ge, le)等值运算符(Equality operators):==, != (eq, ne)条件运算符(Conditional operators)If-then: (if) ? (then)If-then-else: (if) ? (then) : (else)Default: (value) ?: (defaultvalue)所有这些特征可以被组合并嵌套:‘User is of type ’ + (${user.isAdmin()} ? ‘Administrator’ : (${user.type} ?: ‘Unknown’))常用th标签都有那些?关键字功能介绍案例th:id 替换id<input th:id="‘xxx’ + ${collect.id}"/> th:text文本替换<p th:text="${collect.description}">description</p>th:utext支持html的文本替换<p th:utext="${htmlcontent}">conten</p>th:object替换对象<div th:object="${session.user}">th:value属性赋值<input th:value="${user.name}" />th:with变量赋值运算<div th:with=“isEven=${prodStat.count}%2==0”></div>th:style设置样式th:style="‘display:’ + @{(${sitrue} ? ’none’ : ‘inline-block’)} + ‘’“th:onclick点击事件th:onclick="‘getCollect()’“th:each属性赋值tr th:each=“user,userStat:${users}">th:if判断条件<a th:if="${userId == collect.userId}” >th:unless和th:if判断相反<a th:href=”@{/login}” th:unless=${session.user != null}>Login</a>th:href链接地址<a th:href="@{/login}" th:unless=${session.user != null}>Login</a> />th:switch多路选择 配合th:case 使用<div th:switch="${user.role}">th:caseth:switch的一个分支<p th:case="‘admin’">User is an administrator</p>th:fragment布局标签,定义一个代码片段,方便其它地方引用<div th:fragment=“alert”>th:include布局标签,替换内容到引入的文件<head th:include=“layout :: htmlhead” th:with=“title=‘xx’"></head> />th:replace布局标签,替换整个标签到引入的文件<div th:replace=“fragments/header :: title”></div>th:selected selected选择框 选中th:selected=”(${xxx.id} == ${configObj.dd})“th:src图片类地址引入<img class=“img-responsive” alt=“App Logo” th:src=”@{/img/logo.png}" />th:inline定义js脚本可以使用变量<script type=“text/javascript” th:inline=“javascript”>th:action表单提交的地址<form action=“subscribe.html” th:action="@{/subscribe}">th:remove删除某个属性<tr th:remove=“all”> 1.all:删除包含标签和所有的孩子。2.body:不包含标记删除,但删除其所有的孩子。3.tag:包含标记的删除,但不删除它的孩子。4.all-but-first:删除所有包含标签的孩子,除了第一个。5.none:什么也不做。这个值是有用的动态评估。th:attr设置标签属性,多个属性可以用逗号分隔比如 th:attr=“src=@{/image/aa.jpg},title=#{logo}",此标签不太优雅,一般用的比较少。还有非常多的标签,这里只列出最常用的几个,由于一个标签内可以包含多个th:x属性,其生效的优先级顺序为:include,each,if/unless/switch/case,with,attr/attrprepend/attrappend,value/href,src ,etc,text/utext,fragment,remove。几种常用的使用方法1、赋值、字符串拼接 <p th:text="${collect.description}">description</p> <span th:text="‘Welcome to our application, ’ + ${user.name} + ‘!’">字符串拼接还有另外一种简洁的写法<span th:text="|Welcome to our application, ${user.name}!|">2、条件判断 If/UnlessThymeleaf中使用th:if和th:unless属性进行条件判断,下面的例子中,<a>标签只有在th:if中条件成立时才显示:<a th:if="${myself==‘yes’}” > </i> </a><a th:unless=${session.user != null} th:href="@{/login}" >Login</a>th:unless于th:if恰好相反,只有表达式中的条件不成立,才会显示其内容。也可以使用 (if) ? (then) : (else) 这种语法来判断显示的内容3、for 循环 <tr th:each=“collect,iterStat : ${collects}"> <th scope=“row” th:text="${collect.id}">1</th> <td > <img th:src="${collect.webLogo}”/> </td> <td th:text="${collect.url}">Mark</td> <td th:text="${collect.title}">Otto</td> <td th:text="${collect.description}">@mdo</td> <td th:text="${terStat.index}">index</td> </tr>iterStat称作状态变量,属性有:index:当前迭代对象的index(从0开始计算)count: 当前迭代对象的index(从1开始计算)size:被迭代对象的大小current:当前迭代变量even/odd:布尔值,当前循环是否是偶数/奇数(从0开始计算) first:布尔值,当前循环是否是第一个last:布尔值,当前循环是否是最后一个4、URLURL在Web应用模板中占据着十分重要的地位,需要特别注意的是Thymeleaf对于URL的处理是通过语法@{…}来处理的。如果需要Thymeleaf对URL进行渲染,那么务必使用th:href,th:src等属性,下面是一个例子<!– Will produce ‘http://localhost:8080/standard/unread’ (plus rewriting) –> <a th:href="@{/standard/{type}(type=${type})}">view</a><!– Will produce ‘/gtvg/order/3/details’ (plus rewriting) –><a href=“details.html” th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>设置背景<div th:style="‘background:url(’ + @{/<path-to-image>} + ‘);’"></div>根据属性值改变背景 <div class=“media-object resource-card-image” th:style="‘background:url(’ + @{(${collect.webLogo}==’’ ? ‘img/favicon.png’ : ${collect.webLogo})} + ‘)’" ></div>几点说明:上例中URL最后的(orderId=${o.id}) 表示将括号内的内容作为URL参数处理,该语法避免使用字符串拼接,大大提高了可读性@{...}表达式中可以通过{orderId}访问Context中的orderId变量@{/order}是Context相关的相对路径,在渲染时会自动添加上当前Web应用的Context名字,假设Context名字为app,那么结果应该是/app/order5、内联js内联文本:[[…]]内联文本的表示方式,使用时,必须先用th:inline=“text/javascript/none"激活,th:inline可以在父级标签内使用,甚至作为body的标签。内联文本尽管比th:text的代码少,不利于原型显示。<script th:inline=“javascript”>/<![CDATA[/…var username = /[[${sesion.user.name}]]/ ‘Sebastian’;var size = /[[${size}]]/ 0;…/]]>/</script>js附加代码:/[+var msg = ‘This is a working application’;+]/js移除代码:/[- /var msg = ‘This is a non-working template’;/ -]/6、内嵌变量为了模板更加易用,Thymeleaf还提供了一系列Utility对象(内置于Context中),可以通过#直接访问: dates : java.util.Date的功能方法类。 calendars : 类似#dates,面向java.util.Calendar numbers : 格式化数字的功能方法类 strings : 字符串对象的功能类,contains,startWiths,prepending/appending等等。 objects: 对objects的功能类操作。 bools: 对布尔值求值的功能方法。 arrays:对数组的功能类方法。 lists: 对lists功能类方法 sets maps ...下面用一段代码来举例一些常用的方法:dates/* * Format date with the specified pattern * Also works with arrays, lists or sets /${#dates.format(date, ‘dd/MMM/yyyy HH:mm’)}${#dates.arrayFormat(datesArray, ‘dd/MMM/yyyy HH:mm’)}${#dates.listFormat(datesList, ‘dd/MMM/yyyy HH:mm’)}${#dates.setFormat(datesSet, ‘dd/MMM/yyyy HH:mm’)}/ * Create a date (java.util.Date) object for the current date and time /${#dates.createNow()}/ * Create a date (java.util.Date) object for the current date (time set to 00:00) /${#dates.createToday()}strings/ * Check whether a String is empty (or null). Performs a trim() operation before check * Also works with arrays, lists or sets /${#strings.isEmpty(name)}${#strings.arrayIsEmpty(nameArr)}${#strings.listIsEmpty(nameList)}${#strings.setIsEmpty(nameSet)}/ * Check whether a String starts or ends with a fragment * Also works with arrays, lists or sets /${#strings.startsWith(name,‘Don’)} // also array, list* and set*${#strings.endsWith(name,endingFragment)} // also array*, list* and set*/* * Compute length * Also works with arrays, lists or sets /${#strings.length(str)}/ * Null-safe comparison and concatenation /${#strings.equals(str)}${#strings.equalsIgnoreCase(str)}${#strings.concat(str)}${#strings.concatReplaceNulls(str)}/ * Random */${#strings.randomAlphanumeric(count)}使用thymeleaf布局使用thymeleaf布局非常的方便定义代码片段:<footer th:fragment=“copy”> &copy; 2016</footer>在页面任何地方引入:<body> <div th:include=“footer :: copy”></div> <div th:replace=“footer :: copy”></div> </body>th:include 和 th:replace区别,include只是加载,replace是替换返回的HTML如下:<body> <div> &copy; 2016 </div> <footer>&copy; 2016 </footer> </body>下面是一个常用的后台页面布局,将整个页面分为头部,尾部、菜单栏、隐藏栏,点击菜单只改变content区域的页面<body class=“layout-fixed”> <div th:fragment=“navbar” class=“wrapper” role=“navigation”> <div th:replace=“fragments/header :: header”>Header</div> <div th:replace=“fragments/left :: left”>left</div> <div th:replace=“fragments/sidebar :: sidebar”>sidebar</div> <div layout:fragment=“content” id=“content” ></div> <div th:replace=“fragments/footer :: footer”>footer</div> </div></body>任何页面想使用这样的布局值只需要替换中见的 content模块即可 <html xmlns:th=“http://www.thymeleaf.org” layout:decorator=“layout”> <body> <section layout:fragment=“content”> …也可以在引用模版的时候传参<head th:include=“layout :: htmlhead” th:with=“title=‘Hello’"></head>layout 是文件地址,如果有文件夹可以这样写 fileName/layout:htmlheadhtmlhead 是指定义的代码片段 如th:fragment=“copy"参考新一代Java模板引擎ThymeleafThymeleaf基本知识thymeleaf总结文章Thymeleaf 模板的使用thymeleaf 学习笔记写在最后欢迎关注、喜欢、和点赞后续将推出更多的工具集教程,敬请期待。欢迎关注我的微信公众号获取更多更全的学习资源,视频资料,技术干货!公众号回复“学习”,拉你进程序员技术讨论群,干货资源第一时间分享。公众号回复“视频”,领取800GJava视频学习资源。公众号回复“全栈”,领取1T前端,Java,产品经理,微信小程序,Python等资源合集大放送。公众号回复“慕课”,领取1T慕课实战学习资源。公众号回复“实战”,领取750G项目实战学习资源。公众号回复“面试”,领取8G面试实战学习资源。 ...

April 14, 2019 · 3 min · jiezi

工具集核心教程 | 第四篇: Velocity模板引擎入门到进阶

Velocity是一个基于java的模板引擎(template engine)。它允许任何人仅仅简单的使用模板语言(template language)来引用由java代码定义的对象。 当Velocity应用于web开发时,界面设计人员可以和java程序开发人员同步开发一个遵循MVC架构的web站点,也就是说,页面设计人员可以只 关注页面的显示效果,而由java程序开发人员关注业务逻辑编码。Velocity将java代码从web页面中分离出来,这样为web站点的长期维护提 供了便利,同时也为我们在JSP和PHP之外又提供了一种可选的方案。Velocity脚本语法摘要1. 变量(1)变量的定义:#set($name = “hello”) 说明:velocity中变量是弱类型的。当使用#set 指令时,括在双引号中的字面字符串将解析和重新解释,如下所示:#set($directoryRoot = “www” )#set($templateName = “index.vm” )#set($template = “$directoryRoot/$templateName” )$template输出将会是:www/index.vm注:在velocity中使用$2.5这样的货币标识是没有问题得的,因为velocity中的变量总是以一个大写或者小写的字母开始的。(2)变量规范的写法${name} ,也可以写成:$name。提倡用前面的写法。例如:你希望通过一个变量$vice来动态的组织一个字符串。 Jack is a $vicemaniac.本来变量是$vice现在却变成了$vicemaniac,这样Veloctiy就不知道您到底要什么了。所以,应该使用规范的格式书写 : Jack is a ${vice}maniac现在Velocity知道变量是$vice而不是$vicemaniac。注意:当引用属性的时候不能加{}(3)变量的赋值: $name=“hello"赋值的左边必须是一个变量或者是属性引用。右边可以是下面六种类型之一: 变量引用,字面字符串,属性引用,方法引用,字面数字,数组列表。下面的例子演示了上述的每种类型:#set( $monkey = $bill ) ## variable reference#set( $monkey.Friend = “monica” ) ## string#set( $monkey.Blame = $whitehouse.Leak ) ## property reference#set( $monkey.Plan = $spindoctor.weave($web) ) ## method reference#set( $monkey.Number = 123 ) ##number#set( $monkey.Say = [“Not”, $my, “fault”] ) ## ArrayList注意:①如果上述例子中的右值是null, 则左值不会被赋值,也就是说会保留以前的值。②velocity模板中未被定义的变量将被认为是一个字符串。例如:#set($foo = “gibbous”)$moon = $foo输出结果为:$moon = gibbous③velocity模板中不会将reference解释为对象的实例变量。例如:$foo.Name将被解释为Foo对象的getName()方法,而不是Foo对象的Name实例变量。例如:$foo.getBar() 等同于$foo.Bar ;$data.getUser(“jon”) 等同于$data.User(“jon”) ;data.getRequest().getServerName() 等同于$data.Request.ServerName等同于${data.Request.ServerName}2. 循环#foreach ($element in $list) This is $element. $velocityCount#end例子:#set( $list = [“pine”, “oak”, “maple”])#foreach ($element in $list)$velocityCountThis is $element.#end输出的结果为:1 This is pine.2 This is oak.3 This is maple.每次循环$list中的一个值都会赋给$element变量。$list可以是一个Vector、Hashtable或者Array。分配给$element的值是一个java对象,并且可以通过变量被引用。例如:如果$element t是一个java的Product类,并且这个产品的名字可以通过调用他的getName()方法得到。#foreach ( $key in $list.keySet())Key: $key -> Value: $list.get($key) <br>#end提示:velocity中大小写敏感。Velocity还特别提供了得到循环次数的方法,$velocityCount变量的名字是Velocity默认的名字。 例子:First example:#foreach ( $foo in [1..5] ) $foo #endSecond example:#foreach ( $bar in [2..-2] ) $bar #endThird example:#set ( $arr = [0..1] ) #foreach ( $i in $arr ) $i #end上面三个例子的输出结果为: First example: 1 2 3 4 5Second example: 2 1 0 -1 -2Third example: 0 13. 条件语句#if (condition)#elseif (condition)#else #end4. 语句的嵌套#foreach ($element in $list) ## inner foreach 内循环#foreach ($element in $list)This is $element. $velocityCount <br>inner<br>#end ## inner foreach 内循环结束 ## outer foreachThis is $element.$velocityCount <br>outer<br>#end语句中也可以嵌套其他的语句,如#if…#else…#end等。5. 注释(1)单行注释: ## This is a single line comment.(2)多行注释:# Thus begins a multi-line comment. Online visitors won’t see this text because the Velocity Templating Engine will ignore it. #(3)文档格式:#** This is a VTL comment block and may be used to store such information as the document author and versioninginformation:@version 1.1@author xiao*#6. 关系和逻辑操作符Velocity 也具有逻辑AND, OR 和 NOT 操作符。如## example for AND#if($foo && $bar)<strong>This and that</strong>#end例子中#if() 指令仅在$foo 和$bar 都为真的时候才为真。如果$foo 为假,则表达式也为假;并且 $bar 将不被求值。如果 $foo 为真,Velocity 模板引擎将继续检查$bar的值,如果 $bar 为真,则整个表达式为真。并且输出This AND that 。如果 $bar 为假,将没有输出因为整个表达式为假。7.Velocity 中的宏Velocity中的宏我们可以理解为函数。①宏的定义#macro(宏的名称 $参数1 $参数2 …) 语句体(即函数体)#end②宏的调用#宏的名称($参数1 $参数2 …) 说明:参数之间用空格隔开。8.#stop 停止执行模板引擎并返回,把它应用于debug是很有帮助的。9.#include与#parse#include和#parse的作用都是引入本地文件, 为了安全的原因,被引入的本地文件只能在TEMPLATE_ROOT目录下。区别:(1) 与#include不同的是,#parse只能指定单个对象。而#include可以有多个如果您需要引入多个文件,可以用逗号分隔就行:#include (“one.gif”, “two.txt”, “three.htm” )在括号内可以是文件名,但是更多的时候是使用变量的:#include ( “greetings.txt”, $seasonalstock )(2) #include被引入文件的内容将不会通过模板引擎解析; 而#parse引入的文件内容Velocity将解析其中的velocity语法并移交给模板,意思就是说相当与把引入的文件copy到文件中。#parse是可以递归调用的,例如:如果dofoo.vm包含如下行:Count down.<br>#set ($count = 8)#parse (“parsefoo.vm”)<br>All done with dofoo.vm!那么在parsefoo.vm模板中,你可以包含如下VTL:$count#set($count = $count - 1)#if ( $count > 0 )<br>#parse( “parsefoo.vm” )#else<br>All done with parsefoo.vm!#end的显示结果为:Count down.876543210All done with parsefoo.vm!All done with dofoo.vm!注意:在vm中使用#parse来嵌套另外一个vm时的变量共享问题。如:->a.vm 里嵌套 b.vm;->a.vm 里定义了变量 $param;->b.vm 里可以直接使用$param,无任何限制。但需要特别注意的是,如果b.vm里同时定义有变量$param,则b.vm里将使用b.vm里定义的值。10.转义字符’‘的使用如果reference被定义,两个’\’意味着输出一个’\’,如果未被定义,刚按原样输出。如:#set($email = “foo” )$email$email\$email\$email输出:foo$email\foo$email如果$email 未定义$email$email\$email\$email输出:$email$email\$email\$email11.内置对象Velocity内置了一些对象,在vm模版里可以直接调用,列举如下:$request、$response、$session,另外,模板内还可以使用 $msg内的消息工具访问 Struts 的国际化资源,达到简便实现国际化的方法。12. 数组访问对数组的访问在Velocity中存在问题,因为Velocity只能访问对象的方法,而数组又是一个特殊的Array,所以虽然数组可以进行循环列举,但却不能定位访问特定位置的元素,如 strs[2],数组对固定位置元素的访问调用了Array的反射方法get(Object array, int index),而Velocity没能提供这样的访问,所以数组要么改成List等其他类容器的方式来包装,要么就通过公用Util类的方式来提供,传入数组对象和要访问的位置参数,从而达到返回所需值的目的。结束语Velocity 可以被应用在各种各样的情景下,本文介绍的只是它的一种用途而已,它还可以被用来做 MVC 结构中的view 层,或者动态内容静态化等。另外,Velocity 并不是唯一的模板框架,同样很优秀的 Freemarker、Thymeleaf 也获得了非常广泛的应用,有兴趣的读者可以去深入研究更多的功能和用途。附录及参考文献使用 Velocity 模板引擎快速生成代码写在最后欢迎关注、喜欢、和点赞后续将推出更多的工具集教程,敬请期待。欢迎关注我的微信公众号获取更多更全的学习资源,视频资料,技术干货!公众号回复“学习”,拉你进程序员技术讨论群,干货资源第一时间分享。公众号回复“视频”,领取800GJava视频学习资源。公众号回复“全栈”,领取1T前端,Java,产品经理,微信小程序,Python等资源合集大放送。公众号回复“慕课”,领取1T慕课实战学习资源。公众号回复“实战”,领取750G项目实战学习资源。公众号回复“面试”,领取8G面试实战学习资源。 ...

April 14, 2019 · 2 min · jiezi

工具集核心教程 | 第五篇: 利用Velocity模板引擎生成模板代码

前言不知道大家有没有这样的感觉,在平时开发中,经常有很多dao、service类中存着很多重复的代码,Velocity提供了模板生成工具,今天我教大家怎么和这些大量的重复代码说再见。参考项目:https://github.com/bigbeef/cppba-codeTemplate个人博客:http://www.zhangbox.cn注意大家可以写适合自己的模板,这里为了演示,就直接拿cppba-web的模板来示范,至于velocity的语法大家可以查看这篇文章:工具集核心教程 | 第四篇: Velocity模板引擎入门到大神maven配置 <!– velocity –><dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> <version>1.7</version></dependency>创建模板文件首先看下目录结构:这里演示我就只贴出ServiceImplTemplate.java,需要其他模板代码可以到我github里面下载#set ($domain = $!domainName.substring(0,1).toLowerCase()+$!domainName.substring(1))package $!{packageName}.service.impl;import $!{packageName}.core.bean.PageEntity;import $!{packageName}.dao.$!{domainName}Dao;import $!{packageName}.dto.$!{domainName}Dto;import $!{packageName}.dto.BaseDto;import $!{packageName}.entity.$!{domainName};import $!{packageName}.service.$!{domainName}Service;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Objects;/** * 开发者 * nickName:星缘 * email:1342541819@qq.com * github:https://github.com/bigbeef * velocity模板生成 cppba-codeTemplate /@Service@Transactionalpublic class $!{domainName}ServiceImpl implements $!{domainName}Service{ @Resource private $!{domainName}Dao $!{domain}Dao; @Override public void save($!{domainName} $!{domain}) { $!{domain}Dao.save($!{domain}); } @Override public void delete($!{domainName} $!{domain}) { $!{domain}Dao.delete($!{domain}); } @Override public void update($!{domainName} $!{domain}) { $!{domain}Dao.update($!{domain}); } @Override public $!{domainName} findById(int id) { return ($!{domainName}) $!{domain}Dao.get($!{domainName}.class, id); } @Override public PageEntity<$!{domainName}> query(BaseDto baseDto) { String hql = " select distinct $!{domain} from $!{domainName} $!{domain} where 1=1 “; Map params = new HashMap<String,Object>(); $!{domainName}Dto $!{domain}Dto = ($!{domainName}Dto)baseDto; $!{domainName} $!{domain} = $!{domain}Dto.get$!{domainName}(); int page = $!{domain}Dto.getPage(); int pageSize = $!{domain}Dto.getPageSize(); List list = $!{domain}Dao.query(hql,params,page,pageSize); long count = $!{domain}Dao.count(hql,params); PageEntity<$!{domainName}> pe = new PageEntity<$!{domainName}>(); pe.setCount(count); pe.setList(list); return pe; }}模板生成接下来是生成模板的主函数:package com.cppba.core;import org.apache.velocity.Template;import org.apache.velocity.VelocityContext;import org.apache.velocity.app.Velocity;import org.apache.velocity.app.VelocityEngine;import java.io.BufferedWriter;import java.io.File;import java.io.FileOutputStream;import java.io.OutputStreamWriter;import java.util.HashMap;import java.util.Map;import java.util.Properties;/* * 开发者 * nickName:星缘 * email:1342541819@qq.com * github:https://github.com/bigbeef /public class Main { static String domainName = “Articles”; //类名 static String packageName = “com.cppba”;//类包 static String templateDir = “\src\main\webapp\template\”; static String sourcePath = System.getProperty(“user.dir”)+templateDir; static String resultDir = “\out”; static String targetPath = System.getProperty(“user.dir”) + resultDir + “\” + packageName.replace(”.", “\”); public static void main(String []args) throws Exception{ Map<String,Object> map = new HashMap(); map.put(“DaoTemplate.java”,“dao/” + domainName + “Dao.java”); map.put(“ServiceTemplate.java”,“service/” + domainName + “Service.java”); map.put(“ServiceImplTemplate.java”,“service/impl/” + domainName + “ServiceImpl.java”); map.put(“DtoTemplate.java”,“dto/” + domainName + “Dto.java”); for(String templateFile:map.keySet()){ String targetFile = (String) map.get(templateFile); Properties pro = new Properties(); pro.setProperty(Velocity.OUTPUT_ENCODING, “UTF-8”); pro.setProperty(Velocity.INPUT_ENCODING, “UTF-8”); pro.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, sourcePath); VelocityEngine ve = new VelocityEngine(pro); VelocityContext context = new VelocityContext(); context.put(“domainName”,domainName); context.put(“packageName”,packageName); Template t = ve.getTemplate(templateFile, “UTF-8”); File file = new File(targetPath, targetFile); if (!file.getParentFile().exists()) file.getParentFile().mkdirs(); if (!file.exists()) file.createNewFile(); FileOutputStream outStream = new FileOutputStream(file); OutputStreamWriter writer = new OutputStreamWriter(outStream, “UTF-8”); BufferedWriter sw = new BufferedWriter(writer); t.merge(context, sw); sw.flush(); sw.close(); outStream.close(); System.out.println(“成功生成Java文件:” + (targetPath + targetFile).replaceAll("/", “\\”)); } }}生成java文件我们可以修改domainName和packageName来修改我们的包名和类名,我们运行下看:我们看到生成成功,我们打开ArticlesServiceImpl.java看下:package com.cppba.service.impl;import com.cppba.core.bean.PageEntity;import com.cppba.dao.ArticlesDao;import com.cppba.dto.ArticlesDto;import com.cppba.dto.BaseDto;import com.cppba.entity.Articles;import com.cppba.service.ArticlesService;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Objects;/* * 开发者 * nickName:星缘 * email:1342541819@qq.com * github:https://github.com/bigbeef * velocity模板生成 cppba-codeTemplate */@Service@Transactionalpublic class ArticlesServiceImpl implements ArticlesService{ @Resource private ArticlesDao articlesDao; @Override public void save(Articles articles) { articlesDao.save(articles); } @Override public void delete(Articles articles) { articlesDao.delete(articles); } @Override public void update(Articles articles) { articlesDao.update(articles); } @Override public Articles findById(int id) { return (Articles) articlesDao.get(Articles.class, id); } @Override public PageEntity<Articles> query(BaseDto baseDto) { String hql = " select distinct articles from Articles articles where 1=1 “; Map params = new HashMap<String,Object>(); ArticlesDto articlesDto = (ArticlesDto)baseDto; Articles articles = articlesDto.getArticles(); int page = articlesDto.getPage(); int pageSize = articlesDto.getPageSize(); List list = articlesDao.query(hql,params,page,pageSize); long count = articlesDao.count(hql,params); PageEntity<Articles> pe = new PageEntity<Articles>(); pe.setCount(count); pe.setList(list); return pe; }}生成成功,我们拷贝到cppba-web中可完美运行!写在最后欢迎关注、喜欢、和点赞后续将推出更多的工具集教程,敬请期待。欢迎关注我的微信公众号获取更多更全的学习资源,视频资料,技术干货!公众号回复“学习”,拉你进程序员技术讨论群,干货资源第一时间分享。公众号回复“视频”,领取800GJava视频学习资源。公众号回复“全栈”,领取1T前端,Java,产品经理,微信小程序,Python等资源合集大放送。公众号回复“慕课”,领取1T慕课实战学习资源。公众号回复“实战”,领取750G项目实战学习资源。公众号回复“面试”,领取8G面试实战学习资源。 ...

April 14, 2019 · 3 min · jiezi

工具集核心教程 | 第六篇: Freemarker模板引擎入门到进阶

Freemarker的介绍 Freemarker 是一款模板引擎,是一种基于模版生成静态文件的通用 工具,它是为程序员提供的一个开发包,或者说是一个类库,它不是面向最终用户的,而是为程序员提供了一款可以嵌入他们开发产品的应用程序。 Freemarker 是使用纯java编写的,为了提高页面的访问速度,需要把页面静态化, 那么Freemarker就是被用来生成html页面。 到目前为止,Freemarker使用越来越广泛,不光光只是它强大的生成技术,而且它能够与进行很好的集成。 现在开始一层层揭开它的神秘面纱。。。特点1. 轻量级模版引擎,不需要Servlet环境就可以很轻松的嵌入到应用程序中2. 能生成各种文本,如html,xml,java,等3. 入门简单,它是用java编写的,很多语法和java相似工作原理:(借用网上的图片)目录1.FTL指令规则2.插值规则3.表达式4.FreeMarker的常用指令5.高级方法前言FreeMarker的模板文件并不比HTML页面复杂多少,FreeMarker模板文件主要由如下4个部分组成:1.文本:直接输出的部分2.注释:<#– … –>格式部分,不会输出3.插值:即${…}或#{…}格式的部分,将使用数据模型中的部分替代输出4.FTL指令:FreeMarker指定,和HTML标记类似,名字前加#予以区分,不会输出下面是一个FreeMarker模板的例子,包含了以上所说的4个部分:<html> <head> <title>Welcome!</title> </head> <body> <#– 注释部分 –> <#– 下面使用插值 –> <h1>Welcome ${user} !</h1> <p>We have these animals:</p> <u1> <#– 使用FTL指令 –> <#list animals as being> <li>${being.name} for ${being.price} Euros</li> <#list> <u1> </body></html>1. FTL指令规则在FreeMarker中,使用FTL标签来使用指令,FreeMarker有3种FTL标签,这和HTML标签是完全类似的.开始标签:<#directivename parameter>结束标签:</#directivename>空标签:<#directivename parameter/>实际上,使用标签时前面的符号#也可能变成@,如果该指令是一个用户指令而不是系统内建指令时,应将#符号改成@符号.使用FTL标签时,应该有正确的嵌套,而不是交叉使用,这和XML标签的用法完全一样.如果全用不存在的指令,FreeMarker不会使用模板输出,而是产生一个错误消息.FreeMarker会忽略FTL标签中的空白字符.值得注意的是< , /> 和指令之间不允许有空白字符.2. 插值规则FreeMarker的插值有如下两种类型:通用插值${expr};数字格式化插值:#{expr}或#{expr;format}2.1 通用插值对于通用插值,又可以分为以下4种情况:1.插值结果为字符串值:直接输出表达式结果2.插值结果为数字值:根据默认格式(由#setting指令设置)将表达式结果转换成文本输出.可以使用内建的字符串 函数格式化单个插值,如下面的例子:<#settion number_format=“currency”/><#assign answer=42/>${answer}${answer?string} <#– the same as ${answer} –>${answer?string.number}${answer?string.currency}${answer?string.percent}${answer}输出结果是:$42.00$42.0042$42.004,200%3.插值结果为日期值:根据默认格式(由#setting指令设置)将表达式结果转换成文本输出.可以使用内建的字符串函数格式化单个插值,如下面的例子:${lastUpdated?string(“yyyy-MM-dd HH:mm:ss zzzz”)}${lastUpdated?string(“EEE, MMM d, ‘‘yy”)}${lastUpdated?string(“EEEE, MMMM dd, yyyy, hh:mm:ss a ‘(‘zzz’)’”)}输出结果是:2008-04-08 08:08:08 Pacific Daylight TimeTue, Apr 8, ‘03Tuesday, April 08, 2003, 08:08:08 PM (PDT)4.插值结果为布尔值:根据默认格式(由#setting指令设置)将表达式结果转换成文本输出.可以使用内建的字符串函数格式化单个插值,如下面的例子:<#assign foo=true/>${foo?string(“yes”, “no”)}输出结果是:yes2.2 数字格式化插值数字格式化插值可采用#{expr;format}形式来格式化数字,其中format可以是:mX:小数部分最小X位MX:小数部分最大X位如下面的例子:<#assign x=2.582/><#assign y=4/>#{x; M2} <#– 输出2.58 –>#{y; M2} <#– 输出4 –>#{x; m2} <#– 输出2.6 –>#{y; m2} <#– 输出4.0 –>#{x; m1M2} <#– 输出2.58 –>#{x; m1M2} <#– 输出4.0 –>3. 表达式表达式是FreeMarker模板的核心功能,表达式放置在插值语法${}之中时,表明需要输出表达式的值;表达式语法也可与FreeMarker标签结合,用于控制输出.实际上FreeMarker的表达式功能非常强大,它不仅支持直接指定值,输出变量值,也支持字符串格式化输出和集合访问等功能.3.1 直接指定值使用直接指定值语法让FreeMarker直接输出插值中的值,而不是输出变量值.直接指定值可以是字符串,数值,布尔值,集合和MAP对象.字符串直接指定字符串值使用单引号或双引号限定,如果字符串值中包含特殊字符需要转义,看下面的例子:${“我的文件保存在C:\盘”}${‘我名字是"annlee"’}输出结果是:我的文件保存在C:\盘我名字是"annlee"FreeMarker支持如下转义字符:"; 双引号(u0022)'; 单引号(u0027)\; 反斜杠(u005C)\n; 换行(u000A)\r; 回车(u000D)\t; Tab(u0009)\b; 退格键(u0008)\f; Form feed(u000C)\l; <\g; >\a; &{; {\xCode; 直接通过4位的16进制数来指定Unicode码,输出该unicode码对应的字符.如果某段文本中包含大量的特殊符号,FreeMarker提供了另一种特殊格式:可以在指定字符串内容的引号前增加r标记,在r标记后的文件将会直接输出。看如下代码:${r"${foo}"}${r"C:\foo\bar"}输出结果是:${foo}C:\foo\bar数值表达式中的数值直接输出,不需要引号.小数点使用".“分隔,不能使用分组”,“符号.FreeMarker目前还不支持科学计数法,所以"1E3"是错误的.在FreeMarker表达式中使用数值需要注意以下几点:数值不能省略小数点前面的0,所以”.5"是错误的写法数值8 , +8 , 8.00都是相同的布尔值,直接使用true和false,不使用引号.集合, 集合以方括号包括,各集合元素之间以英文逗号",“分隔如下为集合元素遍历的例子:<#list [“星期一”, “星期二”, “星期三”, “星期四”, “星期五”, “星期六”, “星期天”] as x> ${x}</#list>输出结果是:星期一星期二星期三星期四星期五星期六星期天除此之外,集合元素也可以是表达式,例子如下:[2 + 2, [1, 2, 3, 4], “whatnot”]还可以使用数字范围定义数字集合,如2..5等同于[2, 3, 4, 5], 但是更有效率。 注意,使用数字范围来定义集合时无需使用方括号,数字范围也支持反递增的数字范围,如5..2Map对象,Map对象使用花括号包括,Map中的key-value对之间以英文冒号”:“分隔,多组key-value对之间以英文逗号”,“分隔。下面是一个例子:{“语文”:78, “数学”:80}Map对象的key和value都是表达式,但是key必须是字符串。3.2 输出变量值FreeMarker的表达式输出变量时,这些变量可以是顶层变量,也可以是Map对象中的变量,还可以是集合中的变量,并可以使用点(.)语法来访问Java对象的属性。下面分别讨论这些情况:1. 顶层变量所谓顶层变量就是直接放在数据模型中的值,例如有如下数据模型:Map root = new HashMap(); //创建数据模型root.put(“name”,“annlee”); //name是一个顶层变量对于顶层变量,直接使用${variableName}来输出变量值,变量名只能是字母,数字,下划线,$,@和#的组合,且不能以数字开头号.为了输出上面的name的值,可以使用如下语法:${name}2. 输出集合元素如果需要输出集合元素,则可以根据集合元素的索引来输出集合元素,集合元素的索引以方括号指定。假设有索引:[“星期一”,“星期二”,“星期三”,“星期四”,“星期五”,“星期六”,“星期天”],该索引名为week,如果需要输出星期三,则可以使用如下语法:${week[2]} //输出第三个集合元素此外,FreeMarker还支持返回集合的子集合,如果需要返回集合的子集合,则可以使用如下语法:week[3..5] //返回week集合的子集合,子集合中的元素是week集合中的第4-6个元素3. 输出Map元素这里的Map对象可以是直接HashMap的实例,甚至包括JavaBean实例,对于JavaBean实例而言,我们一样可以把其当成属性为key,属性值为value的Map实例.为了输出Map元素的值,可以使用点语法或方括号语法.假如有下面的数据模型:Map root = new HashMap();Book book = new Book();Author author = new Author();author.setName(“annlee”);author.setAddress(“gz”);book.setName(“struts2”);book.setAuthor(author);root.put(“info”,“struts”);root.put(“book”, book);为了访问数据模型中名为struts2的书的作者的名字,可以使用如下语法:book.author.name //全部使用点语法book[“author”].namebook.author[“name”] //混合使用点语法和方括号语法book[“author”][“name”] //全部使用方括号语法使用点语法时,变量名字有顶层变量一样的限制,但方括号语法没有该限制,因为名字可以是任意表达式的结果.3.3 字符串操作FreeMarker的表达式对字符串操作非常灵活,可以将字符串常量和变量连接起来,也可以返回字符串的子串等。字符串连接有两种语法:使用${..}或#{..}在字符串常量部分插入表达式的值,从而完成字符串连接.直接使用连接运算符+来连接字符串例如有如下数据模型:Map root = new HashMap(); root.put(“user”,“annlee”);下面将user变量和常量连接起来:${“hello, ${user}!”} //使用第一种语法来连接${“hello, " + user + “!”} //使用+号来连接上面的输出字符串都是hello,annlee!,可以看出这两种语法的效果完全一样.值得注意的是,${..}只能用于文本部分,不能用于表达式,下面的代码是错误的:<#if ${isBig}>Wow!</#if><#if “${isBig}">Wow!</#if>应该写成:<#if isBig>Wow!</#if>截取子串可以根据字符串的索引来进行,截取子串时如果只指定了一个索引值,则用于取得字符串中指定索引所对应的字符;如果指定两个索引值,则返回两个索引中间的字符串子串。假如有如下数据模型:Map root = new HashMap(); root.put(“book”,“struts2,freemarker”);可以通过如下语法来截取子串:${book[0]}${book[4]} //结果是su${book[1..4]} //结果是tru3.4 集合连接运算符这里所说的集合运算符是将两个集合连接成一个新的集合,连接集合的运算符是+,看如下的例子:<#list [“星期一”,“星期二”,“星期三”] + [“星期四”,“星期五”,“星期六”,“星期天”] as x> ${x}</#list>输出结果是:星期一 星期二 星期三 星期四 星期五 星期六 星期天3.5 Map连接运算符Map对象的连接运算符也是将两个Map对象连接成一个新的Map对象,Map对象的连接运算符是+,如果两个Map对象具有相同的key,则右边的值替代左边的值.看如下的例子:<#assign scores = {“语文”:86,“数学”:78} + {“数学”:87,“Java”:93}>语文成绩是${scores.语文}数学成绩是${scores.数学}Java成绩是${scores.Java}输出结果是:语文成绩是86数学成绩是87Java成绩是933.6 算术运算符FreeMarker表达式中完全支持算术运算,FreeMarker支持的算术运算符包括:+, - , * , / , % 看如下的代码:<#assign x=5>${ x * x - 100 }${ x /2 }${ 12 %10 }输出结果是:-75 2.5 2在表达式中使用算术运算符时要注意以下几点:运算符两边的运算数字必须是数字使用+运算符时,如果一边是数字,一边是字符串,就会自动将数字转换为字符串再连接,如: ${3 + “5”},结果是:35使用内建的int函数可对数值取整,如:<#assign x=5>${ (x/2)?int }${ 1.1?int }${ 1.999?int }${ -1.1?int }${ -1.999?int }结果是:2 1 1 -1 -13.7 比较运算符表达式中支持的比较运算符有如下几个:=或者==:判断两个值是否相等.!=:判断两个值是否不等.>或者gt:判断左边值是否大于右边值>=或者gte:判断左边值是否大于等于右边值<或者lt:判断左边值是否小于右边值<=或者lte:判断左边值是否小于等于右边值注意:=和!=可以用于字符串,数值和日期来比较是否相等,但=和!=两边必须是相同类型的值,否则会产生错误,而且FreeMarker是精确比较,“x”,“x “,“X"是不等的.其它的运行符可以作用于数字和日期,但不能作用于字符串,大部分的时候,使用gt等字母运算符代替>会有更好的效果,因为FreeMarker会把>解释成FTL标签的结束字符,当然,也可以使用括号来避免这种情况,如:<#if (x>y)>。3.8 逻辑运算符逻辑运算符有如下几个:逻辑与: &&逻辑或: ||逻辑非: !逻辑运算符只能作用于布尔值,否则将产生错误。3.9 内建函数FreeMarker还提供了一些内建函数来转换输出,可以在任何变量后紧跟“?”,“?”后紧跟内建函数,就可以通过内建函数来轮换输出变量.下面是常用的内建的字符串函数:html:对字符串进行HTML编码cap_first:使字符串第一个字母大写lower_case:将字符串转换成小写upper_case:将字符串转换成大写trim:去掉字符串前后的空白字符下面是集合的常用内建函数size:获取序列中元素的个数下面是数字值的常用内建函数int:取得数字的整数部分,结果带符号例如:<#assign test=“Tom & Jerry”>${test?html}${test?upper_case?html}结果是:Tom &amp; Jerry TOM &amp; JERRY3.10 空值处理运算符FreeMarker对空值的处理非常严格,FreeMarker的变量必须有值,没有被赋值的变量就会抛出异常,因为FreeMarker未赋值的变量强制出错可以杜绝很多潜在的错误,如缺失潜在的变量命名,或者其他变量错误.这里所说的空值,实际上也包括那些并不存在的变量,对于一个Java的null值而言,我们认为这个变量是存在的,只是它的值为null,但对于FreeMarker模板而言,它无法理解null值,null值和不存在的变量完全相同.为了处理缺失变量,FreeMarker提供了两个运算符:! :指定缺失变量的默认值??:判断某个变量是否存在其中,!运算符的用法有如下两种:variable!或variable!defaultValue,第一种用法不给缺失的变量指定默认值,表明默认值是空字符串,长度为0的集合,或者长度为0的Map对象。使用!指定默认值时,并不要求默认值的类型和变量类型相同.使用??运算符非常简单,它总是返回一个布尔值,用法为:variable??,如果该变量存在,返回true,否则返回false。3.11 运算符的优先级FreeMarker中的运算符优先级如下(由高到低排列):一元运算符: !内建函数: ?乘除法: , / , %加减法: - , +比较: > , < , >= , <= (lt , lte , gt , gte)相等: == , = , !=逻辑与: &&逻辑或: ||数字范围: ..实际上,我们在开发过程中应该使用括号来严格区分,这样的可读性好,出错少。4. FreeMarker的常用指令FreeMarker的FTL指令也是模板的重要组成部分,这些指令可实现对数据模型所包含数据的抚今迭代,分支控制.除此之外,还有一些重要的功能,也是通过FTL指令来实现的.4.1 if指令这是一个典型的分支控制指令,该指令的作用完全类似于Java语言中的if,if指令的语法格式如下:<#if condition>…<#elseif condition>…<#elseif condition>…<#else> …</#if>例子如下:<#assign age=23><#if (age>60)> 老年人<#elseif (age>40)> 中年人<#elseif (age>20)> 青年人<#else> 少年人</#if>输出结果是:青年人上面的代码中的逻辑表达式用括号括起来主要是因为里面有>符号,由于FreeMarker会将>符号当成标签的结束字符,可能导致程序出错,为了避免这种情况,我们应该在凡是出现这些符号的地方都使用括号。4.2 switch , case , default , break指令这些指令显然是分支指令,作用类似于Java的switch语句,switch指令的语法结构如下:<#switch value> <#case refValue>…<#break> <#case refValue>…<#break> <#default>…</#switch>4.3 list, break指令list指令是一个迭代输出指令,用于迭代输出数据模型中的集合,list指令的语法格式如下:<#list sequence as item> …</#list>上面的语法格式中,sequence就是一个集合对象,也可以是一个表达式,但该表达式将返回一个集合对象,而item是一个任意的名字,就是被迭代输出的集合元素.此外,迭代集合对象时,还包含两个特殊的循环变量:item_index:当前变量的索引值item_has_next:是否存在下一个对象除此之外,也可以使用<#break>指令跳出迭代。例子如下:<#list [“星期一”, “星期二”, “星期三”, “星期四”, “星期五”, “星期六”, “星期天”] as x> ${x_index + 1}.${x}<#if x_has_next>,</if> <#if x=“星期四”><#break></#if></#list>4.4 include指令include指令的作用类似于JSP的包含指令,用于包含指定页.include指令的语法格式如下:<#include filename [options]>在上面的语法格式中,两个参数的解释如下:filename:该参数指定被包含的模板文件options:该参数可以省略,指定包含时的选项,包含encoding和parse两个选项,其中encoding指定包含页面时所用的解码集,而parse指定被包含文件是否作为FTL文件来解析,如果省略了parse选项值,则该选项默认是true.4.5 import指令import指令用于导入FreeMarker模板中的所有变量,并将该变量放置在指定的Map对象中,import指令的语法格式如下:<#import “/lib/common.ftl” as com>上面的代码将导入/lib/common.ftl模板文件中的所有变量,交将这些变量放置在一个名为com的Map对象中.4.6 noparse指令noparse指令指定FreeMarker不处理该指定里包含的内容,该指令的语法格式如下:<#noparse>…</#noparse>看如下的例子:<#noparse><#list books as book> <tr><td>${book.name}<td>作者:${book.author}</#list></#noparse>输出如下:<#list books as book> <tr><td>${book.name}<td>作者:${book.author}</#list>4.7 escape , noescape指令escape指令导致body区的插值都会被自动加上escape表达式,但不会影响字符串内的插值,只会影响到body内出现的插值,使用escape指令的语法格式如下:<#escape identifier as expression>… <#noescape>…</#noescape></#escape>看如下的代码:<#escape x as x?html> First name:${firstName} Last name:${lastName} Maiden name:${maidenName}</#escape>上面的代码等同于:First name:${firstName?html}Last name:${lastName?html}Maiden name:${maidenName?html}escape指令在解析模板时起作用而不是在运行时起作用,除此之外,escape指令也嵌套使用,子escape继承父escape的规则,如下例子:<#escape x as x?html> Customer Name:${customerName} Items to ship; <#escape x as itemCodeToNameMap[x]> ${itemCode1} ${itemCode2} ${itemCode3} ${itemCode4} </#escape></#escape>上面的代码类似于:Customer Name:${customerName?html}Items to ship;${itemCodeToNameMap[itemCode1]?html}${itemCodeToNameMap[itemCode2]?html}${itemCodeToNameMap[itemCode3]?html}${itemCodeToNameMap[itemCode4]?html}对于放在escape指令中所有的插值而言,这此插值将被自动加上escape表达式,如果需要指定escape指令中某些插值无需添加escape表达式,则应该使用noescape指令,放在noescape指令中的插值将不会添加escape表达式。4.8 assign指令assign指令在前面已经使用了多次,它用于为该模板页面创建或替换一个顶层变量,assign指令的用法有多种,包含创建或替换一个顶层变量,或者创建或替换多个变量等,它的最简单的语法如下:<#assign name=value [in namespacehash]>这个用法用于指定一个名为name的变量,该变量的值为value,此外,FreeMarker允许在使用assign指令里增加in子句,in子句用于将创建的name变量放入namespacehash命名空间中。assign指令还有如下用法:<#assign name1=value1 name2=value2 … nameN=valueN [in namespacehash]>这个语法可以同时创建或替换多个顶层变量,此外,还有一种复杂的用法,如果需要创建或替换的变量值是一个复杂的表达式,则可以使用如下语法格式:<#assign name [in namespacehash]>capture this</#assign>在这个语法中,是指将assign指令的内容赋值给name变量。如下例子:<#assign x> <#list [“星期一”, “星期二”, “星期三”, “星期四”, “星期五”, “星期六”, “星期天”] as n> ${n} </#list></#assign>${x}上面的代码将产生如下输出:星期一 星期二 星期三 星期四 星期五 星期六 星期天虽然assign指定了这种复杂变量值的用法,但是我们也不要滥用这种用法,如下例子:<#assign x>Hello ${user}!</#assign>以上代码改为如下写法更合适:<#assign x=“Hello ${user}!">4.9 setting指令该指令用于设置FreeMarker的运行环境,该指令的语法格式如下:<#setting name=value>,在这个格式中,name的取值范围包含如下几个:locale:该选项指定该模板所用的国家/语言选项number_format:指定格式化输出数字的格式boolean_format:指定两个布尔值的语法格式,默认值是true,falsedate_format,time_format,datetime_format:指定格式化输出日期的格式time_zone:设置格式化输出日期时所使用的时区4.10 macro , nested , return指令macro可以用于实现自定义指令,通过使用自定义指令,可以将一段模板片段定义成一个用户指令,使用macro指令的语法格式如下:<#macro name param1 param2 … paramN> … <#nested loopvar1, loopvar2, …, loopvarN> … <#return> …</#macro>在上面的格式片段中,包含了如下几个部分:name:name属性指定的是该自定义指令的名字,使用自定义指令时可以传入多个参数paramX:该属性就是指定使用自定义指令时报参数,使用该自定义指令时,必须为这些参数传入值nested指令:nested标签输出使用自定义指令时的中间部分nested指令中的循环变量:这此循环变量将由macro定义部分指定,传给使用标签的模板return指令:该指令可用于随时结束该自定义指令.看如下的例子:<#macro book> //定义一个自定义指令j2ee</#macro><@book /> //使用刚才定义的指令上面的代码输出结果为:j2ee在上面的代码中,可能很难看出自定义标签的用处,因为我们定义的book指令所包含的内容非常简单,实际上,自定义标签可包含非常多的内容,从而可以实现更好的代码复用.此外,还可以在定义自定义指令时,为自定义指令指定参数,看如下代码:<#macro book booklist> //定义一个自定义指令booklist是参数 <#list booklist as book> ${book} </#list></#macro><@book booklist=[“spring”,“j2ee”] /> //使用刚刚定义的指令上面的代码为book指令传入了一个参数值,上面的代码的输出结果为:spring j2ee不仅如此,还可以在自定义指令时使用nested指令来输出自定义指令的中间部分,看如下例子:<#macro page title><html><head> <title>FreeMarker示例页面 - ${title?html}</title></head><body> <h1>${title?html}</h1> <#nested> //用于引入用户自定义指令的标签体</body></html></#macro>上面的代码将一个HTML页面模板定义成一个page指令,则可以在其他页面中如此page指令:<#import “/common.ftl” as com> //假设上面的模板页面名为common.ftl,导入页面<@com.page title=“book list”><u1><li>spring</li><li>j2ee</li></ul></@com.page >从上面的例子可以看出,使用macro和nested指令可以非常容易地实现页面装饰效果,此外,还可以在使用nested指令时,指定一个或多个循环变量,看如下代码:<#macro book><#nested 1> //使用book指令时指定了一个循环变量值<#nested 2></#macro><@book ;x> ${x} .图书</@book >当使用nested指令传入变量值时,在使用该自定义指令时,就需要使用一个占位符(如book指令后的;x).上面的代码输出文本如下:1 .图书 2 .图书在nested指令中使用循环变量时,可以使用多个循环变量,看如下代码:<#macro repeat count> <#list 1..count as x> //使用nested指令时指定了三个循环变量 <#nested x, x/2, x==count> </#list></#macro><@repeat count=4 ; c halfc last> ${c}. ${halfc}<#if last> Last! </#if></@repeat >上面的输出结果为:1. 0.5 2. 1 3. 1.5 4. 2 Last;return指令用于结束macro指令,一旦在macro指令中执行了return指令,则FreeMarker不会继续处理macro指令里的内容,看如下代码:<#macro book>spring<#return>j2ee</#macro><@book />上面的代码输出:spring而j2ee位于return指令之后,不会输出。4.11 t, lt, rt 指令语法:<#t> 去掉左右空白和回车换行<#lt> 去掉左边空白和回车换行<#rt> 去掉右边空白和回车换行<#nt> 取消上面的效果4.12 指令总结if, else, elseifswitch, case, default, breaklist, breakincludeImportcompressescape, noescapeassignglobalsettingmacro, nested, returnt, lt, rt, nt5. 高级方法5.1 自定义方法自定义方法, 如:${timer(“yyyy-MM-dd H:mm:ss”, x)}${timer(“yyyy-MM-dd “, x)}在模板中除了可以通过对象来调用方法外(${object.methed(args)})也可以直接调用java实现的方法,java类必须实现接口TemplateMethodModel的方法exec(List args). 下面以把毫秒的时间转换成按格式输出的时间为例子:public class LongToDate implements TemplateMethodModel { public TemplateModel exec(List args) throws TemplateModelException { SimpleDateFormat mydate = new SimpleDateFormat((String) args.get(0))); return mydate.format(new Date(Long.parseLong((String)args.get(1))); }} 将LongToDate对象放入到数据模型中: root.put(“timer”, new LongToDate());ftl模板里使用:<#assign x = “123112455445”>${timer(“yyyy-MM-dd H:mm:ss”, x)}${timer(“yyyy-MM-dd “, x)}输出结果:2001-10-12 5:21:122001-10-125.2 自定义 Transforms实现自定义的<@transform>文本或表达式</@transform >的功能,允许对中间的最终文本进行解析转换例子:实现<@upcase>str</@upcase > 将str转换成STR的功能.代码如下:import java.io.;import java.util.*;import freemarker.template.TemplateTransformModel;public class UpperCaseTransform implements TemplateTransformModel { public Writer getWriter(Writer out, Map args) { return new UpperCaseWriter(out); } private class UpperCaseWriter extends Writer { private Writer out; UpperCaseWriter (Writer out) { this.out = out; } public void write(char[] cbuf, int off, int len) throws IOException { out.write(new String(cbuf, off, len).toUpperCase()); } public void flush() throws IOException { out.flush(); } public void close() { } }}然后将此对象put到数据模型中, 如下所示:root.put(“upcase”, new UpperCaseTransform());在view(ftl)页面中可以如下方式使用:<@upcase>hello world</@upcase >打印输出:HELLO WORLD写在最后欢迎关注、喜欢、和点赞后续将推出更多的工具集教程,敬请期待。欢迎关注我的微信公众号获取更多更全的学习资源,视频资料,技术干货!公众号回复“学习”,拉你进程序员技术讨论群,干货资源第一时间分享。公众号回复“视频”,领取800GJava视频学习资源。公众号回复“全栈”,领取1T前端,Java,产品经理,微信小程序,Python等资源合集大放送。公众号回复“慕课”,领取1T慕课实战学习资源。公众号回复“实战”,领取750G项目实战学习资源。公众号回复“面试”,领取8G面试实战学习资源。 ...

April 14, 2019 · 4 min · jiezi

工具集核心教程 | 第一篇: .md即markdown文件的基本常用编写语法(图文并茂)

序言:感觉只要是不写博客,人就很变得很懒,学的知识点感觉还是记不住,渐渐地让我明白,看的越多,懂的越少(你这话不是有毛病吗?应该是看的越多,懂的越多才对),此话怎讲,当你在茫茫的知识库里面东看看,西看看的时候,很快就被海量的知识给淹没了,根本就不知道哪些是对的,哪些是错的,感觉好像这个也懂了,那个也懂了,但是真正写起来,脑子又一片空白,又好像什么都不懂,这种状态时有发生,这就叫不懂装懂,最根本的原因就是看的太多,写的太少,所以为了改掉这样毛病,把被动学习变成主动学习,接下来的日子,多写写,即使是写一些学习工作中遇到的坑也是好的,没事翻出来看看,还可以加深印象,好了,废话到处!起因:因为现在前后端、测试交互很频繁,一个完整的项目或者教程,说明性文件必不可少!那就难免要写一些readme等等的说明性文件,但是这样的文件一般都是.md的文件,编写的语法自然跟其他格式的文件有所区别,置于为什么要用这种格式的文件,不要问我,我也不知道,大家都这么用,跟着用就对了,如果有大神知道的,不妨告知小弟,本文也是我学习写markdown文件的一个笔记吧,仅供参考!正文:1.标题的几种写法:第一种:前面带#号,后面带文字,分别表示h1-h6,上图可以看出,只到h6,而且h1下面会有一条横线,注意,#号后面有空格第二种:这种方式好像只能表示一级和二级标题,而且=和-的数量没有限制,只要大于一个就行第三种:这里的标题支持h1-h6,为了减少篇幅,我就偷个懒,只写前面二个,这个比较好理解,相当于标签闭合,注意,标题与#号要有空格那既然3种都可以使用,可不可以混合使用呢?我试了一下,是可以的,但是为了让页面标签的统一性,不建议混合使用,推荐使用第一种,比较简洁,全面为了搞清楚原理,我特意在网上搜一下在线编写markdown的工具,发现实际上是把这些标签最后转化为html标签,如图:在线地址请看这里: markdown在线编辑 (只是想看看背后的转换原理,没有广告之嫌)2.列表我们都知道,列表分为有序列表和无序列表,下面直接展示2种列表的写法:可以看到,无序列表可以用* , + , — 来创建,用在线编辑器看,实际上是转换成了ul>li ,所以使用哪个都可以,推荐使用吧有序列表就相对简单一点,只有这一种方式,注意,数字后面的点只能是英文的点,特别注意,有序列表的序号是根据第一行列表的数字顺序来的,比如说:第一组本来是3 2 1 倒序,但是现实3 4 5 ,后面一组 序号是乱的, 但是还是显示 3 4 5 ,这点必须注意了3.区块引用比如说,你想对某个部分做的内容做一些说明或者引用某某的话等,可以用这个语句无序列表下方的便是引用,可以有多种用途,看你的需求了,用法就是在语句前面加一个 > ,注意是英文的那个右尖括号,注意空格引用因为是一个区块,理论上是应该什么内容都可以放,比如说:标题,列表,引用等等,看看下图:将上面的代码稍微改一下,全部加上引用标签,就变成了一个大的引用,还有引用里面还有引用,那引用嵌套引用还没有别的写法呢?上图可以看出,想要在上一次引用中嵌套一层引用,只需多加一个>,理论上可以无限嵌套,我就不整那么多了,注意:多层嵌套的>是不需要连续在一起的,只要在一行就可以了,中间允许有空格,但是为了好看,还是把排版搞好吧4.华丽的分割线分割线可以由 - _(星号,减号,底线)这3个符号的至少3个符号表示,注意至少要3个,且不需要连续,有空格也可以应该看得懂吧,但是为了代码的排版好看,你们自己定规则吧,前面有用到星号,建议用减号5.链接支持2种链接方式:行内式和参数式,不管是哪一种,链接文字都是用 [方括号] 来标记。上图可知,行内式的链接格式是:链接的文字放在[]中,链接地址放在随后的()中,举一反三,经常出现的列表链接就应该这样写:链接还可以带title属性,好像也只能带title,带不了其他属性,注意,是链接地址后面空一格,然后用引号引起来这是行内式的写法,参数式的怎么写:这就好理解了,就是把链接当成参数,适合多出使用相同链接的场景,注意参数的对应关系,参数定义时,这3种写法都可以:还支持这种写法,如果你不想混淆的话:其实还有一种隐式链接的写法,但是我觉得那种写法不直观,所以就不写了,经常用的一般就上面2种,如果你想了解隐式链接,可以看我文章最后放出的参考地址6.图片图片也有2种方式:行内式和参数式,用法跟链接的基本一样,唯一的不同就是,图片前面要写一个!(这是必须的),没什么好说的7.代码框这个就比较重要了,很多时候都需要展示出一些代码如果代码量比较少,只有单行的话,可以用单反引号包起来,如下:要是多行这个就不行了,多行可以用这个,也就是一对`:多行用三个反引号,如果要写注释,可以在反引号后面写8.表格这个写的有点麻烦,注意看从这3种不同写法看,表格的格式不一定要对的非常起,但是为了好看,对齐肯定是最好的,第一种的分割线后面的冒号表示对齐方式,写在左边表示左对齐,右边为右对齐,两边都写表示居中,还是有点意思的,不过现实出来的结果是,表格外面并没有线框包起来,不知道别人的怎么弄的9.强调一个星号或者是一个下划线包起来,会转换为<em>倾斜,如果是2个,会转换为<strong>加粗10.转义就不一一列举了,基本上跟js转义是一样的11.删除线常用的基本上就这些了,如果还有一些常用的,可以跟我留言,我补充上去,我觉得图文并茂才是高效学习的正确姿势,但愿为你的学习带来帮助!最后推荐一款Window下markdown编辑器typora非常好用的一款本地MD文件编辑器 ,中文版下载地址:typora下载界面如下:参考文献:http://www.appinn.com/markdown/http://sspai.com/25137写在最后欢迎关注、喜欢、和点赞后续将推出更多的工具集教程,敬请期待。欢迎关注我的微信公众号获取更多更全的学习资源,视频资料,技术干货!公众号回复“学习”,拉你进程序员技术讨论群,干货资源第一时间分享。公众号回复“视频”,领取800GJava视频学习资源。公众号回复“全栈”,领取1T前端,Java,产品经理,微信小程序,Python等资源合集大放送。公众号回复“慕课”,领取1T慕课实战学习资源。公众号回复“实战”,领取750G项目实战学习资源。公众号回复“面试”,领取8G面试实战学习资源。

April 14, 2019 · 1 min · jiezi

工具集核心教程 | 第二篇: IDEA入门到进阶(图文并茂)

前言:IntelliJ IDEA如果说IntelliJ IDEA是一款现代化智能开发工具的话,Eclipse则称得上是石器时代的东西了。其实笔者也是一枚从Eclipse转IDEA的探索者,随着近期的不断开发实践和调试,逐步体会到这款智能IDE带来的巨大开发便利,在强大的插件功能支持下,诸如对Git和Maven的支持简直让人停不下来,各种代码提示,包括JS更是手到擒来,最终不得不被这款神奇的IDE所折服。为了让身边更多的小伙伴参与进来,决定写下这篇文章,与君共享。(^_^)高级传送门:IntelliJ IDEA 官网下载 - Ultimate 终极版激活方法: 安装完成后 选择 License 输入 http://intellij.mandroid.cn正文:IntelliJ IDEA 使用教程1. IDEA VS Eclipse 核心术语比较由下图可见:两者最大的转变就在于工作空间概念的转变,并且在IDEA当中,Project和 Module是作为两个不同的概念,对项目结构是重要意义的,这也恰恰是许多IDEA初学者觉得困扰的地方。 1.1 为什么要取消工作空间?答:简单来说,IDEA不需要设置工作空间,因为每一个Project都具备一个工作空间!!对于每一个IDEA的项目工程(Project)而言,它的每一个子模块(Module)都可以使用独立的JDK和MAVEN。这对于传统项目迈向新项目的重构添加了极大的便利性,这种多元化的灵活性正是Eclipse所缺失的,因为开始Eclipse在初次使用时已经绑死了工作空间。 1.2 此外,很多新手都会问,为什么IDEA里面的子工程要称为Module ?答:其实就是模块化的概念,作为聚合工程亦或普通的根目录,它称之为Project,而下面的子工程称为模块,每一个子模块之间可以相关联,也可以没有任何关联。2. 当前项目配置VS 默认配置 2.1 为什么有了当前项目配置,还需要默认配置呢?答:因为IDEA没有工作空间的概念,所以每个新项目(Project)都需要设置自己的JDK和MAVEN等相关配置,这样虽然提高了灵活性,但是却要为每个新项目都要重新配置,这显然不符合我们的预期。在这个背景下,默认配置给予当前项目配置提供了Default选项,问题自然就迎刃而解了。 2.2 初始化步骤 打开默认配置:顶部导航栏 -> File -> Other Settings -> Default Settings /ProjectStructs 打开当前配置:顶部导航栏 -> File -> Settings / ProjectStructs示例图:=============================================接下来,来看看IDEA如何快速搭建Java开发环境!!=============================================3. 全局JDK(默认配置) 具体步骤:顶部工具栏 File ->Other Settins -> Default Project Structure -> SDKs -> JDK 示例: 根据下图步骤设置JDK目录,最后点击OK保存。4. 全局Maven(默认配置) 具体步骤:顶部工具栏 File ->Other Settings -> Default Settings -> Build & Tools -> Maven 示例: 理论上只要配置了Maven主目录即可,实际开发推荐采用User Settins file .5. 版本控制Git/Svn (默认配置) 具体步骤:顶部工具栏 File ->Other Settings -> Default Settings -> Version Control -> Git 示例: IDEA默认集成了对Git/Svn的支持 直接设置执行程序,右边Test提示成功即可。 部分小伙伴反馈说无法找到svn.exe,解决方法:重装SVN,配置项重新选择command line client tools 即可。6. 自动导包和智能移除 (默认配置) 具体步骤:顶部工具栏 File ->Other Settings -> Default Settings -> Auto Import 说明: 在网上看到很多人在提问IDEA为什么不能优化导包而Eclipse可以, 所以特意抽出来跟大家分享IDEA如何优化导包。 7. Tomcat Server(当前项目配置) 很多小伙伴刚开始都找不到Tomcat的配置,其实很简单,Tomcat或者Jetty这些都是部署的容器,自然会联想到Deployment ,打开部署配置,可以看到应用服务器的配置。 配置Tomcat方法: File -> Settings -> Deployment -> Application Servers -> Tomcat Server 具体配置方法,如下图:IDEA 必备小技能 为了提升开发效率,撸主贴心为大家准备以下实用指数五颗星的小技巧:8. 自动编译 具体步骤:顶部工具栏 File ->Other Settings -> Default Settings -> Auto Import 说明:开启自动编译之后,结合Ctrl+Shift+F9 会有热更新效果。自动编译(Runtime) 具体步骤: 敲击 Ctrl + Shift + Alt + / 然后双击Shift搜索进入Registry 找到compiler.automake.allow.when.app.running ,然后勾选上。9. 取消大小写敏感 具体步骤:File | Settings | Editor | General | Code Completion Case | Sensitive Completion = None 取消大小敏感,在编写代码的时候,代码的自动提示将更加全面和丰富。11. 调整字体类型和字体大小默认的白色背景和细小的字体会影响大家的编码体验,这里特意提供了调整代码窗的快捷配置。打开配置,搜索Font,然后再Font可以调整字体类型,Size可以调整字体大小,如图:12. 将快捷键设置为跟Eclipse一样很多人可能并不习惯IDEA的快捷键,为了方便,这里我们将快捷键设置为跟 Eclipse一样。 具体步骤: File -> Settings -> Keymap - > 选择Eclipse .13. 打开常用工具栏 具体步骤:顶部导航栏 - View -> 勾选 Toolbar & Tool Buttons如下图所示:14. 打开Maven神器(强烈推荐!) 具体步骤:右侧直接点击 Maven Project 管理插件 ,记得先打开常用工具栏,详见8.3。 如下图所示: 还在Eclipse使用Update命令苦苦挣扎的童鞋,请火速尝试此款插件,能给你带来前所未有的愉快感!!15. 懒人必备快捷键1. 按【鼠标中键】快速打开智能提示,取代alt+enter 。 File->Settings-> Keymap-> 搜索 Show Intention Actions -> 添加快捷键为鼠标中键。2. 按【F2】快速修改文件名,告别双手操作。 File->Settings-> Keymap-> 搜索 Rename -> 将快捷键设置为F2 。3. 按【F3】直接打开文件所在目录,浏览一步到位。 File->Settings-> Keymap-> 搜索 Show In Explorer -> 将快捷键设置为F3 。4. 按【Ctrl+右键】直接打开实现类,方便开发查询。 File->Settings-> Keymap-> 搜索 implementation-> Add Mouse Shortcut 将快捷键设置为Ctrl+ 鼠标右键。16. 重度强迫症患者1.取消大小写敏感,让自动完成更齐全! File | Settings | Editor | General | Code Completion Case | Sensitive Completion = None。2.自动隐藏注释,让源码阅读更为清爽! File -> Settings -> Editor -> General -> Code Folding -> Documentation comments 勾选。如何想快速一键打开全部注释,则单击鼠标右键,选择Folding -> Expand Doc comments 。3. Maven自动下载源码包,告别反编译,直接上源码注释!! File | Settings | Build, Execution, Deployment | Build Tools | Maven | Importing 将Automatically Download 的 Source 勾上。17. IDEA十问十答 (1)如何打开本地工程/已存在的工程?答:点击File -> Open 打开 工程文件夹即可,注意先配置好JDK、Maven等基础配置。(2)IDEA如何删除项目工程?答:问这个问题的Coder真的好可爱啊哈哈,很肯定的回答你,不需要删,点击File-> Close Project 即可快速关闭当前项目; 示例:什么?你还是想要干掉整个目录?那也阔以,右键Show In Explorer ,删掉文件夹即可。不过笔者建议还是直接Close关掉就好啦,万一以后用得上呢,你说呢?(3)如何在单个窗口打开多个Maven工程啊?答:随便新建一个文件夹,然后将工程都扔进去,使用IDEA打开这个文件夹。(4)如何为当前项目工程添加多个模块啊?答:对着工程右键 -> 选择New -> Module -> 通常选择Spring Initializr ,如图:写在最后欢迎关注、喜欢、和点赞后续将推出更多的工具集教程,敬请期待。欢迎关注我的微信公众号获取更多更全的学习资源,视频资料,技术干货!公众号回复“学习”,拉你进程序员技术讨论群,干货资源第一时间分享。公众号回复“视频”,领取800GJava视频学习资源。公众号回复“全栈”,领取1T前端,Java,产品经理,微信小程序,Python等资源合集大放送。公众号回复“慕课”,领取1T慕课实战学习资源。公众号回复“实战”,领取750G项目实战学习资源。公众号回复“面试”,领取8G面试实战学习资源。 ...

April 14, 2019 · 2 min · jiezi

[IntelliJ] 在格式化代码的同时去掉空的 javadoc 标记

首先如下图所示,打开Settings -> Editor -> Code Style -> Java 在右边选中 JavaDoc 标签页,勾选 “Enable JavaDoc formatting”,去掉勾选 Invalid tags 下面最后三个选项框。这样当你格式化代码时,空的 JavaDoc 标签就会自动去掉了。效果如图:

April 13, 2019 · 1 min · jiezi

IntelliJ IDEA创建Spring Boot项目

File–>New–>Project选择Spring Initializrmaven参数Group组织唯一标识(组织域名倒序)Artifact项目的唯一标识输出格式jar/warjdk版本开发语言javaVersion目前项目版本选择依赖项目名称目录结构创建controller,service,pojo包编写DemoController@RestControllerpublic class DemoController { @GetMapping(“demo-method”) public String demoMethod() { return “success”; }}启动访问http://127.0.0.1:8080/demo-method

April 8, 2019 · 1 min · jiezi

如何永久激活(破解) IntelliJ IDEA 2019.1

之前写过一篇 如何永久激活(破解) IntelliJ IDEA 2018.1.3,很多同学说 2019 新款的不能用了,于是我又找到了另外一种方式。且看。1、去官网下载并安装 idea地址:https://www.jetbrains.com/ide… 文件有点大,耐心等待一会儿。2、下载破解(crack) jar 包链接: https://pan.baidu.com/s/1Z4Mm… 提取码: 4rhx 如果失效,留言回复,或者先用原来的那种方式,也能用,只是可能不是永久激活了。3、下载好了的 crack jar包 放到 idea 的 bin 目录下如下图所示4、修改 bin 目录下的 idea.vmoptions 文件把 idea.vmoptions 文件加一行如下的配置,根据你保存的文件名自行变更-javaagent:../bin/jetbrains-agent.jar有可能版本或者操作系统不同的原因(我的是 Mac),有人可能没有 idea.vmoptions,但是有 idea.exe.vmoptions 和 idea64.exe.vmoptions 这两个文件,在这里面进行配置应该也是一样。5、在 hosts 文件里面添加如下配置0.0.0.0 account.jetbrains.com6、打开 idea,注册选择License server方式地址填入:http://jetbrains-license-server7、应该没啥问题了现在打开你的 idea ,看一下是不是注册成功了。最后提示:破解仅供学习使用,如果不差钱,希望支持正版原文首发于 http://zhige.me/2019/04/03/20…

April 3, 2019 · 1 min · jiezi

IDEA-Maven项目的jdk版本设置

IDEA-Maven项目的jdk版本设置在 Intellij Idea 中,我们需要设置 Settings 中的 Java Compiler 和 Project Structure 中的 Language Level 中的 jdk 版本为自己目前使用的版本,否则会经常提示我们 jdk 版本不正确导致的语法错误。<!–more–>比如配置为 jdk1.8 :但是在 Maven 项目中,Java Compiler 和 Language level 中的设置会自动变回到 pom.xml 文件中设置的 jdk 版本或者默认的 jdk1.5 版本。所以我们需要在 pom.xml 文件中修改 jdk 版本的配置或者自己添加配置:<!– 这里一般有 maven 的默认配置,修改即可 –><properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties>或者:<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins></build>注意: 如果 properties 和 build 里面都有配置的话,那么 properties 会覆盖掉 build 里面的配置,即以 properties 里面的配置为准。

March 31, 2019 · 1 min · jiezi

一键实现Android项目国际化的插件

当下国内应用市场竞争日趋激烈,用户获取困难,许多应用开发者开始将目光投向海外,跟随抖音tiktok的步伐去征战全球市场。但国际化对于广大中小开发者来说,最主要的问题是怎样实现App的国际化翻译。目前在intellij插件库里有一款插件正好可以解决这个问题。这个插件叫i18n robot, 在intellij或者android studio插件库中可以直接搜索安装,也可点击这里下载安装。使用这个插件可以一键完成Android项目的国际化翻译。下面简单介绍一下这个插件的使用方法。使用方法安装插件选择需要翻译的字符串文件 注意,需要翻译的文件需要是英文字符串。原因是英文作为base语言的内容翻译成其他语言会相对比较准确。该插件也仅支持从英文到其他语言的翻译。 从项目管理的角度,最好将需要国际化的字符串和不需要国际化的字符串分开存放,这也是google推荐的做法。点击右键,选择 translate string。打开语言选择菜单,勾选需要翻译的语言点击确定,稍等片刻即会完成翻译。 翻译过程会自动创建多语言文件夹并生成翻译文件。检查翻译结果,调试UI。 翻译完多语言后,还需要对UI展示进行调试,因为有些语言很长,比如俄语,可能导致显示溢出。还需要注意的是,如果支持阿拉伯语,需要实现right-to-left的布局。因为阿拉伯语是从右到左的,UI内容展示需要支持rtl布局。翻译原理i18n robot插件基于大量国际化应用的翻译资源,主要数据基于 i18ns.com,这是一个应用国际化翻译搜索网站,该网站收集了上百万App的多语言翻译字符串,提供免费的搜索查询服务。i18n robot插件在翻译时会向i18ns.com查询最优的翻译结果,然后进行自动化翻译,如果没有找到字符串,则会调用google翻译进行自动化翻译。经过测试发现i18n robot的翻译结果能达到预期,满足国际化翻译需求。对于应用常用字符串和短语的翻译非常准确。感兴趣的同学可以试试效果。

March 29, 2019 · 1 min · jiezi

IntelliJ IDEA 2019.1 现已正式发布,附破解链接

.lanyus.com及.qinxi1992.cn下的全部授权服务器已遭JetBrains封杀 ,现提供最新破解方法打开电脑C:WindowsSystem32driversetc 下用记事本的打开方式打开hosts 将0.0.0.0 account.jetbrains.com”及“0.0.0.0 www.jetbrains.com”添加到hosts文件中,亲测可用请搭建自己的IntelliJ IDEA授权服务器,教程在http://blog.lanyus.com/archiv…

March 28, 2019 · 1 min · jiezi

来自强迫症患者的IDEA设置

IDEA的功能强大毋庸置疑,配置选项也是多到让人眼花缭乱。然而不是所有的配置都是用户需要的,特别是强迫症患者更是难伺候。今天和大家分享下个人的配置和。持续更新中,也欢迎大家补充。Duplicated code取消重复代码提示,Settings -> General -> Ducplicated code 取消勾选这个功能有时候还是挺有用的,能提示你有多少代码是重复的,然后进行重构。用不用看自己的习惯、需求Typo: In word命名提示。当你的命名不符合IDEA的推荐规则时(驼峰规则),就会在名字下方有波浪线提示。解决方案:Settings -> Spelling -> Type 取消勾线结语持续更新中,欢迎大家补充

March 28, 2019 · 1 min · jiezi

IntelliJ Idea中Play framework/Sbt项目的配置方法

因为Play framework 2.3以后采用SBT来构建项目,而SBT使用HTTPS从Maven服务器下载各种依赖包、资源和工具,导致构建过程非常慢。解决问题的思路是修改sbt配置,让Maven优先从阿里云镜像服务器中下载。搭建步骤:一、在IntelliJ Idea中安装Play 2.x和Scala插件二、到Play framework官网下载Play的起始项目zip包。解压zip文件到文件目录假设是D:\sbt01312\,顶层目录下有一个sbt-dist目录,里面包含sbt的launcher。用winrar打开\bin\sbt-launch.jar文件,把里面的\sbt\sbt.boot.properties解压出来,然后在把[repositories]改为:[repositories] local aliyun-maven: http://maven.aliyun.com/nexus/content/groups/public/ aliyun-ivy: http://maven.aliyun.com/nexus/content/groups/public/, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/artifact.[ext] sbt-plugin-ivy: http://dl.bintray.com/sbt/sbt-plugin-releases/, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/artifact.[ext] dl-ivy: http://dl.bintray.com/typesafe/ivy-releases/, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/artifact.[ext] store_mir:http://mirrors.ibiblio.org/maven2/ store_0:http://maven.net.cn/content/groups/public/ store_2:http://repo2.maven.org/maven2/ maven-central: http://repo1.maven.org/maven2/ typesafe-ivy-releases: https://repo.typesafe.com/typesafe/ivy-releases/, [organization]/[module]/[revision]/[type]s/artifact.[ext], bootOnly sbt-ivy-snapshots: https://repo.scala-sbt.org/scalasbt/ivy-snapshots/, [organization]/[module]/[revision]/[type]s/artifact.[ext], bootOnly把改好后的sbt.boot.properties放回\bin\sbt-launch.jar文件。三、运行IntelliJ Idea,选择File/Other Settings/Default Settings菜单在Build,Execution,Deployment/Build Tools/SBT页中,最下面的"Launcher(sbt-launch.jar)“中,选择Custom,然后输入框中输入第二步修改好的\bin\sbt-launch.jar文件路径,例如D:\sbt01312\bin\sbt-launch.jar四、在IntelliJ Idea中新建一个Play 2.x项目。SBT会自动构建,并且下载各种文件,需要很长时间。执行到 Loading project definition from XXXXX 的时候会卡死。是因为项目根目录的build.sbt文件没有从镜像Maven中下载文件,而是从国外服务器下载。此时可以终止构建,双击build.sbt打开文件,然后把最后的resolvers语句改为resolvers += “aliyun-maven” at “http://maven.aliyun.com/nexus/content/groups/public/"resolvers += Resolver.url(“aliyun-ivy”, url(“http://maven.aliyun.com/nexus/content/groups/public/"))( Patterns("[organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/artifact.[ext]”) )resolvers += Resolver.url(“sbt-plugin-ivy”, url(“http://dl.bintray.com/sbt/sbt-plugin-releases/"))( Patterns("[organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/artifact.[ext]”) )resolvers += Resolver.url(“dl-ivy”, url(“http://dl.bintray.com/typesafe/ivy-releases/"))( Patterns("[organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/artifact.[ext]”) )注意:sbt文件两条语句之间要各一个空行,否则出错。然后选择Run/Edit Configuration菜单,新建一个运行配置,然后运行这个配置。如果顺利,项目会被重新构建,构建成功后启动浏览器进入项目页,此时配置就完全成功了。

March 18, 2019 · 1 min · jiezi

SpringBoot | @Value 和 @ConfigurationProperties 的区别

微信公众号:一个优秀的废人。如有问题,请后台留言,反正我也不会听。前言最近有跳槽的想法,所以故意复习了下 SpringBoot 的相关知识,复习得比较细。其中有些,我感觉是以前忽略掉的东西,比如 @Value 和 @ConfigurationProperties 的区别 。如何使用定义两个对象,一个学生对象,对应着一个老师对象,代码如下:@ConfigurationProperties学生类@Component@ConfigurationProperties(prefix = “student”) // 指定配置文件中的 student 属性与这个 bean绑定public class Student { private String firstName; private String lastName; private Integer age; private String gender; private String city; private Teacher teacher; private List<String> hobbys; private Map<String,Integer> scores; //注意,为了测试必须重写 toString 和 get,set 方法}老师类public class Teacher { private String name; private Integer age; private String gender; //注意,为了测试必须重写 toString 和 get,set 方法}测试类@RunWith(SpringRunner.class)@SpringBootTestpublic class SpringbootValConproDemoApplicationTests { @Autowired private Student student; @Test public void contextLoads() { // 这里为了方便,但工作中千万不能用 System.out System.out.println(student.toString()); }}输出结果Student{firstName=‘陈’, lastName=‘一个优秀的废人’, age=24, gender=‘男’, city=‘广州’, teacher=Teacher{name=‘eses’, age=24, gender=‘女’}, hobbys=[篮球, 羽毛球, 兵兵球], scores={java=100, Python=99, C=99}}@Value@Value 支持三种取值方式,分别是 字面量、${key}从环境变量、配置文件中获取值以及 #{SpEL}学生类@Component//@ConfigurationProperties(prefix = “student”) // 指定配置文件中的 student 属性与这个 bean绑定public class Student { /** * <bean class=“Student”> * <property name=“lastName” value=“字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property> * <bean/> / @Value(“陈”) // 字面量 private String firstName; @Value("${student.lastName}”) // 从环境变量、配置文件中获取值 private String lastName; @Value("#{122}") // #{SpEL} private Integer age; private String gender; private String city; private Teacher teacher; private List<String> hobbys; private Map<String,Integer> scores; //注意,为了测试必须重写 toString 和 get,set 方法}测试结果Student{firstName=‘陈’, lastName=‘一个优秀的废人’, age=24, gender=‘null’, city=‘null’, teacher=null, hobbys=null, scores=null}区别二者区别@ConfigurationProperties@Value功能批量注入配置文件中的属性一个个指定松散绑定(松散语法)支持不支持SpEL不支持支持JSR303数据校验支持不支持复杂类型封装支持不支持从上表可以看见,@ConfigurationProperties 和 @Value 主要有 5 个不同,其中第一个功能上的不同,上面已经演示过。下面我来介绍下剩下的 4 个不同。松散语法松散语法的意思就是一个属性在配置文件中可以有多个属性名,举个栗子:学生类当中的 firstName 属性,在配置文件中可以叫 firstName、first-name、first_name 以及 FIRST_NAME。 而 @ConfigurationProperties 是支持这种命名的,@Value 不支持。下面以 firstName 为例,测试一下。如下代码:@ConfigurationProperties学生类的 firstName 属性在 yml 文件中被定义为 first_name:student: first_name: 陈 # 学生类的 firstName 属性在 yml 文件中被定义为 first_name lastName: 一个优秀的废人 age: 24 gender: 男 city: 广州 teacher: {name: eses,age: 24,gender: 女} hobbys: [篮球,羽毛球,兵兵球] scores: {java: 100,Python: 99,C++: 99}学生类:@Component@ConfigurationProperties(prefix = “student”) // 指定配置文件中的 student 属性与这个 bean绑定public class Student { /** * <bean class=“Student”> * <property name=“lastName” value=“字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property> * <bean/> / //@Value(“陈”) // 字面量 private String firstName; //@Value("${student.lastName}”) // 从环境变量、配置文件中获取值 private String lastName; //@Value("#{122}") // #{SpEL} private Integer age; private String gender; private String city; private Teacher teacher; private List<String> hobbys; private Map<String,Integer> scores; //注意,为了测试必须重写 toString 和 get,set 方法}测试结果:Student{firstName=‘陈’, lastName=‘一个优秀的废人’, age=24, gender=‘男’, city=‘广州’, teacher=Teacher{name=‘eses’, age=24, gender=‘女’}, hobbys=[篮球, 羽毛球, 兵兵球], scores={java=100, Python=99, C=99}}@Value学生类:@Component//@ConfigurationProperties(prefix = “student”) // 指定配置文件中的 student 属性与这个 bean绑定public class Student { /** * <bean class=“Student”> * <property name=“lastName” value=“字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property> * <bean/> / //@Value(“陈”) // 字面量 @Value("${student.firstName}”) private String firstName; //@Value("${student.lastName}") // 从环境变量、配置文件中获取值 private String lastName; //@Value("#{122}") // #{SpEL} private Integer age; private String gender; private String city; private Teacher teacher; private List<String> hobbys; private Map<String,Integer> scores; //注意,为了测试必须重写 toString 和 get,set 方法}测试结果:启动报错,找不到 bean。从上面两个测试结果可以看出,使用 @ConfigurationProperties 注解时,yml 中的属性名为 last_name 而学生类中的属性为 lastName 但依然能取到值,而使用 @value 时,使用 lastName 确报错了。证明 @ConfigurationProperties 支持松散语法,@value 不支持。SpELSpEL 使用 #{…} 作为定界符 , 所有在大括号中的字符都将被认为是 SpEL , SpEL 为 bean 的属性进行动态赋值提供了便利。@Value如上述介绍 @Value 注解使用方法时,有这样一段代码:@Value("#{122}") // #{SpEL}private Integer age;证明 @Value 是支持 SpEL 表达式的。@ConfigurationProperties由于 yml 中的 # 被当成注释看不到效果。所以我们新建一个 application.properties 文件。把 yml 文件内容注释,我们在 properties 文件中把 age 属性写成如下所示:student.age=#{122}把学生类中的 @ConfigurationProperties 注释打开,注释 @value 注解。运行报错, age 属性匹配异常。说明 @ConfigurationProperties 不支持 SpELJSR303 数据校验@Value加入 @Length 校验:@Component@Validated//@ConfigurationProperties(prefix = “student”) // 指定配置文件中的 student 属性与这个 bean绑定public class Student { /** * <bean class=“Student”> * <property name=“lastName” value=“字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property> * <bean/> / //@Value(“陈”) // 字面量 @Value("${student.first-name}”) @Length(min=5, max=20, message=“用户名长度必须在5-20之间”) private String firstName; //@Value("${student.lastName}") // 从环境变量、配置文件中获取值 private String lastName; //@Value("#{122}") // #{SpEL} private Integer age; private String gender; private String city; private Teacher teacher; private List<String> hobbys; private Map<String,Integer> scores;}yaml:student: first_name: 陈测试结果:Student{firstName=‘陈’, lastName=‘null’, age=null, gender=‘null’, city=‘null’, teacher=null, hobbys=null, scores=null}yaml 中的 firstname 长度为 1 。而检验规则规定 5-20 依然能取到属性,说明检验不生效,@Value 不支持 JSR303 数据校验@ConfigurationProperties学生类:@Component@Validated@ConfigurationProperties(prefix = “student”) // 指定配置文件中的 student 属性与这个 bean绑定public class Student { /** * <bean class=“Student”> * <property name=“lastName” value=“字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property> * <bean/> / //@Value(“陈”) // 字面量 //@Value("${student.first-name}”) @Length(min=5, max=20, message=“用户名长度必须在5-20之间”) private String firstName; //@Value("${student.lastName}") // 从环境变量、配置文件中获取值 private String lastName; //@Value("#{122}") // #{SpEL} private Integer age; private String gender; private String city; private Teacher teacher; private List<String> hobbys; private Map<String,Integer> scores;}测试结果:报错[firstName],20,5]; default message [用户名长度必须在5-20之间]校验生效,支持 JSR303 数据校验。复杂类型封装复杂类型封装指的是,在对象以及 map (如学生类中的老师类以及 scores map)等属性中,用 @Value 取是取不到值,比如:@Component//@Validated//@ConfigurationProperties(prefix = “student”) // 指定配置文件中的 student 属性与这个 bean绑定public class Student { /** * <bean class=“Student”> * <property name=“lastName” value=“字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property> * <bean/> / //@Value(“陈”) // 字面量 //@Value("${student.first-name}”) //@Length(min=5, max=20, message=“用户名长度必须在5-20之间”) private String firstName; //@Value("${student.lastName}") // 从环境变量、配置文件中获取值 private String lastName; //@Value("#{122}") // #{SpEL} private Integer age; private String gender; private String city; @Value("${student.teacher}") private Teacher teacher; private List<String> hobbys; @Value("${student.scores}") private Map<String,Integer> scores;}这样取是报错的。而上文介绍 @ConfigurationProperties 和 @Value 的使用方法时已经证实 @ConfigurationProperties 是支持复杂类型封装的。也就是说 yaml 中直接定义 teacher 以及 scores 。 @ConfigurationProperties 依然能取到值。怎么选用?如果说,只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用 @Value;比如,假设现在学生类加多一个属性叫 school 那这个属性对于该校所有学生来说都是一样的,但防止我这套系统到了别的学校就用不了了。那我们可以直接在 yml 中给定 school 属性,用 @Value 获取。当然上述只是举个粗暴的例子,实际开发时,school 属性应该是保存在数据库中的。如果说,专门编写了一个 javaBean 来和配置文件进行映射,我们就直接使用 @ConfigurationProperties。完整代码https://github.com/turoDog/Demo/tree/master/springboot_val_conpro_demo如果觉得对你有帮助,请给个 Star 再走呗,非常感谢。后语如果本文对你哪怕有一丁点帮助,请帮忙点好看。你的好看是我坚持写作的动力。另外,关注之后在发送 1024 可领取免费学习资料。资料详情请看这篇旧文:Python、C++、Java、Linux、Go、前端、算法资料分享 ...

March 17, 2019 · 4 min · jiezi

只因数据过滤,方可模拟beanutils框架

导读上一篇文章已经详细介绍了框架与RTTI的关系,RTTI与反射之间的关系。其中详细介绍了框架与反射的关系,这也是很多培训机构把反射作为高级教程来讲解。其实,我工作年限也不长,大概八九个月吧。但我见过很多技术人员,而我喜欢与别人讨论技术。从中也知道了,很多公司没有实现数据过滤。什么是数据过滤?比如客户端向服务器端发送展示项目图片的请求,服务端接收到前端的请求并从数据库中拿到项目图片的对象,我们只要返回图片的在服务端的地址和名称即可,没必要将整个图片对象返回给客户端,因为,那样将会造成数据的冗余。因而,我们这时需要过滤数据(对象),如代码所示:/** * 常用的把图片转成 {id: 1, path: “xxx”}结构 /public static JSONObject img2Json(Picture picture) { if (isNotNull(picture)) { String[] PICTURE_JSON = {“id”, “remoteRelativeUrl:path”}; JSONObject jsonObject = propsFilter(picture, PICTURE_JSON); return jsonObject; } else { return null; }}如上诉代码的转换,公司使用的是commons-beanutils这个框架。我们只要在项目中农添加其maven配置即可: <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.2</version> </dependency>我个人比较喜欢研究源码,于是,仿照这个框架写了自己的框架,下面,就是介绍我个人的框架。我的beanutils框架框架使用的算法或技术递归算法。我们并不推荐使用递归,因为,方法自调用自己。根据JVM的内部原理,每个方法都是一个方法栈。而栈是存放数据的一种结构,其采用FIFO(First In Last Out),即先进后出。和我们堆放菜盘一样,先垒的最后拿出来。既然是数据存储,肯定会超出容量,因为,内存不是无限大的,恰如水满自溢。但是,我们在这里还是使用递归,因为,在深度调用算法当中,采用递归是合适的。java的反射机制。我们根据Javabean的属性名称获取值。核心算法说明。如果javabean的对象属性类型不是用户自定义的类型,我们根据反射调用get方法拿到属性的值如果javabean的对象属性类型是用户自定义的类型,我们利用递归重新调用改方法,直到出现遇见上面的条件 /* * Created By zby on 20:28 2019/2/13 * * @param bean 实体对象 * @param props 属性名称 / public static Object getProperty(Object bean, String props) { if (bean == null) throw new RuntimeException(“实例化对象不存在bean=” + bean); if (StringHelper.isBlank(props)) throw new RuntimeException(“属性名称不存在props=” + props); Class<?> clazz = null; String methodName = null; String fieldName = null; String typeName = null; try { clazz = bean.getClass(); if (props.indexOf(".") != -1) { methodName = MethodHelper.propsToGetMethod(props.substring(0, props.indexOf("."))); Method method = clazz.getDeclaredMethod(methodName); Object obj = method.invoke(bean); return getProperty(obj, props.substring(props.indexOf(".") + 1)); } Field field = clazz.getDeclaredField(props); typeName = field.getType().getName(); if (typeName.equalsIgnoreCase(“boolean”)) { field.setAccessible(true); return field.getBoolean(bean); } methodName = MethodHelper.propsToGetMethod(props); Method method = clazz.getDeclaredMethod(methodName); return method.invoke(bean); } catch (NoSuchMethodException e) { logger.error(clazz + “类型没有” + methodName + “方法”); e.printStackTrace(); } catch (NoSuchFieldException e) { logger.error(clazz + “类型没有” + fieldName + “属性”); e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return null; }如何使用上诉算法我们既然是通过属性名称来获取属性对象。我们可以设计一个算法,算法该算法有两个参数,一个是当前对象,一个是对象的属性数组。属性数组还可以有别名。为什么需要别名?比如当前对象采用组合关系,使用自定义的类。比如说订单类使用用户类(User)的对象作为属性,我们在订单中希望看到用户姓名,我们可以这样调用user.name,以该字段传给客户端,但客户端需要转换才能拿到用户名,因而,我们需要一个别名,前端不用转换,就可以拿到用户名,比如:user.name:username。当然,我们需要将对象转化为json格式的框架, 这里使用的阿里巴巴的fastjson框架,我们可以在项目中配置maven: <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.28</version></dependency>所示,算法设计为: /* * Created By zby on 9:40 2019/2/13 * 模拟框架中的数据 * * @param object 参数对象 * @param props String类型的变长数组,比如{“id:projectId”, “mobile”,…} * 前半部分是javabean的属性名,后半部分是返回到给客户端的参数名 */public static JSONObject propsFilter(Object object, String… props) { //【1】判断对象和变长数组的是否为空,以及变长数组的长度是否为0 boolean isNull = object == null || (null == props && props.length == 0); if (isNull) { logger.warn(“参数为空object=” + object + “props=” + props); return null; } JSONObject jsonObject = new JSONObject(); for (String prop : props) { //【2】再判断对象不为空,或者长度不为0 if (prop == null && prop.length() == 0) { logger.warn(“参数为空prop= " + prop); throw new RuntimeException(“参数为空prop=” + prop); } Object o = null; String[] namePair = StringUtils.split(prop, “:”); try { o = PropertyUtil.getProperty(object, namePair[0]); } catch (Exception e) { logger.warn(“类” + object.getClass() + “,属性” + namePair[0] + “不存在”); } String key = namePair.length <= 1 ? namePair[0] : namePair[1]; if (o instanceof Date) jsonObject.put(key, DateUtil.simpleFormate((Date) o)); else if (o instanceof BigDecimal) jsonObject.put(key, CommonUtil.toFiexd((BigDecimal) o, 2)); else if (o instanceof TitleEnum) jsonObject.put(key, CommonUtil.builderEnum((TitleEnum) o)); else jsonObject.put(key, o); } return jsonObject;}测试框架和类我们既然写好了这个框,也使用了这个框架,因而,我们可以使用Junit来测试: @Test public void test(){ Address address = new Address(); address.setAddressTag(AddressTagEnum.ADDRESS_TAG_COMPANY); address.setArea(“杭州市….”); address.setConsignee(“zby”); User user = new User(); user.setHobby(HobbyEnum.HOBBY_DANCING); user.setGender(“男”); user.setUserName(“蒋三”); OrderSnapshot orderSnapshot = new OrderSnapshot(); orderSnapshot.setAddress(address); orderSnapshot.setId(1L); orderSnapshot.setName(“复读机”); orderSnapshot.setOrderNo(Long.valueOf(System.currentTimeMillis()).toString() + “1L”); orderSnapshot.setUser(user); String[] json = {“address.consignee:consignee”,“user.hobby:hobby”, “address.addressTag:addressTag”, “address.area:area” ,“address.consignee:consignee”,“user.userName:userName”}; System.out.println(JsonUtil.propsFilter(orderSnapshot, json));测试结果为:可见,我们算法时成功的。总结我们还是要时常看源码,因为,你的目的不是写出框架,而是看别人写框架的思想。毕竟,思想主导一切行为,行为成就一个的未来。致努力的自己。 ...

March 17, 2019 · 2 min · jiezi

intellij idea 配置spark环境遇到的两个问题

在配置spark环境的时候出现两个问题:pom.xml文件如下:<?xml version=“1.0” encoding=“UTF-8”?><project xmlns=“http://maven.apache.org/POM/4.0.0" xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.spark.zwh</groupId><artifactId>spark-test</artifactId><version>1.0-SNAPSHOT</version><properties> <spark.version>2.4.0</spark.version> <scala.version>2.11</scala.version></properties><dependencies> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-core_${scala.version}</artifactId> <version>${spark.version}</version> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-streaming_${scala.version}</artifactId> <version>${spark.version}</version> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-sql_${scala.version}</artifactId> <version>${spark.version}</version> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-hive_${scala.version}</artifactId> <version>${spark.version}</version> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-mllib_${scala.version}</artifactId> <version>${spark.version}</version> </dependency></dependencies><build> <plugins> <plugin> <groupId>org.scala-tools</groupId> <artifactId>maven-scala-plugin</artifactId> <version>2.15.2</version> <executions> <execution> <goals> <goal>compile</goal> <goal>testCompile</goal> </goals> </execution> </executions> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.6.0</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.19</version> <configuration> <skip>true</skip> </configuration> </plugin> </plugins></build></project>其中<spark.version>2.4.0</spark.version><scala.version>2.11</scala.version>在http://spark.apache.org/downl…:groupId: org.apache.sparkartifactId: spark-core_2.11version: 2.4.0其中2.11为scala版本,2.4.0为spark版本,只有相互对应的版本才能正确编译,否则会出现各种奇怪问题。配置好了却发现右键没有新建scala class选项,查了很多都没有说到点子上,最终发现右键项目-》add frameworks support->scala->ok。现在有scala class选项了。 ...

March 14, 2019 · 1 min · jiezi

教你一招用 IDE 编程提升效率的骚操作!

阅读本文大概需要 3 分钟。IDEA 有个很牛逼的功能,那就是后缀补全(不是自动补全),很多人竟然不知道这个操作,还在手动敲代码。这个功能可以使用代码补全来模板式地补全语句,如遍历循环语句(for、foreach)、使用 String.format() 包裹一个字符串、使用类型转化包裹一个表达式、根据判(非)空或者其它判别语句生成 if 语句、用 instanceOf 生成分支判断语句等。使用的方式也很简单,就是在一个表达式后按下点号 . ,然后输入一些提示或者在列表中选择一个候选项,常见的候选项下面会给出 GIF 演示。var 声明null 判空notnull 判非空nn 判非空for 遍历fori 带索引的遍历not 取反if 条件判断cast 强转return 返回值看完怎么样?赶紧用起来装逼啊~·END·路虽远,行则必至本文原发于 同名微信公众号「程序员的成长之路」,回复「1024」你懂得,给个赞呗。微信ID:cxydczzl

March 9, 2019 · 1 min · jiezi

webStorm插件推荐

CodeGlance代码的缩略图,VScode,sublime编辑器都有这个功能{:height=“200” width=“400”}Material Theme UI修改主题颜色,图标那些等等AngularJS支持 angularjs 的语法提示ideaVim可以让 webstorm 编辑器支持 vimAceJump快速定位光标位置,有了它你可以丢弃鼠标了。eslint语法检查的插件activate-power-mode装逼插件,不解释不解释{:height=“200” width=“400”}SVNToolBoxsvn工具APICloud PluginsAPICloud提供的WebStorm标准插件,用于在WebStorm中进行APICloud应用开发,包括:创建应用、应用框架、页面模板、代码提示、代码管理、真机同步、本地打包、日志输出、管理自定义AppLoader等功能,其它的功能插件也在不断增加,所有插件都已开源,开发者也可以在此基础上按需求扩展自己的插件。个人博客 求星星

March 9, 2019 · 1 min · jiezi

webstorm2019.03绿化汉化破解版

华而至简自制的破解便携版,解压放在D盘根目录下,有需要可引入护眼色主题包。如果放在其他盘,自行修改文件路径新手指导–>付费弹窗:选择activation code模式。随便输入文字。点击ok就行破解流程:添加补丁,修改两个文件,即可稳定使用便携版下载地址工具下载地址前言自从webstorm 网上的注册码频频失效后,恰好又遇到产品要上线。心力交瘁。自此就研究各种方法如何稳定使用webStorm(穷学生刚毕业。有余钱还请购买服务,支持下软件开发者)各种尝试自己搭建IntelliJ IDEA授权服务器利用补丁破解webStorm网上注册码,几乎全线崩溃补丁,简单、快捷、稳定第一步:将补丁文件复制到软件目录的bin下第二步:修改同目录下WebStorm.exe.vmoptions 和WebStorm64.exe.vmoptions文件。可以使用编辑器,记事本等的工具打开文件,然后补充这行代码-javaagent: 路径、破解补丁名称(注意:安装的路径不同,自行修改)-javaagent:D:\Program Files\JetBrains\WebStorm\bin\JetbrainsCrack-4.2-release-enc.jar第三步:修改后,保存文件。再次启动WebStorm,选择activation code,随便输入文字。OK汉化将resources_cn.jar文件复制到安装目录的lib目录下重新打开软件, OK美化主题WebStorm文件–>导入设置–>选择settings.jar–>重启即可个人博客 求星星

March 9, 2019 · 1 min · jiezi

使用idea调试lua代码-Openresty

使用idea调试lua代码Openresty是基于nginx与lua的高性能web框架,继承了大量的高质量的lua库、第三方模块以及大多数依赖项。目前对于lua主流开发工具有vscode+lua插件、IntelliJ IDEA+EmmyLua、ZeroBrane Studio、还有其他的一些文本编辑软件等。lua作为一种脚本语言,除了开发简洁,性能优越之外,还应该具备调试功能,对于开发者才能算得上更加友好。本文将使用IntelliJ IDEA+EmmyLua使用远程调试和本地调试。IntelliJ IDEA 2018.2.1Lua 5.1EmmyLua 1.2.6-IDEA182MobDebug 0.70项目目录结构源码位置often-script一、远程调试1、打开idea中调试配置,使用Lua Remote(Mobdebug),如下图:2、配置调试名称和远程调试端口;3、在需要调试的位置加上调试代码;— 启动调试local mobdebug = require(“src.initial.mobdebug”);mobdebug.start();4、启动Openresty项目,然后打开debug模式;5、启动openresty项目;# 进入到工作目录cd /Users/xiaoyueya/projects/vscode/often-script/lua/project# 启动nginxsudo nginx -p ./ -c nginx-debug.conf -s reload6、刷新浏览器;7、断点位置和lua栈信息;8、执行结果;二、本地调试1、打开idea中调试配置,使用lua application ,如下图:2、配置工作目录和执行文件入口;3、编写调试名称为main.lua,然后点击OK,进入主编辑页,找到调试按钮;4、开始本地调试;5、查看堆栈信息;6、查看执行结果

March 8, 2019 · 1 min · jiezi

idea maven工程显示灰色(不亮)

用 idea 开发的同学,有可能会遇到下面这个问题,工程下面所有模块或者部分模块显示灰色,造成这个问题的原因是忽略了 maven 模块,只需要将忽略的文件 ignored files 进行恢复就可以解决这个问题。解决方法:1、打开 preferences -> maven -> ignored files2、把相应的勾去掉就可以了原文链接http://zhige.me/2019/02/28/20…

February 28, 2019 · 1 min · jiezi

Spring Data JPA 必须掌握的 20+ 个查询关键字

微信公众号:一个优秀的废人如有问题或建议,请后台留言,我会尽力解决你的问题。前言又是小师弟的投稿,确是一个喜欢技术的朋友。以下为原文:今天闲的无聊看 Spring Data JPA 官方文档的时候,发现并没有完整的 Jpa 关键字语义翻译。所以今天写了一篇中文文档,如果有错误,望大家轻喷。以下为官方图片以及示例代码和注释 :首先参照官方文档创建指定数据库CREATE TABLE demo_jpa ( id int(11) NOT NULL AUTO_INCREMENT, first_name varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, last_name varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, sex varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, email varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, age int(12) NOT NULL, PRIMARY KEY (id) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;示例代码及注释<参照以上顺序>/** * @Author: EvilSay * @Date: 2019/2/25 16:15 /public interface DemoJpaRepositories extends JpaRepository<DemoJpa,Integer> { //根据firstName与LastName查找(两者必须在数据库有) DemoJpa findByFirstNameAndLastName(String firstName, String lastName); //根据firstName或LastName查找(两者其一有就行) DemoJpa findByLastNameOrFirstName(String lastName,String firstName); //根据firstName查找它是否存在数据库里<类似与以下关键字> //DemoJpa findByFirstName(String firstName); DemoJpa findByFirstNameIs(String firstName); //在Age数值age到age2之间的数据 List<DemoJpa> findByAgeBetween(Integer age, Integer age2); //小于指定age数值之间的数据 List<DemoJpa> findByAgeLessThan(Integer age); //小于等于指定age数值的数据 List<DemoJpa> findByAgeLessThanEqual(Integer age); //大于指定age数值之间的数据 List<DemoJpa> findByAgeGreaterThan(Integer age); //大于或等于指定age数值之间的数据 List<DemoJpa> findByAgeGreaterThanEqual(Integer age); //在指定age数值之前的数据类似关键字<LessThan> List<DemoJpa> findByAgeAfter(Integer age); //在指定age数值之后的数据类似关键字<GreaterThan> List<DemoJpa> findByAgeBefore(Integer age); //返回age字段为空的数据 List<DemoJpa> findByAgeIsNull(); //返回age字段不为空的数据 List<DemoJpa> findByAgeNotNull(); /* * 该关键字我一度以为是类似数据库的模糊查询, * 但是我去官方文档看到它里面并没有通配符。 * 所以我觉得它类似 * DemoJpa findByFirstName(String firstName); * @see https://docs.spring.io/spring-data/jpa/docs/2.1.5.RELEASE/reference/html/#jpa.repositories */ DemoJpa findByFirstNameLike(String firstName); //同上 List<DemoJpa> findByFirstNameNotLike(String firstName); //查找数据库中指定类似的名字(如:输入一个名字"M" Jpa会返回多个包含M开头的名字的数据源)<类似数据库模糊查询> List<DemoJpa> findByFirstNameStartingWith(String firstName); //查找数据库中指定不类似的名字(同上) List<DemoJpa> findByFirstNameEndingWith(String firstName); //查找包含的指定数据源(这个与以上两个字段不同的地方在与它必须输入完整的数据才可以查询) List<DemoJpa> findByFirstNameContaining(String firstName); //根据age选取所有的数据源并按照LastName进行升序排序 List<DemoJpa> findByAgeOrderByLastName(Integer age); //返回不是指定age的所有数据 List<DemoJpa> findByAgeNot(Integer age); //查找包含多个指定age返回的数据 List<DemoJpa> findByAgeIn(List<Integer> age);}单元测试<已经全部通过>@SpringBootTest@RunWith(SpringRunner.class)@Slf4jpublic class DemoJpaRepositoriesTest { @Autowired private DemoJpaRepositories repositories; @Test public void findByFirstNameAndLastName() { DemoJpa demoJpa = repositories.findByFirstNameAndLastName(“May”, “Eden”); Assert.assertEquals(demoJpa.getFirstName(),“May”); } @Test public void findByLastNameOrFirstName() { DemoJpa demoJpa = repositories.findByLastNameOrFirstName(“Geordie”, “Eden”); Assert.assertNotEquals(demoJpa.getLastName(),“Eden”); } @Test public void findByFirstNameIs() { DemoJpa demoJpa = repositories.findByFirstNameIs(“amy”); Assert.assertNull(demoJpa); } @Test public void findByAgeBetween() { List<DemoJpa> demoJpaList = repositories.findByAgeBetween(15, 17); Assert.assertEquals(3,demoJpaList.size()); } @Test public void findByAgeLessThan() { List<DemoJpa> demoJpaList = repositories.findByAgeLessThan(17); Assert.assertEquals(2,demoJpaList.size()); } @Test public void findByAgeLessThanEqual() { List<DemoJpa> demoJpaList = repositories.findByAgeLessThanEqual(17); Assert.assertEquals(3,demoJpaList.size()); } @Test public void findByAgeGreaterThan() { List<DemoJpa> demoJpaList = repositories.findByAgeGreaterThan(17); Assert.assertEquals(2,demoJpaList.size()); } @Test public void findByAgeGreaterThanEqual() { List<DemoJpa> demoJpaList = repositories.findByAgeGreaterThanEqual(17); Assert.assertEquals(3,demoJpaList.size()); } @Test public void findByAgeAfter() { List<DemoJpa> demoJpaList = repositories.findByAgeAfter(17); Assert.assertEquals(2,demoJpaList.size()); } @Test public void findByAgeBefore() { List<DemoJpa> demoJpaList = repositories.findByAgeBefore(17); Assert.assertEquals(2,demoJpaList.size()); } @Test public void findByAgeIsNull() { List<DemoJpa> demoJpaList = repositories.findByAgeIsNull(); Assert.assertEquals(0,demoJpaList.size()); } @Test public void findByAgeNotNull() { List<DemoJpa> demoJpaList = repositories.findByAgeNotNull(); Assert.assertEquals(5,demoJpaList.size()); } @Test public void findByFirstNameLike() { DemoJpa demoJpa = repositories.findByFirstNameLike(“May”); Assert.assertNotNull(demoJpa); } @Test public void findByFirstNameNotLike() { } @Test public void findByFirstNameStartingWith() { List<DemoJpa> demoJpaList = repositories.findByFirstNameStartingWith(“May”); Assert.assertEquals(2,demoJpaList.size()); } @Test public void findByFirstNameEndingWith() { List<DemoJpa> demoJpaList = repositories.findByFirstNameEndingWith(“Evil”); Assert.assertEquals(0,demoJpaList.size()); } @Test public void findByFirstNameContaining() { List<DemoJpa> demoJpaList = repositories.findByFirstNameContaining(“hack”); Assert.assertEquals(0,demoJpaList.size()); } @Test public void findByAgeOrderByLastName() { List<DemoJpa> demoJpaList = repositories.findByAgeOrderByLastName(18); for (DemoJpa demoJpaL : demoJpaList){ log.info(“数据结果”+demoJpaL.toString()); } } @Test public void findByAgeNot() { List<DemoJpa> demoJpaList = repositories.findByAgeNot(20); Assert.assertEquals(5,demoJpaList.size()); } @Test public void findByAgeIn() { List<DemoJpa> demoJpaList = repositories.findByAgeIn(Arrays.asList(15, 16)); Assert.assertEquals(2,demoJpaList.size()); }}后语如果本文对你哪怕有一丁点帮助,请帮忙点好看。你的好看是我坚持写作的动力。另外,关注之后在发送 1024 可领取免费学习资料。资料内容详情请看这篇旧文:Python、C++、Java、Linux、Go、前端、算法资料分享 ...

February 27, 2019 · 2 min · jiezi

SpringBoot 实战 (十一) | 整合数据缓存 Cache

微信公众号:一个优秀的废人如有问题或建议,请后台留言,我会尽力解决你的问题。前言如题,今天介绍 SpringBoot 的数据缓存。做过开发的都知道程序的瓶颈在于数据库,我们也知道内存的速度是大大快于硬盘的,当需要重复获取相同数据时,一次又一次的请求数据库或者远程服务,导致大量时间耗费在数据库查询或远程方法调用上,导致性能的恶化,这便是数据缓存要解决的问题。Spring 的缓存支持Spring 定义了 org.springframework.cache.CacheManager 和 org.springframework.cache.Cache 接口用于统一不同的缓存技术。其中,CacheManager 是 Spring 提供的各种缓存技术的抽象接口,Cache 接口则是包含了缓存的各种操作(增加,删除,获取缓存,一般不会直接和此接口打交道)。1、Spring 支持的 CacheManager 针对不同的缓存技术,实现了不同的 CacheManager ,Spring 定义了下表所示的 CacheManager:CacheManager描述SimpleCacheManager使用简单的 Collection 来存储缓存,主要用于测试ConcurrentMapCacheManager使用 ConcurrentMap 来存储缓存NoOpCacheManager仅测试用途,不会实际缓存数据EhCacheCacheManager使用 EhCache 作为缓存技术GuavaCacheManager使用 Google Guava 的 GuavaCache 作为缓存技术HazelcastCacheManager使用 Hazelcast 作为缓存技术JCacheCacheManager支持 JCache(JSR-107) 标准的实现作为缓存技术,如 ApacheCommonsJCSRedisCacheManager使用 Redis 作为缓存技术在使用以上任意一个实现的 CacheManager 的时候,需注册实现的 CacheManager 的 Bean,如:@Beanpublic EhCacheCacheManager cacheManager(CacheManager ehCacheCacheManager){ return new EhCacheCacheManager(ehCacheCacheManager);}注意,每种缓存技术都有很多的额外配置,但配置 cacheManager 是必不可少的。2、声明式缓存注解Spring 提供了 4 个注解来声明缓存规则(又是使用注解式的 AOP 的一个例子)。4 个注解如下表示:注解解释@Cacheable在方法执行前 Spring 先查看缓存中是否有数据,若有,则直接返回缓存数据;若无数据,调用方法将方法返回值放入缓存中@CachePut无论怎样,都会将方法的返回值放到缓存中。@CacheEvict将一条或多条数据从缓存中删除@Caching可以通过 @Caching 注解组合多个注解策略在一个方法上@Cacheable、@CachePut、@CacheEvict 都有 value 属性,指定的是要使用的缓存名称;key 属性指定的是数据在缓存中存储的键。3、开启声明式缓存支持开启声明式缓存很简单,只需在配置类上使用 @EnableCaching 注解即可,例如:@Configuration@EnableCachingpublic class AppConfig{}SpringBoot 的支持在 Spring 中使用缓存技术的关键是配置 CacheManager ,而 SpringBoot 为我们配置了多个 CacheManager 的实现。它的自动配置放在 org.springframework.boot.autoconfigure.cache 包中。在不做任何配置的情况下,默认使用的是 SimpleCacheConfiguration ,即使用 ConcurrentMapCacheManager。SpringBoot 支持以前缀来配置缓存。例如:spring.cache.type= # 可选 generic、ehcache、hazelcast、infinispan、jcache、redis、guava、simple、nonespring.cache.cache-names= # 程序启动时创建的缓存名称spring.cache.ehcache.config= # ehcache 配置文件的地址spring.cache.hazelcast.config= # hazelcast配置文件的地址spring.cache.infinispan.config= # infinispan配置文件的地址spring.cache.jcache.config= # jcache配置文件的地址spring.cache.jcache.provider= # 当多个 jcache 实现在类路径的时候,指定 jcache 实现# 等等。。。在 SpringBoot 环境下,使用缓存技术只需要在项目中导入相关缓存技术的依赖包,并在配置类中使用 @EnableCaching 开启缓存支持即可。代码实现本文将以 SpringBoot 默认的 ConcurrentMapCacheManager 作为缓存技术,演示 @Cacheable、@CachePut、@CacheEvict。1、准备工作IDEAJDK 1.8SpringBoot 2.1.32、Pom.xml 文件依赖<?xml version=“1.0” encoding=“UTF-8”?><project xmlns=“http://maven.apache.org/POM/4.0.0" xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath/> <!– lookup parent from repository –> </parent> <groupId>com.nasus</groupId> <artifactId>cache</artifactId> <version>0.0.1-SNAPSHOT</version> <name>cache</name> <description>cache Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!– cache 依赖 –> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <!– JPA 依赖 –> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!– web 启动类 –> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!– mysql 数据库连接类 –> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!– lombok 依赖,简化实体 –> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!– 单元测试类 –> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>注释很清楚,无需多言。不会就谷歌一下。3、Application.yaml 文件配置spring: # 数据库相关 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=true username: root password: 123456 # jpa 相关 jpa: hibernate: ddl-auto: update # ddl-auto: 设为 create 表示每次都重新建表 show-sql: true4、实体类package com.nasus.cache.entity;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.Id;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data@Entity@AllArgsConstructor@NoArgsConstructorpublic class Student { @Id @GeneratedValue private Integer id; private String name; private Integer age;}5、dao 层package com.nasus.cache.repository;import com.nasus.cache.entity.Student;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.stereotype.Repository;@Repositorypublic interface StudentRepository extends JpaRepository<Student,Integer> {}6、service 层package com.nasus.cache.service;import com.nasus.cache.entity.Student;public interface StudentService { public Student saveStudent(Student student); public void deleteStudentById(Integer id); public Student findStudentById(Integer id);}实现类:package com.nasus.cache.service.impl;import com.nasus.cache.entity.Student;import com.nasus.cache.repository.StudentRepository;import com.nasus.cache.service.StudentService;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cache.annotation.CacheEvict;import org.springframework.cache.annotation.CachePut;import org.springframework.cache.annotation.Cacheable;import org.springframework.stereotype.Service;@Servicepublic class StudentServiceImpl implements StudentService { // 使用 slf4j 作为日志框架 private static final Logger LOGGER = LoggerFactory.getLogger(StudentServiceImpl.class); @Autowired private StudentRepository studentRepository; @Override @CachePut(value = “student”,key = “#student.id”) // @CachePut 缓存新增的或更新的数据到缓存,其中缓存名称为 student 数据的 key 是 student 的 id public Student saveStudent(Student student) { Student s = studentRepository.save(student); LOGGER.info(“为id、key 为{}的数据做了缓存”, s.getId()); return s; } @Override @CacheEvict(value = “student”) // @CacheEvict 从缓存 student 中删除 key 为 id 的数据 public void deleteStudentById(Integer id) { LOGGER.info(“删除了id、key 为{}的数据缓存”, id); //studentRepository.deleteById(id); } @Override @Cacheable(value = “student”,key = “#id”) // @Cacheable 缓存 key 为 id 的数据到缓存 student 中 public Student findStudentById(Integer id) { Student s = studentRepository.findById(id).get(); LOGGER.info(“为id、key 为{}的数据做了缓存”, id); return s; }}代码讲解看注释,很详细。7、controller 层package com.nasus.cache.controller;import com.nasus.cache.entity.Student;import com.nasus.cache.service.StudentService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.DeleteMapping;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.Mapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/student”)public class StudentController { @Autowired private StudentService studentService; @PostMapping("/put”) public Student saveStudent(@RequestBody Student student){ return studentService.saveStudent(student); } @DeleteMapping("/evit/{id}”) public void deleteStudentById(@PathVariable(“id”) Integer id){ studentService.deleteStudentById(id); } @GetMapping("/able/{id}") public Student findStudentById(@PathVariable(“id”) Integer id){ return studentService.findStudentById(id); }}8、application 开启缓存功能package com.nasus.cache;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cache.annotation.EnableCaching;@EnableCaching // 开启缓存功能@SpringBootApplicationpublic class CacheApplication { public static void main(String[] args) { SpringApplication.run(CacheApplication.class, args); }}测试测试前,先看一眼数据库当前的数据,如下:1、测试 @Cacheable 访问 http://localhost:8080/student/able/2 控制台打印出了 SQL 查询语句,以及指定日志。说明这一次程序是直接查询数据库得到的结果。2019-02-21 22:54:54.651 INFO 1564 — [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 11 msHibernate: select student0_.id as id1_0_0_, student0_.age as age2_0_0_, student0_.name as name3_0_0_ from student student0_ where student0_.id=?2019-02-21 22:54:59.725 INFO 1564 — [nio-8080-exec-1] c.n.c.service.impl.StudentServiceImpl : 为id、key 为2的数据做了缓存postman 第一次测试结果 :再次访问 http://localhost:8080/student/able/2 结果如下图。但控制台无 SQL 语句打印,也无为id、key 为2的数据做了缓存这句话输出。说明 @Cacheable 确实做了数据缓存,第二次的测试结果是从数据缓存中获取的,并没有直接查数据库。2、测试 @CachePut如下图,postman 访问 http://localhost:8080/student/put 插入数据:下面是控制台打印出了 SQL Insert 插入语句,以及指定日志。说明程序做了缓存。Hibernate: insert into student (age, name, id) values (?, ?, ?)2019-02-21 23:12:03.688 INFO 1564 — [nio-8080-exec-8] c.n.c.service.impl.StudentServiceImpl : 为id、key 为4的数据做了缓存插入数据返回的结果:数据库中的结果:访问 http://localhost:8080/student/able/4 Postman 结果如下图。控制台无输出,验证了 @CachePut 确实做了缓存,下图数据是从缓存中获取的。3、测试 @CacheEvict postman 访问 http://localhost:8080/student/able/3 为 id = 3 的数据做缓存。postman 再次访问 http://localhost:8080/student/able/3 确认数据是从缓存中获取的。postman 访问 http://localhost:8080/student/evit/3 从缓存中删除 key 为 3 的缓存数据:Hibernate: select student0_.id as id1_0_0_, student0_.age as age2_0_0_, student0_.name as name3_0_0_ from student student0_ where student0_.id=?2019-02-21 23:26:08.516 INFO 8612 — [nio-8080-exec-2] c.n.c.service.impl.StudentServiceImpl : 为id、key 为3的数据做了缓存2019-02-21 23:27:01.508 INFO 8612 — [nio-8080-exec-4] c.n.c.service.impl.StudentServiceImpl : 删除了id、key 为3的数据缓存再次 postman 访问 http://localhost:8080/student/able/3 观察后台,重新做了数据缓存:Hibernate: select student0_.id as id1_0_0_, student0_.age as age2_0_0_, student0_.name as name3_0_0_ from student student0_ where student0_.id=?2019-02-21 23:27:12.320 INFO 8612 — [nio-8080-exec-5] c.n.c.service.impl.StudentServiceImpl : 为id、key 为3的数据做了缓存这一套测试流程下来,证明了 @CacheEvict 确实删除了数据缓存。源码下载https://github.com/turoDog/Demo/tree/master/springboot_cache_demo切换缓存技术切换缓存技术除了在 pom 文件加入相关依赖包配置以外,使用方式与上面的代码演示一样。1、切换 EhCache 在 pom 中添加 Encache 依赖:<!– EhCache 依赖 –><dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId></dependency>Ehcache 所需配置文件 ehcache.xml 只需放在类路径(resource 目录)下,SpringBoot 会自动扫描,如:<?xml version=“1.0” encoding=“UTF-8”><ehcache> <cache name=“student” maxElementsInMmory=“1000”><ehcache>SpringBoot 会自动配置 EhcacheManager 的 Bean。2、切换 Guava只需在 pom 中加入 Guava 依赖即可:<!– GuavaCache 依赖 –><dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>18.0</version></dependency>SpringBoot 会自动配置 GuavaCacheManager 的 Bean。3、切换 RedisCache与 Guava 一样,只需在 pom 加入依赖即可:<!– cache 依赖 –><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId></dependency>SpringBoot 会自动配置 RedisCacheManager 以及 RedisTemplate 的 Bean。此外,切换其他缓存技术的方式也是类似。这里不做赘述。后语以上为 SpringBoot 数据缓存的教程。如果本文对你哪怕有一丁点帮助,请帮忙点好看。你的好看是我坚持写作的动力。另外,关注之后在发送 1024 可领取免费学习资料。资料内容详情请看这篇旧文:Python、C++、Java、Linux、Go、前端、算法资料分享 ...

February 23, 2019 · 4 min · jiezi

SpringBoot 实战 (二) | 第一个 SpringBoot 工程详解

微信公众号:一个优秀的废人如有问题或建议,请后台留言,我会尽力解决你的问题。前言哎呦喂,按照以往的惯例今天周六我的安排应该是待在家学学猫叫啥的。但是今年这种日子就可能一去不复返了,没法办法啊。前几天年轻,立下了一周至少更两篇文章的 flag 。废话少说,今天接着前文给你们带来了第一个 SpringBoot 工程的详解。第一个 SpringBoot 工程前文已经说过了 SpringBoot 工程的创建,这里不再赘述,还不会的朋友,请看下面这篇文章。如何使用 IDEA 构建 Spring Boot 工程学过编程的都知道,学习一门新语言的第一个项目肯定是 Hello World 。那这里也不例外,我们先创建一个非常简单的 Hello World 工程。给大家讲解 SpringBoot 的项目目录。创建信息如下:由于本文重点旨在讲解 SpringBoot 的项目目录。所以选择的依赖包非常简单,就选择 Web 足矣。SpringBoot 项目目录详解创建成功之后的 SpringBoot 项目目录如下,以下对各主要目录的作用进行讲解:src 是整个工程的根目录,基本上做 web 开发你的代码大部分都放在这里。其中 main 目录下放置的是你的 Java 代码;resource 目录,顾名思义就是放置配置文件、静态资源( static )以及前端模板( template )。test 目录就是放置你的单元测试代码。target 就是项目编译生成的目录,里面包含代码编译后的 class 文件以及一些静态资源和配置文件。External Libraries 这里放置了,pom.xml 导入的依赖包。其他没提到的目录都是不重要的。由上图项目目录,可以看到有几个文件,这些文件有些是我新建的,有些是项目生成的。下面我会讲解:pom.xml 这个文件是整个项目最重要的文件,它包含了整个项目的依赖包。Maven 会根据这个文件导入相关的我们开发需要的依赖包。代码如下:可以看到 pom.xml 中一共有 4 个依赖,其中只有 Junit 是我手动加入的,用于单元测试。其他的如 Spring Boot 启动父依赖、Spring Boot web依赖 、Spring Boot web test依赖都是创建项目时,勾选 web 选项卡而生成的。这几个依赖包的额作用就是 内嵌 Tomcat 容器,集成各 Spring 组件。比如 如果没有依赖 web 包 ,Spring 的两大核心功能 IOC 和 AOP 将不会生效。<?xml version=“1.0” encoding=“UTF-8”?><project xmlns=“http://maven.apache.org/POM/4.0.0" xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.nasus</groupId> <artifactId>helloworld</artifactId> <version>0.0.1-SNAPSHOT</version> <name>helloworld</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <!– Spring Boot 启动父依赖 –> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.1.RELEASE</version> <relativePath/> <!– lookup parent from repository –> </parent> <dependencies> <!– Spring Boot web依赖 –> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!– Junit –> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>HelloworldApplication.java 最为重要,它由项目生成,是整个工程的应用启动类 main 函数。代码由项目生成,代码如下:SpringApplication 引导应用,并将 HelloworldApplication 本身作为参数传递给 run 方法。具体 run 方法会启动嵌入式的 Tomcat 并初始化 Spring环境及其各 Spring 组件。需要注意的是,这个类必须放在其他类的上册目录,拿上述目录举个栗子, 若其他HelloWorldController.java 类位于 com.nasus.controller 下。则 HelloworldApplication.java 类必须放置在 com.nasus 下或者 com 下(层级必须大于其他 java 类)。否则启动项目访问会报 Whitelabel Error Page 错误,原因是项目扫描不到 @RestController、@RequestMapping 等注解配置的方法和类。package com.nasus.helloworld;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class HelloworldApplication { public static void main(String[] args) { SpringApplication.run(HelloworldApplication.class, args); }}HelloWorldController 是我手动编写的,代码如下:@RestController 和 @RequestMapping 注解是来自 SpringMVC 的注解,它们不是 SpringBoot 的特定部分。其中 @RestController 注解的作用是:提供实现了 REST API,可以服务 JSON、XML 或者其他。这里是以 String 的形式渲染出结果。 其中 @RestController 注解的作用是:提供路由信息,"/“路径的HTTP Request都会被映射到sayHello方法进行处理。 具体参考,Spring 官方的文档《Spring Framework Document》package com.nasus.controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/** * Project Name:helloworld <br/> * Package Name:com.nasus.controller <br/> * Date:2019/1/5 13:59 <br/> * <b>Description:</b> TODO: 描述该类的作用 <br/> * * @author <a href=“turodog@foxmail.com”>nasus</a><br/> * Copyright Notice ========================================================= * This file contains proprietary information of Eastcom Technologies Co. Ltd. * Copying or reproduction without prior written approval is prohibited. * Copyright (c) 2019 ======================================================= /@RestControllerpublic class HelloWorldController { @RequestMapping("/hello”) public String sayHello() { return “Hello,World!”; }}写完 Controller 层的代码,我们就可以启动此项目。点击 IDEA 项目启动按钮,效果如下:好的程序必须配备完善的单元测试。HelloWorldControllerTest.java 文件是由我编写的主要作用就是测试 HelloWorldController.java 中的方法。这里用的是 Junit 依赖包进行单元测试,代码如下:这里的逻辑就是测试 HelloWorldController.java 的 sayHello 方法输出的字符是否是 Hello,World!package com.nasus;import static org.junit.Assert.assertEquals;import com.nasus.controller.HelloWorldController;import org.junit.Test;/* * Project Name:helloworld <br/> * Package Name:com.nasus <br/> * Date:2019/1/5 14:01 <br/> * <b>Description:</b> TODO: 描述该类的作用 <br/> * * @author <a href=“turodog@foxmail.com”>nasus</a><br/> * Copyright Notice ========================================================= * This file contains proprietary information of Eastcom Technologies Co. Ltd. * Copying or reproduction without prior written approval is prohibited. * Copyright (c) 2019 ======================================================= */public class HelloWorldControllerTest { @Test public void testSayHello() { assertEquals(“Hello,World!",new HelloWorldController().sayHello()); }}编写完成之后,可以通过以下按钮启动单元测试类。测试结果如下:可以看到红圈框住的地方,出现这个绿色标志证明单元测试没问题。sayhello 方法的结果是对的。后语我为什么要写这种这么简单的教程?是这样的,我始终认为比我聪明的人有很多,但比我笨的人也不少。在中国有很多你认为众所周知的事,其实有一车人根本不知道,这篇文章哪怕只帮助到一个人,足矣。之后我打算出一个 SpringBoot 系列的教程,敬请关注与指正,本人也是一个小菜鸟在打怪升级中,如本文有不正确的地方,烦请指正。一起学习一起进步。以上就是我对 SpringBoot 工程的理解,希望对你们有帮助。最后,对 Python 、Java 感兴趣请长按二维码关注一波,我会努力带给你们价值,如果觉得本文对你哪怕有一丁点帮助,请帮忙点好看,让更多人知道。另外,关注之后在发送 1024 可领取免费学习资料。资料内容详情请看这篇旧文:Python、C++、Java、Linux、Go、前端、算法资料分享 ...

February 19, 2019 · 2 min · jiezi

SpringBoot 实战 (四) | 集成 Swagger2 构建强大的 RESTful API 文档

微信公众号:一个优秀的废人如有问题或建议,请后台留言,我会尽力解决你的问题。前言快过年了,不知道你们啥时候放年假,忙不忙。反正我是挺闲的,所以有时间写 blog。今天给你们带来 SpringBoot 集成 Swagger2 的教程。什么是 Swagger2Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。为什么使用 Swagger2 ?相信刚开始不熟悉 web 开发的时候,大家都有手写 Api 文档的时候。而手写 Api 文档主要有以下几个痛点:文档需要更新的时候,需要再次发送一份给前端,也就是文档更新交流不及时。接口返回结果不明确。不能直接在线测试接口,通常需要使用工具,比如 postman。接口文档太多,不好管理。这些痛点在前后端分离的大型项目上显得尤为烦躁。而 Swagger2 的出现恰好能个解决这些痛点。因为 Swagger2 有以下功能:文档自动更新,只要生成 Api 的网址没变,基本不需要跟前端沟通。接口返回结果非常明确,包括数据类型,状态码,错误信息等。可以直接在线测试文档,而且还有实例提供给你。只需要一次配置便可使用,之后只要会有一份接口文档,非常易于管理。集成演示首先新建一个 SpringBoot 项目,还不会的参考我这篇旧文—— 如何使用 IDEA 构建 Spring Boot 工程构建时,在选择依赖那一步勾选 Web、LomBok、JPA 和 Mysql 依赖。其中 Mysql 可以不勾,因为我这里用于操作实际的数据库,所以我勾选了。生成 SpringBoot 后的 Pom 文件依赖如下:这里使用的是 2.4.0 的 Swagger2 版本。<?xml version=“1.0” encoding=“UTF-8”?><project xmlns=“http://maven.apache.org/POM/4.0.0" xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.2.RELEASE</version> <relativePath/> <!– lookup parent from repository –> </parent> <groupId>com.nasus</groupId> <artifactId>swagger2</artifactId> <version>0.0.1-SNAPSHOT</version> <name>swagger2</name> <description>Demo project for Swagger2</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.4.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.4.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>第二步,在 SpringBoot 启动类(Application)的同级目录新建一个 Swagger 配置类,注意 Swagger2 配置类必须与项目入口类 Application 位于同一级目录,否则生成 Api 文档失败,代码如下:package com.nasus;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import springfox.documentation.builders.ApiInfoBuilder;import springfox.documentation.builders.PathSelectors;import springfox.documentation.builders.RequestHandlerSelectors;import springfox.documentation.service.ApiInfo;import springfox.documentation.service.Contact;import springfox.documentation.spi.DocumentationType;import springfox.documentation.spring.web.plugins.Docket;import springfox.documentation.swagger2.annotations.EnableSwagger2;/** * Project Name:swagger2-demo <br/> * Package Name:com.nasus <br/> * Date:2019/1/22 22:52 <br/> * <b>Description:</b> TODO: 描述该类的作用 <br/> * * @author <a href=“turodog@foxmail.com”>nasus</a><br/> * Copyright Notice ========================================================= * This file contains proprietary information of Eastcom Technologies Co. Ltd. * Copying or reproduction without prior written approval is prohibited. * Copyright (c) 2019 ======================================================= /@Configuration// 启用 Swagger2@EnableSwagger2public class Swagger2 { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) // 文档信息对象 .apiInfo(apiInfo()) .select() // 被注解的包路径 .apis(RequestHandlerSelectors.basePackage(“com.nasus.controller”)) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() // 标题 .title(“springboot 利用 swagger 构建 API 文档”) // Api 文档描述 .description(“简单优雅的 restful 风格,https://blog.csdn.net/turodog/”) .termsOfServiceUrl(“https://blog.csdn.net/turodog/") // 文档作者信息 .contact(new Contact(“陈志远”, “https://github.com/turoDog", “turodog@foxmail.com”)) // 文档版本 .version(“1.0”) .build(); }}第三步,配置被注解的 Controller 类,编写各个接口的请求参数,返回结果,接口描述等等,代码如下:package com.nasus.controller;import com.nasus.entity.Student;import com.nasus.service.StudentService;import io.swagger.annotations.Api;import io.swagger.annotations.ApiImplicitParam;import io.swagger.annotations.ApiImplicitParams;import io.swagger.annotations.ApiOperation;import java.util.List;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.DeleteMapping;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.PutMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;import springfox.documentation.annotations.ApiIgnore;/* * Project Name:swagger2-demo <br/> * Package Name:com.nasus.controller <br/> * Date:2019/1/22 22:07 <br/> * <b>Description:</b> TODO: 描述该类的作用 <br/> * * @author <a href=“turodog@foxmail.com”>nasus</a><br/> * Copyright Notice ========================================================= * This file contains proprietary information of Eastcom Technologies Co. Ltd. * Copying or reproduction without prior written approval is prohibited. * Copyright (c) 2019 ======================================================= */@RestController@RequestMapping("/student”)// @Api:修饰整个类,描述Controller的作用@Api(“StudentController Api 接口文档”)public class StudentController { @Autowired private StudentService studentService; // @ApiOperation:描述一个类的一个方法,或者说一个接口 @ApiOperation(value=“获取所有学生列表”, notes=“获取所有学生列表”) @RequestMapping(value={””}, method= RequestMethod.GET) public List<Student> getStudent() { List<Student> list = studentService.findAll(); return list; } @ApiOperation(value=“添加学生信息”, notes=“添加学生信息”) // @ApiImplicitParam:一个请求参数 @ApiImplicitParam(name = “student”, value = “学生信息详细实体”, required = true, dataType = “Student”) @PostMapping("/save”) public Student save(@RequestBody Student student){ return studentService.save(student); } @ApiOperation(value=“获学生信息”, notes=“根据url的id来获取学生详细信息”) @ApiImplicitParam(name = “id”, value = “ID”, required = true, dataType = “Integer”,paramType = “path”) @GetMapping(”/{id}") public Student findById(@PathVariable(“id”) Integer id){ return studentService.findById(id); } @ApiOperation(value=“删除学生”, notes=“根据url的id来指定删除的学生”) @ApiImplicitParam(name = “id”, value = “学生ID”, required = true, dataType = “Integer”,paramType = “path”) @DeleteMapping("/{id}") public String deleteById(@PathVariable(“id”) Integer id){ studentService.delete(id); return “success”; } @ApiOperation(value=“更新学生信息”, notes=“根据url的id来指定更新学生信息”) // @ApiImplicitParams:多个请求参数 @ApiImplicitParams({ @ApiImplicitParam(name = “id”, value = “学生ID”, required = true, dataType = “Integer”,paramType = “path”), @ApiImplicitParam(name = “student”, value = “学生实体student”, required = true, dataType = “Student”) }) @PutMapping(value="/{id}") public String updateStudent(@PathVariable Integer id, @RequestBody Student student) { Student oldStudent = this.findById(id); oldStudent.setId(student.getId()); oldStudent.setName(student.getName()); oldStudent.setAge(student.getAge()); studentService.save(oldStudent); return “success”; } // 使用该注解忽略这个API @ApiIgnore @RequestMapping(value = “/hi”, method = RequestMethod.GET) public String jsonTest() { return " hi you!"; }}第四步,启动项目,访问 http://localhost:8080/swagger-ui.html 地址,结果如下图:项目源代码github图解接口Swagger2 常用注解简介@ApiOperation:用在方法上,说明方法的作用 1.value: 表示接口名称 2.notes: 表示接口详细描述 @ApiImplicitParams:用在方法上包含一组参数说明@ApiImplicitParam:用在@ApiImplicitParams注解中,指定一个请求参数的各个方面 1.paramType:参数位置 2.header 对应注解:@RequestHeader 3.query 对应注解:@RequestParam 4.path 对应注解: @PathVariable 5.body 对应注解: @RequestBody 6.name:参数名 7.dataType:参数类型 8.required:参数是否必须传 9.value:参数的描述 10.defaultValue:参数的默认值@ApiResponses:用于表示一组响应@ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息 1.code:状态码 2.message:返回自定义信息 3.response:抛出异常的类@ApiIgnore: 表示该接口函数不对swagger2开放展示@Api:修饰整个类,描述Controller的作用@ApiParam:单个参数描述@ApiModel:用对象来接收参数@ApiProperty:用对象接收参数时,描述对象的一个字段@ApiIgnore:使用该注解忽略这个API@ApiError :发生错误返回的信息注意事项@ApiImplicitParam 注解下的 paramType 属性,会影响接口的测试,如果设置的属性跟spring 的注解对应不上,会获取不到参数,例如 paramType=path ,函数内却使用@RequestParam 注解,这样,可能会获取不到传递进来的参数,需要按照上面进行对应,将 @RequestParam 注解改为 @PathVariable 才能获取到对应的参数。后语以上就是我对 Swagger2 的理解与使用,以上只是教大家入门 Swagger2 ,想要深入使用还是希望自行查阅官方文档。最后,对 Python 、Java 感兴趣请长按二维码关注一波,我会努力带给你们价值,如果觉得本文对你哪怕有一丁点帮助,请帮忙点好看,让更多人知道。另外,关注之后在发送 1024 可领取免费学习资料。资料内容详情请看这篇旧文:Python、C++、Java、Linux、Go、前端、算法资料分享 ...

February 19, 2019 · 3 min · jiezi

Intellij IDEA 开发 Spring-boot项目 热部署,自动部署

使用Intellij IDEA 开发 Spring-boot项目 热部署,自动部署使用Intellij IDEA 开发 Spring-boot项目,即使项目使用了spring-boot-devtools,修改了类或者html、js等,idea还是不会自动重启,非要手动去make一下或者重启,就更没有使用热部署一样。网上关于spring-boot-devtools的热部署都是eclipse的配置,并不适合IDEA,IDEA的需要特殊的设置首先,IDEA设置里面这里必须打勾 然后 Shift+Ctrl+Alt+/,选择Registry ok了,重启一下项目,然后改一下类里面的内容,IDEA就会自动去make了。

February 19, 2019 · 1 min · jiezi

SpringBoot 实战 (七) | 默认日志配置

微信公众号:一个优秀的废人如有问题或建议,请后台留言,我会尽力解决你的问题。前言如题,今天介绍 springboot 默认日志的配置。默认日志 Logback默认情况下,Spring Boot 用 Logback 来记录日志,并用 INFO 级别输出到控制台。如果你在平常项目中用过 Spring Boot,你应该已经注意到很多 INFO 级别的日志了。默认日志长这样:2019-02-18 22:02:14.907 INFO 23384 — [ main] org.hibernate.Version : HHH000412: Hibernate Core {5.3.7.Final}2019-02-18 22:02:14.907 INFO 23384 — [ main] org.hibernate.cfg.Environment : HHH000206: hibernate.properties not found2019-02-18 22:02:15.110 INFO 23384 — [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.0.4.Final}从上面的日志可以看到,日志输出内容元素具体如下:时间日期:精确到毫秒日志级别:ERROR, WARN, INFO, DEBUG or TRACE进程 ID分隔符:— 标识实际日志的开始线程名:方括号括起来(可能会截断控制台输出)Logger 名:通常使用源代码的类名日志内容日志依赖Logback 日志框架依赖于 spring-boot-starter-logging 包,但我们并不需要在 maven 中加入这个依赖,因为 spring-boot-starter其中包含了 spring-boot-starter-logging,该依赖内容就是 Spring Boot 默认的日志框架 logback。控制台输出在 Spring Boot 中默认配置了 ERROR、WARN 和 INFO 级别的日志输出到控制台。我们可以通过两种方式切换至 DEBUG 级别:在运行命令后加入 –debug 标志,如:$ java -jar myapp.jar –debug在 application.properties 中配置 debug=true ,该属性置为 true 的时候,核心 Logger(包含嵌入式容器、hibernate、spring)会输出更多内容,但是你自己应用的日志并不会输出为 DEBUG 级别。多彩输出如果你的终端支持ANSI,设置彩色输出会让日志更具可读性。通过在 application.properties 中设置 spring.output.ansi.enabled 参数来支持。NEVER:禁用 ANSI-colored 输出(默认项)DETECT:会检查终端是否支持 ANSI,是的话就采用彩色输出(推荐项)ALWAYS:总是使用 ANSI-colored 格式输出,若终端不支持的时候,会有很多干扰信息,不推荐使用文件输出Spring Boot默认配置只会输出到控制台,并不会记录到文件中,但是我们通常生产环境使用时都需要以文件方式记录。若要增加文件输出,需要在 application.properties 中配置 logging.file 或 logging.path属性。logging.file,设置文件,可以是绝对路径,也可以是相对路径。如:logging.file=my.loglogging.path,设置目录,会在该目录下创建spring.log文件,并写入日志内容,如:logging.path=/var/log注:二者不能同时使用,如若同时使用,则只有logging.file生效 默认情况下,日志文件的大小达到 10MB 时会切分一次,产生新的日志文件,默认级别为:ERROR、WARN、INFO级别控制在 Spring Boot 中只需要在 application.properties 中进行配置完成日志记录的级别控制。配置格式:logging.level.*=LEVELlogging.level:日志级别控制前缀,*为包名或Logger名LEVEL:选项 TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF举例:logging.level.com.nasus=DEBUG:com.nasus 包下所有 class 以 DEBUG 级别输出logging.level.root=WARN:root日志以 WARN 级别输出自定义日志配置根据不同的日志系统,你可以按如下规则组织配置文件名,就能被正确加载:Logback:logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovyLog4j:log4j-spring.properties, log4j-spring.xml, log4j.properties, log4j.xmlLog4j2:log4j2-spring.xml, log4j2.xmlJDK (Java Util Logging):logging.propertiesSpring Boot 官方推荐优先使用带有 -spring 的文件名作为你的日志配置(如使用 logback-spring.xml,而不是 logback.xml),命名为 logback-spring.xml 的日志配置文件,spring boot 可以为它添加一些 spring boot 特有的配置项(下面会提到)。 默认的命名规则,并且放在 src/main/resources 下面即可如果你即想完全掌控日志配置,但又不想用 logback.xml 作为 Logback 配置的名字,application.yml 可以通过 logging.config 属性指定自定义的名字:logging.config=classpath:logging-config.xml虽然一般并不需要改变配置文件的名字,但是如果你想针对不同运行时 Profile 使用不同的日志配置,这个功能会很有用。 一般不需要这个属性,而是直接在 logback-spring.xml 中使用 springProfile 配置,不需要 logging.config 指定不同环境使用不同配置文件。springProfile 配置在下面介绍。多环境日志输出logback-spring.xml :<configuration> … <!– 测试环境+开发环境. 多个使用逗号隔开. –> <springProfile name=“test,dev”> <logger name=“com.example.demo.controller” level=“DEBUG” additivity=“false”> <appender-ref ref=“consoleLog”/> </logger> </springProfile> <!– 生产环境. –> <springProfile name=“prod”> <logger name=“com.example.demo.controller” level=“INFO” additivity=“false”> <appender-ref ref=“consoleLog”/> </logger> </springProfile></configuration>application.yml 增加环境选择的配置 active: devspring: profiles: active: dev datasource: url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8 username: root password: 123456根据 active 的环境,自动采用上面配置的 springProfile 的 logger 日志。后语以上 SpringBoot 默认日志的配置教程。最后,对 Python 、Java 感兴趣请长按二维码关注一波,我会努力带给你们价值,如果觉得本文对你哪怕有一丁点帮助,请帮忙点好看,让更多人知道。另外,关注之后在发送 1024 可领取免费学习资料。资料内容详情请看这篇旧文:Python、C++、Java、Linux、Go、前端、算法资料分享 ...

February 19, 2019 · 2 min · jiezi

读《疯狂Java:突破程序员基本功的16课》之数组与内存控制部分总结

我们在使用java编程的时候免不了使用数组这种数据类型,今天我们就来聊聊数组的初始化。java有两种初始化方式:静态初始化所谓静态初始化就是由我们给数组指定每一个具体的值, 长度由系统给我们分配, 例如: String[] names = {“zhangsan”, “lisi”}; String[] name = new String[]{“zhangsan”, “lisi”};动态初始化所谓动态初始化就是我们指定数组的长度, 由系统给我们指定初始的值,即系统会给我们分配默认的值 例如:String[] name = new String[5];需要注意的是不能同时进行动态初始化和静态初始化, 例如下面的例子编译就不能通过:String[] name = new String[2]{“zhangsan”, “lisi”}(这是错误初始化方式)

February 15, 2019 · 1 min · jiezi

IDEA常用插件整理

Lombok作用:帮使用者提高编码效率,减少重复与冗余的代码。使用教程下载地址阿里巴巴代码规范检测使用教程下载地址GsonFormat作用: json 格式的字符串转换成实体类参数使用教程下载地址Maven Helper作用:一键查看maven依赖,查看冲突的依赖,一键进行exclude依赖。使用教程下载地址Git flow作用:git工作流插件使用教程下载地址VisualVM Launcher作用:查看jvm情况,jvm调优必备工具使用教程下载地址Free Mybatis plugin作用:非常方便进行 Mapper 接口和 XML 文件之间跳转下载地址Codehelper Generator作用:自动生成MyBatis代码使用教程下载地址GenerateAllSetter作用:一键生成对象所有set代码使用教程下载地址Rainbow Brackets作用:彩色配对括号,代码块多看着会很舒服下载地址Translation作用:功能强大的翻译插件使用教程下载地址Iedis作用:Redis可视化插件使用教程(需翻墙)下载地址

February 13, 2019 · 1 min · jiezi

SpringBoot实战 | 配置文件详解

微信公众号:一个优秀的废人如有问题或建议,请后台留言,我会尽力解决你的问题。前言如题,今天解析下 SpringBoot 的配置文件。自定义属性加载首先构建 SpringBoot 项目,不会的看这篇旧文 使用 IDEA 构建 Spring Boot 工程。首先在项目根目录下加入以下自定义属性:# 防止读取乱码spring.http.encoding.charset=UTF-8# 项目启动端口server.port=9999# 自定义配置com.nasus.author.name=一个优秀的废人com.nasus.article.title=SpringBoot配置文件详解使用 @value 注解读取配置文件属性:package com.nasus.bean;import lombok.Data;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;/** * Project Name:springboot_properties_demo <br/> * Package Name:com.nasus.properties <br/> * Date:2019/1/28 20:59 <br/> * <b>Description:</b> TODO: 描述该类的作用 <br/> * @author <a href=“turodog@foxmail.com”>nasus</a><br/> /@Data@Componentpublic class PropertiesBean { @Value("${com.nasus.author.name}") private String name; @Value("${com.nasus.article.title}") private String title; @Value("${com.nasus.doing}") private String desc;}之后新建 controller 测试自定义属性加载,代码如下:package com.nasus.controller;import com.nasus.bean.PropertiesBean;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/* * Project Name:springboot_properties_demo <br/> * Package Name:com.nasus.controller <br/> * Date:2019/1/28 21:41 <br/> * <b>Description:</b> TODO: 测试自定义属性加载<br/> * * @author <a href=“turodog@foxmail.com”>nasus</a><br/> */@RestController@RequestMapping("/test")public class TestController { @Autowired private PropertiesBean propertiesBean; @GetMapping("/getInfo") public PropertiesBean getInfo(){ return propertiesBean; }}访问 http://localhost:8080/test/getInfo 查看加载结果:可以看到,加入 @value 注解之后,配置文件的属性都被读取出来了。以前,或许我们还需要专门写一个读取配置文件的工具类才能把属性读取出来,现在有了 Spring ,我们可以直接使用 @value 就能读取了,简直不能太方便。本例源码在这:github 地址参数间的引用配置文件代码如下:# 防止读取乱码spring.http.encoding.charset=UTF-8# 项目启动端口server.port=9999# 自定义配置com.nasus.author.name=一个优秀的废人com.nasus.article.title=SpringBoot配置文件详解com.nasus.doing=${com.nasus.author.name}写文章《${com.nasus.article.title}》可以看到最后一个参数配置使用了前两个的参数配置,测试结果如下:使用随机数有时项目需求,可能我们需要配置一些随机数,比如说为了安全而随机配置的服务器端口,以及登录密钥。这时我们就可以用 SpringBoot 的 random 属性来配置随机数,比如:# 随机字符串com.nasus.article.value=${random.value}# 随机intcom.nasus.article.number=${random.int}# 随机longcom.nasus.article.bignumber=${random.long}# 10以内的随机数com.nasus.article.test1=${random.int(10)}# 10-20的随机数com.nasus.article.test2=${random.int[10,20]}使用多配置文件很多时候我们开发项目都需要很多套环境,比如有测试环境,开发环境以及生产环境。不同的环境就需要使用不同的配置文件,为此我们可以根据这 3 个环境分别新建 以下 3 个配置文件。application-dev.properties:开发环境application-test.properties:测试环境application-prod.properties:生产环境项目中默认的配置文件是 application.properties 。这时我们可以根据自己的环境去使用相应的配置文件,比如说,项目各个环境的端口必须不一样。那我们可以这样配置:application-dev.properties:开发环境server.port=6666application-test.properties:测试环境server.port=7777application-prod.properties:生产环境server.port=8888假如,现在我打包上线,那就必须用生产环境的配置文件了,这时我们可以在 默认的配置文件 application.properties 中加入以下配置即可spring.profiles.active=prod配置数据库SpringBoot 的配置文件有两种格式,一种是 .properties 格式(以上栗子都是用的这种)还有一种用的是 .yaml 格式。以下是用 yaml 方式配置。这两种格式并无好坏之分,纯看个人使用习惯。我就比较喜欢 yaml 格式,因为看起来比较简洁。spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=true username: 你的数据库名称 password: 你的数据库密码 jpa: hibernate: ddl-auto: update #ddl-auto:设为update 表示每次都重新建表 show-sql: true注意事项使用 yaml 格式需要注意一点就是 键值对冒号后面,必须空一格。application.properties 配置中文值的时候,读取出来的属性值会出现乱码问题。但是 application.yml 不会出现乱码问题。原因是,Spring Boot 是以 iso-8859 的编码方式读取 application.properties 配置文件。解决第二点,只需加入 spring.http.encoding.charset=UTF-8 配置即可。后语以上就是我对 SpringBoot 配置文件的理解与使用,当然以上只是介绍了一下 SpringBoot 配置文件的几个用法,它的用法还有非常多,想要深入使用还是需要各位多多深入实践。最后,对 Python 、Java 感兴趣请长按二维码关注一波,我会努力带给你们价值,如果觉得本文对你哪怕有一丁点帮助,请帮忙点好看,让更多人知道。另外,关注之后在发送 1024 可领取免费学习资料。资料内容详情请看这篇旧文:Python、C++、Java、Linux、Go、前端、算法资料分享 ...

January 29, 2019 · 1 min · jiezi

Spring Boot 实战 | 如何使用 IDEA 构建 Spring Boot 工程

微信公众号:一个优秀的废人如有问题或建议,请后台留言,我会尽力解决你的问题。前言新年立了个 flag,好好运营这个公众号。具体来说,就是每周要写两篇文章在这个号发表。刚立的 flag 可不能这么快打脸。下面送上本周第一篇。本文我们将介绍嵌入 Intellij IDEA 中的 Spring Initializr 工具,它同Web提供的创建功能一样,可以帮助我们快速的构建出一个基础的Spring Boot工程。什么是 SpringBoot ?SpringBoot 官方有一句话可以概括这个问题。那就是「约定大于配置」。这句话什么意思?相信学过 Spring 的人都知道,我们要手动写一大堆的 xml 文件用于配置,集成项目,才能使这个项目具备 web 的功能。而 SpringBoot 做了那些没有它你也会去做的Spring Bean配置。它使用「约定大于配置」的理念让你的项目快速运行起来。使用 Spring Boot 很容易创建一个独立运行(运行jar,内嵌Servlet容器)、准生产级别的基于 Spring 框架的项目,使用 Spring Boot 你可以不用或者只需要很少的Spring配置。如果说 Spring 是一辆汽车的引擎,那 SpringMVC 就给这辆汽车装上了轮子,而 SpringBoot 的出现就相当于赋予了这辆汽车自动驾驶的功能。如何使用 IDEA 构建 SpringBoot 工程?第一步,当然是安装 Intellij IDEA (傻瓜式教程,请自行百度)。点击菜单栏 File ➤New➤Project ➤ 选择 Spring Initializr 创建界面如下图,可以看到图中 default 指定的 Initializr Service URL 就是 Spring 官方提供的 Spring Initializr 工具地址,一般默认即可,所以这里创建的工程实际上也是基于它的 Web 工具来实现的。点击 next 进入下一步,可以看见这里要我们选择的就是关于工程的一些信息:Group 顾名思义就是你的公司名,一般是填写com.公司名。Artifact groupId 和 artifactId 是maven管理项目包时用作区分的字段,就像是地图上的坐标。这里填写项目名即可。Type 就是构建的项目类型,意思就是你希望你的项目使用什么工具构建,可选 maven 和 gradle 一般选 maven。Language 顾名思义就是你的项目用啥语言开发,可选 Java、Groovy、KotlinPackaging 就是你希望你的项目打成什么形式的包,可选 Jar、War SpringBoot 项目一般选 JarJava Version 意指项目使用的 java 版本,根据你的需要选择。Version 项目的初始版本,默认即可。Name 项目名称。Description 项目描述,默认即可。Package 包名,填完 Group 和 Artifact 后自动生成,默认即可。点击 Next 进入下一步,这一步就是选你的项目依赖包,前文所说的「约定大于配置」就体现在这里。进入选择S pring Boot 版本和依赖管理的窗口。在这里值的我们关注的是,它不仅包含了 Spring Boot Starter POMs 中的各个依赖,还包含了 Spring Cloud 的各种依赖。比如,你需要集成前端模板功能,你就到 Template Engines 选项卡上,勾选你想要访问的前端模板引擎 ,项目需要访问数据库,就到 SQL 选项卡,旋转你项目里使用的数据库类型。选择完成并加以简单的配置,项目就具备了集成前端模板能力与数据库访问能力。这里注意一下,无论你选择哪些依赖包,其中 web 选项卡下的 Web 是必选的。这个包是整个项目的基础。这个包里面集成了 Spring 、WebMvc 、tomcat 以及其他各种基本能力。点击 Next 进入下一步,这一步没啥好说的。就是让你确认自己的项目名以及项目路径。确认无误,点 Finish 完成创建即可。Intellij IDEA 中的 Spring Initializr 是基于官方 Web 实现,但是通过工具来进行调用并直接将结果构建到我们的本地文件系统中,让整个构建流程变得更加顺畅。后语我为什么要写这种这么简单的教程?是这样的,我始终认为比我聪明的人有很多,但比我笨的人也不少。在中国有很多你认为众所周知的事,其实有一车人根本不知道,这篇文章哪怕只帮助到一个人,足矣。之后我打算出一个 SpringBoot 系列的教程,敬请关注与指正,本人也是一个小菜鸟在打怪升级中,如本文有不正确的地方,烦请指正。一起学习一起进步。以上就是使用 IDEA 创建 SpringBoot 的过程,希望对你们有帮助。最后,对 Python 、Java 感兴趣请长按二维码关注一波,我会努力带给你们价值,如果觉得本文对你哪怕有一丁点帮助,请帮忙点好看,让更多人知道。 ...

January 25, 2019 · 1 min · jiezi

idea 使用 git 教程

idea 使用 git 教程1.下载 git下载地址: https://git-scm.com/download/win 64-bit Git for Windows Portable(简单版本)2.安装 gitbin 目录: E:gitbin3.获取远程仓库地址这个一般是由组长搭建,我们生成自己公钥给他就好,然后就可以拿到 远程仓库地址# 生成公钥ssh-keygen -t rsa -C “邮箱地址”# 输入秘钥密码4.配置全局用户名和邮箱git config –global user.name “孔乙己"git config –global user.email “kyj@gmail.com"注意: 最好邮箱地址一致,用户名是提交代码是备注的用户名5. 将当前项目目录和git远程仓库关联# 将当前项目作为本地仓库git init# 将当前项目添加到git 缓存中git add src# 将缓存中的内容提交到git本地仓库git commit -m “你妹的git” # 将本地仓库和远程仓库关联git remote add origin https://github.com/aaaa/aaa.git# 将本地仓库中的项目提交到git远程仓库git push -u origin master6. 之后再idea 中就可以正常使用git了7. 建议第一次使用git测试的idea用户按照以下流程使用创建项目将当前项目所在的目录作为git本地仓库3.这是项目右键就会出现 <font color=“lightgreen”>git</font> 的选项了输入注释,提交到本地仓库/推送到远程仓库参考资料: https://www.cnblogs.com/nihao… 感谢 <font color=“red”>Nihaorz</font> 的无私分享

January 24, 2019 · 1 min · jiezi

hibernate和jdbc的渊源

简单介绍jdbc我们学习Java数据库操作时,一般会设计到jdbc的操作,这是一位程序员最基本的素养。jdbc以其优美的代码和高性能,将瞬时态的javabean对象转化为持久态的SQL数据。但是,每次SQL操作都需要建立和关闭连接,这势必会消耗大量的资源开销;如果我们自行创建连接池,假如每个项目都这样做,势必搞死的了。同时,我们将SQL语句预编译在PreparedStatement中,这个类可以使用占位符,避免SQL注入,当然,后面说到的hibernate的占位符的原理也是这样,同时,mybatis的#{}占位符原理也是如此。预编译的语句是原生的SQL语句,比如更新语句:private static int update(Student student) { Connection conn = getConn(); int i = 0; String sql = “update students set Age=’” + student.getAge() + “’ where Name=’” + student.getName() + “’”; PreparedStatement pstmt; try { pstmt = (PreparedStatement) conn.prepareStatement(sql); i = pstmt.executeUpdate(); System.out.println(“resutl: " + i); pstmt.close(); conn.close(); } catch (SQLException e) { e.printStackTrace(); } return i;}上面的sql语句没有使用占位符,如果我们少了varchar类型的单引号,就会保存失败。在这种情况下,如果写了很多句代码,最后因为一个单引号,导致代码失败,对于程序员来说,无疑是很伤自信心的事。如果涉及到了事务,那么就会非常的麻烦,一旦一个原子操作的语句出现错误,那么事务就不会提交,自信心就会跌倒低谷。然而,这仅仅是更新语句,如果是多表联合查询语句,那么就要写很多代码了。具体的jdbc的操作,可以参考这篇文章:jdbc操作。因而,我们在肯定它的优点时,也不应该规避他的缺点。随着工业化步伐的推进,每个数据库往往涉及到很多表,每张表有可能会关联到其他表,如果我们还是按照jdbc的方式操作,这无疑是增加了开发效率。所以,有人厌倦这种复杂繁琐、效率低下的操作,于是,写出了著名的hibernate框架,封装了底层的jdbc操作,以下是jdbc的优缺点:由上图可以看见,jdbc不适合公司的开发,公司毕竟以最少的开发成本来创造更多的利益。这就出现了痛点,商机伴随着痛点的出现。因而,应世而生了hibernate这个框架。即便没有hibernate的框架,也会有其他框架生成。hibernate的底层封装了jdbc,比如说jdbc为了防止sql注入,一般会有占位符,hibernate也会有响应的占位符。hibernate是orm(object relational mapping)的一种,即对象关系映射。什么对象关系映射通俗地来说,对象在pojo中可以指Javabean,关系可以指MySQL的关系型数据库的表字段与javabean对象属性的关系。映射可以用我们高中所学的函数映射,即Javabean顺时态的对象映射到数据库的持久态的数据对象。我们都知道javabean在内存中的数据是瞬时状态或游离状态,就像是宇宙星空中的一颗颗行星,除非行星被其他行星所吸引,才有可能不成为游离的行星。瞬时状态(游离状态)的javabean对象的生命周期随着进程的关闭或者方法的结束而结束。如果当前javabean对象与gcRoots没有直接或间接的关系,其有可能会被gc回收。我们就没办法长久地存储数据,这是一个非常头疼的问题。假如我们使用文件来存储数据,但是文件操作起来非常麻烦,而且数据格式不是很整洁,不利于后期的维护等。因而,横空出世了数据库。我们可以使用数据库存储数据,数据库中的数据才是持久数据(数据库的持久性),除非人为的删除。这里有个问题——怎么将瞬时状态(游离状态)的数据转化为持久状态的数据,肯定需要一个连接Javabean和数据库的桥梁,于是乎就有了上面的jdbc。单独来说mysql,mysql是一个远程服务器。我们在向mysql传输数据时,并不是对象的方式传输,而是以字节码的方式传输数据。为了保证数据准确的传输,我们一般会序列化当前对象,用序列号标志这个唯一的对象。如果,我们不想存储某个属性,它是有数据库中的数据拼接而成的,我们大可不用序列化这个属性,可以使用Transient来修饰。比如下面的获取图片的路径,其实就是服务器图片的文件夹地址和图片的名称拼接而成的。当然,你想存储这个属性,也可以存储这个属性。我们有时候图片的路由的字节很长,这样会占用MySQL的内存。因而,学会取舍,未尝不是一个明智之举。@Entity@Table(name = “core_picture”)public class Picture extends BaseTenantObj { 。。。。 @Transient public String getLocaleUrl() { return relativeFolder.endsWith(”/") ? relativeFolder + name : relativeFolder + “/” + name; } 。。。。}网上流传盛广的对象关系映射的框架(orm)有hibernate、mybatis等。重点说说hibernate和mybatis吧。hibernate是墨尔本的一位厌倦重复的javabean的程序员编写而成的,mybatis是appache旗下的一个子产品,其都是封装了jdbc底层操作的orm框架。但hibernate更注重javabean与数据表之间的关系,比如我们可以使用注解生成数据表,也可以通过注解的方式设置字段的类型、注释等。他将javabean分成了游离态、顺时态、持久态等,hibernate根据这三种状态来触及javabean对象的数据。而mybatis更多的是注重SQL语句的书写,也就是说主要是pojo与SQL语句的数据交互,对此,Hibernate对查询对象有着良好的管理机制,用户无需关心SQL。一旦SQL语句的移动,有可能会影响字段的不对应,因而,mybatis移植性没有hibernate好。mybatis接触的是底层SQL数据的书写,hibernate根据javabean的参数来生成SQL语句,再将SQL查询的结果封装成pojo,因而,mybatis的性能相来说优于hibernate,但这也不是绝对的。性能还要根据你的表的设计结构、SQL语句的封装、网络、带宽等等。我只是抛砖引玉,它们具体的区别,可以参考这篇文档。mybatis和hibernate的优缺点。hibernate和mybatis之间的区别,也是很多公司提问面试者的问题。但是真正熟知他们区别的人,一般是技术选型的架构师。如果你只是负责开发,不需要了解它们的区别,因为他们都封装了jdbc。所以,你不论使用谁,都还是比较容易的。然而,很多公司的HR提问这种问题很死板,HR并不懂技术,他们只是照本宣科的提问。如果你照本宣科的回答,它们觉着你很厉害。但是,如果是一个懂技术的人提问你,如果你只是临时背了它们的区别,而没有相应的工作经验,他们会问的让你手足无措。hibernate的讲解因为我们公司使用的是hibernate,我在这里简单地介绍下hibernate。但相对于jdbc来说,hibernate框架还是比较重的。为什么说他重,因为它集成了太多的东西,看如下的hibernate架构图:你会发现最上层使我们的java应用程序的开始,比如web的Tomcat服务器的启动,比如main方法的启动等。紧接着就是需要(needing)持久化的对象,这里为什么说是需要,而不是持久化的对象。只有保存到文件、数据库中的数据才是持久化的想通过hibernate,我们可以毫不费力的将瞬时状态的数据转化为持久状态的数据,下面便是hibernate的内部操作数据。其一般是这样的流程:我个人画的这个地址的图片如果你是用过jdbc连接数据库的话,我们一般是这样写的:package com.zby.jdbc.config;import com.zby.util.exception.TableException;import org.apache.commons.lang3.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.io.IOException;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.util.Properties;/** * Created By zby on 22:12 2019/1/5 /public class InitJdbcFactory { private static Properties properties = new Properties(); private static Logger logger = LoggerFactory.getLogger(InitJdbcFactory.class); static { try { //因为使用的类加载器获取配置文件,因而,配置文件需要放在classpath下面, // 方能读到数据 properties.load(Thread.currentThread().getContextClassLoader(). getResourceAsStream("./jdbc.properties")); } catch (IOException e) { logger.info(“初始化jdbc失败”); e.printStackTrace(); } } public static Connection createConnection() { String drivers = properties.getProperty(“jdbc.driver”); if (StringUtils.isBlank(drivers)) { drivers = “com.mysql.jdbc.Driver”; } String url = properties.getProperty(“jdbc.url”); String username = properties.getProperty(“jdbc.username”); String password = properties.getProperty(“jdbc.password”); try { Class.forName(drivers); return DriverManager.getConnection(url, username, password); } catch (ClassNotFoundException e) { logger.error(InitColTable.class.getName() + “:连接数据库的找不到驱动类”); throw new TableException(InitColTable.class.getName() + “: 连接数据库的找不到驱动类”, e); } catch (SQLException e) { logger.error(InitColTable.class.getName() + “:连接数据库的sql异常”); throw new TableException(InitColTable.class.getName() + “连接数据库的sql异常”, e); } }}hibernate一般这样连接数据库:public class HibernateUtils { private static SessionFactory sf; //静态初始化 static{ //【1】加载配置文件 Configuration conf = new Configuration().configure(); //【2】 根据Configuration 配置信息创建 SessionFactory sf = conf.buildSessionFactory(); //如果这里使用了hook虚拟机,需要关闭hook虚拟机 Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { @Override public void run() { System.out.println(“虚拟机关闭!释放资源”); sf.close(); } })); } /* * 采用openSession创建一个与数据库的连接会话,但这种方式需要手动关闭与数据库的session连接(会话), * 如果不关闭,则当前session占用数据库的资源无法释放,最后导致系统崩溃。 * / public static org.hibernate.Session openSession(){ //【3】 获得session Session session = sf.openSession(); return session; } / * 这种方式连接数据库,当提交事务时,会自动关闭当前会话; * 同时,创建session连接时,autoCloseSessionEnabled和flushBeforeCompletionEnabled都为true, * 并且session会同sessionFactory组成一个map以sessionFactory为主键绑定到当前线程。 * 采用getCurrentSession()需要在Hibernate.cfg.xml配置文件中加入如下配置: 如果是本地事物,及JDBC一个数据库: <propety name=”Hibernate.current_session_context_class”>thread</propety> 如果是全局事物,及jta事物、多个数据库资源或事物资源: <propety name=”Hibernate.current_session_context_class”>jta</propety> 使用spring的getHiberanteTemplate 就不需要考虑事务管理和session关闭的问题: * / public static org.hibernate.Session getCurrentSession(){ //【3】 获得session Session session = sf.getCurrentSession(); return session; }}mybatis的配置文件:public class DBTools { public static SqlSessionFactory sessionFactory; static{ try { //使用MyBatis提供的Resources类加载mybatis的配置文件 Reader reader = Resources.getResourceAsReader(“mybatis.cfg.xml”); //构建sqlSession的工厂 sessionFactory = new SqlSessionFactoryBuilder().build(reader); } catch (Exception e) { e.printStackTrace(); } } //创建能执行映射文件中sql的sqlSession public static SqlSession getSession(){ return sessionFactory.openSession(); }}hibernate、mybatis、jdbc创建于数据库的连接方式虽然不同,但最红都是为了将顺时态的数据写入到数据库中的,但这里主要说的是hibernate。但是hibernate已经封装了这些属性,我们可以在configuration在配置驱动类、用户名、用户密码等。再通过sessionFactory创建session会话,也就是加载Connection的物理连接,创建sql的事务,然后执行一系列的事务操作,如果事务全部成功即可成功,但反有一个失败都会失败。jdbc是最基础的操作,但是,万丈高楼平地起,只有基础打牢,才能走的更远。因为hibernate封装了这些基础,我们操作数据库不用考虑底层如何实现的,因而,从某种程度上来说,hibernate还是比较重的。hibernate为什么会重?比如我们执行插入语句,可以使用save、saveOrUpdate,merge等方法。需要将实体bean通过反射转化为mysql的识别的SQL语句,同时,查询虽然用到了反射,但是最后转化出来的还是object的根对象,这时需要将根对象转化为当前对象,返回给客户端。虽然很笨重,但是文件配置好了,可以大大地提高开发效率。毕竟现在的服务器的性能都比较好,公司追求的是高效率的开发,而往往不那么看重性能,除非用户提出性能的问题。说说merge和saveOrUpdatemerge方法与saveOrUpdate从功能上类似,但他们仍有区别。现在有这样一种情况:我们先通过session的get方法得到一个对象u,然后关掉session,再打开一个session并执行saveOrUpdate(u)。此时我们可以看到抛出异常:Exception in thread “main” org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session,即在session缓存中不允许有两个id相同的对象。不过若使用merge方法则不会异常,其实从merge的中文意思(合并)我们就可以理解了。我们重点说说merge。merge方法产生的效果和saveOrUpdate方法相似。这是hibernate的原码: void saveOrUpdate(Object var1); void saveOrUpdate(String var1, Object var2); public Object merge(Object object); public Object merge(String var1, Object var2);前者不用返回任何数据,后者返回的是持久化的对象。如果根据hibernate的三种状态,比如顺时态、持久态、游离态来说明这个问题,会比较难以理解,现在,根据参数有无id或id是否已经存在来理解merge,而且从执行他们两个方法而产生的sql语句来看是一样的。参数实例对象没有提供id或提供的id在数据库找不到对应的行数据,这时merger将执行插入操作吗,产的SQL语句如下: Hibernate: select max(uid) from user Hibernate: insert into hibernate1.user (name, age, uid) values (?, ?, ?)一般情况下,我们新new一个对象,或者从前端向后端传输javabean序列化的对象时,都不会存在当前对象的id,如果使用merge的话,就会向数据库中插入一条数据。参数实例对象的id在数据库中已经存在,此时又有两种情况(1)如果对象有改动,则执行更新操作,产生sql语句有: Hibernate: select user0_.uid as uid0_0_, user0_.name as name0_0_, user0_.age as age0_0_ from hibernate1.user user0_ where user0_.uid=? Hibernate: update hibernate1.user set name=?, age=? where uid=?(2)如果对象未改动,则执行查询操作,产生的语句有: Hibernate: select user0_.uid as uid0_0_, user0_.name as name0_0_, user0_.age as age0_0_ from hibernate1.user user0_ where user0_.uid=?以上三种是什么情况呢?如果我们保存用户时,数据库中肯定不存在即将添加的用户,也就是说,我们的保存用户就是向数据库中添加用户。但是,其也会跟着某些属性, 比如说用户需要头像,这是多对一的关系,一个用户可能多个对象,然而,头像的关联的id不是放在用户表中的,而是放在用户扩张表中的,这便用到了切分表的概念。题外话,我们有时会用到快照表,比如商品快照等,也许,我们购买商品时,商品是一个价格,但随后商品的价格变了,我们需要退商品时,就不应该用到商品改变后的价格了,而是商品改变前的价格。扩展表存放用户额外的信息,也就是用户非必须的信息,比如说昵称,性别,真实姓名,头像等。 因而,头像是图片类型,使用hibernate的注解方式,创建用户表、图片表、用户扩展表。如下所示(部分重要信息已省略) //用户头像 @Entity @Table(name = “core_user”) public class User extends BaseTenantConfObj { / * 扩展表 * / @OneToOne(mappedBy = “user”, fetch = FetchType.LAZY, cascade = CascadeType.ALL) private UserExt userExt; } //用户扩展表的头像属性 @Entity @Table(name = “core_user_ext”) public class UserExt implements Serializable { /* * 头像 / @ManyToOne @JoinColumn(name = “head_logo”) private Picture headLogo; } //图片表 @Entity @Table(name = “core_picture”) public class Picture extends BaseTenantObj { private static Logger logger = LoggerFactory.getLogger(Picture.class); 。。。。。。 //图片存放在第三方的相对url。 @Column(name = “remote_relative_url”, length = 300) private String remoteRelativeUrl; // 图片大小 @Column(length = 8) private Integer size; /* * 图片所属类型 * user_logo:用户头像 / @Column(name = “host_type”, length = 58) private String hostType; //照片描述 @Column(name = “description”, length = 255) private String description; } 前端代码是://这里使用到了vue.js的代码,v-model数据的双向绑定,前端的HTML代码<tr> <td class=“v-n-top”>头像:</td> <td> <div class=“clearfix”> <input type=“hidden” name=“headLogo.id” v-model=“pageData.userInfo.logo.id”/> <img class=“img-user-head fl” :src="(pageData.userInfo&&pageData.userInfo.logo&&pageData.userInfo.logo.path) ? (constant.imgPre + pageData.userInfo.logo.path) : ‘img/user-head-default.png’"> <div class=“img-btn-group”> <button cflag=“upImg” type=“button” class=“btn btn-sm btn-up”>点击上传</button> <button cflag=“delImg” type=“button” class=“btn btn-white btn-sm btn-del”>删除</button> </div> </div> <p class=“img-standard”>推荐尺寸800800;支持.jpg, .jpeg, .bmp, .png类型文件,1M以内</p> </td></tr>//这里用到了js代码,这里用到了js的属性方法upImg: function(me) { Utils.asyncImg({ fn: function(data) { vm.pageData.userInfo.logo = { path: data.remoteRelativeUrl, id: data.id }; } });},上传头像是异步提交,如果用户上传了头像,我们在提交用户信息时,通过“headLogo.id”可以获取当前头像的持久化的图片对象,hibernate首先会根据属性headLogo找到图片表,根据当前头像的id找到图片表中对应的行数据,为什么可以根据id来获取行数据?-图片表的表结构信息从这张图片可以看出,图片采用默认的存储引擎,也就是InnoDB存储引擎,而不是myiSam的存储引擎。我们都知道这两种存储引擎的区别,如果不知道的话,可以参这篇文章MySQL中MyISAM和InnoDB的索引方式以及区别与选择。innodb采用BTREE树的数据结构方式存储,它有0到1直接前继和0到n个直接后继,这是什么意思呢?一棵树当前叶子节点的直接父节点只有一个,但其儿子节点可以一个都没有,也可以有1个、2个、3个……,如果mysql采用的多对一的方式存储的话,你就会发现某条外键下有许多行数据,比如如下的这张表这张表记录的是项目的完成情况,一般有预约阶段,合同已签,合同完成等等。你会发现project_id=163的行数据不止一条,我们通过查询语句:SELECT zpp.* from zq_project_process zpp WHERE zpp.is_deleted = 0 AND zpp.project_id=163,查找速度非常快。为什么这么快呢,因为我刚开始说的innodb采用的BTREE树结构存储,其数据是放在当前索引下,什么意思?innodb的存储引擎是以索引作为当前节点值,比如说银行卡表的有个主键索引,备注,如果我们没有创建任何索引,如果采用的innodb的数据引擎,其内部会创建一个默认的行索引,这就像我们在创建javabean对象时,没有创建构造器,其内部会自动创建一个构造器的道理是一样的。其数据是怎么存储的呢,如下图所示:mysql银行卡数据其内部存储数据其所对应的行数据是放在当前索引下的,因而,我们取数据不是取表中中的数据,而是取当前主键索引下的数据。项目进程表如同银行卡的主键索引,只不过其有三个索引,分别是主键索引和两个外键索引,如图所示的索引:索引名是hibernate自动生成的一个名字,索引是项目id、类型两个索引。因为我们不是从表中取数据,而是从当前索引的节点下取数据,所以速度当然快了。索引有主键索引、外键索引、联合索引等,但一般情况下,主键索引和外键索引使用频率比较高。同时,innodb存储引擎的支持事务操作,这是非常重要,我们操作数据库,一般都是设计事务的操作,这也mysql默认的存储引擎是innodb。我们通过主键获取图片的行数据,就像通过主键获取银行卡的行数据。这也是上面所说的,根据是否有id来确定是插入还是更新数据。通过图片主键id获取该行数据后,hibernate会在堆中创建一个picture对象。用户扩展表的headLogo属性指向这个图片对象的首地址,从而创建一个持久化的图片对象。前台异步提交头像时,如果是编辑头像,hibernate会觉擦到当前对象的属性发生了改变,于是,在提交事务时将修改后的游离态的类保存到数据库中。如果我们保存或修改用户时,我们保存的就是持久化的对象,其内部会自动存储持久化头像的id。这是hibernate底层所做的,我们不需要关心。再举一个hibernate事务提交的例子:我们在支付当中搞得提现事务时,调用第三方支付的SDK时,第三方一般会用我们到订单号,比如我们调用连连支付这个第三方支付的SDK的payRequestBean的实体类:/** * Created By zby on 11:00 2018/12/11 * 发送到连连支付的body内容 /@Data@AllArgsConstructor@NoArgsConstructorpublic class PaymentRequestBean extends BaseRequestBean { /* * 版本号 / @NonNull private String api_version; /* * 银行账户 / @NonNull private String card_no; /* * 对私 / @NonNull private String flag_card; /* * 回调接口 / @NonNull private String notify_url; /* * 商户订单号 / @NonNull private String no_order; /* * 商户订单时间,时间格式为 YYYYMMddHHmmss / @NonNull private String dt_order; /* * 交易金额 / @NonNull public String money_order; /* * 收款方姓名 即账户名 / @NonNull private String acct_name; /* * 收款银行姓名 / private String bank_name; /* * 订单描述 ,代币类型 + 支付 / @NonNull private String info_order; /* * 收款备注 / private String memo; /* * 支行名称 */ private String brabank_name;}商户订单号是必传的,且这个订单号是我们这边提供的,这就有一个问题了,怎么避免订单号不重复呢?我们可以在提现记录表事先存储一个订单号,订单号的规则如下:“WD” +系统时间+ 当前提现记录的id,这个id怎么拿到呢?既然底层使用的是merge方法,我们事先不创建订单号,先保存这个记录,其返回的是已经创建好的持久化的对象,该持久化的对象肯定有提现主键的id。我们拿到该持久化对象的主键id,便可以封装订单号,再次保存这个持久化的对象,其内部会执行类似以下的操作:Hibernate: select user0_.uid as uid0_0_, user0_.name as name0_0_, user0_.age as age0_0_ from hibernate1.user user0_ where user0_.uid=? Hibernate: update hibernate1.user set name=?, age=? where uid=?代码如下: withdraw.setWithdrawStatus(WITHDRAW_STATUS_WAIT_PAY); withdraw.setApplyTime(currentTime); withdraw.setExchangeHasThisMember(hasThisMember ? YES : NO); withdraw = withdrawDao.save(withdraw); withdraw.setOrderNo(“WD” + DateUtil.ISO_DATETIME_FORMAT_NONE.format(currentTime) + withdraw.getId()); withdrawDao.save(withdraw);不管哪种情况,merge的返回值都是一个持久化的实例对象,但对于参数而言不会改变它的状态。 ...

January 23, 2019 · 4 min · jiezi

idea 护眼主题配置

idea 护眼主题配置1.效果展示2.护眼色主题配置1. 将 Editor > Color Scheme > General > Text 下的条目所有的背景都颜色设置为自己想要的颜色2. 设置控制台的背背景背景颜色3. 设置其他常用文件的背景颜色注意: **标记1处是可以点击的,点击往后设置其颜色属性就可以动态的在下方展示区域显示效果4. 设置 HTML, JSP, Java, CSS, JavaScript 下的所有条目背景颜色和上面的一致注意:1. 其实不用配置那么多,只是新手可能不知道各个条目的意思,所以这里就傻瓜是的设置所有的,当然入如果想换回来,只需要要设置主题为默认的三款之一的就可以了.2. 顺便说一下,这里的控制台的颜色调整需要用到 的 gerp Console 插件

January 21, 2019 · 1 min · jiezi

springboot+多线程简单实现

搭建springboot环境创建ThreadConfig/** * 线程 * * @author zhoumin * @create 2018-09-18 13:58 /@Configuration@EnableAsyncpublic class ThreadConfig implements AsyncConfigurer{ @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(8); executor.setMaxPoolSize(1000); executor.setQueueCapacity(500); executor.setKeepAliveSeconds(30000); executor.initialize(); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return null; }}创建service和接口void test(int i);service实现类@Override@Asyncpublic void test(int i) { System.out.println(“线程” + Thread.currentThread().getName() + " 执行异步任务:" + i);}测试:@RunWith(SpringRunner.class)@SpringBootTestpublic class BaseTest {}/* * @author zhoumin * @create 2018-09-18 14:12 */public class ThreadTest extends BaseTest{ @Autowired private DeviceStatisticsTaskService deviceStatisticsTaskService; @org.junit.Test public void threadTest() { for (int i = 0; i < 5000; i++) { deviceStatisticsTaskService.test(i); } }} ...

January 17, 2019 · 1 min · jiezi

Python数据分析:折线图和散点图的绘制

1.绘制折线图和散点图要用到matplotlib下的pylab,因此我们需要先引入,因为要用到数组实现,还要引入numpy。然后确定x轴和y轴的数据,最后将其呈现出来。import matplotlib.pylab as pylimport numpy as npx = [1, 3, 5, 6, 8, 13, 14, 16]y = [5, 1, 6, 7, 9, 3, 2, 10]pyl.plot(x, y)pyl.show()这样一个简单的折线图就绘制好了。上面的plot()有三个参数,第一个参数为x轴坐标,第二个参数为y轴坐标,第三个参数为确定线型,可有可无,如果要将上面的折线图改为散点图,只需更改第三个参数为‘o’。如果让折线图和散点图叠加还可以突出每个点。2.我们还可以改变线和点的颜色,只需修改plot()的第三个参数。c–cyan–青色r–red–红色m–magente–品红g–green–绿色b–blue–蓝色y–yellow–黄色k–black–黑色w–white–白色上述参数可以叠加。3.我们还可以改变线型,也是修改plot的第三个参数。- 实线– 虚线-. 形式即为-.: 细小的虚线 4.我们还可以改变点型,同样是修改第三个参数。s–方形h–六角形H–六角形*–*形+–加号x–x形d–菱形D–菱形p–五角形5.我们目前绘制的图形,无图像名称及横纵坐标轴的名称,我们需要在程序中添加如下语句pyl.title()pyl.xlabel()pyl.ylabel()6.现在绘图的x,y轴的范围是系统自动生成的,我们要想自定义,需要加上下面的两条语句,括号内为取值范围pyl.xlim()pyl.ylim()7.如果要在同一幅图中绘制多个图像,只需在show()之前再定义另外两个变量即可

January 13, 2019 · 1 min · jiezi

为什么选择 Intellij IDEA 作为日常开发工具

作为一个从事 Java 开发的程序员,每天离不开编辑器的帮助。还记得刚开始学习 Java 编程的时候,使用 Eclipse 作为日常开发工具。后来工作以后,需要使用 Intellij IDEA,刚开始其实并不想怎么用。毕竟 Eclipse 已经足够强大,可以满足日常开发的需求,何必再花时间再去学习其他工具那。刚开始改变是困难的。但是没办法,公司强制使用,不得不去了解去使用。后来用了一段时间才发现 IDEA 是的真的强大。真香啊下面就来介绍一下本人觉得 IDEA 一些强大的功能。文中提到的快捷键只适用于 Windows 平台更加智能的协助开发我们使用编辑器的目的就是在于简化开发难度,加快开发速度。IDEA 就有许多功能,可以更加智能的、更加快速的帮你完成代码开发。代码提示下面先介绍最基本的代码提示功能。一般编辑器都会提供基本提示功能,可以快速提供可用的方法,变量等。当然 IDEA 也存在这个,在 IDEA 中使用 Ctrl + Space 可以快速提示。PS:对于 Windows 平台用户,这个快捷键十分不友好,与输入法切换快捷键冲突,可以使用如下方法解决。1、 打开注册表,跳转到HKEY_CURRENT_USER/Control Panel/Input Method/Hot Keys目录下面 2.、选择00000070(中文繁体)或者00000010(中文简体) 3.、将Key Modifiers的第一个字节设置为00(02c00000->00c00000) 4、 将Virtual Key的第一个字节设置为ff(20000000->ff000000) 5、 注销用户然后重新登录,搞定。 另外 HKEY_CURRENT_USER/Control Panel/Input Method/Hot Keys,保存的是当前用户的快捷键配置;HKEY_USERS.DEFAULTControl PanelInput MethodHot Keys,保存的是默认的快捷键配置;若修改上一个注册表不好使,那就把下面的默认的也修改了。 经测试,修改第一个,重启之后不再生效,所以默认配置也需要修改。除了最基本的代码提示功能,IDEA 还提供更加智能的代码提示功能,该功能可以基于上下文环境,智能帮你过滤可以使用方法,推导出最适合的方法。该快捷键为 Ctrl+Shift+Space。我们用下面两张图比对两者的区别。基本提示功能:智能代码提示:观察上面两图可以看出,基本代码提示功能会显示所有可用的方法建议,而智能代码提示根据上下文过滤了其他不可用的提示。参数提示当一个方法参数列表过多时,我们往往只会记住前两个参数类型,而后面参数类型我们只能去翻阅方法才。在 IDEA 中,你无需这般做。只要你将光标放置在放入参数列表中,暂停一会,IDEA 就会帮你智能提示。如果并不想等待一会,也可以,在方法内使用 ctrl +P 也可以快速出现提示框。快速完成语句在 IDEA 中,可以使用快捷键 Ctrl+Shift+Enter 快速完成声明 if while 等语句。在下面的例子中,我们输入 while ,接着我们输入快捷键,我们可以看到 IDEA 自动帮我们完整这个结构,然后只需要输入判断条件即可。此外,我们还可以用该快捷键完成下面的操作。Postfix Code这个模式可以在编写代码时减少向后插入符号跳转。我们可以在变量后面直接跟上 if 、for 等表达式,IDEA 会直接转换成相应的语句。我们还可以查看在设置中 Editor | General | Postfix Completion 查看更多用法。Live Template我们有时候会保存一些代码片段,然后在需要的时候直接粘贴。而 IDEA Live Template 就可以帮我们保存这个代码片段,且可以自定义关键字,需要的时候只需要输入关键字,就可以直接输出代码。而且 IDEA 也已经定义很多,我们可以直接上手使用。如上图,我们可以输入 psfs,然后输入回车键或者 Tab 键,直接生成 public static final String。输入 psvm,快速生成 main 方法。我们可以使用下面的步骤自定义自己的 Live Template。强大的搜索功能开发的时候我们会去查看类的源码,有时我们只知道类的名字,却不知道具体包的位置,这个时候IDEA 强大的搜索功能可以帮我们迅速的找到。我们可以按两下 shift,在弹出的窗口输入类名,就可以找到。这个功能不仅可以找类,也可以用于找文件等。还有的时候我们可能只记得类中的某个关键字,那上面的方法就无效。但是没关系,IDEA 还可以帮你用关键字去搜索找到我们只要输入 Ctrl+Alt+F 快捷键。版本控制功能在团队开发中,我们就需要使用到相关版本控制工具,比如 SVN、Git 等。IDEA 默认自带强大版本控制工具,可以快速浏览代码变更,仓库提交历史以及合并代码。我们以 Dubbo Git 项目为例。我们可以在 Version Control Log 处图形化查看仓库历史。协作开发的时候,很容易发生冲突,这个时候如果没有其他很好的工具,解决冲突是一件很麻烦的事,非常容易将代码合丢。不过使用 IDEA 强大的解决冲突的功能,可以帮我们解决这个问题。当提交代码时,若存在代码冲突时,IDEA 显示冲突的文件.点击文件,选择 Merge, 然后会显示窗口,我们可以浏览两边代码,自己灵活选择到底选择本地变更或者服务端的变更。这里说个小技巧,我们协作开发时,若有些人使用 TAB 作为缩进然后提交代码,而当你使用空格作为缩进,一旦将代码格式化,你提交代码的时候,这个时候冲突就会是个在灾难。如下所示.这样满屏充满干扰的变更的时候,很容易合错代码。。。。。 。。。。我们选择忽略空白行,IDEA 会把这种自动或略空白行,这样我们就可以针对自己变更合并即可。重构功能我们编码的时候有可能会写错单词,写错并不可怕,怕的是你到最后才发现。这个时候你发现许多地方都用到这个,这个时候你在一个个变更就真的很费劲了。不用怕,IDEA 重构功能就可以帮助到我们。IDEA 重命名功能可以快速帮修改所有引用这个变量的地方。重构功能还可以快速提取方法。其他插件IDEA 安装时就会集成很多官方插件,增加对其他技术,语言的支持。你如果不喜欢,可以根据自己的选择在 Setting/Plugins 自由选择启动或禁用。你还可以在官方的插件平台 https://plugins.jetbrains.com/找到一些第三方非常优秀的插件,实现其他扩展功能,如翻译。主题IDEA 自带两套非常漂亮的主题。一套为亮色的,另一套为暗色的。个人觉得暗色系列的主题,更加好看,且不刺眼。如果不喜欢自带的主题,可以自定义,或者下载主题插件,如 Material Theme UI。版本更新IDEA 迭代更新速度较快,基本每半年就会有一个大版本更新,以及时常会有一些小版本更新。每次更新以后都会一些新功能。IDEA 一些缺点上面说了这么多 IDEA 功能,也讲讲一些 IDEA 的缺点。IDEA 很多强大的功能都是基于其缓存与索引。当打开一个新项目的时候,IDEA 会自动建立索引。这个有时候对大型项目特别不友好,可能会出现卡顿现象。特别对于机械硬盘用户,这种现象会更加明显。因此强烈建议 IDEA 创建索引的时候不要动项目,等待创建完毕即可。还有一点就是 IDEA 中没有类似 Eclipse 中的 workspace 的概念,无法做到一个 IDEA 工程打开多个项目。这是刚从 Eclipse 转过来同学困惑的地方。不过等你真正熟悉 IDEA,真的需要 workspace 吗?结束上面介绍 IDEA 这么多功能,没有在使用的读者们,不妨下载使用看看。刚开始从其他编辑器转过来确实很难,但是一旦你喜欢上 IDEA,你就不会释手了。IDEA 还有其他很多功能,一篇文章不能全部都说到,各位读者可以自行去探索。博主每次研究 IDEA 的功能时,都能发现一些以前不知道的技巧。如果觉得好的话,请帮作者点个赞呗 谢谢喜欢本文的读者们,欢迎长按关注订阅号程序通事~让我与你分享程序那些事。 ...

January 13, 2019 · 1 min · jiezi

spring security使用详解

spring security使用详解由于公司新的后台项目独立,和其他服务没有关系,所以考虑单独实现自己的用户登录模块。 所以,我将对spring security基于 “springboot” 框架的使用方式总结如下: (1)当我们在pom文件中,添加依赖后 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> 启动服务,访问 http://localhost:8080这个地址,端口可以自己设置通过server.port=进行配置,下面弹出如下页面: 此时,系统已经处于受保护状态,这是spring security默认的登录页面。系统生成默认用户名为 “user”,密码打印到控制台总,如下: 我们输入用户名和密码便可以登录进去,我们也可以application.properties文件中自定义用户名和密码如下: 到此,最简单的使用方式已经完成,但是这还远远不能满足我们的需要,截下来介绍自定义表单登录。(2)spring security 表单登录 具体流程如下图: 本文参考地址:https://blog.csdn.net/mj86534…

January 11, 2019 · 1 min · jiezi

Intellij IDEA 初体验

idea 入门教程1.开启 Ctrl + / 按键联想功能,默认的 Ctrl + Space与输入法冲突,改为eclpse联想快捷键,移除占用的Ctrl + /Ctrl + Alt + S 进入设置 –> Keymap –> Main maue –> Completion –>basic2.进入全屏自由编辑模式3.常用快捷键Alt + 1 打开/关闭 目录结构Ctrl + Shift + F10 运行项目Alt + F12 开启/关闭 终端syso –> sout(System.out.println();)main –> psvm(main函数)4.注册码K71U8DBPNE-eyJsaWNlbnNlSWQiOiJLNzFVOERCUE5FIiwibGljZW5zZWVOYW1lIjoibGFuIHl1IiwiYXNzaWduZWVOYW1lIjoiIiwiYXNzaWduZWVFbWFpbCI6IiIsImxpY2Vuc2VSZXN0cmljdGlvbiI6IkZvciBlZHVjYXRpb25hbCB1c2Ugb25seSIsImNoZWNrQ29uY3VycmVudFVzZSI6ZmFsc2UsInByb2R1Y3RzIjpbeyJjb2RlIjoiSUkiLCJwYWlkVXBUbyI6IjIwMTktMDUtMDQifSx7ImNvZGUiOiJSUzAiLCJwYWlkVXBUbyI6IjIwMTktMDUtMDQifSx7ImNvZGUiOiJXUyIsInBhaWRVcFRvIjoiMjAxOS0wNS0wNCJ9LHsiY29kZSI6IlJEIiwicGFpZFVwVG8iOiIyMDE5LTA1LTA0In0seyJjb2RlIjoiUkMiLCJwYWlkVXBUbyI6IjIwMTktMDUtMDQifSx7ImNvZGUiOiJEQyIsInBhaWRVcFRvIjoiMjAxOS0wNS0wNCJ9LHsiY29kZSI6IkRCIiwicGFpZFVwVG8iOiIyMDE5LTA1LTA0In0seyJjb2RlIjoiUk0iLCJwYWlkVXBUbyI6IjIwMTktMDUtMDQifSx7ImNvZGUiOiJETSIsInBhaWRVcFRvIjoiMjAxOS0wNS0wNCJ9LHsiY29kZSI6IkFDIiwicGFpZFVwVG8iOiIyMDE5LTA1LTA0In0seyJjb2RlIjoiRFBOIiwicGFpZFVwVG8iOiIyMDE5LTA1LTA0In0seyJjb2RlIjoiR08iLCJwYWlkVXBUbyI6IjIwMTktMDUtMDQifSx7ImNvZGUiOiJQUyIsInBhaWRVcFRvIjoiMjAxOS0wNS0wNCJ9LHsiY29kZSI6IkNMIiwicGFpZFVwVG8iOiIyMDE5LTA1LTA0In0seyJjb2RlIjoiUEMiLCJwYWlkVXBUbyI6IjIwMTktMDUtMDQifSx7ImNvZGUiOiJSU1UiLCJwYWlkVXBUbyI6IjIwMTktMDUtMDQifV0sImhhc2giOiI4OTA4Mjg5LzAiLCJncmFjZVBlcmlvZERheXMiOjAsImF1dG9Qcm9sb25nYXRlZCI6ZmFsc2UsImlzQXV0b1Byb2xvbmdhdGVkIjpmYWxzZX0=-Owt3/+LdCpedvF0eQ8635yYt0+ZLtCfIHOKzSrx5hBtbKGYRPFDrdgQAK6lJjexl2emLBcUq729K1+ukY9Js0nx1NH09l9Rw4c7k9wUksLl6RWx7Hcdcma1AHolfSp79NynSMZzQQLFohNyjD+dXfXM5GYd2OTHya0zYjTNMmAJuuRsapJMP9F1z7UTpMpLMxS/JaCWdyX6qIs+funJdPF7bjzYAQBvtbz+6SANBgN36gG1B2xHhccTn6WE8vagwwSNuM70egpahcTktoHxI7uS1JGN9gKAr6nbp+8DbFz3a2wd+XoF3nSJb/d2f/6zJR8yJF8AOyb30kwg3zf5cWw==-MIIEPjCCAiagAwIBAgIBBTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTE1MTEwMjA4MjE0OFoXDTE4MTEwMTA4MjE0OFowETEPMA0GA1UEAwwGcHJvZDN5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxcQkq+zdxlR2mmRYBPzGbUNdMN6OaXiXzxIWtMEkrJMO/5oUfQJbLLuMSMK0QHFmaI37WShyxZcfRCidwXjot4zmNBKnlyHodDij/78TmVqFl8nOeD5+07B8VEaIu7c3E1N+e1doC6wht4I4+IEmtsPAdoaj5WCQVQbrI8KeT8M9VcBIWX7fD0fhexfg3ZRt0xqwMcXGNp3DdJHiO0rCdU+Itv7EmtnSVq9jBG1usMSFvMowR25mju2JcPFp1+I4ZI+FqgR8gyG8oiNDyNEoAbsR3lOpI7grUYSvkB/xVy/VoklPCK2h0f0GJxFjnye8NT1PAywoyl7RmiAVRE/EKwIDAQABo4GZMIGWMAkGA1UdEwQCMAAwHQYDVR0OBBYEFGEpG9oZGcfLMGNBkY7SgHiMGgTcMEgGA1UdIwRBMD+AFKOetkhnQhI2Qb1t4Lm0oFKLl/GzoRykGjAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBggkA0myxg7KDeeEwEwYDVR0lBAwwCgYIKwYBBQUHAwEwCwYDVR0PBAQDAgWgMA0GCSqGSIb3DQEBCwUAA4ICAQC9WZuYgQedSuOc5TOUSrRigMw4/+wuC5EtZBfvdl4HT/8vzMW/oUlIP4YCvA0XKyBaCJ2iX+ZCDKoPfiYXiaSiH+HxAPV6J79vvouxKrWg2XV6ShFtPLP+0gPdGq3x9R3+kJbmAm8w+FOdlWqAfJrLvpzMGNeDU14YGXiZ9bVzmIQbwrBA+c/F4tlK/DV07dsNExihqFoibnqDiVNTGombaU2dDup2gwKdL81ua8EIcGNExHe82kjF4zwfadHk3bQVvbfdAwxcDy4xBjs3L4raPLU3yenSzr/OEur1+jfOxnQSmEcMXKXgrAQ9U55gwjcOFKrgOxEdek/Sk1VfOjvS+nuM4eyEruFMfaZHzoQiuw4IqgGc45ohFH0UUyjYcuFxxDSU9lMCv8qdHKm+wnPRb0l9l5vXsCBDuhAGYD6ss+Ga+aDY6f/qXZuUCEUOH3QUNbbCUlviSz6+GiRnt1kA9N2Qachl+2yBfaqUqr8h7Z2gsx5LcIf5kYNsqJ0GavXTVyWh7PYiKX4bs354ZQLUwwa/cG++2+wNWP+HtBhVxMRNTdVhSm38AknZlD+PTAsWGu9GyLmhti2EnVwGybSD2Dxmhxk3IPCkhKAK+pl0eWYGZWG3tJ9mZ7SowcXLWDFAk0lRJnKGFMTggrWjV8GYpw5bq23VmIqqDLgkNzuoog==5.host修改0.0.0.0 account.jetbrains.com6.重新启动7.干掉安全连接,排除插件中心连接网络超时的问题8.安装svn插件 SlikSVN(https://sliksvn.com/download/...9.配置svn插件10.连接svn检出项目[注意] 我这里已经配置了用户的连接信息, 没有配置的需要自行配置,要确保账户已经存在,url和用户名密码正确

January 4, 2019 · 1 min · jiezi

Tomcat日志乱码问题

昨天本来准备更新一下Tomcat版本,但是发现新版本的日志打印中文会出现乱码(Tomcat自身打印的日志),不管是使用bat脚本启动还是在Idea中启动,都是乱码。研究了一个晚上,百度上的那些方式都试遍了,都是设置各种JVM启动参数,发现并没有卵用。在使用bat文件启动Tomcat时,Tomcat目录下的logs文件夹会生成相应的日志文件,发现旧版本生成的日志文件编码是GBK,而Windows控制台的编码也是GBK,所以不会乱码。而新版本生成的日志文件编码是UTF-8,所以就造成了中文乱码问题定位到问题以后,就去看Tomcat的日志配置文件,tomcat/conf/logging.properties这个文件就是tomcat的日志配置文件,通过使用BCompare对新老版本的配置文件进行对比,发现tomcat在新版的日志配置文件中加了指定编码为UTF-8的配置。这就是乱码的根源了。解决方法:将配置UTF-8那一行配置删除(这样应该就是采用操作系统默认编码,Windows下即为GBK)将UTF-8改为GBK若文章有任何问题,欢迎留言指出——作者博客:桔子笔记

January 3, 2019 · 1 min · jiezi

Spring Bean注入/单例理解/循环依赖

理解循环依赖问题,首先明白spring有四种注入方式。第一种,SET注入a类中持有b类的引用,并且a类有b的set方法。在bean中添加<property>标签即可注入。实质上是将b实例化,然后调用set方法注入。 <bean id=“a” class=“com.qunar.pojo.StudentA” scope=“singleton”> <property name=“studentB” ref=“b”></property> </bean>第二种,构造器注入a类中持有b类的引用,并且a的构造函数参数中有b。实质上就是通过构造函数注入,创建a对象时要把b对象传进去。 <bean id=“a” class=“com.qunar.pojo.StudentA”> <constructor-arg index=“0” ref=“b”></constructor-arg> </bean>第三种,静态工厂如果有需要静态工厂实例化的类,不能通过静态工厂.方法实现。在bean属性中对应类指向静态工厂,对应方法指向返回实例的方法图片描述第四种,实例工厂如果工厂不是静态,需要实例化,就实例化对应工厂,设定factory-bean和factory-method进行方法调用。图片描述设定三个实体类,StudentA,B,C代码如下,A持有B,B持有C,C持有Apublic class StudentA { private StudentB studentB ; public void setStudentB(StudentB studentB) { this.studentB = studentB; } public StudentA() { } public StudentA(StudentB studentB) { this.studentB = studentB; }}当我通过构造器注入时,会产生BeanCurrentlyInCreationException异常。为什么会出现这种异常,spring如何加载实体?图片描述Spring容器会将每一个正在创建的Bean 标识符放在一个“当前创建Bean池”中,Bean标识符在创建过程中将一直保持在这个池中,因此如果在创建Bean过程中发现自己已经在“当前创建Bean池”里时将抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的Bean将从“当前创建Bean池”中清除掉。Spring容器先创建单例StudentA,StudentA依赖StudentB,然后将A放在“当前创建Bean池”中,此时创建StudentB,StudentB依赖StudentC ,然后将B放在“当前创建Bean池”中,此时创建StudentC,StudentC又依赖StudentA, 但是,此时Student已经在池中,所以会报错,因为在池中的Bean都是未初始化完的,所以会依赖错误.解决这个问题,可以用setter注入的方式。图片描述Spring是先将Bean对象实例化之后,再设置对象属性。所以会先调用他的无参构造函数实例化。每个对象存在一个map中。当遇到依赖,就去map中调用对应的单例对象。图片描述一部分源码另外: 对于“prototype”作用域Bean,Spring容器无法完成依赖注入,因为“prototype”作用域的Bean,Spring容器不进行缓存,因此无法提前暴露一个创建中的Bean。Spring装配Bean的过程实例化;设置属性值;如果实现了BeanNameAware接口,调用setBeanName设置Bean的ID或者Name;如果实现BeanFactoryAware接口,调用setBeanFactory 设置BeanFactory;如果实现ApplicationContextAware,调用setApplicationContext设置ApplicationContext调用BeanPostProcessor的预先初始化方法;调用InitializingBean的afterPropertiesSet()方法;调用定制init-method方法;调用BeanPostProcessor的后初始化方法;Spring容器关闭过程调用DisposableBean的destroy();调用定制的destroy-method方法;图片描述了解了bean默认是单例模式,不由想spring的单例和设计模式单例同一种吗?其实不一样。单例模式是指在一个JVM进程中仅有一个实例,而Spring单例是指一个Spring Bean容器(ApplicationContext)中仅有一个实例。如果有多个Spring容器,可能有多个Bean对象。spring单例是一种类似注册表实现的方式。利用hashmap,向map中注册和取值,思路类似下面代码public class Singleton { private static Map<String,Singleton> map = new HashMap<String,Singleton>(); static{ Singleton single = new Singleton(); map.put(single.getClass().getName(), single); } //保护的默认构造子 protected Singleton(){} //静态工厂方法,返还此类惟一的实例 public static Singleton getInstance(String name) { if(name == null) { name = Singleton.class.getName(); } if(map.get(name) == null) { try { map.put(name, (Singleton) Class.forName(name).newInstance()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } return map.get(name); }} ...

December 28, 2018 · 1 min · jiezi

使用Maven Helper解决Maven插件冲突

1、何为依赖冲突Maven是个很好用的依赖管理工具,但是再好的东西也不是完美的。Maven的依赖机制会导致Jar包的冲突。举个例子,现在你的项目中,使用了两个Jar包,分别是A和B。现在A需要依赖另一个Jar包C,B也需要依赖C。但是A依赖的C的版本是1.0,B依赖的C的版本是2.0。这时候,Maven会将这1.0的C和2.0的C都下载到你的项目中,这样你的项目中就存在了不同版本的C,这时Maven会依据依赖路径最短优先原则,来决定使用哪个版本的Jar包,而另一个无用的Jar包则未被使用,这就是所谓的依赖冲突。在大多数时候,依赖冲突可能并不会对系统造成什么异常,因为Maven始终选择了一个Jar包来使用。但是,不排除在某些特定条件下,会出现类似找不到类的异常,所以,只要存在依赖冲突,在我看来,最好还是解决掉,不要给系统留下隐患。2、解决方法解决依赖冲突的方法,就是使用Maven提供的<exclusion>标签,<exclusion>标签需要放在<exclusions>标签内部,就像下面这样:<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.10.0</version> <exclusions> <exclusion> <artifactId>log4j-api</artifactId> <groupId>org.apache.logging.log4j</groupId> </exclusion> </exclusions></dependency>log4j-core本身是依赖了log4j-api的,但是因为一些其他的模块也依赖了log4j-api,并且两个log4j-api版本不同,所以我们使用<exclusion>标签排除掉log4j-core所依赖的log4j-api,这样Maven就不会下载log4j-core所依赖的log4j-api了,也就保证了我们的项目中只有一个版本的log4j-api。3、Maven Helper看到这里,你可能会有一个疑问。如何才能知道自己的项目中哪些依赖的Jar包冲突了呢?Maven Helper这个InteliJ IDEA的插件帮我们解决了这个问题。插件的安装方法我就不讲了,既然你都会Maven了,我相信你也是会安装插件的。在插件安装好之后,我们打开pom.xml文件,在底部会多出一个Dependency Analyzer选项点开这个选项找到冲突,点击右键,然后选择Exclude即可排除冲突版本的Jar包。4、小技巧除了使用Maven Helper查看依赖冲突,也可以使用IDEA提供的方法——Maven依赖结构图,打开Maven窗口,选择Dependencies,然后点击那个图标(Show Dependencies)或者使用快捷键(Ctrl+Alt+Shift+U),即可打开Maven依赖关系结构图在图中,我们可以看到有一些红色的实线,这些红色实线就是依赖冲突,蓝色实线则是正常的依赖。若文章有任何问题,欢迎留言指出——作者博客:桔子笔记

December 28, 2018 · 1 min · jiezi

一对一直播系统搭建方法展示:Java 实现阿里云直播

准备步骤创建 阿里云账号根据 流程 完成实名认证,以确保可以使用阿里云相应服务在密钥管理页面获取阿里云访问密钥,AccessKeyId 和 AccessKeySecret开通阿里云直播服务关键点阿里云直播服务端提供了 一系列 API ,但如果只是单纯的直播[推流和拉流] ,实际不需要使用这些 API推流准备推流即直播人员进行视频播放的操作,这需要使用推流客户端 第三方推流工具 OBS在推流工具中需要指定推流地址、流名称、鉴权密钥如果上述信息阿里云验证合法,既可以开始直播,在阿里云后端可以看到正在直播的流信息拉流准备拉流即直播观众通过视频播放器在线获取直播信息,播放器使用 阿里云播放器 即可,该播放器目前只是阿里云的点播和直播服务获取拉流地址后传入播放器,即可开始观看直播Java 开发注意点在阿里云直播的文档中有提供 Java SDK目前 SDK 中推荐引入的版本号是 2.3.0 ,但其实所有 API 参照的都是最新版 SDK ,最新的版本号可在 阿里云SDK频道 找到但如果只是单纯的直播[推流和拉流] ,则不需要进行以上操作推流的关键点在于 直播鉴权此处介绍的直播鉴权只是说的 auth_key 的拼接和验证规则完整的推流和拉流地址并不知这些,需要依旧案例参考获取推流地址此处获取的只是推流地址的房间号及其他请求参数完整的推流地址需要加上阿里云直播中心地址和用户的产品名称直播中心地址 http://video-center.alivecdn.com产品名称[支持自定义] /appName/vhost 用于接收拉流地址,即申请阿里云直播时准备的直播域名此处使用 Java MD5加密 实现字串加密,加密后长度需要是 32 位加密串中的 Constants.ALI_LIVE_PRIVATE_KEY 可在阿里云后端的直播鉴权处获取1// 获取推流地址public String getPushUri(String roomName, Long endTime) {return getRoomName(roomName) + “vhost=” + Constants.ALI_LIVE_PULL_URL + “&” + generateAuthKey(roomName, endTime);}// 房间号private String getRoomName(String roomName) {return roomName + “?”;}// 完整验签串private String generateAuthKey(String roomName, Long endTime) {return “auth_key=” + endTime + generateUuid() + generateEncryptStr(roomName, endTime);}// 唯一标识private String generateUuid() {String uuid = “0”;String uid = “0”;return “-” + uuid + “-” + uid + “-”;}// 验签密钥private String generateEncryptStr(String roomName, Long endTime) {String uri = Constants.ALI_LIVE_APP_NAME + roomName;return md5(uri + “-” + endTime + generateUuid() + Constants.ALI_LIVE_PRIVATE_KEY);}获取拉流地址此处获取的拉流地址是完整的,因为拉流地址是直接获取后传入前端的阿里云播放器中注意房间名后面加的后缀 .m3u8 用于表示接受的直播视频类型,阿里云官方还提供其他几种类型,可在文档中查看拉流地址和推流地址最大的区别在于请求地址的不同,拉流是请求自己提供给阿里云的直播域名,而拉流是请求阿里云的直播中心而且推流时需要指定 vhost 告知阿里云直播域名,但拉流时不需要获取到拉流地址后可直接参照 Java + jQuery 实现阿里云播放器接口 实现播放器的对接在播放器的的配置中指明 isLive: true 表名是直播操作上述笔记中实现的是点播接口,利用的通过 vid 获取 playAuth 的方式,这不适用于直播直播需要直接指定 source: url 即可public String getPullUrl(String roomName, Long endTime) {roomName += “.m3u8”;return “http://” + Constants.ALI_LIVE_PULL_URL + generateUri(roomName) + generateAuthKey(roomName, endTime);}// 获取请求参数private String generateUri(String roomName) {return Constants.ALI_LIVE_APP_NAME + getRoomName(roomName);}// 房间号private String getRoomName(String roomName) {return roomName + “?”;}// 完整验签串private String generateAuthKey(String roomName, Long endTime) {return “auth_key=” + endTime + generateUuid() + generateEncryptStr(roomName, endTime);}// 唯一标识private String generateUuid() {String uuid = “0”;String uid = “0”;return “-” + uuid + “-” + uid + “-”;}// 验签密钥private String generateEncryptStr(String roomName, Long endTime) {String uri = Constants.ALI_LIVE_APP_NAME + roomName;return md5(uri + “-” + endTime + generateUuid() + Constants.ALI_LIVE_PRIVATE_KEY); ...

December 25, 2018 · 2 min · jiezi

在maven离线模式使用intellij idea的配置

今天在内网使用intellij idea,把maven仓库也拷贝了过来,但是使用maven install 命令的时候,却还是从私服里面下载。(内网无法连接到任何私服)导致install的时候报缺少依赖的错误。经过各种搜索,终于发现了解决办法。 ...

June 11, 2018 · 1 min · jiezi