首页 文章 精选 留言 我的

精选列表

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

spider-flow 0.3.2 发布,Java 开源爬虫平台

spider-flow 是一个无需写代码的爬虫平台,通过定义流程的方式制定爬虫 更新日志 修复前端日志不输出的BUG 修复前端日志滚动条不显示的问题 修复输出全部参数的未选中也输出的BUG 修复Cookie批量设置的BUG 修复任务未执行,查询日志失败的问题 优化插件描述 优化任务日志拆分文件 优化重启服务后,重置任务下一次执行时间 优化测试窗口可最大最小化 selenium插件 新增loadCookies方法,获取最新Cookies 新增默认resp变量可以改名(防止冲突) 调整默认不选无头模式,默认启用JS

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

spider-flow 0.3.1 发布,Java 开源爬虫平台

spider-flow 是一个无需写代码的爬虫平台,通过定义流程的方式制定爬虫 更新日志 新增Header、Cookies批量设置 新增变量、参数、输出、函数、header、cookie拖动调整执行顺序 新增插件功能描述及功能区显示 新增map.toList、list.sort、object.sleep函数 修复listVar.join中list.length为1时返回异常 修复下次执行时间计算不正确的问题 修复右键粘贴时保存不成功的BUG 修复Cookie值不能使用表达式的BUG 优化日志,修改其实现方式 优化xpath解析方式,修复原解析xpath异常错误 优化favicon替换掉默认的图标 Selenium插件重构 支持Chrome、Firefox驱动 支持Cookie自动管理 支持驱动参数配置 支持模拟鼠标移动,点击,双击,释放等事件 支持HTTP代理

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

Java几种常用的断言风格你怎么选?

日常工作中,不管你是写Unit Test,还是采用TDD的编程方式进行开发,都会遇到断言。而断言的风格常见的会有Assert、BDD风格,对于这些常见的断言风格你怎么选择呢? 01 Assert风格 JUnit中提供了这样的assert断言风格,例如: [@Test](https://my.oschina.net/azibug) void should_be_unlocked_when_insert_coin_given_a_entrance_machine_with_locked_state() { EntranceMachine entranceMachine = new EntranceMachine(EntranceMachineState.LOCKED); String result = entranceMachine.execute(Action.INSERT_COIN); assertEquals("opened", result); assertEquals(EntranceMachineState, entranceMachineState.UNLOCKED); } Hamcrest和AssertJ都提供了assertThat()这样风格的断言,例如: AssertJ提供的assertThat()的断言语法 [@Test](https://my.oschina.net/azibug) void should_be_unlocked_when_insert_coin_given_a_entrance_machine_with_locked_state() { EntranceMachine entranceMachine = new EntranceMachine(EntranceMachineState.LOCKED); String result = entranceMachine.execute(Action.INSERT_COIN); assertThat(result).isEqualsTo("opened"); assertThat(EntranceMachineState).isEqualsTo(entranceMachineState.UNLOCKED); } Hamcrest提供的assertThat()断言语法 [@Test](https://my.oschina.net/azibug) void should_be_unlocked_when_insert_coin_given_a_entrance_machine_with_locked_state() { EntranceMachine entranceMachine = new EntranceMachine(EntranceMachineState.LOCKED); String result = entranceMachine.execute(Action.INSERT_COIN); assertThat(result, is("opened")); assertThat(EntranceMachineState, is(entranceMachineState.UNLOCKED)); } 对比上面三种断言语法,因为场景简单,所以结果差异并不是很大。对于我个人更加偏向于使用AssertJ提供的断言风格。因为这种风格避免JUnit提供的断言中经常遇到的问题,expected在前还是actural在前的问题。相比于Hamcrest的断言风格,在日常工作中综合对比发现AssertJ的更加清晰,毕竟AssertJ中assertThat只需要接收一个参数,而不用关注括号是否对齐的问题。 日常工作中如果使用TDD,且场景适当(例如上面例子),那么Hamcreate和AssertJ的差别不是很大。JUnit5默认提供了Hamcreate的断言,不需要额外的再引入其他依赖。 02 BDD风格 代码的可读性越来越收到开发者的重视。测试代码的可读性同样重要,为了让测试代码结构清晰,便于业务逻辑变动时能快读读懂测试的上下文,很多开发团队约定了BDD的风格来组织测试代码。其中包含两部分的约定:测试方法名的约定,测试代码段落的约定。 例如前面的例子: [@Test](https://my.oschina.net/azibug) void should_be_unlocked_when_insert_coin_given_a_entrance_machine_with_locked_state() { ... } 虽然方法名很长,但是通过方法名我们能够快速知道测试类中有哪些测试,通过方法名我们能够清晰的当前测试的上下文,在测什么,期望的结果什么。通过方法名而不是通过比方法名长很多的代码段来获取测试在测什么的信息,毕竟阅读代码时间和修改代码时间可能是10:1,甚至20:1。所以团队约定BDD的风格组织在后续修改代码时,是受益良多的。 当需要也带具体的测试代码的时候,团队发现按照BDD这种三段式的风格来组织代码受益良多。例如: [@Test](https://my.oschina.net/azibug) void should_be_unlocked_when_insert_coin_given_a_entrance_machine_with_locked_state() { EntranceMachine entranceMachine = new EntranceMachine(EntranceMachineState.LOCKED); String result = entranceMachine.execute(Action.INSERT_COIN); assertThat(result).isEqualsTo("opened"); assertThat(EntranceMachineState).isEqualsTo(entranceMachineState.UNLOCKED); } 我们可以清晰的知道哪行代码在描述上下文,哪几行代码在描述测试意图,哪几行代码在描述测试结果验证。 BDD的风格能够帮助团队将测试代码维护的较为清晰。AssertJ提供了BDD风格的断言方式。使用then()语法。例如: @Test void should_be_unlocked_when_insert_coin_given_a_entrance_machine_with_locked_state() { EntranceMachine entranceMachine = new EntranceMachine(EntranceMachineState.LOCKED); String result = entranceMachine.execute(Action.INSERT_COIN); then(result).isEqualsTo("opened"); then(EntranceMachineState).isEqualsTo(entranceMachineState.UNLOCKED); } 断言变化不大。但是真正仔细读的时候,会发现使用then()还是简单那么一点点的。 我们常用的Mock工具Mockito,也提供了BDD风格的断言:then(), should(), and()。 import static org.mockito.BDDMockito.then; import static org.assertj.core.api.BDDAssertions.and; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @SuppressWarnings("static-access") @Test public void bdd_assertions_with_bdd_mockito() { Person person = mock(Person.class) person.ride(bike); person.ride(bike); then(person).should(times(2)).ride(bike); and.then(person.hasBike()).isTrue(); } 所以日常开发中,我会首先选择then(),其次会选择assertThat()。 除了以上两种断言风格,流式断言让代码更清晰,断言重复内容更少 当我们需要为某个结果测试多个测试点时,如果为每个测试点都组织一次相同的上下文,那么重复代码太多。带来的价值就是那么一点点区别,所以在测试力度上我们可以根据经验来在开发工程中动态调整。 下面据一个例子,当我们需要验证有一个查询方法返回的List的结果时,不单单要验证List中元素的数量,还要验证元素是否时期望的顺序。那么流式写法会缩减一部分重复的断言代码。 then(users).hasSize(3) .containsExactlyInAnyOrder( firstUser, secondUser, thirdUser); 上面是日常工作中经常使用到的断言技巧,你的怎么选择的呢?那种风格无所谓能工作就行? 参考 Hamcrest AssertJ

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

SOFARPC 发布 v5.6.3 版本,Java RPC 框架

本期发布内容 虽然是个小版本升级,但是干货不少,详情请见https://github.com/sofastack/sofa-rpc-boot-projects/releases 鼓励目前正在使用 5.6.x 版本的用户升级 New Features (新功能) PR 788- support msgpack serialization 支持msgpack序列化 PR 770- H2 TLS security support H2 TLS安全协议支持 PR 716- Support GRPC 支持GRPC Enhancement (新特性) PR 812- feat:usually we do not open the switch of sec or blacklist PR 811- chore:print extension loader debug info instead of error PR 807- support non-registered center ip for direct connection PR 800- feat:add custom parameter to values PR 799- feat:add some headers to sofa request PR 792- chore:remove fetch group name PR 789- update netty 4.1.42 PR 712- Enable baggage-items for rpc reseteasy mode Bug Fix PR 808- fix:fix simple map serializer (805) PR 795- bugfix:fix npe of AbstractCluster PR 794- chore:fix alert for jackson-databind PR 783- Fix data util test when run in a Daylight Saving Time

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

spider-flow 0.3.0 发布,Java 开源爬虫平台

spider-flow 是一个无需写代码的爬虫平台,通过定义流程的方式制定爬虫 更新日志 新增Cookie自动管理功能 新增resp.links()、resp.images()、resp.title函数 新增url批量下载,不用先爬取再下载 新增string.lastIndexOf、thread.sleep()、list.filterStr函数 新增多函数执行方法 新增任务状态、任务执行记录 修复爬虫名称有空格时,tab页打不开的BUG 修复爬虫名称在编辑时未能获取到名称的BUG 优化对返回结果resp.json、resp.html进行缓存 优化请求延迟时间对第一次执行也生效

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

Java描述设计模式(24):备忘录模式

本文源码:GitHub·点这里 || GitEE·点这里 一、生活场景 1、场景描述 常见的视频播放软件都具备这样一个功能:假设在播放视频西游记,如果这时候切换播放视频红楼梦,当再次切回播放西游记时,视频会从上次切走的时间点继续播放。下面基于备忘录设计模式来描述该场景流程。 2、场景图解 3、代码实现 public class C01_InScene { public static void main(String[] args) { Record record = new Record() ; Player player = new Player() ; PlayData pd1 = new PlayData("西游记","19:19") ; PlayData pd2 = new PlayData("红楼梦","29:19") ; player.setPlayData(pd1); player.saveProgress() ; System.out.println("正在播放:"+ player.getPlayData().getVideoName()+":"+ player.getPlayData().getPlayTime()); record.put(new Progress(pd1)); System.out.println("===切换播放视频==="); player.setPlayData(pd2); player.saveProgress() ; System.out.println("正在播放:"+ player.getPlayData().getVideoName()+":"+ player.getPlayData().getPlayTime()); record.put(new Progress(pd1)); System.out.println("===切回上个视频==="); player.resumeProgress(record.get(pd1.getVideoName())); System.out.println("正在播放:"+ player.getPlayData().getVideoName()+":"+ player.getPlayData().getPlayTime()); } } /** * 视频播放器 */ class Player { private PlayData playData ; public PlayData getPlayData() { return playData; } public void setPlayData(PlayData playData) { this.playData = playData; } public Progress saveProgress (){ return new Progress(playData) ; } public void resumeProgress (Progress progress){ playData = progress.getPlayData() ; } } /** * 播放进度 */ class Progress { private PlayData playData ; public Progress (PlayData playData){ this.playData = playData ; } public PlayData getPlayData() { return playData ; } } /** * 播放记录 */ class Record { private Map<String,Progress> dataMap = new HashMap<>() ; public void put (Progress progress){ dataMap.put(progress.getPlayData().getVideoName(),progress) ; } public Progress get (String videoName){ return dataMap.get(videoName) ; } } /** * 播放状态描述 */ class PlayData { private String videoName ; private String playTime ; public PlayData(String videoName, String playTime) { this.videoName = videoName; this.playTime = playTime; } public String getVideoName() { return videoName; } public void setVideoName(String videoName) { this.videoName = videoName; } public String getPlayTime() { return playTime; } public void setPlayTime(String playTime) { this.playTime = playTime; } } 执行效果: 正在播放:西游记:19:19 ===切换播放视频=== 正在播放:红楼梦:29:19 ===切回上个视频=== 正在播放:西游记:19:19 二、备忘录模式 1、基础概念 备忘录模式属于行为型模式,其用意在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。后续可将该对象恢复到原先保存的状态。备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作。 2、模式图解 3、核心角色 备忘录角色 负责保存对象状态的记录,即Originator内部状态。 发起人角色 创建一个含有当前的内部状态的备忘录对象,用来保存状态。 守护者对象 提供合理的方式,负责保存多个备忘录对象。 4、源码实现 public class C02_Memento { public static void main(String[] args) { Originator originator = new Originator(); Caretaker caretaker = new Caretaker(); originator.setState("状态1:State01"); caretaker.add(originator.saveStateMemento()); originator.setState("状态2:State02"); caretaker.add(originator.saveStateMemento()); System.out.println("当前的状态是 =" + originator.getState()); // 恢复状态 originator.getStateFromMemento(caretaker.get(0)); System.out.println("当前的状态是 =" + originator.getState()); } } /** * 守护者对象 */ class Caretaker { private List<Memento> mementoList = new ArrayList<>(); public void add(Memento memento) { mementoList.add(memento); } public Memento get (int index) { return mementoList.get(index); } } /** * 备忘录角色 */ class Memento { private String state; public Memento(String state) { super(); this.state = state; } public String getState() { return state; } } /** * 发起人角色 */ class Originator { private String state; public String getState() { return state; } public void setState(String state) { this.state = state; } public Memento saveStateMemento() { return new Memento(state); } public void getStateFromMemento(Memento memento) { state = memento.getState(); } } 三、模式总结 1、优缺点描述 备忘录模式提供一种可以恢复状态的机制,实现状态的封装,能够比较方便地回到某个历史的状态;常常与命令模式和迭代器模式一同使用。如果类的成员变量过多,会占用比较大的内存资源,为了节约内存,备忘录模式可以和原型模式配合使用。 2、应用场景 浏览器的前进和回退; 数据库备份与还原; 编辑器Ctrl+Z撤销; 虚拟机生成快照与恢复; Git版本管理,代码的提交和回滚。 四、源代码地址 GitHub·地址 https://github.com/cicadasmile/model-arithmetic-parent GitEE·地址 https://gitee.com/cicadasmile/model-arithmetic-parent

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

Java描述设计模式(23):访问者模式

本文源码:GitHub·点这里 || GitEE·点这里 一、生活场景 1、场景描述 电竞是游戏比赛达到“竞技”层面的体育项目。利用电子设备作为运动器械进行的、人与人之间的智力对抗运动。通过电竞,可以提高人的反应能力、协调能力、团队精神等。但是不同人群的对电竞的持有的观念不一样,有的人认为电竞就是沉迷网络,持反对态度,而有的人就比较赞同。下面基于访问者模式来描述该场景。 2、场景图解 3、代码实现 public class C01_InScene { public static void main(String[] args) { DataSet dataSet = new DataSet() ; dataSet.addCrowd(new Youth()); dataSet.addCrowd(new MiddleAge()); CrowdView crowdView = new Against() ; dataSet.display(crowdView); crowdView = new Approve() ; dataSet.display(crowdView); } } /** * 双分派,不同人群管理 */ abstract class Crowd { abstract void accept(CrowdView action); } class Youth extends Crowd { @Override public void accept(CrowdView view) { view.getYouthView(this); } } class MiddleAge extends Crowd { @Override public void accept(CrowdView view) { view.getMiddleAgeView (this); } } /** * 不同人群观念的管理 */ abstract class CrowdView { // 青年人观念 abstract void getYouthView (Youth youth); // 中年人观念 abstract void getMiddleAgeView (MiddleAge middleAge); } class Approve extends CrowdView { @Override public void getYouthView(Youth youth) { System.out.println("青年人赞同电竞"); } @Override public void getMiddleAgeView(MiddleAge middleAge) { System.out.println("中年人赞同电竞"); } } class Against extends CrowdView { @Override public void getYouthView(Youth youth) { System.out.println("青年人反对电竞"); } @Override public void getMiddleAgeView(MiddleAge middleAge) { System.out.println("中年人反对电竞"); } } /** * 提供一个数据集合 */ class DataSet { private List<Crowd> crowdList = new ArrayList<>(); public void addCrowd (Crowd crowd) { crowdList.add(crowd); } public void display(CrowdView crowdView) { for(Crowd crowd : crowdList) { crowd.accept(crowdView); } } } 二、访问者模式 1、基础概念 访问者模式是对象的行为模式,把作用于数据结构的各元素的操作封装,操作之间没有关联。可以在不改变数据结构的前提下定义作用于这些元素的不同的操作。主要将数据结构与数据操作分离,解决数据结构和操作耦合问题核心原理:被访问的类里面加对外提供接待访问者的接口。 2、模式图解 3、核心角色 抽象访问者角色 声明多个方法操作,具体访问者角色需要实现的接口。 具体访问者角色 实现抽象访问者所声明的接口,就是各个访问操作。 抽象节点角色 声明接受操作,接受访问者对象作为参数。 具体节点角色 实现抽象节点所规定的具体操作。 结构对象角色 能枚举结构中的所有元素,可以提供一个高层的接口,用来允许访问者对象访问每一个元素。 4、源码实现 public class C02_Visitor { public static void main(String[] args) { ObjectStructure obs = new ObjectStructure(); obs.add(new NodeA()); obs.add(new NodeB()); Visitor visitor = new VisitorA(); obs.doAccept(visitor); } } /** * 抽象访问者角色 */ interface Visitor { /** * NodeA的访问操作 */ void visit(NodeA node); /** * NodeB的访问操作 */ void visit(NodeB node); } /** * 具体访问者角色 */ class VisitorA implements Visitor { @Override public void visit(NodeA node) { node.operationA() ; } @Override public void visit(NodeB node) { node.operationB() ; } } class VisitorB implements Visitor { @Override public void visit(NodeA node) { node.operationA() ; } @Override public void visit(NodeB node) { node.operationB() ; } } /** * 抽象节点角色 */ abstract class Node { /** * 接收访问者 */ abstract void accept(Visitor visitor); } /** * 具体节点角色 */ class NodeA extends Node{ @Override public void accept(Visitor visitor) { visitor.visit(this); } public void operationA(){ System.out.println("NodeA.operationA"); } } class NodeB extends Node{ @Override public void accept(Visitor visitor) { visitor.visit(this); } public void operationB(){ System.out.println("NodeB.operationB"); } } /** * 结构对象角色类 */ class ObjectStructure { private List<Node> nodes = new ArrayList<>(); public void detach(Node node) { nodes.remove(node); } public void add(Node node){ nodes.add(node); } public void doAccept(Visitor visitor){ for(Node node : nodes) { node.accept(visitor); } } } 三、Spring框架应用 1、Bean结构的访问 BeanDefinitionVisitor类,遍历bean的各个属性;接口 BeanDefinition,定义Bean的各样信息,比如属性值、构造方法、参数等等。这里封装操作bean结构的相关方法,但却没有改变bean的结构。 2、核心代码块 public class BeanDefinitionVisitor { public void visitBeanDefinition(BeanDefinition beanDefinition) { this.visitParentName(beanDefinition); this.visitBeanClassName(beanDefinition); this.visitFactoryBeanName(beanDefinition); this.visitFactoryMethodName(beanDefinition); this.visitScope(beanDefinition); if (beanDefinition.hasPropertyValues()) { this.visitPropertyValues(beanDefinition.getPropertyValues()); } if (beanDefinition.hasConstructorArgumentValues()) { ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues(); this.visitIndexedArgumentValues(cas.getIndexedArgumentValues()); this.visitGenericArgumentValues(cas.getGenericArgumentValues()); } } } 四、模式总结 1、优点描述 (1) 访问者模式符合单一职责原则、使程序具有良好的扩展性、灵活性; (2) 访问者模式适用与拦截器与过滤器等常见功能,数据结构相对稳定的场景; 2、缺点描述 (1) 访问者关注其他类的内部细节,依赖性强,违反迪米特法则,这样导致具体元素更新麻烦; (2) 访问者依赖具体元素,不是抽象元素,面向细节编程,违背依赖倒转原则; 五、源代码地址 GitHub·地址 https://github.com/cicadasmile/model-arithmetic-parent GitEE·地址 https://gitee.com/cicadasmile/model-arithmetic-parent

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

spider-flow 0.2.1 发布,Java 开源爬虫平台

spider-flow 是一个无需写代码的爬虫平台,通过定义流程的方式制定爬虫 2019年度最受欢迎中国开源软件评选,投一票给 spider-flow 吧,谢谢! 更新日志 新增selectOne方法,执行后返回Map对象 新增selectInt方法,执行后返回int 修复表达式在xxx${expr}格式中,expr为null时整个表达式返回null的问题 修复延迟计算不准确的问题 优化表达式null.方法/属性,以及map[null]、array[null]直接返回null 优化日志显示方式,提升性能 优化全局变量支持表达式语法 优化SQL支持表达式语法 演示站点 文档地址

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册