14.TimeTrackingService定义

我们现在处于迭代2阶段。这个迭代的目标是要构建搜索结果界面。要刷新我们的内存,下面是搜索屏幕的模型。搜索结果界面是时间卡显示的右面的内容。

Value Objects

要支持搜索结果面板,服务层必须提供根据匹配的特定规则获取timecard列表的能力。让我们决定我们创建一个service叫做TimeTrackingService,提供需要的功能:

public interface TimeTrackingService
{
    public TimecardSummaryVO[] findTimecards(TimecardSearchCriteriaVO criteria);
}

有一个TimecardSearchCriteriaVO是value object,timecard包搜索条件-如果在这个类中的一个字段被指定,这个搜索条件就被应用了;如果这个filed被保留为null,则那个查询条件不会被用到。TimecardSummaryVO是另外的一个VO,在summary information包中。基于在Search屏幕中的条件,让我们来设计这两个VO。注意,我们需要一个状态field,在所有的Value Object中包含。因此首先,创建一个项目叫做TimecardStatus在org.andromda.timetracker.domain包中。在”Domain Objects”图中创建一个类,把sterotype设置为Enumeration。像下面显示的那样,这些被增加到属性中。确认enumeration被创建在org.andromda.timetracker.domain包中。

TimecardStatus被定义了,我们能定义被findTimecards()服务方法使用的两个value object。创在org.andromda.timetracker.vo包中建立TimecardSearchCriteriaVO和TimecardSummaryVO。在”Value Objects”图中创建这些value object。在TimecardSearchCriteriaVO中的每个属性的多重性被设置为0..1-指出他们的值可以有或没有。要指定一个属性的多重性,你必须在属性上双击打开它的描述,然后选择正确的多重性。在另外的两个value objects,创建一个类叫做TimecardSummaryVO[]来表现timecard summary数组。确保这三个类被创建在org.andromda.timetracker.vo包中。

TimeTrackingService

我们现在已经有了定义我们的TimeTrackingService的任何东西。在org.andromda.timetracker.service包中创建这个服务。在”Services”图中创建并且确保服务被创建在org.andromda.timetracker.service包中。已经确保findTimecards方法的返回类型为TimecardSummaryVO[]。

现在,让我们要求AndroMDA来生成代码:

1.在命令提示符中,执行mvn install。确保你看到了BUILD SUCCESSFUL消息。验证下面的文件会被生成:

  1. common/target/src/org/andromda/timetracker/domain/TimecardStatus.java
  2. common/target/src/org/andromda/timetracker/vo/TimecardSummaryVO.java
  3. common/target/src/org/andromda/timetracker/vo/TimecardSearchCriteriaVO.java
  4. common/target/src/org/andromda/timetracker/service/TimeTrackingService.java
  5. core/target/src/org/andromda/timetracker/service/TimeTrackingServiceBase.java
  6. core/src/main/java/org/andromda/timetracker/service/TimeTrackingServiceImpl.java

TimeTrackingService Test

像UserService一样,我们首先要给TimeTrackingService写一个测试用例并且写实现来使测试通过。下面的步骤用来创建TimeTrackingServiceTest并且运行:

1.      C:/timetracker-completed/core/src/test/java/org/andromda/timetracker/service位置拷贝TimeTrackingServiceTest.java在你的实现的位置中。这个文件包括对findTimecards()方法的两个测试,testFindAllTimecards()和testFindTimecardsForSubmitter。确保你确认和理解这些测试。

2.       编辑在C:/timetracker/core/src/test/resources目录下的testng.xml,取消注释TimeTrackingServiceTest以便能执行。

3.       执行下面的命令,在C:/timetracker目录来运行测试。

Mvn –f core/pom.xml test

你可以看到下面的错误信息,说明了测试失败:

-------------------------------------------------------

T E S T S

-------------------------------------------------------

Running Services Test

Tests run: 3, Failures: 2, Errors: 0, Skipped: 0, Time elapsed: 0.661 sec <<< FAILURE!

Results :

Tests run: 3, Failures: 2, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------

[ERROR] BUILD FAILURE

[INFO] ------------------------------------------------------------------------

[INFO] There are test failures.

[INFO] ------------------------------------------------------------------------

[INFO] For more information, run Maven with the -e switch

[INFO] ------------------------------------------------------------------------

[INFO] Total time: 19 seconds

[INFO] Finished at: Sun Aug 06 18:38:26 EDT 2006

[INFO] Final Memory: 5M/11M

4.让我们看测试结果,看看发生了什么。打开c:/timetracker/core/target/surefire-reports/Services Test.txt。你可以看到下面的日志:

-------------------------------------------------------------------------------
Test set: Services Test
-------------------------------------------------------------------------------
Tests run: 3, Failures: 2, Errors: 0, Skipped: 0, Time elapsed: 0.661 sec <<< FAILURE!
testFindAllTimecards  Time elapsed: 0.31 sec  <<< FAILURE!
java.lang.NullPointerException
      at ...(TimeTrackingServiceTest.java:58)
      at ...(TimeTrackingServiceTest.java:41)
 
testFindTimecardsForSubmitter  Time elapsed: 0.01 sec  <<< FAILURE!
java.lang.NullPointerException
      at ...(TimeTrackingServiceTest.java:58)
      at ...(TimeTrackingServiceTest.java:50)
            

很明显,服务当前返回了null而不是TimecardSummaryVO[]。这个问题会当我们实现服务后被纠正。

15.TimeTrackingServcie实现

在这一章,我们会实现TimeTrackingService并且确保能通过测试。

我们会首先需要增强我们的领域模型来支持新value object TimecardSearchCriteriaVO并且TimecardSummaryVO。注意TimecardSearchCriteriaVO是一个简单的数据传输对象,来指定搜索参数。没有领域实体被需要表达这个概念。然而TimecardSummaryVO代表了timecard实体并且必须被增加到领域模型中。这个图如下面的显示:

Timecard与User实体有两个关系:

1.  submitter:提交timecard的人。

2.  approver:审核timecard的人。

两个关联关系的箭头指出了从Timecard到Person的方向。例如,timecard保持了对submitter和approver的饮用。在关联末端的数字和型号指明了多重性。顶端的关联指出timecard能有一个submitter,但是submitter能提交多个timecard(用*显示)。相似的,地下的关系指出了有0或1个审批者,一个审批者可以审批多个timecard。我们允许0审批者,以便审批是与提交延迟的。

我们在Timecard实体中增加一个方法叫做findByCriteria()。TimeTrackingService会使用这个方法来查找timecard。注意在findByCriteria()方法的下划线-他表示在UML中的”classifier”范围。如果在方法上没有下划线,会实现”instance”范围。这是在AndroMDA中重要的概念,特别注意。

一个带classifier scope的方法是一种一般目的的工具方法,操作一个或更多的实体。这样的方法在DAO类中被生成。带instance scope的方法生成在实体自己上并且被生成在EntityEntityImpl类中。

我们现在准备增加Timecard实体到TimeTracker模型中,请根据下面的连接来继续。

  • ArgoUML (under construction)
  • MagicDraw 9.x
  • MagicDraw 11.5
  • RSM 6

接下来从Timecard实体到TimecardSummaryVO增加依赖关系。正如你所知,这种依赖告诉AndroMDA在Timecard和TimecardSummaryVO对象间生成转换。转换方法被生成在TimecardDaoBase中。然而,注意TimecardSummaryVO包含被从另外一个实体中获取的属性,叫做User,因此我们会覆盖默认的转换方法在这些其他的属性中。

接下来从TimeTrackingService到Timecard实体增加一个依赖。正如你所知,这个依赖告诉AndroMDA让TimeTrackingService访问Timecard实体。

现在,让我们要求AndroMDA为Timecard实体生成代码:

1.在命令提示符中执行mvn install。注意构建不会成功,因为测试仍然失败,然而代码生成的部分会成功。

Eclipse User:打开AndroMDA生成的.classpath文件。注意AndroMDA不会增加TestNG到classpath路径中。增加下面的条目到到这个文件中,以确保TestNG在Classpath中。如果你不想AndroMDA覆盖已经修改了的.classpath文件,通过注释在mda/pom.xml文件中的andromdapp-maven-plugin段就可以了。

<classpathentry kind="var" path="M2_REPO/org/testng/testng/4.7/testng-4.7-jdk15.jar"/>

现在,让我们开始实现findByCriteria()方法,增加下面显示的粗体行在生成的TimeTrackingServiceImpl类中。

// license-header java merge-point
/**
 * This is only generated once! It will never be overwritten.
 * You can (and have to!) safely modify it by hand.
 */
package org.andromda.timetracker.service;
 
import java.util.List;
 
import org.andromda.timetracker.vo.TimecardSummaryVO;
 
/**
 * @see org.andromda.timetracker.service.TimeTrackingService
 */
public class TimeTrackingServiceImpl
    extends org.andromda.timetracker.service.TimeTrackingServiceBase
{
 
    /**
     * @see org.andromda.timetracker.service.TimeTrackingService#findTimecards(...)
     */
    protected org.andromda.timetracker.vo.TimecardSummaryVO[] handleFindTimecards(
            org.andromda.timetracker.vo.TimecardSearchCriteriaVO criteria)
        throws java.lang.Exception
    {
         List timecards = getTimecardDao().findByCriteria(criteria);
         getTimecardDao().toTimecardSummaryVOCollection(timecards);
         return (TimecardSummaryVO[])timecards.toArray(new TimecardSummaryVO[0]);
    }
}
    

我们首先在TimeDao中做一个调用findByCriteria()方法。还记得这是一个我们增加到Timecard实体的经典的方法吗?我们会手动实现这个方法。findByCriteria()方法返回一个匹配Timecard实体的列表。然而,handleFindTimecards()需要返回一个TimecardSummaryVO对象的数组,因此,我们首先使用TimecardDao帮助方法来转换实体列表为VO集合。然后我们转换集合到一个数组,返回它。

现在在TimecardDaoImpl中实现handleFindByCriteria()方法。注意,代码使用Hibernate criteria查询来实现搜索功能。因此,我们无需关注实现细节,这在本教程的范围之外。

 

package org.andromda.timetracker.domain;
 
import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.criterion.Restrictions;
 
/**
 * @see org.andromda.timetracker.domain.Timecard
 */
public class TimecardDaoImpl
    extends org.andromda.timetracker.domain.TimecardDaoBase
{
    ...
    protected java.util.List handleFindByCriteria(
        org.andromda.timetracker.vo.TimecardSearchCriteriaVO criteria)
    {
        // Create the timecard criteria
        Criteria timecardCriteria = this.getSession()
            .createCriteria(Timecard.class)
            .setFetchMode("submitter", FetchMode.JOIN)
            .setFetchMode("approver", FetchMode.JOIN);
 
        // Add submitter criteria
        if (criteria.getSubmitterId() != null) {
            timecardCriteria.createCriteria("submitter")
                .add(Restrictions.idEq(criteria.getSubmitterId()));
        }
 
        // Add approver criteria
        if (criteria.getApproverId() != null) {
            timecardCriteria.createCriteria("approver")
                .add(Restrictions.idEq(criteria.getApproverId()));
        }
 
        // Add status criteria
        if (criteria.getStatus() != null) {
          timecardCriteria.add(Restrictions.eq("status", criteria.getStatus()));
        }
 
        // Add startDateMin criteria
        if (criteria.getStartDateMin() != null) {
          timecardCriteria.add(
              Restrictions.ge("startDate", criteria.getStartDateMin()));
        }
 
        // Add startDateMax criteria
        if (criteria.getStartDateMax() != null) {
          timecardCriteria.add(
              Restrictions.le("startDate", criteria.getStartDateMax()));
        }
 
        java.util.List timecards = timecardCriteria.list();
        if (logger.isDebugEnabled()) {
            logger.debug(timecards.size() + " timecards found");
        }
        return timecards;
    }
    ...
}

现在改变在TimecardDaoImpl类的TimecardSummaryVO()方法的默认实现,因为他不正确。注意,这个方法要被TimeTrackerService.findByCriteria()来调用,用来转换Timecard实体。目前,toTimecardSummaryVO()简单调用了在父类中的方法(TimecardDaoBase)。如果你还记得,在DaoBase中的默认的转换实现非常简单化。特殊的,TimecardDaoBase类只拷贝Timecard属性到TimecardSummaryVO;他不在从相关的实体的属性中填充,叫做submitterName和approverName。因此在TimecardDaoImpl类的TimecardSummaryVO()方法中做下面的修改:

 

public void toTimecardSummaryVO(
        org.andromda.timetracker.domain.Timecard sourceEntity,
        org.andromda.timetracker.vo.TimecardSummaryVO targetVO)
    {
        super.toTimecardSummaryVO(sourceEntity, targetVO);
        targetVO.setSubmitterName(sourceEntity.getSubmitter().getUsername());
        if (sourceEntity.getApprover() != null) {
            targetVO.setApproverName(sourceEntity.getApprover().getUsername());
        }
    }

 

最后一件我们需要做的事情是在数据库中创建Timecard表并且填充数据。下面的步骤会完成这项工作:

1.    在命令提示符中运行下面的命令。要求androMDA创建我们的数据库schema:

Mvn –f core/pom.xml andromdapp:schema –Dtask=create

确保看到了构建成功的字样

2.    打开MySQL Query Browser。用timetracker登录。你能看到user和timecard表。

3.    选择文件->New Script粘贴下面的脚本

insert into USERS (ID, USERNAME, FIRST_NAME, LAST_NAME)
    values (1, 'nbhatia',      'Naresh', 'Bhatia');
insert into USERS (ID, USERNAME, FIRST_NAME, LAST_NAME)
    values (2, 'lcoude',       'Louis',  'Coude');
insert into USERS (ID, USERNAME, FIRST_NAME, LAST_NAME)
    values (3, 'ecrutchfield', 'Eric',   'Crutchfield');
insert into USERS (ID, USERNAME, FIRST_NAME, LAST_NAME)
    values (4, 'cmicali',      'Chris',  'Micali');
 
insert into TIMECARD (ID, STATUS, START_DATE, COMMENTS, APPROVER_FK, SUBMITTER_FK)
    values ( 1, 'Approved',  '2006/05/15', 'Timecard 01', 1, 2);
insert into TIMECARD (ID, STATUS, START_DATE, COMMENTS, APPROVER_FK, SUBMITTER_FK)
    values ( 2, 'Approved',  '2006/05/15', 'Timecard 02', 2, 3);
insert into TIMECARD (ID, STATUS, START_DATE, COMMENTS, APPROVER_FK, SUBMITTER_FK)
    values ( 3, 'Approved',  '2006/05/15', 'Timecard 03', 3, 4);
insert into TIMECARD (ID, STATUS, START_DATE, COMMENTS, APPROVER_FK, SUBMITTER_FK)
    values ( 4, 'Approved',  '2006/05/15', 'Timecard 04', 4, 1);
 
insert into TIMECARD (ID, STATUS, START_DATE, COMMENTS, APPROVER_FK, SUBMITTER_FK)
    values ( 5, 'Rejected',  '2006/05/22', 'Timecard 05', 1, 2);
insert into TIMECARD (ID, STATUS, START_DATE, COMMENTS, APPROVER_FK, SUBMITTER_FK)
    values ( 6, 'Rejected',  '2006/05/22', 'Timecard 06', 2, 3);
insert into TIMECARD (ID, STATUS, START_DATE, COMMENTS, APPROVER_FK, SUBMITTER_FK)
    values ( 7, 'Rejected',  '2006/05/22', 'Timecard 07', 3, 4);
insert into TIMECARD (ID, STATUS, START_DATE, COMMENTS, APPROVER_FK, SUBMITTER_FK)
    values ( 8, 'Rejected',  '2006/05/22', 'Timecard 08', 4, 1);
 
insert into TIMECARD (ID, STATUS, START_DATE, COMMENTS, APPROVER_FK, SUBMITTER_FK)
    values ( 9, 'Submitted', '2006/05/29', 'Timecard 09', 1, 2);
insert into TIMECARD (ID, STATUS, START_DATE, COMMENTS, APPROVER_FK, SUBMITTER_FK)
    values (10, 'Submitted', '2006/05/29', 'Timecard 10', 2, 3);
insert into TIMECARD (ID, STATUS, START_DATE, COMMENTS, APPROVER_FK, SUBMITTER_FK)
    values (11, 'Submitted', '2006/05/29', 'Timecard 11', 3, 4);
insert into TIMECARD (ID, STATUS, START_DATE, COMMENTS, APPROVER_FK, SUBMITTER_FK)
    values (12, 'Submitted', '2006/05/29', 'Timecard 12', 4, 1);
 
insert into TIMECARD (ID, STATUS, START_DATE, COMMENTS, APPROVER_FK, SUBMITTER_FK)
    values (13, 'Draft',     '2006/06/05', 'Timecard 13', null, 2);
insert into TIMECARD (ID, STATUS, START_DATE, COMMENTS, APPROVER_FK, SUBMITTER_FK)
    values (14, 'Draft',     '2006/06/05', 'Timecard 14', null, 3);
insert into TIMECARD (ID, STATUS, START_DATE, COMMENTS, APPROVER_FK, SUBMITTER_FK)
    values (15, 'Draft',     '2006/06/05', 'Timecard 15', null, 4);
insert into TIMECARD (ID, STATUS, START_DATE, COMMENTS, APPROVER_FK, SUBMITTER_FK)
    values (16, 'Draft',     '2006/06/05', 'Timecard 16', null, 1);

2.  在顶端靠右点击执行按钮。

好,我们认为TimeTrackingService现在完成了。让我们执行下面的命令来再次运行测试,并且看看发生了什么:

C:/timetracker>mvn -f core/pom.xml test
...
...
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running Services Test
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.272 sec
 
Results :
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 30 seconds
[INFO] Finished at: Fri Aug 11 21:05:53 EDT 2006
[INFO] Final Memory: 6M/14M
[INFO] ------------------------------------------------------------------------
    

棒极了!所有的测试都通过了。打开文件C:/timetracker/core/timetracker-test.log,

21:05:46.850 INFO  - Initializing ServiceLocator
21:05:46.870 INFO  - Initializing UserService
21:05:51.767 INFO  - Initializing ServiceLocator
21:05:51.767 INFO  - Initializing TimeTrackingService
21:05:51.858 INFO  - testFindAllTimecards:
...
21:05:52.819 DEBUG - 16 timecards found
21:05:52.859 INFO  - Submitter     Approver      Status     Start Date
21:05:52.869 INFO  - nbhatia       cmicali       Approved   05/15/06
21:05:52.869 INFO  - nbhatia       cmicali       Rejected   05/22/06
21:05:52.869 INFO  - nbhatia       cmicali       Submitted  05/29/06
21:05:52.869 INFO  - nbhatia       null          Draft      06/05/06
21:05:52.869 INFO  - lcoude        nbhatia       Approved   05/15/06
21:05:52.869 INFO  - lcoude        nbhatia       Rejected   05/22/06
21:05:52.869 INFO  - lcoude        nbhatia       Submitted  05/29/06
21:05:52.869 INFO  - lcoude        null          Draft      06/05/06
21:05:52.869 INFO  - ecrutchfield  lcoude        Approved   05/15/06
21:05:52.869 INFO  - ecrutchfield  lcoude        Rejected   05/22/06
21:05:52.869 INFO  - ecrutchfield  lcoude        Submitted  05/29/06
21:05:52.869 INFO  - ecrutchfield  null          Draft      06/05/06
21:05:52.869 INFO  - cmicali       ecrutchfield  Approved   05/15/06
21:05:52.869 INFO  - cmicali       ecrutchfield  Rejected   05/22/06
21:05:52.869 INFO  - cmicali       ecrutchfield  Submitted  05/29/06
21:05:52.869 INFO  - cmicali       null          Draft      06/05/06
21:05:52.879 INFO  - testFindTimecardsForSubmitter:
...
21:05:52.959 DEBUG - 4 timecards found
21:05:52.959 INFO  - Submitter     Approver      Status     Start Date
21:05:52.969 INFO  - nbhatia       cmicali       Approved   05/15/06
21:05:52.969 INFO  - nbhatia       cmicali       Rejected   05/22/06
21:05:52.969 INFO  - nbhatia       cmicali       Submitted  05/29/06
21:05:52.969 INFO  - nbhatia       null          Draft      06/05/06
...

AndroMDA Getting started(七)相关推荐

  1. 今天的学生要做汤饭吗

    Nineteen or so years ago, I imagine, today's college students were banging their spoons against thei ...

  2. 【C#实践】详解三层转七层:登录

    背景 一开始借用别人的代码,敲完也是很多地方看不懂!不知道从什么地方下手!不懂三层到七层到底是怎么映射过去的! 后来就是多查,慢慢有大体的轮廓,逐个部分解决! 过程: 1.看整体,对于不懂的部分,先查 ...

  3. NLP进阶之(七)膨胀卷积神经网络

    NLP进阶之(七)膨胀卷积神经网络 1. Dilated Convolutions 膨胀卷积神经网络 1.2 动态理解 1.2.2 转置卷积动画 1.2.3 理解 2. Dilated Convolu ...

  4. TVM性能评估分析(七)

    TVM性能评估分析(七) Figure 1. Performance Improvement Figure 2. Depthwise convolution Figure 3. Data Fusion ...

  5. 2021年大数据Kafka(七):Kafka的分片和副本机制

    全网最详细的大数据Kafka文章系列,强烈建议收藏加关注! 新文章都已经列出历史文章目录,帮助大家回顾前面的知识重点. 目录 系列历史文章 Kafka的分片和副本机制 一.分片机制 二.副本机制 三. ...

  6. 2021年大数据HBase(七):Hbase的架构!【建议收藏】

    全网最详细的大数据HBase文章系列,强烈建议收藏加关注! 新文章都已经列出历史文章目录,帮助大家回顾前面的知识重点. 目录 系列历史文章 前言 Hbase的架构 一.Client 二.Master ...

  7. 2021年大数据Hive(七):Hive的开窗函数

    全网最详细的Hive文章系列,强烈建议收藏加关注! 后面更新文章都会列出历史文章目录,帮助大家回顾知识重点. 目录 系列历史文章 前言 Hive的开窗函数 一.窗口函数 ROW_NUMBER,RANK ...

  8. 九零后的五年七次工作经历

    1990年农历10月22生日(阴历),2008年上大学,2010年工作,至此,一路走过来.磕磕绊绊,酸甜苦辣.准备把自己的经历写出来,不敢谈什么启发,仅仅希望对他人稍有借鉴. 上过班.创过业.卖过小吃 ...

  9. ABAP性能实例七例

    一.SQL Interface 1.Select ... Where vs. Select + Check 用Select - Where语句效率比Select + Check语句要高,例如: SEL ...

最新文章

  1. netsh命令修改ip
  2. 笔试题目“翻转字符串”的实现
  3. ZOJ1563 (动态规划)
  4. mysql哪些优化手段_mysql explain 及常见优化手段
  5. mariadb 10.2.3支持延时复制
  6. 第一次失效_神兵小将:净化之力失效地魔兵兽,全靠特殊办法,铁心方式真霸气...
  7. 在SqlServer 2008中将数据导成脚本
  8. 解决chrome/Edge提示您的连接不是私密连接的方法
  9. SQL反模式学习笔记15 分组
  10. python json解释器_Python JSON
  11. 转:java 进阶之路
  12. 目前游戏行业内部主要几款游戏引擎的技术对比
  13. nps内网穿透p2p隧道实战
  14. android 高通手机精品游戏合集
  15. Tableau数据分析-Chapter04标靶图、甘特图、瀑布图
  16. Log-normal distribution对数正态分布
  17. Multimodal Fusion(多模态融合)
  18. qt.network.monitor: Could not get the INetworkConnection instance for the adapter GUID.QT关闭时程序异常结束
  19. 安卓一键清理内存_微清理下载软件-微清理app下载v1.0.1.2 安卓版
  20. LoRA转4G及网关中继器工作原理

热门文章

  1. 【自动驾驶】高级辅助驾驶系统与车联网
  2. codeforces 849B Tell Your World(计算几何)
  3. 基于JavaScript实现的网页版贪吃蛇
  4. Java微信公众号开发(附源码!!!)
  5. 飞机机器人特摄片_机器人大战怪兽-好莱坞的特摄片「环太平洋」
  6. 子网掩码是什么,IP段的24是什么写法(CIDR写法,斜杠记法斜线记法)
  7. 码斗士的修炼之路 -- 如何保持并提升战斗力
  8. 在kile中为stm32移植FreeRTOS
  9. amd显卡多屏识别了 但是屏幕不亮_最近发布:针对AMD显卡多屏显示设置的完整解决方案。ppt28...
  10. TCP连接耗尽攻击异常报文攻击与防御