activiti监听器使用
分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519)
activiti使用的时候,通常需要跟业务紧密的结合在一起,有些业务非常的复杂,通常有如下一些场景:
1.activiti人员动态的分配。
2.当前任务节点完成的时候,指定需要指定下一个节点的处理人(比如,一个请假流程,a员工请假,需要指定下一步需要处理请假流程的领导。)。
3.任务节点完成的时候,需要一些复杂业务,(比如当前节点完成的时候,需要调用我们的jms消息系统发送消息)。
4.任务流转到当前的节点的时候,需要监控当前任务节点的一些信息或者其他的业务信息。
5.当前的任务节点分配处理人的时候,需要触发自定义的一些业务。
6.流程开始结束的时候,需要处理业务信息。
7.经过任务节点的出线,也就是连线的时候,需要触发自定义的业务。
那我们怎么实现以上的这些需求呢?这个时候,我们就需要使用activiti监听器,activiti提供的监听器怎么实现,以及如何触发,这些都需要我们一步步了解。下面就详细的介绍activiti监听器。
从 activiti监听器的使用范围来看,大致分为三种:
1.全局的监听器。
2.连线的监听器。
3.节点的监听器。
下面我们一个个的来看如何使用这些监听器。
1.1.1. 全局的监听器
全局监听器主要使用的场景就是监控这个流程的启动和结束。流程开始的时候可以监控,流程结束的时候可以监控,这里说的是流程实例启动结束的监控,并非是流程引擎的启动结束监控。流程引擎的启动结束监控可以参考http://blog.csdn.net/qq_30739519/article/details/51217614 不要混淆了这些概念。
下面我们先定义一个简单的bpmn xml文件看一下如何使用全局的监听器。
全局监听器实现的接口是org.activiti.engine.delegate.ExecutionListener,org.activiti.engine.impl.pvm.delegate.ExecutionListener这个接口新版本已经废弃。
1.1.1.1. ExecutionListener定义
ExecutionListener定义如下:流程实例start、end、take的时候调用。take是监控连线的时候使用的。
public interface ExecutionListener extends Serializable { String EVENTNAME_START = "start"; String EVENTNAME_END = "end"; String EVENTNAME_TAKE = "take"; void notify(DelegateExecution execution) throws Exception; }
下面定义一个类监控流程实例的启动监控,看如何使用。
1.1.1.2. 流程图
1.1.1.3. 流程定义xml
流程的xml如下所示:
<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="daling"> <process id="daling" name="name_daling" isExecutable="true" activiti:candidateStarterUsers="a,b,c,d"> <extensionElements> <activiti:executionListener event="start" class="com.daling.ch1.listener.MyExecutionListener"></activiti:executionListener> <activiti:executionListener event="end" class="com.daling.ch1.listener.MyExecutionListener"></activiti:executionListener> </extensionElements> <startEvent id="startevent1" name="Start"></startEvent> <endEvent id="endevent1" name="End"></endEvent> <userTask id="usertask2" name="User Task"></userTask> <sequenceFlow id="flow3" sourceRef="startevent1" targetRef="usertask2"></sequenceFlow> <sequenceFlow id="flow4" sourceRef="usertask2" targetRef="endevent1"></sequenceFlow> </process> <bpmndi:BPMNDiagram id="BPMNDiagram_daling"> <bpmndi:BPMNPlane bpmnElement="daling" id="BPMNPlane_daling"> <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1"> <omgdc:Bounds height="35.0" width="35.0" x="120.0" y="110.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1"> <omgdc:Bounds height="35.0" width="35.0" x="640.0" y="90.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2"> <omgdc:Bounds height="55.0" width="105.0" x="300.0" y="90.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3"> <omgdi:waypoint x="155.0" y="127.0"></omgdi:waypoint> <omgdi:waypoint x="300.0" y="117.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4"> <omgdi:waypoint x="405.0" y="117.0"></omgdi:waypoint> <omgdi:waypoint x="640.0" y="107.0"></omgdi:waypoint> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </definitions>
1.1.1.4. 监听类
上面我们定义了两个监听器分别指向同一个类com.daling.ch1.listener.MyExecutionListener我们在类中监控事件。
com.daling.ch1.listener.MyExecutionListener类的实现如下所示:
package com.daling.ch1.listener; import org.activiti.engine.delegate.DelegateExecution; import org.activiti.engine.delegate.ExecutionListener; public class MyExecutionListener implements ExecutionListener { public void notify(DelegateExecution execution) throws Exception { String eventName = execution.getEventName(); //start if ("start".equals(eventName)) { System.out.println("start========="); }else if ("end".equals(eventName)) { System.out.println("end========="); } } }
1.1.1.5. 开始监控
下面我们部署启动流程看一下效果,部署流程可参考之前的章节,启动流程如下所示:
ProcessInstance processInstance = runtimeService
.startProcessInstanceByKey(key,vars);
我们看一下程序的部分输出如下:
08:34:07.050 [main] DEBUG o.a.e.i.h.DefaultHistoryManager - Current history level: AUDIT, level required: ACTIVITY
08:34:07.056 [main] DEBUG o.a.e.i.h.DefaultHistoryManager - Current history level: AUDIT, level required: ACTIVITY
08:34:07.057 [main] DEBUG o.a.e.i.h.DefaultHistoryManager - Current history level: AUDIT, level required: FULL
start=========
08:34:07.064 [main] DEBUG o.a.e.i.p.r.AtomicOperationActivityExecute - ProcessInstance[107509] executes Activity(startevent1): org.activiti.engine.impl.bpmn.behavior.NoneStartEventActivityBehavior
08:34:07.064 [main] DEBUG o.a.e.i.p.e.J.selectJobsByExecutionId - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@eeb514]
08:34:07.064 [main] DEBUG o.a.e.i.p.e.J.selectJobsByExecutionId - ==> Preparing: select * from ACT_RU_JOB J where J.EXECUTION_ID_ = ?
可以看出流程实例启动的时候,触发了我们自定义的类中的函数。
1.1.1.6. 结束监控
下面我们把usertask的任务结束,然后流程实例结束,看看触发了我们自定义的类中的end没有。
String taskId="107515";
demo.getTaskService().complete(taskId);
我们看一下程序的部分输出如下:
08:39:22.016 [main] DEBUG o.a.e.i.p.r.AtomicOperationActivityExecute - ProcessInstance[107509] executes Activity(endevent1): org.activiti.engine.impl.bpmn.behavior.NoneEndEventActivityBehavior
08:39:22.016 [main] DEBUG o.a.e.i.h.DefaultHistoryManager - Current history level: AUDIT, level required: ACTIVITY
end=========
1.1.1.7. DelegateExecution类
在上面的全局监听器中我们可以拿到DelegateExecution对象,这个对象可以让我们操作activiti引擎中的一些东西,下面看一下DelegateExecution类中的定义。
DelegateExecution类图如下:
启动最常用的方法如下:
/** execution Id */ String getId(); /** 流程实例id*/ String getProcessInstanceId(); /** 这个比较有用 主要就是start、end、take */ String getEventName(); /** * 业务id已经废弃 */ String getBusinessKey(); /** * 业务id */ String getProcessBusinessKey(); /** * 流程定义id */ String getProcessDefinitionId(); /** * 获取父id,并发的时候有用 */ String getParentId(); /** * 获取当前的.Activityid */ String getCurrentActivityId(); /** * 获取当前的.Activity name */ String getCurrentActivityName(); /** * 获取TenantId 当有多个TenantId 有用 */ String getTenantId(); /** * 这个非常有用吧。当拿到EngineServices 对象所有的xxxService都可以拿到。 */ EngineServices getEngineServices();
1.1.2. 连线监听器
当节点结束的时候,经过连线的时候,我们可以在线上定义类,实现自己的业务逻辑。连线监听器怎么实现呢?
这里为了方便测试,我们还是采用上面全局监听器中的流程图,下面我们自定义一个连线监听器类,看看如何使用,可以拿到什么对象。
1.1.2.1. 流程图
1.1.2.2. 流程定义xml
<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="daling"> <process id="daling" name="name_daling" isExecutable="true" activiti:candidateStarterUsers="a,b,c,d"> <extensionElements> <activiti:executionListener event="start" class="com.daling.ch1.listener.MyExecutionListener"></activiti:executionListener> <activiti:executionListener event="end" class="com.daling.ch1.listener.MyExecutionListener"></activiti:executionListener> </extensionElements> <startEvent id="startevent1" name="Start"></startEvent> <endEvent id="endevent1" name="End"></endEvent> <userTask id="usertask2" name="User Task"></userTask> <sequenceFlow id="flow3" name="flow3" sourceRef="startevent1" targetRef="usertask2"> <extensionElements> <activiti:executionListener event="take" class="com.daling.ch1.listener.MyExecutionListener"></activiti:executionListener> </extensionElements> </sequenceFlow> <sequenceFlow id="flow4" sourceRef="usertask2" targetRef="endevent1"></sequenceFlow> </process> <bpmndi:BPMNDiagram id="BPMNDiagram_daling"> <bpmndi:BPMNPlane bpmnElement="daling" id="BPMNPlane_daling"> <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1"> <omgdc:Bounds height="35.0" width="35.0" x="120.0" y="110.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1"> <omgdc:Bounds height="35.0" width="35.0" x="640.0" y="90.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2"> <omgdc:Bounds height="55.0" width="105.0" x="300.0" y="90.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3"> <omgdi:waypoint x="155.0" y="127.0"></omgdi:waypoint> <omgdi:waypoint x="300.0" y="117.0"></omgdi:waypoint> <bpmndi:BPMNLabel> <omgdc:Bounds height="14.0" width="100.0" x="155.0" y="127.0"></omgdc:Bounds> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge>ss <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4"> <omgdi:waypoint x="405.0" y="117.0"></omgdi:waypoint> <omgdi:waypoint x="640.0" y="107.0"></omgdi:waypoint> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </definitions>
1.1.2.3. 监听类
上面我们定义了一个监听器com.daling.ch1.listener.MyExecutionListener类中监控事件。
public class MyExecutionListener implements ExecutionListener { /** * */ private static final long serialVersionUID = 7960387497099642910L; //ExecutionListener类的实现 public void notify(DelegateExecution execution) throws Exception { String eventName = execution.getEventName(); //start if ("start".equals(eventName)) { System.out.println("start========="); }else if ("end".equals(eventName)) { System.out.println("end========="); } else if ("take".equals(eventName)) { System.out.println("take========="); } } }
因为我们在flow3中定义的监听类,所以流程经过flow3的时候触发定义的take中的自定义事件。
1.1.3. 节点监听器
1.1.3.1. 节点监听器定义
在实际项目开发中,任务节点是经常用到的,所以我们必须要会使用节点监听器。
节点监听器的定义接口org.activiti.engine.delegate.TaskListener,org.activiti.engine.impl.pvm.delegate.TaskListener已经废弃不用。
TaskListener类的定义如下所示:
String EVENTNAME_CREATE = "create"; String EVENTNAME_ASSIGNMENT = "assignment"; String EVENTNAME_COMPLETE = "complete"; String EVENTNAME_DELETE = "delete"; /** * Not an actual event, used as a marker-value for {@link TaskListener}s that should be called for all events, * including {@link #EVENTNAME_CREATE}, {@link #EVENTNAME_ASSIGNMENT} and {@link #EVENTNAME_COMPLETE} and {@link #EVENTNAME_DELETE}. */ String EVENTNAME_ALL_EVENTS = "all"; void notify(DelegateTask delegateTask);
1.1.3.2. 节点监听器类图
节点监听器的子类如下图所示:
下面开始我们详细的使用吧。首先还是定义一个xml,xml流程还是上面所示的,只是多了一些任务节点的监听。
1.1.3.3. 流程图
1.1.3.4. 流程定义xml
流程的xml如下所示:
<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="daling"> <process id="daling" name="name_daling" isExecutable="true" activiti:candidateStarterUsers="a,b,c,d"> <extensionElements> <activiti:executionListener event="start" class="com.daling.ch1.listener.MyExecutionListener"></activiti:executionListener> <activiti:executionListener event="end" class="com.daling.ch1.listener.MyExecutionListener"></activiti:executionListener> </extensionElements> <startEvent id="startevent1" name="Start"></startEvent> <endEvent id="endevent1" name="End"></endEvent> <userTask id="usertask2" name="User Task" activiti:assignee="c"> <extensionElements> <activiti:taskListener event="all" class="com.daling.ch1.listener.MyExecutionListener"></activiti:taskListener> </extensionElements> </userTask> <sequenceFlow id="flow3" name="flow3" sourceRef="startevent1" targetRef="usertask2"> <extensionElements> <activiti:executionListener event="take" class="com.daling.ch1.listener.MyExecutionListener"></activiti:executionListener> </extensionElements> </sequenceFlow> <sequenceFlow id="flow4" sourceRef="usertask2" targetRef="endevent1"></sequenceFlow> </process> <bpmndi:BPMNDiagram id="BPMNDiagram_daling"> <bpmndi:BPMNPlane bpmnElement="daling" id="BPMNPlane_daling"> <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1"> <omgdc:Bounds height="35.0" width="35.0" x="120.0" y="110.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1"> <omgdc:Bounds height="35.0" width="35.0" x="640.0" y="90.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2"> <omgdc:Bounds height="55.0" width="105.0" x="300.0" y="90.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3"> <omgdi:waypoint x="155.0" y="127.0"></omgdi:waypoint> <omgdi:waypoint x="300.0" y="117.0"></omgdi:waypoint> <bpmndi:BPMNLabel> <omgdc:Bounds height="14.0" width="100.0" x="155.0" y="127.0"></omgdc:Bounds> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4"> <omgdi:waypoint x="405.0" y="117.0"></omgdi:waypoint> <omgdi:waypoint x="640.0" y="107.0"></omgdi:waypoint> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </definitions>
1.1.3.5. 监听类
任务节点的监听只要实现TaskListener接口即可。下面是任务监听的实现类,如下所示:
public class MyExecutionListener implements ExecutionListener,TaskListener { /** * */ private static final long serialVersionUID = 7960387497099642910L; //ExecutionListener类的实现 public void notify(DelegateExecution execution) throws Exception { String eventName = execution.getEventName(); //start if ("start".equals(eventName)) { System.out.println("start========="); }else if ("end".equals(eventName)) { System.out.println("end========="); } else if ("take".equals(eventName)) { System.out.println("take========="); } } //实现TaskListener中的方法 public void notify(DelegateTask delegateTask) { String eventName = delegateTask.getEventName(); if ("create".endsWith(eventName)) { System.out.println("create========="); }else if ("assignment".endsWith(eventName)) { System.out.println("assignment========"); }else if ("complete".endsWith(eventName)) { System.out.println("complete==========="); }else if ("delete".endsWith(eventName)) { System.out.println("delete============="); } } }
当流程运转到usertask2我们看一下程序的输出:
assignment========
create=========
因为usertask2节点配置了处理人所以触发assignment事件监听,当任务运转到usertask2的时候触发了create事件。
这里我们也可以得出一个结论:assignment事件比create先执行。
使用代码结束任务,代码如下:
String taskId="127515";
demo.getTaskService().complete(taskId);
当我们结束usertask2我们看一下程序的输出:
complete===========
delete=============
在这里我们可以得出结论:
任务完成的时候,触发complete事件,因为任务完成之后,要从ACT_RU_TASK中删除这条记录,所以触发delete事件。
1.1.3.6. DelegateTask类
在上面的任务监听器中我们可以拿到DelegateTask对象,这个对象可以让我们操作activiti引擎中的一些东西,下面看一下DelegateTask类中的定义主要访法。
public interface DelegateTask extends VariableScope { /** 数据库中的taskId主键*/ String getId(); /** 任务名称 */ String getName(); /** 修改任务名称 */ void setName(String name); /** 获取任务的描述信息 */ String getDescription(); /** 修改任务的描述信息 */ void setDescription(String description); /** * lower priority: [0..19] lowest, [20..39] low, [40..59] normal, [60..79] high * [80..100] highest 任务处理的优先级范围是0-100 */ int getPriority(); /** 修改优先级*/ void setPriority(int priority); /** 获取流程实例id */ String getProcessInstanceId(); /**获取执行id*/ String getExecutionId(); /** 获取流程定义id*/ String getProcessDefinitionId(); /** Adds the given user as a candidate user to this task. */ void addCandidateUser(String userId); /** 添加候选人 */ void addCandidateUsers(Collection<String> candidateUsers); /** 添加候选组 */ void addCandidateGroup(String groupId); }
上面我们总结了,全局监听器、连线、任务节点的使用,下面我们看一下共性的东西再做一次总结。
1.1.4. 其他的使用方式
1.1.4.1. spring整合
上面我们在定义类的的时候,我们直接使用的类定义的方式,当然我们还可以跟sring整合使用,因为在实际项目中类的管理可能交给spring容器。使用也很简单如下图所示:
需要填写的表达书,只需要使用spring方式注入即可,${}里面的值为需要使用的bean在spring中的id.可以xml定义或者在注解中定义。注解中定义需要开启spring包扫描机制。比如我的@Service("myExecutionListener")。
1.1.5. 小结
1.activiti监听方式分为三大类,节点监听、全局监听、连线监听。
2.activiti监听主要实现的类是两个 节点监听、全局监听实现org.activiti.engine.delegate.ExecutionListener
节点的监听实现org.activiti.engine.delegate.TaskListener接口即可。
3.监听器其实就是一个观察者模式。(关于观察者模式,我们后续章节讲解,限于篇幅有限)
4.监听器获取spring对象,如何获取spring中的bean呢?
1.1.6. 遗留点
5.监听器关于观察者模式,我们后续章节讲解。
6.监听器获取spring对象,如何获取spring中的bean呢?留到下一个章节讲解吧。
activiti监听器使用的更多相关文章
- activiti监听器
activiti使用的时候,通常需要跟业务紧密的结合在一起,有些业务非常的复杂,通常有如下一些场景: 1.activiti人员动态的分配. 2.当前任务节点完成的时候,指定需要指定下一个节点的处理人( ...
- activiti节点跳转
分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519) activiti使用的时候,通常需要跟业务紧密的结合在一起,有些业 ...
- ACTIVITI 5.14事件监听器的BUG
在ACTIVITI 5.14中,测试内部子流程时发现事件定义的事件监听器不能触发. <activiti:executionListener event="start" del ...
- Liferay7 BPM门户开发之46: 集成Activiti用户、用户组、成员关系同步
在实际的BPM集成开发过程中,Liferay和Activiti这两个异构的系统之间,用户.组的同步需求非常重要,用来实现签收组的概念,比如指定签收组.会签.抢签都需要用到. Activiti可以通过自 ...
- Liferay7 BPM门户开发之44: 集成Activiti展示流程列表
处理依赖关系 集成Activiti之前,必须搞清楚其中的依赖关系,才能在Gradle里进行配置. 依赖关系: 例如,其中activiti-engine依赖于activiti-bpmn-converte ...
- activiti学习总结
Activiti界面元素的使用总结 一.图形设计中元素的使用 1.SequenceFlow:连接线,可以连接两个任务,来管理流程实例的流向 -----General -----id:流程的id,用与程 ...
- activiti 任务节点 处理人设置【转】
转自http://blog.csdn.net/qq_30739519/article/details/51225067 1.1.1. 前言 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自 ...
- activiti搭建(四)八项服务介绍
转载请注明源地址:http://www.cnblogs.com/lighten/p/5927949.html 1.前言 之前学习的时候一直在其它文章看到activiti提供了七个接口来操作工作流,但在 ...
- Activiti 多个并发子流程的应用
多个部门发起资金计划,最后统一到财务部审批,每个部门发起资金计划是一个子流程,财务部审批是多个部门的计划同时审批,审批完成后,再提交上级领导审批. 流程如下: 要解决以上问题,需要实现多个子流程并行处 ...
随机推荐
- [LeetCode] Design Compressed String Iterator 设计压缩字符串的迭代器
Design and implement a data structure for a compressed string iterator. It should support the follow ...
- scrapy下载图片到自己的目录,创建缩略图,存储入库
环境和工具:python2.7,scrapy 实验网站:http://www.27270.com/tag/333.html 爬去所有兔女郎图片,下面的推荐需要过滤 逻辑:分析网站信息,下载图片和入库 ...
- Java必须了解的“递归”与“IO流”!!!
>>>First: 递归! 1. 定义: 在函数自身内部,调用函数本身的方式,称为递归. 2. 注意: 递归包括递进去.归出来两步. 首先,依次执行[函数调自身语句]上半部分的代码, ...
- Chtholly Nota Seniorious
题目背景 大样例下发链接: https://pan.baidu.com/s/1nuVpRS1 密码: sfxg こんなにも.たくさんの幸せをあの人に分けてもらった だから.きっと 今の.私は 谁が何と ...
- [JLOI2015]管道连接
题目描述 小铭铭最近进入了某情报部门,该部门正在被如何建立安全的通道连接困扰.该部门有 n 个情报站,用 1 到 n 的整数编号.给出 m 对情报站 ui;vi 和费用 wi,表示情报站 ui 和 v ...
- 计蒜客NOIP2017提高组模拟赛(五)day1-展览
传送门 发现这题选或不选对状态的优劣程度不会产生影响,如果已经确定了两个数a和b,那么最优的首项和公比也都是唯一确定的, 与对于后面的数x,加进去也好不加进去也好,首项和公比依旧是原来的 于是我们用尺 ...
- Gradle学习之构建java与web项目
一.使用Gradle的java插件构建Java项目 1)Gradle插件包含了若干个接口定义和已有的任务项,语法结构:apply plugin:'插件名' ,此处我们定义插件 apply plugin ...
- Linux学习之CentOS(十八)-----恢复Ext3下被删除的文件与 使用grep恢复被删文件内容(转)
前言 下面是这个教程将教你如何在Ext3的文件系统中恢复被rm掉的文件. 删除文件 假设我们有一个文件名叫 'test.txt' $ls -il test.txt 15 -rw-rw-r– 2 roo ...
- sqlserver 判断字段是否为空字符串或者null
isnull(f.mzm,'')<>'' 不为null且不为‘’ not(f.mzm is null) 不为null
- JAVA中接口的使用
抽象类是从多个类中抽象出来的模板,如果将这种抽象进行的更彻底,那么就是接口(interface)了.什么是接口,简单的讲,接口就是抽象类的进一步抽象,这种进一步的抽象只定义了一种规范,而不需要关心具体 ...