构建一个应用(续)

来源:互联网 发布:印度国产航母 知乎 编辑:程序博客网 时间:2024/06/05 20:36
<script type="text/javascript">google_ad_client = "pub-8800625213955058";/* 336x280, 创建于 07-11-21 */google_ad_slot = "0989131976";google_ad_width = 336;google_ad_height = 280;//</script><script type="text/javascript"src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>3.4 构造应用 我们已经详细探讨了应用的驱动,踢了踢轮胎,并检查了引擎盖下面的东西,准备上路了。我们已经知道了要做什么,并且怎样做,但是从那里开始构建的你应用呢? 在这一节,我们回到开头并向你展示可以如何从头至尾建立你的应用。因为我们已经有一个很好的关于我们想要应用干什么的印象,我们从一个实际的需求集开始。这样我们可以创建一个白板计划,包括那些明显的对象以及我们准备叫它们什么名称。然后我们就可以开始为对象编码,精化和扩展所作的计划。这种 计划/编码,再精化-计划/再精化-编码的方法称之为 “螺旋式” 方法。 当然,有其它方式来进行软件开发。其它方法也可以很好的用于Struts。但本节的目的并不是节是软件开发方法论。我们想要的是展示,构架一个简单的Struts 应用需要做些什么。所以,让我们开始上路喽... 3.4.1 定义需求 Requirements are the effects that the computer is to exert in the problem domain, by virtue of the computer’s programming —Practical Software Requirements, by Benjamin L. Kovitz 既然我们已经有一个关于应用需要做些什么的充分理解,从一个需求集开始是一个很好的实践方法。下面的章节,我们将汲取最简单有用的需求集。 我们的简单需求文档包括3个主要部分:目标、需求、规约。 表格 错误!文档中没有指定样式的文字。-1 需求文档的主要标题 标题 目的 目标 我们在问题领域中需要达到的结果 域需求 为达到目标,我们需要实现的东西 编程规约 我们要实现需求需要做的事情 3.4.1.1 目标l 允许有权限的用户向应用标识自己 3.4.1.2 域需求 允许用户递交他们的身份信用信息(username 和password) 检验递交的信息是否有效 允许纠正无效的信用信息 身分信用信息有效时,通知用户 允许校验有效的用户访问需要权限的特征 允许用户在需要时,使访问无效 3.4.1.3 程序规约可以从标准的web 浏览器访问, 可以使用或不使用JavaScript 为新访问者从welcome 页面提供登陆链接 允许在logon 页面输入用户信用(身份)信息 要求每个身份信息包含1-20个字符 要求输入username 和password 提交信息给业务层方法来进行校验 返回无效的身份信息给用户做纠正 如果身份有效,使用户登入 登陆后,用用户名定制welcome 页面 允许登陆的用户从页面登出 当然,这是一个非常简单的规约。许多规约要消耗大量的纸张,并用很多图, 数据表, 屏幕定义和问题域的详细描述来进行修饰。 3.4.2 规划应用 根据手头的这些需求,我们可以开始勾画应用并规划要用哪些对象来实现这些程序规范。一个方法是列出规约的列表,以及有助于实现他们的组件清单。通常,团队做这种事情是在一个大白板上进行,作为一个初始项目会议的一部分。 注意 这个过程中非常重要的一点是要注意到,规约项目和实现它们的组件之间并不是严格的1:1对应关系。虽然规约和程序都是为了一个相同的目标,它们却是从不同的方面的接近目标。 所以,象这样的列表并不是一个“规范化”的东西。某些规约可能已不止一个组件的方式出现。 3.4.2.1 视图 实际上,大多数应用都是以一个故事板(情节串联板)开始的。JSP定义应用的可见部分。 下面是表现层的大概要求。 表格 错误!文档中没有指定样式的文字。-2 白板视图规划 规约 JSP页面 向新访问者提供从欢迎页面开始的登陆 Welcome.jsp允许在登陆页面输入用户身份信息(username 和 password) Welcome.jsp返回无效的身份信息给用户纠正 Logon.jsp 当用户登陆进去时,根据用户名定制欢迎页面 Welcome.jsp允许将校验的用户从welcome 页面登出 Welcome.jsp可以从标准浏览器中访问,有或者没用JavaScript Logon.jsp; Welcome.jsp导引用户到欢迎页面 index.jsp 请注意我们在规约中加了一个自定的规约。这是个小技巧,在 Struts 应用中使应用的welcome 页面重定向到一个Struts action。这尽可能将控制流纳入框架,以便有助于在应用增长时最小化改变。 3.4.2.2 控制器在一个强壮的分层应用中(见第2章), 所有对页面和数据的请求请求都传递给控制层。表8是我们的控制器( “前端控制器” [Go3])的需求。 表格 错误!文档中没有指定样式的文字。-3 控制器规划 规约 ActionForms允许在登录页面中输入身份信息(username andpassword) LogonFormAction 用业务层方法校验身份信息 LogonAction向返回返回无效身份信息供纠正 LogonForm;LogonAction如果身份信息有效登入用户 LogonAction允许经过校验的用户从欢迎页面登出 LogoffActionActionForwards向新访问用户提供从欢迎页面的登录 Welcome, logon允许经过校验的用户从欢迎页面登出 Logoff ActionMappings 提交身份信息给业务方法校验 LogonSubmit Utility 记录所有内部常数 Constants 请注意我们自己添加了一个规约 “记录所有内部常数”。这在多层应用中也非常重要,因为这些常数可以“松散绑定”。Java 编译器不能验证我们在 XML 配置 文件中使用的标志,所以必须仔细跟踪我们所使用的标志。 3.4.2.3 模型 我们对数据访问层仅有一个需要。 表格 错误!文档中没有指定样式的文字。-4 model规划 规约 方法接口 提交身份信息给业务方法校验 boolean isUserLogon(String username,String password);3.4.3 规划源代码树 现在有了一个基线应用计划,我们可以规划一下应用的代码结构。因为应用很简单,我们可以为页面使用一个单独的子目录,为Java类使用一个单独的包。 我们的结构如图3-6: 注意 Struts希望在classpath中有一个应用资源包。logon 应用将它的属性文件方在其自己的资源包中。国际化应用可能具有多个属性文件。将他们放入包中可以方便进行组织。我们的构建文件(build file )将这些文件拷贝到类文件夹,以便在运行时它们可以在classpath中找到。在进行某些资源改变的时候,请记住要重新构建应用。图 错误!文档中没有指定样式的文字。-1 代码结构 如果你想跟随我们并建立了你自己的logon应用, 跳到代码结构和Struts classes得好办法是部署一个空白的应用: 下载一个空白应用。 将blank.war拷贝为logon.war。 将 WAR 放入容器的自动部署文件夹 (通常是webapps). 这既是为什么要提供一个 Blank 应用。它们其实是一个通用的应用模板。我们将在第3.4.4到 3.4.8节讨论基本结构。然后再来关注logon 应用。 为了修改和重新部署应用, 需要安装一些开发工具。 3.4.4 设置开发工具 略3.4.4.1 安装 Ant 略 3.4.4.2 安装jEdit 略 3.4.5 设置 build.xml文件 象现今的其他一些Java 产品,Struts 也希望使用Jakarta Ant工具 [ASF, Ant] 作为构建过程的一部分。Ant 也使用一个XML 配置文件, 名字叫build.xml。 通常,你可以为你的应用设置一个固定的build 文件,自始至终不变。在第4章,我们再表述 logon 应用的构建文件。 3.4.6 设置web.xml文件 Java 2 Servlet 框架使用一个配置文件来帮助设置应用。 web 部署描述符, web.xml, 标明需要的servlets,以及其他应用设定参数。其格式在 servlet 规范[Sun, JST]有描述。大多数 Struts 应用仅需要部署一个单一的servlet和几个标记库,以便保持相对简单。我们在第4章表述logon应用的web.xml 文件。 3.4.7 设置 struts-config.xml 文件 非常象web 部署描述符, Struts 也有一个 XML 配置 文件。你的应用在此注册其 ActionForm, ActionForward, 和ActionMapping。每个类在文件中都有其自己的一个配置段,在这里你可以定义在启动时需要创建的缺省对象。下面是我们的起始Struts 配置文件: 表格 错误!文档中没有指定样式的文字。-5 配置文件 <?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts configuration 1.0//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_0.dtd"> <struts-config> <form-beans> <!-- ... --> </form-beans> <global-forwards> <forward name="welcome" path=" /Welcome.do"/> <!-- ... --> </global-forwards> <action-mappings> <action path="/Welcome" type="org.apache.struts.actions.ForwardAction"parameter="/pages/Welcome.jsp"/> <!-- ... --> </action-mappings> </struts-config> 在你配置你的应用时, 你可以从一个空白配置文件开始,就像这个一样,并一步步加入你需要的对象。我们将在本章余下的内容做这个事情,以便你可以看到是如何实际配置Struts 配置文件。第4章将更深入讨论Struts 配置文件。你可能会注意到我们的起始并不是完全空白的。为方便已经提供了一个缺省 welcome forward 。 3.4.7.1 welcome action 通常, 路由页面流要尽可能的通过Struts 控制器。这样将应用总体设计都保持在Struts 配置之中。这样你可以从一个单一的地方来调整应用的控制流。不幸的是, 容器要求一个物理的welcome 页面。在web 部署 描述符 (web.xml) 中列出一个Struts action URI作为welcome 页面是不允许的。 最好的方案是在一个根index.jsp文件中将控制重定向到你的welcome action。struts-blank 就提供了这样一个根。这是一个极其简单页面仅有两行的页面: <%@ taglib uri="/tags/struts-logic" prefix="logic" %> <logic:redirect forward="welcome"/> 空白应用提供 index.jsp 转发页面和一个缺省的welcome 页面。我们将照原样使用index.jsp 弹要对welcome 页面做些改变。然而在开始之前,我们测试一下部署情况。 3.4.8 测试部署情况 在测试一个新的应用之前确保一切OK, 应该打开一个运行的应用作为基线。Struts Blank 应用是一个基线应用的好选择。其缺省welcome 页面包括了一些基本的系统检测,以看看配置文件是否被正确装入,标签扩展是否能找到,以及消息资源文件是否有效。 Struts Blank 应用的 WAR 文件可以在本书的站点或者Struts发布包中找到。将blank.war 文件放入容器的自动部署文件夹并重新启动容器。然后可以使用这个url访问应用的welcome 页面 : http://localhost:8080/blank 如果一切OK, 就会看到页面图3-8一样的页面:图 错误!文档中没有指定样式的文字。-2 空白应用的欢迎页面 下面是它的源代码: <%@ page language="java" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <html:html locale="true"> <head> <title><bean:message key="index.title"/></title> <html:base/> </head> <body bgcolor="white"> <logic:notPresent name="org.apache.struts.action.MESSAGE" scope="application"> <font color="red"> ERROR: Application resources not loaded -- check servlet container logs for error messages. </font> </logic:notPresent> <h3><bean:message key="index.heading"/></h3> <p><bean:message key="index.message"/></p> </body> </html:html> 清单 错误!文档中没有指定样式的文字。-1 空白应用的缺省欢迎页面源代码 3.4.9 构造欢迎页面大多数软件开发方法的一个基本原则是尽快的得到一个可以工作的原型。如果你同意这点。那么我们应该做的第一件事就是根据我们的规约构造我们的welcome 页面。这个welcome 页面早期版本, 没有逻辑条件,可能看起来像: <%@ taglib uri="/tags/struts-html" prefix="html" %> <html> <head> <title>Welcome World!!</title> <html:base/> </head> <body> </ul> <li><html:link forward="logon">Sign in</html:link</li> </ul> <img src='struts-power.gif' alt='Powered by Struts'> </body> </html> 清单 错误!文档中没有指定样式的文字。-2 欢迎页面的早期版本 因为这里引用到logon ActionForward,我们需要将它添加到我们的Struts配置中去。我们也可以改变缺省 welcome 页面从Index.jsp 到Welcome.jsp: <global-forwards> <forward name="logon" path /Logon.do"/> <forward name="welcome" path /Welcome.do"/> <!-- ... --> </global-forwards> 这时,我们可以重新启动容器装入新的Struts 配置。 某些容器,象 Tomcat,让你重新装入一个单独的应用。 1.0 vs 1.1 在Struts 1.0中,有大量的管理Action,包括重新装入Struts 配置。在Struts 1.1被取消了,因为他们和支持多应用模块相冲突. 一旦新的配置装入, 我们可以打开新的welcome 页面: http://localhost:8080/logon/ 你将看到图3-9的屏幕。图 错误!文档中没有指定样式的文字。-3 登录前的欢迎屏幕 然而,如果你点击连接,你将无法前进,会得到图3-10的信息。 为修正这个错误,我们需要看看下一个目标,构造logon 页面。 图 错误!文档中没有指定样式的文字。-4 文件未找到错误 3.4.10 构造logon 页面 回去看看3.4.2的白板, 我们看到logon 页面需要收集username 和 password, 并提交到一个名为/LogonSubmit的Mapping。这意味着我们需要创建一个Struts form 来标明 /LogonSubmit action, 作为一个文本域和密码域的输入控件。下面是logon.jsp的源代码: <%@ taglib uri="/tags/struts-html" prefix="html" %> <html><head><title>Sign in, Please!</title></head> <body> <html:errors/> <html:form action="/LogonSubmit" focus="username"> <table border="0" width="100%"> <tr> <th align="right>"Username: </th><td align="left"><html:text property="username"/></td> </tr> <tr><th align="right">Password: </th> <td align="left"><html:password property="password"/></td> </tr> <tr> <td align="right"><html:submit property="submit" value="Submit"/></td <td align="left"><html:reset/></td> </tr> </table> </html:form> </body> </html> 清单 错误!文档中没有指定样式的文字。-3 Logon页面的源代码 <html:form> 标签记引用一个 ActionMapping 对象, 它再引用其他对象(org.apache.struts.action.ActionMapping)。我们先配置ActionMapping,然后是它使用的对象: <action-mappings <action path="/LogonSubmit" name="logonForm" scope="Request" validate="true" input="/pages/Logon.jsp"/> <!-- ... --> </action-mappings> 两个相关的对象是 logonForm form bean 和 LogonAction。我们也需要在Struts 配置中注册ActionForm Bean。我们使用的名称成为对象在请求上下文中创建时的缺省属性名称。 <form-beans <form-bean name="logonForm" type="app.LogonForm"/> <!-- ... --> </form-beans 这时我们需要加入两个特别的Java 类, LogonForm 和 LogonAction。 3.4.11 构造Constants类虽然不是严格要求,但是将ActionForward 名称和其他标记记录在文档中则是强烈推荐的。这其实很简单,而且可以使你的代码易于管理。我们展示代码时,通常忽略了 JavaDoc 说明。但是,在这里我们将留下他们。为什么?因为整个类都是记录常数的类。所以,在这里,文档就是代码。下面是整个类的代码: package app; public final class Constants { /** * The session scope attribute under which the Username * for the currently logged in user is stored. */ public static final String USER_KEY = "user"; /** * The value to indicate debug logging. */ public static final int DEBUG = 1; /** * The value to indicate normal logging. */ public static final int NORMAL = 0; /** * The token that represents a nominal outcome * in an ActionForward. */ public static final String SUCCESS= "success"; /** * The token that represents the logon activity * in an ActionForward. */ public static final String LOGON = "logon"; /** * The token that represents the welcome activity * in an ActionForward. */ public static final String WELCOME = "welcome"; } 清单 错误!文档中没有指定样式的文字。-4 Constants类的源代码 3.4.12 构造其他类 我们在3.3.8 和 3.3.9展示了logonForm和logonAction的源代码。我们也需要在第1章中讲述过的UserDirectory 和UserDirectoryException 类。我们可以将他们不经改变的加入到我们的新应用中。在我们的源代码结构中将他们放在/WEB-INF/src/java/app/。图 错误!文档中没有指定样式的文字。-5 LogonAction,LogonForrm以及其它Java类的位置 LogonAction 也引用 Constants 类。在便以前我们要先加入它。 3.4.13 创建user directory 在第1章,我们引入了一个简单的registration 应用来存储user ID和password。这些登陆账号纪录在一个标准的属性文件中,名为user.properties。它可以从那个应用中引入,或者在 WEB-INF/src/java/resources下重新创建,如图3-12: 图 错误!文档中没有指定样式的文字。-6 用户属性文件的位置 属性文件是一个简单的文本文件。这里是一个简单的例子,使用本书作者的名为用户名,姓为密码。 TED=Husted CEDRIC=Dumoulin GEORGE=Franciscus DAVID=WinterfeldtCRAIG=McClanahan 如果你喜欢,你可以只输入这些,或者你愿意输入的内容,并将他们存放在/WEB-INF/src/java/resources/user.properties。只是保证user ID全部大写,因为这是业务逻辑中要求的。 当然,你的应用也可以容易的根据一个JNDI 服务,或者数据库来进行校验, 或者使用容器的安全领域。我们在第14章说明在Struts中使用数据服务。 3.4.14 配置ActionErrors 可能记得, LogonForm 和 LogonAction 都要产生错误信息。ActionError 系统是集成在应用消息之中的。 在测试LogonForm 和LogonAction 之前,我们需要添加这些消息到Application.properties 文档中: errors.header=<H3><font color="red">Validation Error</font></H3>You must correct the following error(s) before proceeding:<UL> errors.footer=</UL><HR> error.username.required=<LI>Username is required</LI> error.password.required=<LI>Password is required</LI> error.logon.invalid=<LI>Username and password provided not found in user directory. Password must match exactly, including any lower or upper case characters.</LI> 1.0 vs 1.1 Struts 1.1中的新标记允许从消息中忽略标记。见第10章的详细内容。 作为构建过程的一部分,我们将应用资源文档从/WEB-INF/src/java/resource拷贝到 classes 文件夹下的资源包,这里ActionServlet 才能找到它们。 3.4.15 编译并测试logon 页面 在3.4.8中,我们为logonForm创建了 JSP 页面。但为了使他们运行,我们得添加相应的配置元素和JAVA类,如表10。 表格 错误!文档中没有指定样式的文字。-6 Logon 页面配置元素 配置元素 Java 类 LogonSubmit action-mapping logonForm form-bean LogonAction, subclass of Action LogonForm, subclass of ActionForm现在我们可以编译应用并测试logon页面了。有一个存好的build.xml文件放在 WEB-INF 目录,你可以在Ant使用它。 按缺省方式构建目标,编译它,将从java源代码构建 Java类,并将应用资源消息文件拷贝到classes 目录。当构建成功,你可以使用下面的链接进到应用中的 logon 页面。 (根据你的容器如何重新装入Java类,你可能需要重新启动容器。如果不确定,就重启它。) logon 应用应该可以工作的象我们原来漫游的一样 (从3.3.3 到 3.3.6)。所不同的是welcome 页面在用户登录后不会发生变化,或者不提供登出的机会。下面我们将修正它,然后我们的应用就完成了。 3.4.16 修改welcome页面 我们先前的welcome 页面忽略关于判断用户是否登陆的条件逻辑。既然,用户已经可以登陆,我们就可以将这些语句加入,以符合3.3.2中的版本 。黑体部分是我们加入的内容: <%@ taglib uri="/tags/struts-bean" prefix="bean" %> <%@ taglib uri="/tags/struts-html" prefix="html" %> <%@ taglib uri="/tags/struts-logic" prefix="logic" %> <html> <head> <title>Welcome!</title> <html:base/> </head> <body> <logic:present name="user"> <h3>Welcome <bean:write name="user" property="username"/>!</h3> </logic:present> <logic:notPresent scope="session" name="user"/> <h3>Welcome World!</h3> </logic:notPresent> <html:errors/> <ul> <li><html:link forward="logon">Sign in</html:link></li> <logic:present name="user"> <li><html:link forward="logoff">Sign out</html:link></li> </logic:present> </ul> <img src='struts-power.gif' alt='Powered by Struts'> </body> </html> 清单 错误!文档中没有指定样式的文字。-5 welcome 页面 (/页面s/Welcome.jsp)的修订版本 如图3-13, 这使我们回到了原来的情形。当用户到来时,他被邀请登录进去。一旦登录进去,则按名字欢迎它并提供登出的机会。这是你的进步! 3.4.17 Struts ActionForward Action 如果你注意到你浏览器的地址栏,你可能会注意到我们一直没有暴露JSP 页面的地址。许多应用可能不注意这个方式,但如果你想要严格的MVC架构(见第2章), 你可能不希望暴露一些关于你的视图的实现,包括你是否使用JSP 页面或者你如和存储它们。理想情况下,所有的导航都应该通过.do Action进行,因为他们都通过控制器管理。 当然,并不是所有的事情控制器都能干的很好。这也是为何使用logon 和 welcome 页面。他们并不需要任何来自于model的信息,直接提供显示JSP 页面的连接。 但这也使用户可以将页面的位置存为书签。接下来,你可能在显示logon 页面之前需要执行一些背景动作,或者你可能需要移动或者将JSP 页面改名。如果用户将这些页面存为书签了,他们就会回到旧的地址,绕开你的逻辑,并返回一个 File not found 错误。在实践中,通常导致将遗留检查放入服务器页面并重定向到web server—事情发生错误的方式更多了,并有更多代码需要维护。 道德?我们必须尽可能虚拟化详细的导航。否则,我们就会不断的为浏览器是否进行缓存或者存储进行补偿。不要直接连接到物理的JSP, 我们应该总是连接到一个虚拟的 Struts Action, 然后由它提供相应的页面。 当然,不管是否需要,为每个页面编写一个Action, 会是一个非常繁琐的工作。一个更有效的办法是,部署一个有用的 Action,它可以在Struts 配置中定制,并在需要的地方可以重用。因为Struts 为每个Action创建一个多线程实例,这是一个非常有效的办法,确保控制保留在控制器之中。我们需要做的就是将Action 和 path传递给页面。 高兴的是,你可以使用struts.jar 中的标准的ForwardAction 对象。你可以简单的目标路径作为ActionMappin的parameter属性进行传递: <action path=" /Welcome" type="org.apache.struts.action.ForwardAction" parameter="/pages/Welcome.jsp"/> ForwardAction 将会从mapping 中查找目标路径,并使用它来返回一个ActionForward 给servlet。 实际结果是http://localhost:8080/logon/pages/Welcome.jsp不会出现在浏览器的地址栏中,那样的话就可以被标为书签直接访问,浏览其中的Action URI 会出现: http://localhost:8080/logon/Welcome.do 用户仍然可以把这个地址记为书签,但是现在你有更多的控制了。你可以logon 活动的实现而不用担心有什么被浏览器记为书签了。之前,你可能不得不考虑如果他们直接访问原来的服务器地址,会发生什么事情。 在MVC架构中,action就是你的API。服务器页面是一个实现细节。如果JSP作为导航系统的一部分暴露给了浏览器,控制器和试图层就混合了,MVC的优势被削弱。 在需要直接导航至其他页面时,我们也可以加入其他ForwardAction实例。因为应用仅仅实例化一个ForwardAction,我们所添加的实际上是一个ActionMapping 对象。如果要求使用ForwardAction通常的MVC架构原因还不充分,Struts 1.1引入的模块化特征要求所有的 JSP 请求都要通过Action。这是因为每个模块有其自己的配置上下文并且控制必须通过ActionServlet 控制器传递,以便为JSP页面选择配置。当然,如果你使用单个的缺省应用,这并不是必须的。但是如果从一开始便遵循这个实践,那么你便可以在不改变任何东西的情况下将你的应用变成一个模块。3.5 小结 不管你在开发团队中担任何种角色—工程师,设计员,架构师,QA—对整个应用是如何运行的有个总体印象总是有帮助的。在这一章,我们综合的解剖了一个小而有用的应用 。 通过漫游,解剖,然后构造一个logon 应用,我们向你展示了 Struts 框架实际上是如何工作的。作为构造阶段的一部分,我们还创建了一个设计文档,来规划我们的目标、客户需求和程序规约。要使一个应用能运行,我们还构建了应用的 web.xml,Ant的build.xml脚本,以及Struts配置文件。 为了使正确的构建部件到位,我们按需要的顺序构建了每个组件。 在此过程中,我们也指出了最好的实践方法,并强调分离模型、视图和控制器的重要性。 在第4章,我们会详细探讨 Struts配置文件。如我们在此所见,配置扮演了一个强有力的角色,使应用易于设计和维护。
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 念珠样菌阳性 念珠杆菌 念珠球菌症状 念珠球菌尿道炎 霉菌是什么引起的 白色念珠球菌感染 霉菌是怎么感染的 如何杀死白色念珠菌 女性为什么会得念珠菌 念珠藻 念珠藻是原核生物吗 念珠性阴炎的症状有 念珠性阴炎症状 念球菌是什么 链球菌感染的症状 念球菌阴炎的症状 球菌 念球菌是啥 隐球菌 球菌是什么意思 念球菌是什么病 念球菌病 念球菌是怎么感染的 琳球菌 念珠菌是什么病 念珠菌是什么意思 光滑念珠菌 念珠菌和霉菌有区别吗 阴道白色念珠菌 梅菌性阴炎是怎么引起的 念珠菌阴炎症状 霉菌性炎症 念珠菌性阴炎图片 霉菌炎症 美念堂祛斑怎么样 乛这个笔画怎么念 念字笔画 汉字的笔画 念红颜 念组词 念的组词