- Drools 教程
- Drools - 首页
- Drools - 简介
- Drools - Eclipse 插件
- Drools - 运行时
- Drools - 创建 Drools 程序
- Drools - 常见术语
- Drools - 规则编写
- Drools - 规则语法
- Drools - Drools 程序示例
- Drools - 调试
- Drools 有用资源
- Drools 快速指南
- Drools - 有用资源
- Drools - 讨论
Drools 快速指南
Drools - 简介
任何 Java 企业级应用程序都可以分为三个部分:
- UI - 用户界面(前端)
- 服务层,它又连接到数据库
- 业务层
我们有很多框架可以一起处理 UI 和服务层,例如 Spring 和 Struts。然而,在 Drools 出现之前,我们并没有标准的方法来处理业务逻辑。
什么是 Drools?
Drools 是一个**业务逻辑集成平台 (BLiP)**。它是用 Java 编写的。它是一个开源项目,由 JBoss 和 Red Hat, Inc. 支持。它扩展并实现了 Rete 模式匹配算法。
简单来说,Drools 是一组工具,允许我们分离和推理业务流程中发现的逻辑和数据。我们需要注意的两个重要关键词是**逻辑**和**数据**。
Drools 主要分为两部分:**创作**和**运行时**。
**创作** - 创作过程涉及创建规则文件(.DRL 文件)。
**运行时** - 它涉及创建工作内存和处理激活。
什么是规则引擎?
Drools 是规则引擎或生产规则系统,它使用基于规则的方法来实现专家系统。专家系统是基于知识的系统,它使用知识表示将获取的知识处理成可以用于推理的知识库。
生产规则系统是图灵完备的,它专注于知识表示,以简洁、明确和声明的方式表达命题逻辑和一阶逻辑。
生产规则系统的核心是一个**推理引擎**,它可以扩展到大量的规则和事实。推理引擎将事实和数据与生产规则(也称为**产生式**或仅称为**规则**)进行匹配,以推断导致行动的结论。
生产规则是一个两部分的结构,它使用一阶逻辑对知识表示进行推理。业务规则引擎是一个软件系统,它在运行时生产环境中执行一个或多个业务规则。
规则引擎允许你定义“**做什么**”,而不是“**如何做**”。
什么是规则?
规则是知识片段,通常表达为:“_当_某些条件发生时,_则_执行某些任务。”
When <Condition is true> Then <Take desired Action>
规则最重要的部分是它的**when**部分。如果**when**部分满足,则触发**then**部分。
rule <rule_name> <attribute> <value> when <conditions> then <actions> end
模式匹配
将新的或现有的事实与生产规则进行匹配的过程称为模式匹配,它由推理引擎执行。有很多算法用于模式匹配,包括:
- 线性
- Rete
- Treat
- Leaps
Drools 实现并扩展了 Rete 算法。Drools 的 Rete 实现称为 ReteOO,这表示 Drools 对面向对象的系统进行了 Rete 算法的增强和优化实现。
规则引擎的优点
声明式编程
规则使表达对复杂问题的解决方案并验证解决方案变得容易。与代码不同,规则是用不太复杂的语言编写的;业务分析师可以轻松阅读和验证一组规则。
逻辑和数据分离
数据驻留在领域对象中,业务逻辑驻留在规则中。根据项目的类型,这种分离可能非常有利。
速度和可扩展性
Drools 所基于的 Rete OO 算法已经是一个经过验证的算法。借助 Drools,您的应用程序变得非常可扩展。如果频繁更改请求,可以添加新的规则而无需修改现有规则。
知识中心化
通过使用规则,您可以创建一个可执行的知识库(知识库)。它是业务策略的单一事实来源。理想情况下,规则的可读性很高,也可以用作文档。
工具集成
Eclipse 等工具提供编辑和管理规则以及获得即时反馈、验证和内容辅助的方法。审核和调试工具也可用。
Drools - Eclipse 插件
以下是安装 Drools 插件的先决条件:
- Java 1.5(或更高版本)SE JDK
- Eclipse 4.2(或任何版本)和 Drools 插件
由于 Drools 是用 Java 编写的 BRMS(业务规则管理系统),本节将介绍如何添加所需的插件。考虑到大多数 Java 用户使用 Eclipse,让我们看看如何在 Eclipse 中添加 Drools 5.x.0 插件。
步骤 1:下载二进制文件
从以下链接下载二进制文件:
https://download.jboss.org/drools/release/5.3.0.Final/
下载完成后,将文件解压缩到您的硬盘。
步骤 2:安装软件
启动 Eclipse 并转到帮助→安装新软件。单击“添加”,如下图所示。
然后,单击此处显示的“本地”,然后选择“……/binaries/org.drools.updatesite”。
选择 Drools 和 jBPM,然后单击“下一步”。
再次单击“下一步”。然后,接受条款和许可协议,然后单击“完成”。
单击“完成”后,软件安装开始:
安装成功后,您将获得以下对话框:
单击“是”。Eclipse 重启后,转到 Windows → 首选项
您可以在首选项下看到 Drools。您的 Drools 插件安装现在已完成。
Drools - 运行时
Drools 运行时需要指示编辑器使用特定版本的 Drools jar 运行程序。您可以使用不同的 Drools 运行时运行您的程序/应用程序。
单击 Windows → 首选项 → Drools → 已安装的 Drools 运行时。然后单击“添加”,如下图所示。
然后,单击此处显示的“创建新的 Drools 运行时”。
输入到您已下载 droolsjbpm-tools-distribution-5.3.0.Final.zip 的二进制文件文件夹的路径。
单击“确定”并为 Drools 运行时提供一个名称。Drools 运行时现在已创建。
Drools - 创建 Drools 程序
要创建一个基本的 Drools 程序,请打开 Eclipse。转到文件→新建→项目。
选择 Drools 项目。为项目命名。例如,DroolsTest。
下一个屏幕提示您选择要在第一个 Drools 项目中包含的一些文件。
选择前两个文件。第一个文件是 .drl 文件(Drools 规则文件),第二个文件是用于加载和执行 HelloWorld 规则的 Java 类。
单击“下一步”→“完成”。
单击“完成”后,将在您的工作区中创建一个
接下来,我们将讨论规则引擎中常用的术语。
Drools - 常见术语
规则
规则引擎的核心,您可以在其中指定条件(如果“a”,则“b”)。
事实
事实是规则将作用于的数据。从 Java 的角度来看,事实是 POJO(普通旧 Java 对象)。
会话
Drools 中的知识会话是触发规则的核心组件。它是保存所有规则和其他资源的知识会话。知识会话是从知识库创建的。
为了使规则引擎工作,事实被插入到会话中,当条件满足时,随后的规则被触发。会话有两种类型:
- 无状态知识会话
- 有状态知识会话
议程
这是一个逻辑概念。议程是激活等待被触发的逻辑位置。
激活
激活是规则的**then**部分。激活被放置在议程中,其中适当的规则被触发。
Drools - 规则编写
如果您查看在 HelloWorld 项目(Sample.drl)中编写的默认规则,则使用了许多我们将解释的关键字。
Sample.drl
**包** - 每个规则都以包名称开头。包充当规则的命名空间。包中的规则名称必须唯一。规则中的包类似于 Java 中的包。
**导入语句** - 您想要应用规则的事实需要导入。例如,在上面的示例中为 com.sample.DroolsTest.Message;
**规则定义** - 它由规则名称、条件和结果组成。Drools 关键字为**rule、when、then**和**end**。在上面的示例中,规则名称为“Hello World”和“GoodBye”。**when**部分是两个规则中的条件,**then**部分是结果。在规则术语中,**when**部分也称为 LHS(左侧),**then**部分也称为 RHS(右侧)。
现在让我们来看一下用于加载 Drools 并执行规则的 Java 文件中使用的术语。
知识库
知识库是一个接口,它管理规则、流程和内部类型的集合。它包含在包**org.drools.KnowledgeBase**中。在 Drools 中,这些通常称为**知识定义**或**知识**。知识定义分组到**知识包**中。可以添加或删除知识定义。知识库的主要目的是存储和重用它们,因为它们的创建成本很高。知识库提供创建知识会话的方法。
知识会话
知识会话是从知识库中检索的。它是与 Drools 引擎交互的主要接口。知识会话可以有两种类型:
无状态知识会话
有状态知识会话
无状态知识会话
无状态知识会话是一个无状态会话,它构成了最简单的用例,不使用推理。无状态会话可以像函数一样调用,将一些数据传递给它,然后接收一些结果。无状态会话的常见示例包括:
验证
这个人是否有资格获得抵押贷款?
计算
计算抵押贷款保险费。
路由和过滤
将传入的消息(例如电子邮件)过滤到文件夹中。
将传入的消息发送到目的地
有状态知识会话
有状态会话的寿命更长,并允许随着时间的推移进行迭代更改。有状态会话的一些常见用例包括:
监控
股票市场监控和分析,用于半自动购买。
诊断
故障查找,医学诊断
物流
包裹追踪和交付供应
知识构建器
KnoledgeBuilder 接口负责从知识定义(规则、流程、类型)构建 KnowledgePackage。它包含在包**org.drools.builder.KnowledgeBuilder**中。知识定义可以采用多种格式。如果构建有任何问题,KnowledgeBuilder 将通过这两种方法报告错误:**hasErrors** 和 **getError**。
下图解释了这个过程
在上面的例子中,因为我们采用的是无状态知识会话的简单示例,我们在会话中插入了事实,然后调用了fireAllRules()方法,您可以看到输出。
对于有状态知识会话,一旦规则被触发,有状态知识会话对象必须调用方法dispose()来释放会话并避免内存泄漏。
Drools - 规则语法
正如您所看到的.drl(规则文件)有其自身的语法,让我们在本章中介绍规则语法的部分内容。
规则中的条件
一条规则可以包含许多条件和模式,例如:
- Account (balance == 200)
- Customer (name == “Vivek”)
上述条件检查账户余额是否为200或客户姓名是否为“Vivek”。
规则中的变量
Drools中的变量名以美元符号($)开头。
- $account − Account( )
- $account 是Account()类的变量
Drools可以处理所有原生Java类型,甚至枚举。
规则中的注释
可以使用特殊字符#或//来标记单行注释。
对于多行注释,请使用以下格式:
/* Another line ......... ......... */
全局变量
全局变量是分配给会话的变量。它们可以出于各种原因使用,如下所示:
用于输入参数(例如,可以逐个会话自定义的常量值)。
用于输出参数(例如,报告——规则可以向全局报告变量写入一些消息)。
用于日志记录等服务的入口点,这些服务可在规则中使用。
规则中的函数
函数是一个便利特性。它们可用于条件和结论中。函数代表实用程序/辅助类的替代方案。例如:
function double calculateSquare (double value) { return value * value; }
方言
方言指定在条件或结论中的任何代码表达式中使用的语法。它包括返回值、evals、内联evals、谓词、salience表达式、结论等等。默认值为Java。Drools目前支持另一种称为MVEL的方言。默认方言可以在包级别指定,如下所示:
package org.mycompany.somePackage dialect "mvel"
MVEL方言
MVEL是用于基于Java的应用程序的表达式语言。它支持字段和方法/getter访问。它基于Java语法。
优先级 (Salience)
优先级是规则语法的一个非常重要的特性。优先级由冲突解决策略用于决定首先触发哪个规则。默认情况下,它是主要标准。
我们可以使用优先级来定义规则的触发顺序。优先级有一个属性,它接受任何返回int类型数字的表达式(正数和负数都是有效的)。值越高,冲突解决策略选择该规则触发的可能性就越大。
salience ($account.balance * 5)
默认优先级值为0。我们在只为某些规则分配优先级值时应记住这一点。
规则语法中还有许多其他特性/参数,但我们这里只介绍了重要的部分。
规则结论关键字
规则结论关键字是在规则的“then”部分中使用的关键字。
Modify − 事实的属性可以在规则的then部分中修改。
Insert − 基于某个条件,如果为真,则可以将新的事实插入到规则引擎的当前会话中。
Retract − 如果规则中特定条件为真,并且您不想对该事实采取任何其他操作,则可以从规则引擎中撤回该特定事实。
注意 − 在规则结论中使用条件逻辑(if语句)被认为是一种非常不好的做法。大多数情况下,应该创建一个新的规则。
Drools - Drools 程序示例
在本章中,我们将为以下问题陈述创建一个Drools项目:
根据城市和产品类型(城市和产品的组合),找出与该城市相关的当地税。
我们的Drools项目将有两个DRL文件。这两个DRL文件将表示所考虑的两个城市(Pune和Nagpur)和四种类型的产品(食品杂货、药品、手表和奢侈品)。
这两个城市药品的税收均视为零。
对于食品杂货,我们假设Pune的税收为2卢比,Nagpur的税收为1卢比。
我们使用了相同的售价来演示不同的输出。请注意,所有规则都在应用程序中被触发。
这是一个用于保存每个itemType的模型:
package com.sample; import java.math.BigDecimal; public class ItemCity { public enum City { PUNE, NAGPUR } public enum Type { GROCERIES, MEDICINES, WATCHES, LUXURYGOODS } private City purchaseCity; private BigDecimal sellPrice; private Type typeofItem; private BigDecimal localTax; public City getPurchaseCity() { return purchaseCity; } public void setPurchaseCity(City purchaseCity) { this.purchaseCity = purchaseCity; } public BigDecimal getSellPrice() { return sellPrice; } public void setSellPrice(BigDecimal sellPrice) { this.sellPrice = sellPrice; } public Type getTypeofItem() { return typeofItem; } public void setTypeofItem(Type typeofItem) { this.typeofItem = typeofItem; } public BigDecimal getLocalTax() { return localTax; } public void setLocalTax(BigDecimal localTax) { this.localTax = localTax; } }
DRL文件
如前所述,我们这里使用了两个DRL文件:Pune.drl和Nagpur.drl。
Pune.drl
这是执行Pune市规则的DRL文件。
// created on: Dec 24, 2014 package droolsexample // list any import classes here. import com.sample.ItemCity; import java.math.BigDecimal; // declare any global variables here dialect "java" rule "Pune Medicine Item" when item : ItemCity (purchaseCity == ItemCity.City.PUNE, typeofItem == ItemCity.Type.MEDICINES) then BigDecimal tax = new BigDecimal(0.0); item.setLocalTax(tax.multiply(item.getSellPrice())); end rule "Pune Groceries Item" when item : ItemCity(purchaseCity == ItemCity.City.PUNE, typeofItem == ItemCity.Type.GROCERIES) then BigDecimal tax = new BigDecimal(2.0); item.setLocalTax(tax.multiply(item.getSellPrice())); end
Nagpur.drl
这是执行Nagpur市规则的DRL文件。
// created on: Dec 26, 2014 package droolsexample // list any import classes here. import com.sample.ItemCity; import java.math.BigDecimal; // declare any global variables here dialect "java" rule "Nagpur Medicine Item" when item : ItemCity(purchaseCity == ItemCity.City.NAGPUR, typeofItem == ItemCity.Type.MEDICINES) then BigDecimal tax = new BigDecimal(0.0); item.setLocalTax(tax.multiply(item.getSellPrice())); end rule "Nagpur Groceries Item" when item : ItemCity(purchaseCity == ItemCity.City.NAGPUR, typeofItem == ItemCity.Type.GROCERIES) then BigDecimal tax = new BigDecimal(1.0); item.setLocalTax(tax.multiply(item.getSellPrice())); end
我们根据城市编写了DRL文件,因为它使我们能够在以后添加新的城市时添加任意数量的规则文件。
为了演示所有规则都从我们的规则文件中触发,我们使用了两种商品类型(药品和食品杂货);药品免税,食品杂货按城市征税。
我们的测试类加载规则文件,将事实插入会话,并产生输出。
Droolstest.java
package com.sample; import java.math.BigDecimal; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseFactory; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderError; import org.drools.builder.KnowledgeBuilderErrors; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.io.ResourceFactory; import org.drools.runtime.StatefulKnowledgeSession; import com.sample.ItemCity.City; import com.sample.ItemCity.Type; /* *This is a sample class to launch a rule. */ public class DroolsTest { public static final void main(String[] args) { try { // load up the knowledge base KnowledgeBase kbase = readKnowledgeBase(); StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession(); ItemCity item1 = new ItemCity(); item1.setPurchaseCity(City.PUNE); item1.setTypeofItem(Type.MEDICINES); item1.setSellPrice(new BigDecimal(10)); ksession.insert(item1); ItemCity item2 = new ItemCity(); item2.setPurchaseCity(City.PUNE); item2.setTypeofItem(Type.GROCERIES); item2.setSellPrice(new BigDecimal(10)); ksession.insert(item2); ItemCity item3 = new ItemCity(); item3.setPurchaseCity(City.NAGPUR); item3.setTypeofItem(Type.MEDICINES); item3.setSellPrice(new BigDecimal(10)); ksession.insert(item3); ItemCity item4 = new ItemCity(); item4.setPurchaseCity(City.NAGPUR); item4.setTypeofItem(Type.GROCERIES); item4.setSellPrice(new BigDecimal(10)); ksession.insert(item4); ksession.fireAllRules(); System.out.println(item1.getPurchaseCity().toString() + " " + item1.getLocalTax().intValue()); System.out.println(item2.getPurchaseCity().toString() + " " + item2.getLocalTax().intValue()); System.out.println(item3.getPurchaseCity().toString() + " " + item3.getLocalTax().intValue()); System.out.println(item4.getPurchaseCity().toString() + " " + item4.getLocalTax().intValue()); } catch (Throwable t) { t.printStackTrace(); } } private static KnowledgeBase readKnowledgeBase() throws Exception { KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(); kbuilder.add(ResourceFactory.newClassPathResource("Pune.drl"), ResourceType.DRL); kbuilder.add(ResourceFactory.newClassPathResource("Nagpur.drl"), ResourceType.DRL); KnowledgeBuilderErrors errors = kbuilder.getErrors(); if (errors.size() > 0) { for (KnowledgeBuilderError error: errors) { System.err.println(error); } throw new IllegalArgumentException("Could not parse knowledge."); } KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(); kbase.addKnowledgePackages(kbuilder.getKnowledgePackages()); return kbase; } }
如果您运行此程序,其输出将如下所示:
PUNE 0 PUNE 20 NAGPUR 0 NAGPUR 10
对于Pune和Nagpur,当商品是药品时,当地税为零;而当商品是食品杂货时,税收则根据城市而定。可以在DRL文件中为其他产品添加更多规则。这只是一个示例程序。
从DRL文件调用外部函数
在这里,我们将演示如何从DRL文件中调用Java文件中的静态函数。
首先,在同一个包com.sample中创建一个类HelloCity.java。
package com.sample; public class HelloCity { public static void writeHello(String name) { System.out.println("HELLO " + name + "!!!!!!"); } }
然后,在DRL文件中添加import语句以从DRL文件调用writeHello方法。在下面的代码块中,Pune.drl文件中更改的部分以黄色突出显示。
// created on: Dec 24, 2014 package droolsexample // list any import classes here. import com.sample.ItemCity; import java.math.BigDecimal; import com.sample.HelloCity; //declare any global variables here dialect "java" rule "Pune Medicine Item" when item : ItemCity(purchaseCity == ItemCity.City.PUNE, typeofItem == ItemCity.Type.MEDICINES) then BigDecimal tax = new BigDecimal(0.0); item.setLocalTax(tax.multiply(item.getSellPrice())); HelloCity.writeHello(item.getPurchaseCity().toString()); end rule "Pune Groceries Item" when item : ItemCity(purchaseCity == ItemCity.City.PUNE, typeofItem == ItemCity.Type.GROCERIES) then BigDecimal tax = new BigDecimal(2.0); item.setLocalTax(tax.multiply(item.getSellPrice())); end
再次运行程序,其输出将如下所示:
HELLO PUNE!!!!!!
PUNE 0
PUNE 20
NAGPUR 0
NAGPUR 10
现在输出中的差异以黄色标记,显示了Java类中静态方法的输出。
调用Java方法的优点是我们可以用Java编写任何实用程序/辅助函数,并从DRL文件中调用它。
Drools - 调试
调试Drools项目的方法有很多种。在这里,我们将编写一个实用程序类来让您知道哪些规则正在触发或被触发。
通过这种方法,您可以检查Drools项目中触发的所有规则。这是我们的实用程序类
Utility.java
package com.sample; import org.drools.spi.KnowledgeHelper; public class Utility { public static void help(final KnowledgeHelper drools, final String message){ System.out.println(message); System.out.println("\nrule triggered: " + drools.getRule().getName()); } public static void helper(final KnowledgeHelper drools){ System.out.println("\nrule triggered: " + drools.getRule().getName()); } }
第一个方法help打印触发的规则以及您可以通过DRL文件作为String传递的一些额外信息。
第二个规则helper打印特定规则是否被触发。
我们在每个DRL文件中添加了一个实用程序方法。我们还在DRL文件(Pune.drl)中添加了导入函数。在规则的then部分,我们添加了实用程序函数调用。修改后的Pune.drl如下所示。更改部分以蓝色突出显示。
修改后的Pune.drl
//created on: Dec 24, 2014 package droolsexample //list any import classes here. import com.sample.ItemCity; import java.math.BigDecimal; import com.sample.HelloCity; import function com.sample.Utility.helper; // declare any global variables here dialect "java" rule "Pune Medicine Item" when item : ItemCity(purchaseCity == ItemCity.City.PUNE, typeofItem == ItemCity.Type.MEDICINES) then BigDecimal tax = new BigDecimal(0.0); item.setLocalTax(tax.multiply(item.getSellPrice())); HelloCity.writeHello(item.getPurchaseCity().toString()); helper(drools); end rule "Pune Groceries Item" when item : ItemCity(purchaseCity == ItemCity.City.PUNE, typeofItem == ItemCity.Type.GROCERIES) then BigDecimal tax = new BigDecimal(2.0); item.setLocalTax(tax.multiply(item.getSellPrice())); helper(drools); end
同样,我们在第二个DRL文件(Nagpur.drl)中添加了另一个实用程序函数。以下是修改后的代码:
修改后的Nagpur.drl
// created on: Dec 26, 2014 package droolsexample // list any import classes here. import com.sample.ItemCity; import java.math.BigDecimal; import function com.sample.Utility.help; //declare any global variables here dialect "java" rule "Nagpur Medicine Item" when item : ItemCity(purchaseCity == ItemCity.City.NAGPUR, typeofItem == ItemCity.Type.MEDICINES) then BigDecimal tax = new BigDecimal(0.0); item.setLocalTax(tax.multiply(item.getSellPrice())); help(drools,"added info"); end rule "Nagpur Groceries Item" when item : ItemCity(purchaseCity == ItemCity.City.NAGPUR, typeofItem == ItemCity.Type.GROCERIES) then BigDecimal tax = new BigDecimal(1.0); item.setLocalTax(tax.multiply(item.getSellPrice())); help(drools,"info"); end
再次运行程序,它应该产生以下输出:
info rule triggered: Nagpur Groceries Item added info rule triggered: Nagpur Medicine Item rule triggered: Pune Groceries Item HELLO PUNE!!!!!! rule triggered: Pune Medicine Item PUNE 0 PUNE 20 NAGPUR 0 NAGPUR 10
调用了两个实用程序函数,它显示了特定规则是否被调用。在上面的例子中,所有规则都被调用,但在企业应用程序中,此实用程序函数对于调试和找出特定规则是否被触发非常有用。
在Eclipse中使用调试透视图
您可以在Drools应用程序执行期间调试规则。您可以在规则的结论中添加断点,每当在规则执行期间遇到这样的断点时,执行将暂时停止。然后,您可以像在Java应用程序中一样检查此时已知的变量,并使用Eclipse中提供的正常调试选项。
要在DRL文件中创建断点,只需双击要在其中创建断点的行即可。请记住,您只能在规则的then部分创建断点。可以通过双击DRL编辑器中的断点来删除断点。
应用断点后,您需要将应用程序作为Drools应用程序进行调试。只有当您的应用程序作为Drools应用程序进行调试时,Drools断点(DRL文件中的断点)才能工作。以下是您需要执行的操作:
将应用程序作为Drools应用程序调试后,您将看到DRL文件上的控件,如下面的屏幕截图所示:
您可以看到变量以及该调试点处对象的当前值。此处同样适用F6移至下一行和F8跳转至下一个调试点的相同控件。通过这种方式,您可以调试Drools应用程序。
注意 − 直到Drools 5.x,只有当方言为MVEL时,Drools应用程序中的调试透视图才有效。