热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

springstatemachine的企业可用级开发指南8复杂状态机的实现,choice,guard和action...

2019独角兽企业重金招聘Python工程师标准1、讲讲复杂流程的需求除了上面文章里面提到的一根筋状态机流程,实际的企业应用中状态机的流程会更加复杂࿰

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

1、讲讲复杂流程的需求    

除了上面文章里面提到的一根筋状态机流程,实际的企业应用中状态机的流程会更加复杂,而我们最常用到的就是choice。它类似于java的if语句,作为条件判断的分支而存在,让我们先看一张图:

    这张图表现的是一个表单(form)的整个状态流程:

  • 创建初始的空白表单( BLANK_FORM)
  • 填写(WRITE)表单,成为填充完表单(FULL_FORM)
  • 检查(CHEKC)表单
    • 如果是表单名(formName)不为null,表单成为待提交表单(CONFIRM_FROM)
    • 提交(SUBMIT)表单,成为成功提交表单(SUCCESS_FORM)
  • 检查(CHECK)表单
    • 如果表单名为null,表单成为待处理表单(DEAL_FORM),修改formName
    • 处理(DEAL)表单
      • 如果表单名还是null或者里面有个“坏”字,表单状态变成废单(FAILED_FORM)
      • 如果表单名称没问题,表单状态变成填充完表单状态(FULL_FORM),重新走流程

    大家不要在意这个例子的幼稚,毕竟它还是能用简单的方式体现出流程的复杂性(这话多么的辩证统一)。它有判断分支(还有两个),还有流程的循环,还有分支判断后的直接失败和分支判断后的后续环节,后面我们会在代码中告诉大家这里面需要注意的东西。

2、代码实现

    先上状态机的四件套:States,Events,Builder和EventConfig(spring statemachine还是蛮简单的,来来回回就这几样东西)

public enum ComplexFormStates {BLANK_FORM, // 空白表单FULL_FORM, // 填写完表单CHECK_CHOICE,//表单校验判断DEAL_CHOICE,//表单处理校验DEAL_FORM,//待处理表单CONFIRM_FORM, // 校验完表单SUCCESS_FORM,// 成功表单FAILED_FORM//失败表单
}

注意一点,choice判断分支本身也是一种状态,要声明出来,这里是CHECK_CHOICE和DEAL_CHOICE

public enum ComplexFormEvents {WRITE, // 填写CHECK,//校验DEAL,//处理SUBMIT // 提交
}

同样的分支事件也要声明,这里是CHECK和DEAL。

大头是MachineBuilder,这个代码就比较多,大家最好对照着上面这张图来看,会比较清晰

/*** 复杂订单状态机构建器*/
@Component
public class ComplexFormStateMachineBuilder {private final static String MACHINEID = "complexFormMachine";/*** 构建状态机* * @param beanFactory* @return* @throws Exception*/public StateMachine build(BeanFactory beanFactory) throws Exception {StateMachineBuilder.Builder builder = StateMachineBuilder.builder();System.out.println("构建复杂表单状态机");builder.configureConfiguration().withConfiguration().machineId(MACHINEID).beanFactory(beanFactory);builder.configureStates().withStates().initial(ComplexFormStates.BLANK_FORM).choice(ComplexFormStates.CHECK_CHOICE).choice(ComplexFormStates.DEAL_CHOICE).states(EnumSet.allOf(ComplexFormStates.class));builder.configureTransitions().withExternal().source(ComplexFormStates.BLANK_FORM).target(ComplexFormStates.FULL_FORM).event(ComplexFormEvents.WRITE).and().withExternal().source(ComplexFormStates.FULL_FORM).target(ComplexFormStates.CHECK_CHOICE).event(ComplexFormEvents.CHECK).and().withChoice().source(ComplexFormStates.CHECK_CHOICE).first(ComplexFormStates.CONFIRM_FORM, new ComplexFormCheckChoiceGuard()).last(ComplexFormStates.DEAL_FORM).and().withExternal().source(ComplexFormStates.CONFIRM_FORM).target(ComplexFormStates.SUCCESS_FORM).event(ComplexFormEvents.SUBMIT).and().withExternal().source(ComplexFormStates.DEAL_FORM).target(ComplexFormStates.DEAL_CHOICE).event(ComplexFormEvents.DEAL).and().withChoice().source(ComplexFormStates.DEAL_CHOICE).first(ComplexFormStates.FULL_FORM, new ComplexFormDealChoiceGuard()).last(ComplexFormStates.FAILED_FORM);return builder.build();}
}

这里面出现了几个新东西,要说一下:

  • 在configureStates时,要把每个分支都要写上去,我之前写了ComplexFormStates.CHECK_CHOICE,忘了写DEAL_CHOICE,后面的choice就不执行,搞得我找了很久,大家要注意(是的,我犯的就是这种简单弱智的错误,我还找了很久)
  • 在我们熟悉的withExternal之后,我们迎来了为choice专门准备的withChoice()和跟随它的first(),last()。这两个代表的就是分支判断时TRUE和FALSE的状态流程去处,这里面涉及到的一个问题就是,TRUE和FALSE的判断条件是什么呢,根据啥来判断呢?
  • Guard就承担了这个判断的功能,看名字似乎不像。它在spring statemachine本来是用来保护这个状态跳转过程的,所以用guard,但在choice里面,它就是作为判断代码而存在的,代码如下:

public class ComplexFormCheckChoiceGuard implements Guard {@Overridepublic boolean evaluate(StateContext context) {boolean returnValue = false;Form form = context.getMessage().getHeaders().get("form", Form.class);if (form.formName == null) {returnValue = false;} else {returnValue = true;}return returnValue;}}

它只implements一个方法evaluate(),返回boolean类型,所以很适合做这个判断的功能。另外一个DEAL_CHOICE的gurad代码是这样的

public class ComplexFormDealChoiceGuard implements Guard {@Overridepublic boolean evaluate(StateContext context) {System.out.println("ComplexFormDealChoiceGuard!!!!!!!!!!!!!");boolean returnValue = false;Form form = context.getMessage().getHeaders().get("form", Form.class);if ((form.formName == null)||(form.formName.indexOf("坏") > -1)) {returnValue = false;} else {returnValue = true;}System.out.println(form.toString()+" is "+returnValue);return returnValue;}}

还有四件套的最后一个EventConfig:

@WithStateMachine(id="complexFormMachine")
public class ComplexFormEventConfig {
private Logger logger = LoggerFactory.getLogger(getClass());/*** 当前状态BLANK_FORM*/@OnTransition(target = "BLANK_FORM")public void create() {logger.info("---空白复杂表单---");}@OnTransition(source = "BLANK_FORM", target = "FULL_FORM")public void write(Message message) {System.out.println("传递的参数:" + message.getHeaders().get("form"));logger.info("---填写完复杂表单---");}@OnTransition(source = "FULL_FORM", target = "CHECK_CHOICE")public void check(Message message) {System.out.println("传递的参数:" + message.getHeaders().get("form").toString());logger.info("---校验复杂表单---");}//不会执行@OnTransition(source = "CHECK_CHOICE", target = "CONFIRM_FORM")public void check2confirm(Message message) {System.out.println("传递的参数:" + message.getHeaders().get("form").toString());logger.info("---校验表单到待提交表单(choice true)---");}//不会执行@OnTransition(source = "CHECK_CHOICE", target = "DEAL_FORM")public void check2deal(Message message) {System.out.println("传递的参数:" + message.getHeaders().get("form").toString());logger.info("---校验表单到待提交表单(choice false)---");}@OnTransition(source = "DEAL_FORM", target = "DEAL_CHOICE")public void deal(Message message) {System.out.println("传递的参数:" + message.getHeaders().get("form").toString());logger.info("---处理复杂表单---");}//不会执行@OnTransition(source = "DEAL_CHOICE", target = "FAILED_FORM")public void deal2fail(Message message) {System.out.println("传递的参数:" + message.getHeaders().get("form").toString());logger.info("---处理复杂表单失败(choice false)---");}//不会执行@OnTransition(source = "DEAL_CHOICE", target = "FULL_FORM")public void deal2full(Message message) {System.out.println("传递的参数:" + message.getHeaders().get("form").toString());logger.info("---处理复杂表单到重新填写表单(choice true)---");}/*** CONFIRM_FORM->SUCCESS_FORM 执行的动作*/@OnTransition(source = "CONFIRM_FORM", target = "SUCCESS_FORM")public void submit(Message message) {System.out.println("传递的参数:" + message.getHeaders().get("form").toString());logger.info("---表单提交成功---");}}

这边的代码没有任何特别的地方,唯一需要需要注意的是我标注的不会执行的这几个方法。因为在choice判断的时候,从一个状态转变到另外一个状态时,是不会在eventConfig触发方法的,比如这个方法:

//不会执行@OnTransition(source = "CHECK_CHOICE", target = "CONFIRM_FORM")public void check2confirm(Message message) {System.out.println("传递的参数:" + message.getHeaders().get("form").toString());logger.info("---校验表单到待提交表单(choice true)---");}

我定义了如果用户从CHECK_CHOICE状态如果判断后变成CONFIRM_FORM,执行check2confirm方法,但可惜,状态的确变化了,但这个方法不会执行,只有在做判断的时候会执行ComplexFormCheckChoiceGuard的evaluate()。这就有个问题,在实际运行中,我们的确会需要在choice做出判断状态改变的时候要做些业务处理,比如表单check成功后需要通知后续人员来处理什么的,这该怎么办呢?

  • 简单除暴第一招,直接在gurad里面处理。这种方法其实没有任何问题,因为既然判断的业务代码在这里面,我们把判断完成后需要做的事也在这里写完不就行了。但是,本着无关的业务能解耦就解耦的原则,我们还有一个办法
  • 漂亮解耦第二招,写个action。下面介绍下这个action。

    action,看这个名字就知道是要搞事情的,之前我们把业务代码都是写到eventConfig的方法里面,但其实可以不怎么干,我们完全可以在每个状态变化的时候独立写一个action,这样的话就能做到业务的互不打扰。不如下面这段:

.withChoice().source(ComplexFormStates.CHECK_CHOICE).first(ComplexFormStates.CONFIRM_FORM, new ComplexFormCheckChoiceGuard(),new ComplexFormChoiceAction()).last(ComplexFormStates.DEAL_FORM,new ComplexFormChoiceAction()).and()

这是builder里面的代码片段,我们直接把action插入进来,在状态变化的时候就能在action里面处理了,下面是这个action

的代码:

public class ComplexFormChoiceAction implements Action {@Overridepublic void execute(StateContext context) {System.out.println("into ComplexFormChoiceAction");Form form = context.getMessage().getHeaders().get("form", Form.class);System.out.println(form);System.out.println(context.getStateMachine().getState());}}

这里面只有一个execute的方法,简单明了,我们需要的参数通过context传递,其实还是message,这样的话,不同业务用不同的action就行了,我们再回头看builder里面插入action的地方:

.first(ComplexFormStates.CONFIRM_FORM, new ComplexFormCheckChoiceGuard(),new ComplexFormChoiceAction(),new ComplexFormChoiceAction())

action可以多个插入,也就是有多少单独的业务需要在这里面处理都行,其实回过头来,不止在withChoice()里面可以,之前的withExternal()也是可以的,看代码:

.withExternal().source(ComplexFormStates.FULL_FORM).target(ComplexFormStates.CHECK_CHOICE).event(ComplexFormEvents.CHECK).action(new ComplexFormChoiceAction(),new ComplexFormChoiceAction()).guard(new ComplexFormCheckChoiceGuard()).and()

action可以插入多个,但guard在这里恢复了本来的作用,保护状态变化,所以只能插入一个。

3、在controller里面看结果

    最后,我们还是需要看下运行的结果,我准备了三个流程:

  • check成功,成为SUCCESS_FORM
  • check失败,deal成功,回到FULL_FORM
  • check失败,deal失败,成为FAILED_FORM

对应的是form1,form2和form3,看代码:

@Autowiredprivate ComplexFormStateMachineBuilder complexFormStateMachineBuilder;
......
@RequestMapping("/testComplexFormState")public void testComplexFormState() throws Exception {StateMachine stateMachine = complexFormStateMachineBuilder.build(beanFactory);System.out.println(stateMachine.getId());Form form1 = new Form();form1.setId("111");form1.setFormName(null);Form form2 = new Form();form2.setId("222");form2.setFormName("好的表单");Form form3 = new Form();form3.setId("333");form3.setFormName(null);// 创建流程System.out.println("-------------------form1------------------");stateMachine.start();Message message = MessageBuilder.withPayload(ComplexFormEvents.WRITE).setHeader("form", form1).build();stateMachine.sendEvent(message);System.out.println("当前状态:" + stateMachine.getState().getId());message = MessageBuilder.withPayload(ComplexFormEvents.CHECK).setHeader("form", form1).build();stateMachine.sendEvent(message);System.out.println("当前状态:" + stateMachine.getState().getId());message = MessageBuilder.withPayload(ComplexFormEvents.DEAL).setHeader("form", form1).build();stateMachine.sendEvent(message);System.out.println("当前状态:" + stateMachine.getState().getId());message = MessageBuilder.withPayload(ComplexFormEvents.SUBMIT).setHeader("form", form1).build();stateMachine.sendEvent(message);System.out.println("最终状态:" + stateMachine.getState().getId());System.out.println("-------------------form2------------------");stateMachine = complexFormStateMachineBuilder.build(beanFactory);stateMachine.start();message = MessageBuilder.withPayload(ComplexFormEvents.WRITE).setHeader("form", form2).build();stateMachine.sendEvent(message);System.out.println("当前状态:" + stateMachine.getState().getId());message = MessageBuilder.withPayload(ComplexFormEvents.CHECK).setHeader("form", form2).build();stateMachine.sendEvent(message);System.out.println("当前状态:" + stateMachine.getState().getId());message = MessageBuilder.withPayload(ComplexFormEvents.DEAL).setHeader("form", form2).build();stateMachine.sendEvent(message);System.out.println("当前状态:" + stateMachine.getState().getId());message = MessageBuilder.withPayload(ComplexFormEvents.SUBMIT).setHeader("form", form2).build();stateMachine.sendEvent(message);System.out.println("最终状态:" + stateMachine.getState().getId());System.out.println("-------------------form3------------------");stateMachine = complexFormStateMachineBuilder.build(beanFactory);stateMachine.start();message = MessageBuilder.withPayload(ComplexFormEvents.WRITE).setHeader("form", form3).build();stateMachine.sendEvent(message);System.out.println("当前状态:" + stateMachine.getState().getId());message = MessageBuilder.withPayload(ComplexFormEvents.CHECK).setHeader("form", form3).build();stateMachine.sendEvent(message);System.out.println("当前状态:" + stateMachine.getState().getId());form3.setFormName("好的表单");message = MessageBuilder.withPayload(ComplexFormEvents.DEAL).setHeader("form", form3).build();stateMachine.sendEvent(message);System.out.println("当前状态:" + stateMachine.getState().getId());message = MessageBuilder.withPayload(ComplexFormEvents.SUBMIT).setHeader("form", form3).build();stateMachine.sendEvent(message);message = MessageBuilder.withPayload(ComplexFormEvents.CHECK).setHeader("form", form3).build();stateMachine.sendEvent(message);System.out.println("当前状态:" + stateMachine.getState().getId());message = MessageBuilder.withPayload(ComplexFormEvents.SUBMIT).setHeader("form", form3).build();stateMachine.sendEvent(message);System.out.println("最终状态:" + stateMachine.getState().getId());}
......

看console的结果就知道正确与否

构建复杂表单状态机
complexFormMachine
-------------------form1------------------
2019-05-13 18:07:19.364 INFO 6188 --- [nio-9991-exec-1] tConfig$$EnhancerBySpringCGLIB$$37df7571 : ---空白复杂表单---
2019-05-13 18:07:19.368 INFO 6188 --- [nio-9991-exec-1] o.s.s.support.LifecycleObjectSupport : started org.springframework.statemachine.support.DefaultStateMachineExecutor@16603576
2019-05-13 18:07:19.369 INFO 6188 --- [nio-9991-exec-1] o.s.s.support.LifecycleObjectSupport : started CONFIRM_FORM BLANK_FORM FAILED_FORM FULL_FORM SUCCESS_FORM DEAL_FORM DEAL_CHOICE CHECK_CHOICE / BLANK_FORM / uuid=2aa87c74-dd28-4790-9722-d04657eaf046 / id=complexFormMachine
传递的参数:Form [id=111, formName=null]
2019-05-13 18:07:19.381 INFO 6188 --- [nio-9991-exec-1] tConfig$$EnhancerBySpringCGLIB$$37df7571 : ---填写完复杂表单---
当前状态:FULL_FORM
传递的参数:Form [id=111, formName=null]
2019-05-13 18:07:19.386 INFO 6188 --- [nio-9991-exec-1] tConfig$$EnhancerBySpringCGLIB$$37df7571 : ---校验复杂表单---
ComplexFormCheckChoiceGuard!!!!!!!!!!!!!
Form [id=111, formName=null] is false
into ComplexFormChoiceAction
Form [id=111, formName=null]
ObjectState [getIds()=[FULL_FORM], getClass()=class org.springframework.statemachine.state.ObjectState, hashCode()=381700599, toString()=AbstractState [id=FULL_FORM, pseudoState=null, deferred=[], entryActions=[], exitActions=[], stateActions=[], regions=[], submachine=null]]
当前状态:DEAL_FORM
传递的参数:Form [id=111, formName=null]
2019-05-13 18:07:19.389 INFO 6188 --- [nio-9991-exec-1] tConfig$$EnhancerBySpringCGLIB$$37df7571 : ---处理复杂表单---
ComplexFormDealChoiceGuard!!!!!!!!!!!!!
Form [id=111, formName=null] is false
into ComplexFormChoiceAction
Form [id=111, formName=null]
ObjectState [getIds()=[DEAL_FORM], getClass()=class org.springframework.statemachine.state.ObjectState, hashCode()=980487842, toString()=AbstractState [id=DEAL_FORM, pseudoState=null, deferred=[], entryActions=[], exitActions=[], stateActions=[], regions=[], submachine=null]]
当前状态:FAILED_FORM
最终状态:FAILED_FORM
-------------------form2------------------
构建复杂表单状态机
2019-05-13 18:07:19.394 INFO 6188 --- [nio-9991-exec-1] tConfig$$EnhancerBySpringCGLIB$$37df7571 : ---空白复杂表单---
2019-05-13 18:07:19.394 INFO 6188 --- [nio-9991-exec-1] o.s.s.support.LifecycleObjectSupport : started org.springframework.statemachine.support.DefaultStateMachineExecutor@51adbaad
2019-05-13 18:07:19.394 INFO 6188 --- [nio-9991-exec-1] o.s.s.support.LifecycleObjectSupport : started CONFIRM_FORM BLANK_FORM FAILED_FORM FULL_FORM SUCCESS_FORM DEAL_FORM DEAL_CHOICE CHECK_CHOICE / BLANK_FORM / uuid=fa133ea8-bf48-437e-ae35-dc7aa616a23c / id=complexFormMachine
传递的参数:Form [id=222, formName=好的表单]
2019-05-13 18:07:19.395 INFO 6188 --- [nio-9991-exec-1] tConfig$$EnhancerBySpringCGLIB$$37df7571 : ---填写完复杂表单---
当前状态:FULL_FORM
传递的参数:Form [id=222, formName=好的表单]
2019-05-13 18:07:19.396 INFO 6188 --- [nio-9991-exec-1] tConfig$$EnhancerBySpringCGLIB$$37df7571 : ---校验复杂表单---
ComplexFormCheckChoiceGuard!!!!!!!!!!!!!
Form [id=222, formName=好的表单] is true
into ComplexFormChoiceAction
Form [id=222, formName=好的表单]
ObjectState [getIds()=[FULL_FORM], getClass()=class org.springframework.statemachine.state.ObjectState, hashCode()=249611509, toString()=AbstractState [id=FULL_FORM, pseudoState=null, deferred=[], entryActions=[], exitActions=[], stateActions=[], regions=[], submachine=null]]
当前状态:CONFIRM_FORM
当前状态:CONFIRM_FORM
传递的参数:Form [id=222, formName=好的表单]
2019-05-13 18:07:19.399 INFO 6188 --- [nio-9991-exec-1] tConfig$$EnhancerBySpringCGLIB$$37df7571 : ---表单提交成功---
最终状态:SUCCESS_FORM
-------------------form3------------------
构建复杂表单状态机
2019-05-13 18:07:19.404 INFO 6188 --- [nio-9991-exec-1] tConfig$$EnhancerBySpringCGLIB$$37df7571 : ---空白复杂表单---
2019-05-13 18:07:19.405 INFO 6188 --- [nio-9991-exec-1] o.s.s.support.LifecycleObjectSupport : started org.springframework.statemachine.support.DefaultStateMachineExecutor@6f12d1a
2019-05-13 18:07:19.406 INFO 6188 --- [nio-9991-exec-1] o.s.s.support.LifecycleObjectSupport : started CONFIRM_FORM BLANK_FORM FAILED_FORM FULL_FORM SUCCESS_FORM DEAL_FORM DEAL_CHOICE CHECK_CHOICE / BLANK_FORM / uuid=03e8c891-eedc-4922-811c-ab375a1e70ae / id=complexFormMachine
传递的参数:Form [id=333, formName=null]
2019-05-13 18:07:19.409 INFO 6188 --- [nio-9991-exec-1] tConfig$$EnhancerBySpringCGLIB$$37df7571 : ---填写完复杂表单---
当前状态:FULL_FORM
传递的参数:Form [id=333, formName=null]
2019-05-13 18:07:19.410 INFO 6188 --- [nio-9991-exec-1] tConfig$$EnhancerBySpringCGLIB$$37df7571 : ---校验复杂表单---
ComplexFormCheckChoiceGuard!!!!!!!!!!!!!
Form [id=333, formName=null] is false
into ComplexFormChoiceAction
Form [id=333, formName=null]
ObjectState [getIds()=[FULL_FORM], getClass()=class org.springframework.statemachine.state.ObjectState, hashCode()=608638875, toString()=AbstractState [id=FULL_FORM, pseudoState=null, deferred=[], entryActions=[], exitActions=[], stateActions=[], regions=[], submachine=null]]
当前状态:DEAL_FORM
传递的参数:Form [id=333, formName=好的表单]
2019-05-13 18:07:19.412 INFO 6188 --- [nio-9991-exec-1] tConfig$$EnhancerBySpringCGLIB$$37df7571 : ---处理复杂表单---
ComplexFormDealChoiceGuard!!!!!!!!!!!!!
Form [id=333, formName=好的表单] is true
into ComplexFormChoiceAction
Form [id=333, formName=好的表单]
ObjectState [getIds()=[DEAL_FORM], getClass()=class org.springframework.statemachine.state.ObjectState, hashCode()=1203626264, toString()=AbstractState [id=DEAL_FORM, pseudoState=null, deferred=[], entryActions=[], exitActions=[], stateActions=[], regions=[], submachine=null]]
当前状态:FULL_FORM
传递的参数:Form [id=333, formName=好的表单]
2019-05-13 18:07:19.413 INFO 6188 --- [nio-9991-exec-1] tConfig$$EnhancerBySpringCGLIB$$37df7571 : ---校验复杂表单---
ComplexFormCheckChoiceGuard!!!!!!!!!!!!!
Form [id=333, formName=好的表单] is true
into ComplexFormChoiceAction
Form [id=333, formName=好的表单]
ObjectState [getIds()=[FULL_FORM], getClass()=class org.springframework.statemachine.state.ObjectState, hashCode()=608638875, toString()=AbstractState [id=FULL_FORM, pseudoState=null, deferred=[], entryActions=[], exitActions=[], stateActions=[], regions=[], submachine=null]]
当前状态:CONFIRM_FORM
传递的参数:Form [id=333, formName=好的表单]
2019-05-13 18:07:19.415 INFO 6188 --- [nio-9991-exec-1] tConfig$$EnhancerBySpringCGLIB$$37df7571 : ---表单提交成功---
最终状态:SUCCESS_FORM

大家可以跑一下,对照状态机的流程图,看下结果。

4、继续废话

    其实spring statemachine写到这里,基本上已经可以上手去做企业开发了。当然,spring statemachine里面还有很多其他的东西,大部分是处理复杂状态机流程的,以后有机会我们再展开讲。

源代码地址


转:https://my.oschina.net/u/173343/blog/3049036



推荐阅读
  • Java实战之电影在线观看系统的实现
    本文介绍了Java实战之电影在线观看系统的实现过程。首先对项目进行了简述,然后展示了系统的效果图。接着介绍了系统的核心代码,包括后台用户管理控制器、电影管理控制器和前台电影控制器。最后对项目的环境配置和使用的技术进行了说明,包括JSP、Spring、SpringMVC、MyBatis、html、css、JavaScript、JQuery、Ajax、layui和maven等。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 标题: ... [详细]
  • 在springmvc框架中,前台ajax调用方法,对图片批量下载,如何弹出提示保存位置选框?Controller方法 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 如何搭建Java开发环境并开发WinCE项目
    本文介绍了如何搭建Java开发环境并开发WinCE项目,包括搭建开发环境的步骤和获取SDK的几种方式。同时还解答了一些关于WinCE开发的常见问题。通过阅读本文,您将了解如何使用Java进行嵌入式开发,并能够顺利开发WinCE应用程序。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • 本文记录了在vue cli 3.x中移除console的一些采坑经验,通过使用uglifyjs-webpack-plugin插件,在vue.config.js中进行相关配置,包括设置minimizer、UglifyJsPlugin和compress等参数,最终成功移除了console。同时,还包括了一些可能出现的报错情况和解决方法。 ... [详细]
author-avatar
imba-Y_685
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有