一、Eclipse简介

Eclipse最初是由IBM捐献给开源社区的,目前已经发展成为人气最旺的Java IDE。Eclipse插件化的功能模块吸引了无数开发者开发基于Eclipse的功能插件。事实上,Eclipse已经超越了一般Java IDE的概念。Eclipse是一个平台,一个开放的平台,你可以为Eclipse添加任何你想要的功能,比如播放音乐,观看电影,聊天……这些不是天方夜谭,而是已经实现的事实。虽然Eclipse可以添加很多附加功能,可以编辑C/C++,可以编辑Word文件,可以开发UML等等,但是Eclipse最基本,也是最强大的功能还是Java IDE。

二、RCP简介

RCP的全称是Rich Client Platform,可以把它看成是Eclipse的骨架,其他的插件是器官与血肉。我们可以把这个骨架拿过来填入自己的器官和血肉,这样就创造了我们自己的“Eclipse”!

使用RCP来开发Java桌面应用可以把开发的焦点转移到系统的逻辑功能上,而不是界面上。我们自己的程序可以继承Eclipse的风格与功能,而不用自己去编写诸如菜单,工具条,子窗口等等的界面元素。甚至我们可以把Eclipse本身的功能插件,比如Console拿来放在自己的程序里,这样就避免了很多重复开发。

三、知识准备

我写这篇文章并不是面向Java的初学者,而是面向有一定Eclipse使用基础的开发者。所以我假设你已经具备一下基本知识:

1、 Java基础

2、 用过Eclipse进行开发

3、 SWT/JFace开发基础(可选)

如果你还不具备上述条件,那么看我的文章你会看的很郁闷,建议你先去学习这些基本知识。

四、Eclipse组件

在开发Eclipse插件(RCP可以看成是Eclipse的插件,只不过是脱离Eclipse运行的)之前,得先对Eclipse的结构有个了解。这里我简单介绍一下Eclipse的基本组件,这些名词可能比较陌生,但这都是开发Eclipse插件必须了解的。

如上图所示,我逐一介绍一个各个组件:

1、 Menu bar:这个东西你一定不陌生,每个软件都有的。不过Eclipse的菜单栏是动态的,也就是说,根据所编辑的内容不同,显示的菜单也可以不一样。

2、 Tool bar:这个东西也是每个软件都有的,和菜单栏一样,工具栏也是可以根据所编辑的内容不同而不同。

3、 Editor:编辑器,Eclipse的主要编辑工作是在Editor里面完成的。

4、 View:视图,视图是为了方便用户编辑提供一些辅助功能或编辑一些属性。比如最常见的Outline视图往往用来提供当前编辑的文档的结构。

5、 Page:页,一个页表示了当前用户的工作状态,包括View和Editor。

6、 Workbench Window:涵盖所有上述组件的组件叫做工作台窗口(这个名词的翻译我没见到过,我这里纯粹是直译,感觉有些词不达意)。Eclipse是允许创建多个工作台的。通过Window->New window菜单可以创建当前工作台的副本。

除了这些组件以外我还要介绍另外两个概念,一个是“Work Space”,在Eclipse启动的时候都要求指定一个Work Space,而且Work Space是不能被共用的。也就是说在同一时间,同一个Work Space只可以被一个Eclipse使用。但是一个Work Space是可以被多个Workbench Window共享的。很容易联想到,Workbench Window上面还有一层Workbench。事实上Workbench才是Eclipse的UI的最高管理者。另外一个概念是“Perspective”,中文翻译是“透视图(或者观察点)”。所谓Perspective是指当前Page的布局。最常见的是Java透视图和Debug透视图,可以看到这两个透视图的Page排布完全不一样。通过切换透视图可以很方便的切换开发环境以完成不同功能的开发。这里可以看出View和Editor的区别,Editor是在不同的透视图中共享的,而View不是。

五、开发前的准备

Eclipse是自带插件开发环境PDE(Plug-in Develop Environment)的,所以要开发Eclipse插件只需要下载一个标准的Eclipse即可。我现在用的Eclipse版本是3.3.0,是最新的稳定版,建议下载这个版本进行开发(我用的是英文版,所以下文提到的Eclipse相关的选项都是英文描述)。

虽然Eclipse生来就是开放的插件平台,但是Eclipse插件,特别是RCP是从3.0开始才走红的。Eclipse 3.0是一个具有里程碑意义的版本,它对Eclipse以前的结构做了一定的改进,并且升级了PDE,极大的简化了插件开发的配置,基本上实现了插件开发全图形的化操作,使得插件开发人员可以专注于插件功能的开发,而不用去管琐碎的配置文件。

六、第一个RCP程序

Eclipse提供了一些RCP程序的模板,通过PDE的插件创建向导能直接生成一个可用的RCP程序。

首先要新建一个Plug-in Project

然后输入Project名字,其他都用默认选项就行,点击“next”

在Rich Client Application部分选择“Yes”,点击“Next”

模板选择Hello RCP,点击“Next”

点击“Finish”将提示转换到Plug-in development perspective,选择“Yes”。

我们先看一下刚才新建的RCP的运行结果,右键点击“Demo”project,选择Run As,选择Eclipse Application,就可以看到一个最简单的RCP程序窗口。

接下来我先分析一下这个应用程序的结构。在Demo包下面有六个类和三个配置文件,下面我解释一下这些元素:

六个类

三个配置文件

1、 Application:这个类是程序的入口,虽然没有Main函数,但是这个类实现了IPlatformRunnable接口,当JVM完毕,初始化RCP框架以后会调用这个类的run函数来完成UI设置和开始执行我们指定的程序功能。在绝大多数RCP程序中,这个类不用更改。

2、 ApplicationActionBarAdvisor:简单的说这个类是用来配置程序的菜单栏和工具栏的,具体的应用在后面会讲到。

3、 ApplicationWorkbenchAdvisor:这个类是RCP程序的Workbench,RCP是Eclipse的简化,但是所有的组件都是和Eclipse一样的。一个RCP程序也只能有一个Workbench。

4、 ApplicationWorkbenchWindowAdvisor:这个类是RCP的WorkbenchWindow,隶属于当前的Workbench。可以有多个WorkbenchWindow。

5、 DemoPlugin:这个类代表了我们的插件,因为RCP程序也是一个插件,Eclipse中所有的插件都必须继承AbstractUIPlugin。这个类为我们提供了很多和插件相关的信息,比如插件的资源,配置等等。

6、 Perspective:是我们新建的RCP的默认透视图。可以在这个类中指定View和Editor的排布。

7、 plugin.xml:这个文件是我们插件的配置文件,包括我们的插件用了哪些其他的插件,具体是怎么配置这些插件的等等。

8、 build.properties:这个文件是用来配置我们插件的编译信息的,用来指定如何编译我们的插件。

9、 MANIFEST.MF:这个文件用来指定我们插件的元数据,比如插件的版本信息。一般来说,这个文件不用手动的去更改。

七、添加菜单和工具栏

这一节我演示以下如何为程序创建菜单和工具栏。在创建菜单和工具栏之前我想先介绍一个概念“Action”。开发Eclipse插件会经常看到这个东西,它和事件处理有点相似,如果你了解JFace的话,这里的Action你应该很熟悉,两者是一样的,我们自己定义的Action必须继承org.eclipse.jface.action.Action。程序可以定义当用户执行某项操作时触发某个Action。比如用户点击工具栏的一个按钮,或者选中了某个菜单项。

下面我们创建一个类HelloAction

package firstrcp;

import org.eclipse.jface.action.Action;

import org.eclipse.jface.resource.ImageDescriptor;

import org.eclipse.swt.SWT;

import org.eclipse.swt.widgets.MessageBox;

import org.eclipse.ui.IWorkbenchWindow;

import org.eclipse.ui.internal.IWorkbenchGraphicConstants;

import org.eclipse.ui.internal.WorkbenchImages;

public class HelloAction extends Action {

private IWorkbenchWindow window;

public HelloAction(IWorkbenchWindow window) {

this.window = window;

this.setText("Hello");

ImageDescriptor imgDes = WorkbenchImages.getImageDescriptor(IWorkbenchGraphicConstants.IMG_ETOOL_HOME_NAV);

this.setImageDescriptor(imgDes);

}

}

由于Action是一个UI元素,所以往往创建一个带IWorkbenchWindow参数的构造函数,以便在Action内部调用。setText()是设置Action对外显示的名字。setImageDescriptor()是设置Action的图标,这里我为了简化,用的是Eclipse Workbench自带的图标。

下面增加一个run方法,功能是弹出一个对话框显示“Hello World!”

public void run() {

MessageBox mb = new MessageBox(window.getShell(), SWT.OK);

mb.setMessage("Hello world!");

mb.setText("Demo");

mb.open();

}

这里你可以看到构造函数传入window的好处了吧。

上述代码都很简单,我不多解释,接下来我们把创建的Action关联到菜单栏和工具栏上。我们可以把同一个Action同时关联到多个菜单项和工具栏按钮,这和事件处理中的事件处理函数可以被多个事件共享一样。

上文我已经提到过,ApplicationActionBarAdvisor是用来配置工具栏和菜单栏的,那么我们现在来看以下具体如何操作。修改ApplicationActionBarAdvisor如下:

package firstrcp;

import org.eclipse.jface.action.ICoolBarManager;

import org.eclipse.jface.action.IMenuManager;

import org.eclipse.jface.action.IToolBarManager;

import org.eclipse.jface.action.MenuManager;

import org.eclipse.jface.action.ToolBarContributionItem;

import org.eclipse.jface.action.ToolBarManager;

import org.eclipse.swt.SWT;

import org.eclipse.ui.IWorkbenchWindow;

import org.eclipse.ui.application.ActionBarAdvisor;

import org.eclipse.ui.application.IActionBarConfigurer;

public class ApplicationActionBarAdvisor extends ActionBarAdvisor {

private HelloAction helloAction;

public ApplicationActionBarAdvisor(IActionBarConfigurer configurer) {

super(configurer);

}

//用来初始化action

protected void makeActions(IWorkbenchWindow window) {

super.makeActions(window);

helloAction = new HelloAction(window);

}

/* 用来设置菜单栏.创建一个MenuManager,然后把helloAction加入到这个菜单下面,

* 如果有多个action的话,可以加入同一个MenuManager,

* 这样demoMenu相当于一个下拉菜单,

* 而加入的action就是菜单项。最后把demoMenu加入到菜单栏。

*/

protected void fillMenuBar(IMenuManager menuBar) {

super.fillMenuBar(menuBar);

MenuManager demoMenu = new MenuManager("&Demo", "");

demoMenu.add(helloAction);

menuBar.add(demoMenu);

}

/*

* 用来设置工具栏的,原理和菜单栏是一样的,coolBar也有分组的,

* 每个toolbar就是一组。这里的toolbar相当于demoMenu。

*/

protected void fillCoolBar(ICoolBarManager coolBar) {

IToolBarManager toolbar = new ToolBarManager(SWT.FLAT | SWT.RIGHT);

coolBar.add(new ToolBarContributionItem(toolbar, "main"));

toolbar.add(helloAction);

}

}

代码看起来有点晕,我来一一解释。首先是makeActions()方法,这个方法是用来初始化action的,所有要用到的action可以都在这里初始化,但是这一步与界面无关。fillMenuBar()是用来设置菜单栏的。先创建一个MenuManager,然后把helloAction加入到这个菜单下面,如果有多个action的话,可以加入同一个MenuManager,这样demoMenu相当于一个下拉菜单,而加入的action就是菜单项。最后把demoMenu加入到菜单栏。当然,一个菜单栏可以加入多个下拉菜单。多级菜单也是可以做的,有兴趣的可以自行研究。fillCoolBar()是用来设置工具栏的,原理和菜单栏是一样的,coolBar也有分组的,每个toolbar就是一组。这里的toolbar相当于demoMenu。

运行一下程序你会发现菜单栏出来了,但是工具栏没有出现。原因是Hello RCP默认是不显示工具栏的,我们得改一下设置。打开ApplicationWorkbenchWindowAdvisor类,更改preWindowOpen方法:

public void preWindowOpen() {

IWorkbenchWindowConfigurer configurer = getWindowConfigurer();

configurer.setInitialSize(new Point(400, 300));

//configurer.setShowCoolBar(false);

configurer.setShowStatusLine(false);

configurer.setTitle("Hello RCP");

}

以上代码也很简单,从字面就能看出什么意思,我也不多解释,注意看注释掉的那行。preWindowOpen()这个函数在程序的窗口打开之前被调用,可以在这个函数里设置一些初始化的内容。类似的还有postWindowClose()等等,你可以查阅API来了解这些函数的用途。

现在我们的程序可以正确的运行了,看一下运行结果:

八、创建View和Editor

我用Designer来编辑View和Editor。我用的版本是WindowBuilder Pro 4.1.0,建议你也去下载一个,因为这个插件能够支持SWT,JFace,RCP,Swing的可视化编辑,极大的缩短了界面开发的时间。不过这个是商业软件,要破解了才能用,网上有很多破解的方法,google一下就行了。

下面开始创建第一个View。

右键点击demo包,选择“New”,“Other” 然后选择Designer->RCP->ViewPart,点击“Next”

输入“HelloView”,点击“Finish”

你可以在Source和Design之间切换。View相当于一个窗口,在View的界面上可以放任何组件。我拖了一个button上去,名字就叫button。HelloView类中要添加一个属性ID,我稍后解释这个属性。

你可以用同样的方法创建一个EditorPart,名字就叫HelloEditor。在editor上面放一个text组件吧,这样看起来像个editor,然后把HelloEditor的layout改成FillLayout。当然也要为HelloEditor添加一个ID属性。这里需要改一下Editor的init方法,否则editor无法正常运行。

setSite是让editor能够使用workbench所提供的一些功能,EditorSite可以看成是一个代理,EditorPart通过EditorSite来访问workbench。EditorSite是editor part和workbench之间的一层接口,详细情况你可以去google一下或者参考API。setInput的意思就很明了,就是让editor知道显示什么东西。这样我们就创建好了基本的View和Editor,下面来看怎么把它们加到我们的程序中去。

打开plugin.xml,选择Extensions标签页,在All Extensions部分点击“Add”按钮,选择org.eclipse.ui.views, org.eclipse.ui.editors, 两个extension,结果如下:

右键org.eclipse.ui.view,选择New->view

在Extension Element Details部分设置View的属性。ID可以随意设置,但是不能重复,我这里设置为Demo.helloView(虽说是随意设置,但是程序是通过这个ID来获得View的,所以最好与HelloView中的ID属性一致,我在HelloView中加入这个ID属性也是为了方便程序获得HelloView),Name也可以随意设置,我这里设置为Hello View,这个属性会显示在View的顶端,就像Outline View顶部显示的名称是“Outline”。class选择刚才创建的demo.HelloView。icon是显示在View顶部的图标,没有可以不设置。

类似的创建Hello Editor,设置Hello Editor的详细属性,设置id, name, class三个属性以外,还要设置icon属性(Editor的Icon属性是必须的!),随便给个图片文件就行,其他的属性可以不填。这里属性比较多,有兴趣可以去看Eclipse的插件开发帮助,里面对每个属性都有解释。

到目前为止,我们的配置工作已经完成,但是要让我们的程序正确的显示View和Editor,我们还要更改默认的Perspective。打开Perspective.java,修改createInitialLayout()方法(默认这个方法是空的)

public

这个方法实际上就是修改layout,在这个方法中安排每个view的位置和大小。addStandaloneView()是为当前的Perspective添加一个独立的View,所谓独立的View就是View所占的位置不能和其他View共享,你在Eclipse里应该看到过几个View叠在一起的情况。接下来一行设置是去除了关闭View的功能,就是这个View的顶部没有关闭按钮。我们来看一下运行结果

void createInitialLayout(IPageLayout layout)

{

layout.addStandaloneView(HelloView.ID, true,

IPageLayout.LEFT, .50f, layout.getEditorArea());

layout.getViewLayout(HelloView.ID).setCloseable(false);

}

也许你正在想怎么打开Editor,IPageLayout并不能直接把一个Editor加入显示,但是它会预留一块空间给Editor。如果你仔细观察上面那段代码,你会发现,事实上整个空间都是Editor的,View是在瓜分Editor的空间。接下来我将介绍如何打开Editor。

九、设置并打开Editor

相对View来说,Editor有点麻烦。因为要打开Editor的话必须给Editor内容,因为Editor是个编辑器,你得让它知道要编辑什么东西它才能打开。这里的内容就是Eclipse里面的EditorInput。没有现成合适的EditorInput用(一般情况下可以用FileEditorInput,把某个文件作为Input让Editor打开,在Eclipse里面双击打开某个文件就是这个过程),我这里创建一个HelloEditorInput继承IEditorInput接口。用Eclipse向导创建类的时候,先选择Interface,然后记得选中“Inherited abstract methods”,这样会自动继承所有abstract的方法,不用再去手工创建了。

HelloEditorInput的代码比较长,我不全部贴出来了,我贴一部分。我贴的两个方法是必须要修改的,这个类的其他代码用Eclipse自动生成的就好(做实际开发的时候肯定是要改的,我这里只是演示一个过程,所以尽量简化了)。

public

这里的getName是返回当前input的名字,比如文件名,getToolTipText是返回当前input的提示,当鼠标移动到editor的顶部标签上时就会显示。这两个方法都不能返回null。

好了现在一切准备工作已经就绪,就等打开Editor了。我们通过HelloView上面的那个按钮来打开HelloEditor,每按一次按钮打开一个Editor。首先更改按钮的Text属性为“Open Editor”,然后为按钮添加事件,事件代码如下:

关键代码就一行,就是那个openEditor方法,这个函数的两个参数,前面的代表资源,后面的代表工具,就是用后面的Editor去打开前面的内容。提醒一下,这里的Editor ID必须和plugin.xml文件里面的一样,否则会找不到合适的Editor。可以看到openEditor是Page的方法,这也和我前面介绍的Eclipse的组件结构一致,View和Editor是属于某个page的。其实在配置Perspective的时候也能看出这个问题,我们都是用IPageLayout

String getName()

{

return "Hello";

}

public String getToolTipText()

{

return "Demo";

}

openEditorButton.addSelectionListener(new SelectionAdapter()

{

public void widgetSelected(SelectionEvent e)

{

try

{

getViewSite().getWorkbenchWindow().getActivePage()

.openEditor(new HelloEditorInput(), HelloEditor.ID);

}

catch (Exception ex)

{

System.out.println(ex);

}

}

});

来布置界面。下面运行一下程序,看一下运行结果。

可能你会感觉不太协调,那么我们把窗口默认最大化(似乎能好看点),这就需要修改ApplicationWorkbenchWindowAdvisor类中的postWindowOpen函数:

如果你以前接触过SWT/JFace的话这段代码应该很熟悉吧,就算没接触过,也应该能大概明白什么意思,所以我不解释了。这句话一定要放在postWindowOpen里面,因为在preWindowOpen的时候还没有Window存在,所以没法设置。我们来看一下最终的运行结果:

下面我介绍一下如何导出/发布RCP程序,使程序能够独立运行。

public void postWindowOpen()

{

this.getWindowConfigurer().getWindow().getShell().setMaximized(true);

}

十、导出RCP程序

从Eclipse 3.0开始,RCP程序的配置和导出就变得相当方便。RCP程序又称为Eclipse Product,我们先为Demo Project新建一个Product配置文件。右键Demo Project,选择New->Other->Product Configuration,然后选择Demo文件夹,File name输入demo.product,点击Finish

接下来我们要新建一个Product Id,点击New

然后点击Browse选择Demo,并输入Product ID为hello,点击Finish

Overview标签页的Product Definition部分,输入Product Name为Hello Demo。在Testing部分点击一下“Synchronize”(最好点一下,有时候不点也没问题),这是为了让我们的Product配置和插件的配置保持一致。然后切换到Configuration标签页,点击Add,输入Demo,点击OK

然后点击“Add Required Plug-ins”,这一步是添加我们的Product所用到的其他插件,这样才可以脱离Eclipse运行。

然后进入Branding标签页,这一页是设置Product的图标,程序名字等等,可以不设置,系统会自动使用默认值,我这里输入一个Launcher Name为HelloDemo。

现在我们已经完成了导出的配置工作,现在来导出我们的RCP程序。进入Demo.product的Overview标签页,在右下角Exporting部分点击“Eclipse Product export wizard”,输入Root Directory和Export Destination,点击Finish。

什么是Eclipse RCP?相关推荐

  1. Eclipse RCP 中将窗口始终保持在最前

    Eclipse RCP 中将窗口始终保持在最前 将生成的子窗口(ApplicationWindow的子类)显示在最前,并且点击任何地方始终将此窗口保持在最前 通过设置   setShellStyle( ...

  2. 设置Eclipse RCP程序的外观和首选项

    RCP应用程序的缺省外观是一个空白窗口,一般我们要通过一个WorkbenchAdvisor类对界面进行定制. WorkbenchAdvisor有很多回调方法,可以在preWindowOpen()方法里 ...

  3. Eclipse RCP 中使用UTF8编码出现乱码问题的解决办法

    來源:http://hi.baidu.com/uniquejava/blog/item/00f49a10d3281904213f2e90.html 在eclipse rcp 使用utf-8编码,调试运 ...

  4. 保护Eclipse RCP应用的商业Java编译器

    近日Excelsior LLC发布了最新版的Excelsior JET(此前InfoQ对其做过介绍)--Excelsior JET 6.5.该版本增加的一个主要特性就是处理Eclipse RCP应用的 ...

  5. Eclipse Rcp系列 http://www.blogjava.net/dreamstone/archive/2007/02/08/98706.html

    Eclise Rcp 系列一 第一个SWT程序 写在开始: 由于工作须要,做了一周时间的Rcp开发,发现由于Eclipse开发方面的中文资料较少,对入门者来说有些困难, 所以把自己一周的内容放上,共享 ...

  6. Eclipse RCP - 第一个RCP程序

    Eclipse RCP (Rich Client Platform) 为Java桌面程序开发提供了一个基础平台, 基于该平台用户可以快速开发出一个具有很好扩展性的桌面程序, 我们使用的Eclipse实 ...

  7. Eclipse RCP入门

    Eclipse RCP入门 zhlmmc 2006-4-23 最近做了一个基于RCP的项目,感受颇深,觉得RCP有希望扭转Java桌面应用的颓势.在项目中积累了一点经验与心得,拿出来与大家分享,希望能 ...

  8. Eclipse RCP开发桌面程序

    所谓RCP,就是Rich Client Platform的缩写,即富客户平台,是Eclipse进化的产物(自3.0版以后出现),是Eclipse组织向用户提供的强大的开放性开发平台,能 够使用户方便地 ...

  9. 初探 Eclipse RCP

    最近公司的产品需要用到桌面应用程序, 为此开始研究Eclispe rcp 框架. 1 Eclipse RCP (Rich Client Platform) 基于eclipse的应用, 用于创建富客户端 ...

  10. eclipse rcp项目打包

    eclipse rcp项目打包 目录 eclipse rcp项目打包 理论说明 主要内容 1.rcp项目打包用到的软件和方法 2.具体的打包方法 后文 理论说明 eclipse的rcp项目挺老的了,网 ...

最新文章

  1. 【转】矮个子女生夏天穿衣法则
  2. 压缩 20M 文件从 30 秒到 1 秒的优化过程!
  3. linux性能优化实战-内存性能指标
  4. new与malloc的区别,以及内存分配浅析
  5. Java不要放弃之路
  6. SpringBoot_配置-properties配置文件编码问题
  7. [BZOJ 2957]楼房重建(线段树)
  8. sdut 1730 数字三角形问题
  9. React Native之组件(Component)生命周期学习笔记
  10. SVN+AnkhSVN端配置
  11. LeetCode(合集)删除数组中的元素(26,80,283)
  12. 利用expect实现自动化操作
  13. 控制台应用和空项目有什么区别_在公司做的项目和自己在学校做的有什么区别?...
  14. iOS发展系列II - UILabel 使用摘要
  15. php类中引函数变量,一个非线性差分方程的隐函数解
  16. 蓝桥杯单片机:11届决赛
  17. [转载] python字典查询功能_Python中的字典功能
  18. 下载百度文库的受限文件-冰点下载器
  19. confusion matrix的理解
  20. 【面试题】计算机网络 -- 常见面试题总结

热门文章

  1. 我们常说祝你一切顺利,实际上,顺利的状态是危险的,因为顺利意味着怠惰和懒于思考,是会让人失去奋斗的意志,丧失竞争力。
  2. 多台路由器堆叠_关于多个无线网络叠加本人亲身经历!!最后成功了!!更新完毕!...
  3. 生活随笔:心血来潮的今天
  4. git 问题解决之remote: Permission to xxx/xxx.git denied to xxx.
  5. [微信小程序]小程序引入腾讯地图选点插件出现:未取得授权
  6. Automatic fall detection of human in video using combination of features译文
  7. 为什么码农要了解业务?
  8. 如何用数字化构建企业的“韧性”?
  9. 平安好医生技术栈的分析
  10. 浏览器开发者工具菜鸡相谈