首页 > 其他分享 >flowable 流程动态设置监听器(非xml中定义)及发起时从驳回节点开始审批实现

flowable 流程动态设置监听器(非xml中定义)及发起时从驳回节点开始审批实现

时间:2024-09-19 17:27:48浏览次数:8  
标签:xml String flowable private 监听器 import org togeek cn

一、flowable 使用代码动态修改监听器

1、配置类

@Configuration
public class FlowableGlobListenerConfig {


   @Lazy
   @Autowired
   private TaskStartListener taskStartListener;

   @Lazy
   @Autowired
   private TaskCompleteListener taskCompletedListener;

   @Lazy
   @Autowired
   private ActivityCompletedListener activityCompletedListener;

   @Bean
   @Primary
   public EngineConfigurationConfigurer<SpringProcessEngineConfiguration> globalListenerConfigurer() {
      return engineConfiguration -> engineConfiguration.setTypedEventListeners(this.customFlowableListeners());
   }

   /**
    * 监听类配置 flowable监听器级别
    *
    * @return java.util.Map<java.lang.String, java.util.List < org.flowable.common.engine.api.delegate.event.FlowableEventListener>>
    */
   private Map<String, List<FlowableEventListener>> customFlowableListeners() {
      Map<String, List<FlowableEventListener>> listenerMap = Maps.newHashMap();
      listenerMap.put(FlowableEngineEventType.TASK_CREATED.name(),
         new ArrayList<>(Collections.singletonList(taskStartListener)));
      listenerMap.put(FlowableEngineEventType.TASK_COMPLETED.name(),
         new ArrayList<>(Collections.singletonList(taskCompletedListener)));
      listenerMap.put(FlowableEngineEventType.ACTIVITY_COMPLETED.name(),
         new ArrayList<>(Collections.singletonList(activityCompletedListener)));
      return listenerMap;
   }
}

说明:通过注册关注的事件,实现对应的监听逻辑实现相应业务逻辑

2、流程节点创建时的监听逻辑

import cn.hutool.core.collection.CollUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.landray.kmss.sys.notify.webservice.NotifyTodoSendContextVo;

import cn.togeek.geekbidder.crm.contract.entity.ContractEntity;
import cn.togeek.geekbidder.crm.contract.mapper.ContractMapper;
import cn.togeek.geekbidder.crm.contract.service.ContractNewServiceImpl;
import cn.togeek.geekbidder.crm.contract.util.TitleUtil;
import cn.togeek.maple.message.api.enums.MessageType;
import cn.togeek.maple.message.api.enums.ReadState;
import cn.togeek.maple.message.api.model.Message;
import cn.togeek.maple.message.api.model.Recipient;
import cn.togeek.maple.message.api.service.MessageService;
import cn.togeek.maple.platform.api.model.User;
import cn.togeek.maple.platform.api.service.UserService;
import cn.togeek.maple.process.TaskStartInvoker;
import cn.togeek.maple.process.api.model.ProcessMessage;
import cn.togeek.maple.process.api.util.SpringContextUtil;
import cn.togeek.oa.TaskQueryService;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.common.engine.api.delegate.event.FlowableEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
import org.flowable.common.engine.impl.event.FlowableEntityEventImpl;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.identitylink.api.IdentityLink;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("taskStartListener")
public class TaskStartListener implements FlowableEventListener {
   private static final Logger log = LoggerFactory.getLogger(TaskStartListener.class);
   @Autowired
   private TaskService taskService;
   @Autowired
   private MessageService messageService;
   @Autowired
   private RepositoryService repositoryService;
   @Autowired
   private UserService userService;
   @Autowired
   private ObjectMapper objectMapper;

   @Autowired
   private TaskQueryService taskQueryService;

   @Autowired
   private HistoryService historyService;

   @Value("${system.url}")
   private String url;

   @Autowired
   private ContractMapper contractMapper;

   @Autowired
   private ContractNewServiceImpl contractNewService;


   /**
    * 流程发起时统一通知逻辑
    * process-task/processVariables/33a9feee-65a6-11ef-8ac0-024204500072
    */
   public TaskStartListener() {
   }

   public void onEvent(FlowableEvent event) {
      TaskEntity taskEntity = (TaskEntity)((FlowableEntityEventImpl)event).getEntity();
      String taskId = taskEntity.getId();
      List<IdentityLink> idList = this.taskService.getIdentityLinksForTask(taskId);
      if (!CollectionUtils.isEmpty(idList)) {
         List<Recipient> recipientList = new ArrayList();
         idList.stream().distinct().forEach((identityLink) -> {
            Recipient recipient;
            if (StringUtils.isNotBlank(identityLink.getUserId()) && "assignee".equals(identityLink.getType())) {
               recipient = new Recipient();
               recipient.setId(identityLink.getUserId());
               recipientList.add(recipient);
            }

            if (StringUtils.isNotBlank(identityLink.getUserId()) && "candidate".equals(identityLink.getType())) {
               recipient = new Recipient();
               recipient.setId(identityLink.getUserId());
               recipientList.add(recipient);
            } else if (StringUtils.isNotBlank(identityLink.getGroupId()) && "candidate".equals(identityLink.getType())) {
               List<User> userList = this.userService.getUserByRole(identityLink.getGroupId());
               userList.forEach((obj) -> {
                  Recipient r = new Recipient();
                  r.setId(obj.getId());
                  recipientList.add(r);
               });
            }

            this.sendMessage(taskEntity, recipientList);
         });
         /**
          * 流程通知oa系统相关人逻辑
          * process-task/processVariables/33a9feee-65a6-11ef-8ac0-024204500072
          * //todo 需确认与oa系统对应用户的关系、及连接点击时token信息的有效性
          */
         List<String> ids = recipientList.stream().map(Recipient::getId).collect(Collectors.toList());
         List<String> taskUsers = this.userService.getUsersByIds(ids).stream().map(User::getName).collect(Collectors.toList());
         String instId = taskEntity.getProcessInstanceId();
         HistoricProcessInstance instance =
            historyService.createHistoricProcessInstanceQuery().processInstanceId(instId).singleResult();
         List<HistoricVariableInstance> list =
            historyService.createHistoricVariableInstanceQuery().processInstanceId(instId).list();
         String definitionKey = instance.getProcessDefinitionKey();
         ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
            .processDefinitionKey(definitionKey)
            .active()
            .singleResult();
         String deploymentId = processDefinition.getDeploymentId();
         Map<String, Object> map = list.stream().filter(e->Objects.nonNull(e.getValue())).collect(Collectors.toMap(HistoricVariableInstance::getVariableName, HistoricVariableInstance::getValue, (o1, o2)->o2));
         String processName = processDefinition.getName();
         String activeTab = processDefinition.getCategory();
         String businessKey = instance.getBusinessKey();
         // String activeTab = OAConstant.PROCESS_MAP.get(definitionKey).get(1);
         ContractEntity contractEntity;
         if((contractEntity=contractMapper.selectById(businessKey))!=null) {
            contractEntity.setCurrentApprovalUser(String.join(",", taskUsers));
            contractMapper.updateById(contractEntity);
         }
         //根据规则创建subject
         CompletableFuture.runAsync(()-> {
            if(CollUtil.isEmpty(map)) {
               return;
            }
            log.info("==================BEGIN异步同步oa代办BEGIN=================");
            NotifyTodoSendContextVo vo = new NotifyTodoSendContextVo();
            vo.setBusinessKey(instId);
            vo.setModelId(instId);
            vo.setType(2);
            vo.setSubject(TitleUtil.buildSubject(map, processName));
            // vo.setLink(url + "/process-task/processVariables/"+ instId);
            vo.setLink(buildLink(deploymentId, instId, taskId, activeTab));
            taskQueryService.syncAgency(vo, taskUsers);
            log.info("==================END异步同步oa代办END=================");
         });
      }
      Map<String, TaskStartInvoker> type = SpringContextUtil.getBeansOfType(TaskStartInvoker.class);
      type.values().parallelStream().forEach(
         (invoker) -> {
            invoker.consume(event);
         }
      );
   }

   private String buildLink(String deployId, String procInsId, String taskId, String activeTab) {
      return url+ "/process/record?deployId="+deployId+"&procInsId=="+procInsId+"&taskId="+taskId+"&activeTab="+activeTab;
   }

   public boolean isFailOnException() {
      return false;
   }

   public boolean isFireOnTransactionLifecycleEvent() {
      return false;
   }

   public String getOnTransaction() {
      return null;
   }

   public void sendMessage(TaskEntity taskEntity, List<Recipient> recipientList) {
      ProcessMessage processMessage = new ProcessMessage();
      ProcessDefinition pd =
         this.repositoryService.createProcessDefinitionQuery().processDefinitionId(taskEntity.getProcessDefinitionId()).singleResult();
      processMessage.setDeployId(pd.getDeploymentId());
      processMessage.setProcInsId(taskEntity.getProcessInstanceId());
      processMessage.setRouterName("审批详情路由");
      processMessage.setTaskId(taskEntity.getId());
      processMessage.setCheckUrl("/process-task/runing/" + taskEntity.getId());
      Message message = new Message();
      message.setCreateTime(LocalDateTime.now());
      message.setMessage("您有新的流程待办消息,请及时处理!");
      message.setRecipients(recipientList);
      message.setTitle("流程待办提醒");
      message.setReadState(ReadState.NOT_READ);
      message.setType(MessageType.MESSAGE);
      message.setMessageType("PROCESS_TODO_MESSAGE");
      String transmissionText = null;

      try {
         transmissionText = this.objectMapper.writeValueAsString(processMessage);
      } catch (JsonProcessingException var8) {
         log.error(var8.getMessage(), var8);
      }

      message.setTransmissionText(transmissionText);
      this.messageService.sendMessage(message);
   }
}

3、流程节点结束时逻辑

import cn.togeek.maple.process.TaskComplateInvoker;
import cn.togeek.maple.process.api.util.SpringContextUtil;

import java.util.Map;

import org.flowable.common.engine.api.delegate.event.FlowableEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component("taskCompleteListener")
public class TaskCompleteListener implements FlowableEventListener {
   private static final Logger log = LoggerFactory.getLogger(TaskCompleteListener.class);

   public TaskCompleteListener() {
   }

   public void onEvent(FlowableEvent event) {
      Map<String, TaskComplateInvoker> type = SpringContextUtil.getBeansOfType(TaskComplateInvoker.class);
      type.values().parallelStream().forEach(
         (invoker) -> {
            invoker.consume(event);
         }
      );
   }

   public boolean isFailOnException() {
      return false;
   }

   public boolean isFireOnTransactionLifecycleEvent() {
      return false;
   }

   public String getOnTransaction() {
      return null;
   }


}

4、流程任意节点结束时

@Slf4j
@Component("activityCompletedListener")
public class ActivityCompletedListener implements FlowableEventListener {

   @Autowired
   private RepositoryService repositoryService;
   @Autowired
   private RuntimeService runtimeService;
   @Autowired
   private TaskService taskService;
   @Autowired
   private HistoryService historyService;
   @Autowired
   private ApplicationEventPublisher applicationEventPublisher;

   @Override
   public void onEvent(FlowableEvent event) {
      if(event instanceof FlowableActivityEventImpl) {
         FlowableActivityEventImpl activityEvent = (FlowableActivityEventImpl) event;
         String activityType = activityEvent.getActivityType();

         if(!"userTask".equals(activityType)) {
            return;
         }

         String processDefinitionId = activityEvent.getProcessDefinitionId();
         String processInstanceId = activityEvent.getProcessInstanceId();

         ProcessInstance processInstance =
            runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();

         ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);

         List<HistoricActivityInstance> allProcessNodes = historyService.createHistoricActivityInstanceQuery()
            .activityId(processInstance.getActivityId())
            .processInstanceId(processInstanceId)
            .list();
         List<HistoricActivityInstance> userTaskList =
            allProcessNodes.stream().filter(e -> e.getActivityType().equals("userTask")).collect(Collectors.toList());
         //判断节点列表中是否大于两个节点、第一个必为填报节点、第二个必为审批节点
         String businessKey = processInstance.getBusinessKey();
         if(userTaskList.size() >= 2) {

            ListenerEvent listenerEvent = new ListenerEvent();
            listenerEvent.setApprovalResults("PROGRESS_IN");
            listenerEvent.setBusinessKey(businessKey);
            listenerEvent.setDefinitionId(processDefinitionId);
            listenerEvent.setProcessInstanceId(processInstanceId);
            listenerEvent.setDefinitionKey(processDefinition.getKey());
            listenerEvent.setDefinitionName(processDefinition.getName());
            this.applicationEventPublisher.publishEvent(new ListenerEventSource(listenerEvent));
         }
      }
   }

   @Override
   public boolean isFailOnException() {
      return false;
   }

   @Override
   public boolean isFireOnTransactionLifecycleEvent() {
      return false;
   }

   @Override
   public String getOnTransaction() {
      return null;
   }
}

二、流程跳过逻辑

/**
    * 对已驳回的流程发起时选择从驳回节点开始审批
    * @param oldInstId
    * @param newInstId
    */
   public void skipPreAgreeNodes(String oldInstId, String newInstId){
      HistoricProcessInstance processInstance =
         historyService.createHistoricProcessInstanceQuery().processInstanceId(oldInstId).singleResult();
      String deployId = Optional.ofNullable(processInstance)
         .map(HistoricProcessInstance::getProcessDefinitionId)
         .map(item -> item.substring(item.lastIndexOf(":") + 1))
         .orElse(null);
      Map<String, Object> record = this.flowTaskService.flowRecord(oldInstId, deployId);
      List<FlowTask> flowList = (List<FlowTask>) record.getOrDefault("flowList", Lists.newArrayList());
      if(flowList.size()<2) {
         return;
      }
      FlowTask lastRejectNode = flowList.get(0);
      List<Task> list;
      int count = 0;
      int times = 0;
      int excludeFillAndRejectCount = flowList.size() - 2;
      while((list=taskService.createTaskQuery().processInstanceId(newInstId).list()).stream()
         .noneMatch(e->e.getTaskDefinitionKey().equals(lastRejectNode.getTaskDefKey()))
         || count< excludeFillAndRejectCount
         || times< excludeFillAndRejectCount
      ) {
         //正常情况下list中只有一个元素(非多实例)、多实例节点时此处有多个元素
         for(Task task : list) {
            //筛选办理记录中节点定义id与当前id一致的记录
            List<FlowTask> allTasks = flowList.stream()
               .filter(e -> e.getTaskDefKey().equals(task.getTaskDefinitionKey()))
               .collect(Collectors.toList());
            String assignee = task.getAssignee();
            for(FlowTask allTask : allTasks) {
               //多实例节点时assignee不可能为空,需根据审批记录中的审批人id 判断需不需要跳过该用户
               if(assignee !=null && !assignee.equals(allTask.getAssigneeId())) {
                  continue;
               }
               FlowTaskVo flowTaskVo = new FlowTaskVo();
               BeanUtils.copyProperties(task, flowTaskVo);
               flowTaskVo.setInstanceId(newInstId);
               flowTaskVo.setComment(allTask.getComment().getComment());
               flowTaskVo.setAssignee(allTask.getAssigneeId());
               flowTaskVo.setUserId(allTask.getAssigneeId());
               flowTaskVo.setTaskId(task.getId());
               List<Attachment> attachmentList = allTask.getAttachmentList();
               if(CollUtil.isNotEmpty(attachmentList)) {
                  List<MultipartFile> files = attachmentList.stream().map(f -> {
                     InputStream attachmentContent = taskService.getAttachmentContent(f.getId());
                     try {
                        return new MockMultipartFile(f.getName(), attachmentContent);
                     }
                     catch(Exception e) {
                        e.printStackTrace();
                        return null;
                     }
                  }).filter(Objects::nonNull).collect(Collectors.toList());
                  flowTaskVo.setFiles(files);
               }
               this.flowTaskService.complete(flowTaskVo);
               count++;
            }
         }
         times ++;
      }
   }

三、流程结束时遇到的问题(结束了但业务表状态未修改成功)

import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import cn.togeek.foundation.web.api.BusinessCode;
import cn.togeek.foundation.web.exception.BusinessException;
import cn.togeek.geekbidder.crm.contract.entity.ContractEntity;
import cn.togeek.geekbidder.crm.contract.enums.ApprovalState;
import cn.togeek.geekbidder.crm.contract.mapper.ContractNewMapper;
import cn.togeek.maple.process.api.enums.FlowCommentEnum;
import cn.togeek.maple.process.api.model.ListenerEvent;
import cn.togeek.maple.process.api.model.ListenerEventSource;

import java.util.Arrays;
import java.util.Map;
import java.util.Objects;

import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;


@Component
@AllArgsConstructor
@Slf4j
public class ContractFlowEventListener implements ApplicationListener<ListenerEventSource> {

   @Autowired
   private RuntimeService runtimeService;
   @Autowired
   private RepositoryService repositoryService;
   @Autowired
   private ContractNewMapper contractNewMapper;
   @Autowired
   private HistoryService historyService;
   /**
    * 流程结束监听
    *
    * @param event
    */
   @Override
   public void onApplicationEvent(ListenerEventSource event) {
      callBackWithStateCal(event);
   }

   public synchronized void callBackWithStateCal(ListenerEventSource event){
      log.info("------------------流程结束回调--------------------");
      HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery()
         .processInstanceId(event.getListenerEvent().getProcessInstanceId())
         .singleResult();

      if(Objects.isNull(processInstance)) {
         return;
      }
      String deploymentId = processInstance.getDeploymentId();
      ProcessDefinition processDefinition =
         repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
      if(Objects.isNull(processDefinition)) {
         return;
      }
      String defKey = processDefinition.getKey();
      //只对发布的事件中关注的事件进行处理
      boolean needDeal = Arrays.stream(ContractApproveGlobal.values())
         .map(ContractApproveGlobal::getDefKey)
         .anyMatch(e -> e.equals(defKey));
      log.info("------------------needDeal{}--------------------"+needDeal);
      if(needDeal) {
         String id = processInstance.getBusinessKey();
         ListenerEvent listenerEvent = event.getListenerEvent();
         String approvalResults = listenerEvent.getApprovalResults();
         log.info("------------------approvalResults{}--------------------"+approvalResults);
         ContractEntity contractEntity = contractNewMapper.selectById(id);
         Assert.notNull(contractEntity, () -> {
            throw new BusinessException(BusinessCode.CLIENT_ERROR.getCode(), String.format(
               "合同id为"+ id + "的合同不存在,可能已被删除,请检查!!!"));
         });
         if(FlowCommentEnum.NORMAL.getType().equals(approvalResults)) {
            ContractApproveGlobal dbNode = ContractApproveGlobal.getByCode(contractEntity.getApprovalNode());
            ContractApproveGlobal nextNode = dbNode.getNearNode()[1];
            contractEntity.setCurrentApprovalUser("无");
            //上一个流程节点结束时将全局流程节点改为下一个节点
            if(nextNode!=null && Integer.parseInt(nextNode.getCode()) <=3) {
               // 审批通过
               contractEntity.setApprovalState("0");
               contractEntity.setApprovalNode(nextNode.getCode());
            } else {
               contractEntity.setApprovalState(ApprovalState.CHANGE.getCode());
            }
         }
         else if(FlowCommentEnum.REJECT.getType().equals(approvalResults)) {
            // 审批驳回
            Integer rejectCnt = contractEntity.getRejectCnt();
            contractEntity.setCurrentApprovalUser("无");
            rejectCnt = rejectCnt == null ? 0 : rejectCnt;
            contractEntity.setRejectCnt(rejectCnt+1);
            contractEntity.setApprovalState(ApprovalState.TERMINATION.getCode());
         }
         else if("PROGRESS_IN".equals(approvalResults)) {
            contractEntity.setApprovalState(ApprovalState.EXECUTE.getCode());
         } else if(FlowCommentEnum.REBACK.getType().equals(approvalResults)) {
            // 审批退回
            contractEntity.setApprovalState(ApprovalState.UN_SUBMIT.getCode());
         }
         contractNewMapper.updateById(contractEntity);
      }
   }
}

原因总结:由于流程结束时使用了 runtimeService 取流程实例已经取不到了,导致后续的操作都失败了,使用 historyService 则可以避免此问题

 

标签:xml,String,flowable,private,监听器,import,org,togeek,cn
From: https://www.cnblogs.com/g177w/p/18420999

相关文章

  • BeautifulSoup与lxml解析网页:技术详解与实战案例
    在Python的Web数据抓取和网页解析领域,BeautifulSoup和lxml是两个极为强大且常用的库。它们能够帮助开发者轻松地从HTML或XML文档中提取所需数据,广泛应用于爬虫开发、数据预处理、自动化测试等领域。本文将详细介绍如何使用BeautifulSoup和lxml解析网页,并通过丰富的代码和案例帮助......
  • xml字符串转JSON字符串
    xml字符串转JSON字符串,可以直接通过jackson提供的方法进行快速转换。在web项目中通常会引入spring-boot-starter-web依赖。但是spring-boot-starter-web依赖包括Jackson的JSON处理库(如jackson-databind、jackson-core等),不一定直接包含处理XML的Jackson库(如jackson-dataformat-xml......
  • 最终版本的pom.xml(springboot连接hive)
    1<?xmlversion="1.0"encoding="UTF-8"?>2<projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"3xsi:schemaLocation="http://maven.apa......
  • XML映射器-高级查询结果映射
    01-高级查询结果映射emp表dept表02-多表关联查询映射多对一映射项目中Emp类的数据项目中dept类的数据想要多表查询需要建个公共类里面写入两个表中的属性,如下面方法type里要写用到的类型,由于继承Emp所有Emp里面的属性直接写,column是写数据库的别字,property是......
  • C#——LINQ to XML(使用 Descendants 方法查找单个子代)
    xml位于命名空间中时查找staticvoidMain(string[]args){XElementroot=XElement.Parse(@"<aw:Rootxmlns:aw='http://www.efun.com'><aw:Child1><aw:GrandChild1>GC1Value</aw:GrandChild1>&l......
  • C#——LINQ to XML(内容快速查找)
    staticvoidMain(string[]args){XElementpurchaseOrder=XElement.Load("Contacts.xml");stringpartNos=(string)(fromiteminpurchaseOrder.Descendants("City")......
  • XML标记语言
    XML简介XML(可扩展标记语言)是一种标记语言,设计用于存储和传输数据。与HTML相比,XML主要用于描述数据的内容,而不是展示数据的外观。它由万维网联盟(W3C)制定,是一种跨平台、系统无关的语言。XML的核心特性之一是它的可扩展性,允许用户根据需要创建自定义标签,从而使得XML可以在......
  • C#里方法的怎么编写XML文档注释说明,用于调用时参数提示等
    一.什么是xml的注释?答:XML注解是一种用于描述XML文档结构和元素内容的标记语言。它是通过在XML文档中使用特殊的标记来定义文档结构和元素属性的。XML注解通常用于数据编码和数据交换的应用程序之间,以确保数据的一致性和互操作性。XML注解具有良好的可扩展性和可读性,因此它通......
  • XXE-XML实体注入漏洞概述
    XXE-"xmlexternalentityinjection"既"xml外部实体注入漏洞"。概括一下就是"攻击者通过向服务器注入指定的xml实体内容,从而让服务器按照指定的配置进行执行,导致问题"也就是说服务端接收和解析了来自用户端的xml数据,而又没有做严格的安全控制,从而导致xml外部实体注入......
  • AndroidManifest.xml文件的重要信息
    AndroidManifest.xml文件详解一、概述AndroidManifest.xml文件是Android应用的核心配置文件,它位于应用程序的根目录下(通常在app/src/main/文件夹中)。这个文件对于Android系统来说至关重要,因为它提供了关于应用程序的所有必要信息,包括应用程序的组件、权限要求、应用程序的配......