﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-Exciting IN JAVA WAY-文章分类-J2ee  Dev</title><link>http://www.blogjava.net/cgogg/category/23084.html</link><description>Take Easy</description><language>zh-cn</language><lastBuildDate>Wed, 20 Jun 2007 14:52:44 GMT</lastBuildDate><pubDate>Wed, 20 Jun 2007 14:52:44 GMT</pubDate><ttl>60</ttl><item><title>AppFuse学习笔记-视图层</title><link>http://www.blogjava.net/cgogg/articles/124989.html</link><dc:creator>chenguo</dc:creator><author>chenguo</author><pubDate>Mon, 18 Jun 2007 10:11:00 GMT</pubDate><guid>http://www.blogjava.net/cgogg/articles/124989.html</guid><wfw:comment>http://www.blogjava.net/cgogg/comments/124989.html</wfw:comment><comments>http://www.blogjava.net/cgogg/articles/124989.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/cgogg/comments/commentRss/124989.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cgogg/services/trackbacks/124989.html</trackback:ping><description><![CDATA[<p>1. 程序描述<br>&nbsp;&nbsp;&nbsp; 视图层主要由JSP网页构成，此外还包括Resource Bundle资源文件及ActionForm Bean、Validation等，这些组件提供对国际化、接收用户输入的表单数据、表单验证和错误处理等的支持。<br>&nbsp;&nbsp;&nbsp; 与User的视图层有关的文件有：<br>&nbsp;&nbsp;&nbsp; userList.jsp：用户列表页面，用于具有管理权限的用户对其他用户的管理。<br>&nbsp;&nbsp;&nbsp; userForm.jsp：用户信息页面，用于对用户信息的增、删、改<br>&nbsp;&nbsp;&nbsp; UserForm.java：与用户信息表单对应的ActionForm Bean<br>&nbsp;&nbsp;&nbsp; validation.xml：对用户信息表单进行验证的配置文件<br>&nbsp;&nbsp;&nbsp; Resource Bundle资源文件，中文的就是ApplicationResource_Zh_cn.properties<br>&nbsp;&nbsp;&nbsp; default.jsp：用于复合网页的sitemesh文件<br>&nbsp;&nbsp;&nbsp; JavaScript、CSS文件</p>
<p>2. JSP页面<br>&nbsp;&nbsp;&nbsp; 与用户相关的JSP页面包括用户列表和用户信息。head及foot等通用信息放置在head.jsp、foot.jsp中，因此userList.jsp及userForm.jsp只包含与业务相关的标签。下面介绍主要的标签功能：<br>&nbsp;&nbsp;&nbsp;
&lt;logic:messagesPresent&gt;：判断指定的消息是否存在。若指定message属性为true，则从request范围内
检索属性key为Globals.MESSAGE_KEY的ActionMessages对象。若不指定属性，则默认检索属性key为
Globals.ERROR_KEY的ActionMessages对象。在本系统中一个检索Success消息，一个检索Error消息。<br>&nbsp;&nbsp;&nbsp; &lt;fmt:message&gt;：JSTL标签。用于输出Resource Bundle中的一条消息。<br>&nbsp;&nbsp;&nbsp;
&lt;bean:struts&gt;：用于检索Struts框架内在的对象，如ActionFormBean、ActionForward、
ActionMapping。在这里用于在JSP页面上定义一个ActionForward
变量以方便其它地方的引用。forward为struts-config.xml中定义的global-forward。<br>&nbsp;&nbsp;&nbsp; &lt;c:set&gt;：JSTL标签。用于定义一个变量，以方便其它地方的引用。<br>&nbsp;&nbsp;&nbsp; &lt;c:out&gt;：JSTL标签。用于在页面上显示一个EL表达式的值，如显示&lt;c:set&gt;定义的变量。EL是JSTL采用的简单的表达式语言，该语言提供一个访问和操作应用程序数据的简单方式。<br>&nbsp;&nbsp;&nbsp; &lt;c:if&gt;&lt;c:when&gt;&lt;c:forEach&gt;&lt;c:choose&gt;：JSTL标签。用于处理条件的标记，根据特定的逻辑条件来控制输出网页内容，或者循环遍历集合中的所有元素。<br>&nbsp;&nbsp;&nbsp; &lt;display&gt;：Display Tag的标签，用于显示控制层返回的List。可以方便的定制表格是否分页、对列排序、导出数据等。<br>&nbsp;&nbsp;&nbsp; &lt;html:form&gt;：用来定义HTML表单。Struts的HTML标签可以和标准的HTML元素完成相同的功能，Struts框架能够把表单中的数据自动映射到相应的ActionForm Bean中。<br>&nbsp;&nbsp;&nbsp; &lt;html:text&gt;&lt;html:password&gt;&lt;html:hidden&gt;：在表单上生成相应的HTML元素。<br>&nbsp;&nbsp;&nbsp;
&lt;html:submit&gt;&lt;html:cancel&gt;：在表单上生成提交按钮和取消按钮。当用户按下按钮时，将产生一个提交事
件或取消事件，由Action类捕获。本系统中&lt;html:submit&gt;&lt;html:cancel&gt;的属性相同（均为
method），用户按下提交按钮或取消按钮后执行哪个方法由lookupMethods.properties决定。<br>&nbsp;&nbsp;&nbsp; &lt;html-el:multibox&gt;：在表单上生成HTML的CheckBox标签。html-el使用了JSTL扩展，可以使用EL表达式。<br>&nbsp;&nbsp;&nbsp;
&lt;html:messages&gt;&lt;html:errors&gt;：用于在网页中输出消息。&lt;html:errors&gt;用
于输出错误消息，本系统中用于显示表单中字段级的错误信息，&lt;html:messages&gt;显示全局的消息。<br>&nbsp;&nbsp;&nbsp; 自定义标签：<br>&nbsp;&nbsp;&nbsp; 在你最初运行ant new时，AppFuse就会根据你键入的项目名自动生成一个标签文件，在userForm.jsp中你会看到这个标签。<br>&nbsp;&nbsp;&nbsp; &lt;YOURAPPNAME:label&gt;：它用于显示表单输入域前的提示文本，对文本添加了一些特别的样式，如在必填项前自动加"*"号等。<br>&nbsp;&nbsp;&nbsp; &lt;YOURAPPNAME:country&gt;：userForm中有设置国家一项，该标签用于显示国家的下拉列表。<br>&nbsp;&nbsp;&nbsp; &lt;YOURAPPNAME:constants&gt;：用于在页面上显示常量类org.appfuse.Constants中的常量的值。</p>
<p>3. ActionForm Bean<br>&nbsp;&nbsp;&nbsp;
ActionForm
Bean是Struts提供的表单数据传输对象，用于在视图层和控制层之间传递HTML表单数据。控制层可以从ActionForm
Bean中读取用户输入的表单数据，也可以把来自模型层的数据存放到ActionForm Bean中，然后把它返回给视图。<br>&nbsp;&nbsp;&nbsp;
User表单的ActionForm
Bean为UserForm。UserForm继承BaseForm。BaseForm是ActionForm的子类对象，扩展了三个通用方法：
toString()、equals(Object o)
和hashCode()。并定义了一个验证，用于允许用户在点击"删除"或"取消"按钮时表单无须验证。<br>&nbsp;&nbsp;&nbsp; UserForm中定义了与用户信息表单中的字段对应的属性。</p>
<p>4. Validator验证框架<br>&nbsp;&nbsp;&nbsp; Validator验证框架负责数据验证，采用基于XML的配置文件来配置验证规则。其相关的文件有：<br>&nbsp;&nbsp;&nbsp; validation.xml：针对具体的Struts表单，为ActionForm配置所需的验证规则。<br>&nbsp;&nbsp;&nbsp; validator-rules.xml：框架自带文件，包含了一组通用的验证规则。<br>&nbsp;&nbsp;&nbsp;
validator-rules-custom.xml：自定义验证规则。本系统中定义了验证两个指定的输入域值是否相等的规则。用于验证密码和确认密码
是否相等。其实将这个方法稍微修改一下，把&#8220;=&#8221;变为&#8220;&lt;、&gt;&#8221;就可以验证一个域不能大于/小于另外一个域，非常的实用。<br>&nbsp;&nbsp;&nbsp; Resource Bundle：定义验证失败时显示的提示文本。Resource Bundle 中以errors为前缀的Key绝大部分用于Validator的错误提示。<br>&nbsp;&nbsp;&nbsp; 下面是在validation.xml 中配置的userForm验证规则<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;form name="userForm"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;field property="username"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; depends="required"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;arg0 key="userForm.username"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/field&gt;<br>&nbsp;&nbsp;&nbsp; &#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;<br>&nbsp;&nbsp;&nbsp; &lt;/form&gt;<br>&nbsp;&nbsp;&nbsp; 该配置通过depends="required"定义了userForm的username字段即用户名是必填项。<br>&nbsp;&nbsp;&nbsp;
&lt;arg0 key="userForm.username"/&gt;声明了该字段所对应的文本在Resource
Bundle中的Key。Validator会在Resource Bundle中寻找errors.required对应的文本：'{0}'
为必填项，将userForm.username对应的文本带入{0}。如用户没有填写该项，Validator会提示&#8220;'用户名' 为必填项&#8221;的消息。<br>&nbsp;&nbsp;&nbsp; validation.xml中使用正则表达式定义了一些简单的全局的规则，如电话号码的格式等。<br>&nbsp;&nbsp;&nbsp; 默认情况下，Validator框架在Web服务器端执行表单验证。validator-rules.xml里已经定义了客户端JavaScript的生成规则，可以在JSP网页中生成JavaScript脚本。需要进行客户端验证时，在JSP中包含：<br>&nbsp;&nbsp;&nbsp; &lt;html:javascript formName="userForm" cdata="false"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dynamicJavascript="true" staticJavascript="false"/&gt;<br>&nbsp;&nbsp;&nbsp; &lt;script type="text/javascript"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; src="&lt;c:url value="/scripts/validator.jsp"/&gt;"&gt;&lt;/script&gt;<br>&nbsp;&nbsp;&nbsp; 并在&lt;html:form&gt;中定义onsubmit事件：<br>&nbsp;&nbsp;&nbsp; onsubmit="return validateUserForm(this)"<br>&nbsp;&nbsp;&nbsp; 在userForm表单的提交和取消按钮中包含了onclick="bCancel=false"事件。bCancel是Validator定义的是否进行验证的变量。当用户点击删除或取消按钮时，表单无须验证，此时bCancel= true。</p><img src ="http://www.blogjava.net/cgogg/aggbug/124989.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cgogg/" target="_blank">chenguo</a> 2007-06-18 18:11 <a href="http://www.blogjava.net/cgogg/articles/124989.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>AppFuse学习笔记-控制层</title><link>http://www.blogjava.net/cgogg/articles/124990.html</link><dc:creator>chenguo</dc:creator><author>chenguo</author><pubDate>Mon, 18 Jun 2007 10:11:00 GMT</pubDate><guid>http://www.blogjava.net/cgogg/articles/124990.html</guid><wfw:comment>http://www.blogjava.net/cgogg/comments/124990.html</wfw:comment><comments>http://www.blogjava.net/cgogg/articles/124990.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/cgogg/comments/commentRss/124990.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cgogg/services/trackbacks/124990.html</trackback:ping><description><![CDATA[<p>1. 我们先详细介绍一下<strong>Struts的控制器</strong>的工作流程。<br>&nbsp;&nbsp;&nbsp;
首先，控制器（ActionServlet）进行初始化工作，读取配置文件struts-config.xml，为不同的Struts模块初始化相应的
ModuleConfig对象。相应地有ControlConfig集合、FormBeanConfig集合、ForwardConfig集合和
MessageResourcesConfig集合等。<br>&nbsp;&nbsp;&nbsp; AppFuse中设计了一个继承ActionServlet 的子类：LoginServlet做为控制器，对ActionServlet的功能进行了扩展。主要的功能是对本系统使用到的一些组件进行初始化工作，如用户身份的安全校验url、密码加密规则等。<br>&nbsp;&nbsp;&nbsp; 控制器接收到HTTP请求，并从ActionConfig中找出对应于该请求的Action子类，如果没有对应的Action，控制器直接将请求转发给JSP或者静态页面。否则控制器将请求分发至具体Action类进行处理。<br>&nbsp;&nbsp;&nbsp;
在控制器调用具体Action的方法之前，相应的ActionForm对象将利用HTTP请求中的参数来填充自己。此外，若所操作的ActionForm
类配置了validate，系统会调用validate组件来检查请求参数的合法性，并且返回一个包含所有错误信息的ActionErrors对象。如果
执行成功，ActionForm自动将这些参数信息以FormBean的方式保存在Servlet
Context中，这样它们就可以被其它Action对象或者JSP调用。<br>&nbsp;&nbsp;&nbsp; 执行完成Action中的方法之后，返回一个ActionForward对象，控制器通过该ActionForward对象来进行转发工作。</p>
<p>&nbsp;&nbsp;&nbsp; AppFuse实现的最主要的功能是对用户的管理。下面就以对用户的管理来说明一下AppFuse控制器的实现。</p>
<p>&nbsp;&nbsp;&nbsp; 与用户信息表单数据相关的FormBean是UserForm。UserForm用来存放用户的基本信息。<br>&nbsp;&nbsp;&nbsp;
UserForm的控制器是org.appfuse.webapp.action.UserAction。UserAction继承了
BaseAction。BaseAction也是系统中几乎所有Action类的父类，它继承自Struts提供的DispatchAction，并提供
了控制层通用的方法。</p>
<p>2. <strong>Action</strong><br>Action是用户请求和业务逻辑之间的桥梁，每个Action充当客户的一项业务代理。主要完成以下任务：<br>&nbsp;&nbsp;&nbsp; 1.&nbsp;接收用户请求。<br>&nbsp;&nbsp;&nbsp; 2.&nbsp;根据用户请求，调用合适的模型组件来执行相应的业务逻辑。<br>&nbsp;&nbsp;&nbsp; 3.&nbsp;获取业务逻辑执行结果<br>&nbsp;&nbsp;&nbsp; 4.&nbsp;根据当前状态以及业务逻辑执行结果，选择合适的视图组件返回给用户。</p>
<p>3. <strong>DispatchAction</strong><br>&nbsp;&nbsp;&nbsp; org.apache.struts.actions.DispatchAction是org.apache.struts.action.Action的子类。<br>&nbsp;&nbsp;&nbsp; Action的execute()方法是调用模型的业务方法，完成用户请求的业务逻辑，然后根据执行结果把请求转发给其它合适的Web组件。通常，在一个Action类中只能完成一种业务操作（通过execute()方法）。<br>&nbsp;&nbsp;&nbsp; DispatchAction允许用户将完成一个业务逻辑所需要的连续动作和相关动作集中于一个Action类中。无须覆盖execute()方法，而是可以创建一些实现实际业务操作的方法，用户通过method请求参数指定所需要使用的方法。</p>
<p>4. <strong>BaseAction</strong><br>&nbsp;&nbsp;&nbsp; BaseAction中实现了系统中Action子类需要用到的通用方法，主要有：<br>&nbsp;&nbsp;&nbsp; 1.&nbsp;public Object getBean(String name):通过读取Spring的applicationContext-service.xml配置文件来创建实例从而实现&#8220;依赖注入&#8221;的方法。<br>&nbsp;&nbsp;&nbsp; 2.&nbsp;public ActionMessages getMessages(HttpServletRequest request)：初始化Struts的ActionMessages，并返回。<br>&nbsp;&nbsp;&nbsp; 3.&nbsp;protected Object convert(Object o) ：POJO与FormBean之间的转换方法。<br>&nbsp;&nbsp;&nbsp; 4.&nbsp;execute()：置换了原始的execute()方法。作用是根据请求的url寻找相关的业务方法（没有method请求参数时）。例如请求的页面是editUser.html,则执行edit()方法。<br>&nbsp;&nbsp;&nbsp; 5.&nbsp;protected ActionForm getActionForm(ActionMapping mapping,&nbsp;HttpServletRequest request)：在mapping范围内得到一个Action FormBean。<br>&nbsp;&nbsp;&nbsp; 6.&nbsp;protected User getUser(HttpSession session) ：从Session中拿到当前登录用户信息。<br>&nbsp;&nbsp;&nbsp;
7.&nbsp;protected void updateFormBean(ActionMapping
mapping,&nbsp;HttpServletRequest request, ActionForm form)：在指定的范围内更新当前Action
所对应的Form Bean对象。<br>&nbsp;&nbsp;&nbsp; 8. protected void removeFormBean(ActionMapping mapping, HttpServletRequest request)：去除无用的Form Bean。&nbsp;</p>
<p>5. <strong>UserAction</strong><br>&nbsp;&nbsp;&nbsp; UserAction是用户数据管理的控制器，提供了对用户数据的添加、修改、删除、查询等操作的控制，其包含的业务方法有：<br>&nbsp;&nbsp;&nbsp; 1.&nbsp;add()：进入添加用户页面。<br>&nbsp;&nbsp;&nbsp; 2.&nbsp;cancel()：当用户在表单中点击&#8220;取消&#8221;按钮时，执行该方法。如果用户是通过用户列表进入该表单，即当前用于拥有管理其它用户的权限，返回到用户列表，否则返回到主页面。<br>&nbsp;&nbsp;&nbsp; 3.&nbsp;delete()：根据请求的用户ID调用业务层的removeUser ()方法删除相应的记录，返回到用户列表。<br>&nbsp;&nbsp;&nbsp;
4.&nbsp;edit()：首先校验用户请求的URL是否为editProfile.html，若是，表示当前登录用户要查看自己的信息，此时请求路径中不应该
含有用户ID（用户ID从Session中取得）或用于标识请求是通过用户列表的from参数，如果含有这两个参数，发出&#8220;无权限&#8221;的错误信息。若不是，
表示当前用户是通过用户列表进行请求。根据请求的用户ID调用业务层的getUser ()方法取得相应的记录。更新相应的FormBean。<br>&nbsp;&nbsp;&nbsp;
5.&nbsp;save()：当需要要添加或修改用户信息时，执行该方法。首先校验请求参数中是否有&#8220;encryptPass&#8221;并值为&#8220;true&#8221;，若是，表示密
码需要加密，随即将密码加密。然后调用业务层的getRole ()方法将提交的用户权限持久化。调用业务层的saveUser
()方法，保存用户信息。如果用户编辑的是自己的信息，更新Session中的当前登录用户信息。如果用户在登录时选择了&#8220;记住我&#8221;，更新保存的
Cookie。如果用户编辑的是其它用户的信息，根据version判断是新添数据还是修改数据，在ActionMessages中存储不同的消息，返回
到添加/修改用户信息页面。若是新添数据，调用sendNewUserEmail()给新用户发送一封Email。<br>&nbsp;&nbsp;&nbsp; 6.&nbsp;search()：具有管理权限的用户进入用户列表时执行该方法。调用业务层的getUsers ()方法，取得包含所有用户信息的List。返回到用户列表页面。<br>&nbsp;&nbsp;&nbsp; 7.&nbsp;unspecified()：如果请求的url没有包含method参数，通过BaseAction的execute()方法也找不到指定的方法时，执行该方法。在该方法中转到search()方法。<br>&nbsp;&nbsp;&nbsp; 以上是很多Action需要用到的基本的方法。<br>&nbsp;&nbsp;&nbsp; 8.&nbsp;sendNewUserEmail()：根据FormBean中的内容给用户发送一封Email。<br>&nbsp;&nbsp;&nbsp; 9.&nbsp;checkForCookieLogin()：如果用户是通过Cookie登录（用户在登录时选择了&#8220;记住我&#8221;），发出一个消息警告用户不能修改密码。<br></p><img src ="http://www.blogjava.net/cgogg/aggbug/124990.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cgogg/" target="_blank">chenguo</a> 2007-06-18 18:11 <a href="http://www.blogjava.net/cgogg/articles/124990.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>AppFuse学习笔记-模型层</title><link>http://www.blogjava.net/cgogg/articles/124988.html</link><dc:creator>chenguo</dc:creator><author>chenguo</author><pubDate>Mon, 18 Jun 2007 10:09:00 GMT</pubDate><guid>http://www.blogjava.net/cgogg/articles/124988.html</guid><wfw:comment>http://www.blogjava.net/cgogg/comments/124988.html</wfw:comment><comments>http://www.blogjava.net/cgogg/articles/124988.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/cgogg/comments/commentRss/124988.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cgogg/services/trackbacks/124988.html</trackback:ping><description><![CDATA[<p>3.3 模型层结构<br>&nbsp;&nbsp;&nbsp; Model层为整个系统的核心部分，完成应用的业务逻辑及与数据库的通信。AppFuse中将Model分为两层：持久层和业务层。采用Spring＋Hibernate框架实现，这里以对用户User数据的操作为例详细阐述其实现方式。</p>
<p>&nbsp;&nbsp;&nbsp; 对持久化数据的访问基于DAO（Data Access Object）模式实现。DAO模式提供了访问关系型数据库系统所需的所有接口操作的接口。DAO模式将底层数据访问操作与高层业务逻辑分离开，对上层提供面向对象的数据访问接口。</p>
<p>&nbsp;&nbsp;&nbsp; Model层与User相关的类有：<br>&nbsp;&nbsp;&nbsp; POJO：<br>&nbsp;&nbsp;&nbsp; User：管理员表的业务对象。<br>&nbsp;&nbsp;&nbsp; 业务层：<br>&nbsp;&nbsp;&nbsp; UserManager：业务层接口，为控制层所调用。<br>&nbsp;&nbsp;&nbsp; UserManagerImpl：业务层接口的实现，调用持久层接口。<br>&nbsp;&nbsp;&nbsp; 持久层：<br>&nbsp;&nbsp;&nbsp; UserDAO：持久层接口，为业务层实现所调用。<br>&nbsp;&nbsp;&nbsp; UserDAOHibernate：持久层接口的实现。<br>&nbsp;&nbsp;&nbsp; XML配置文件：<br>&nbsp;&nbsp;&nbsp; applicationContext-service.xml：业务层接口的配置文件。<br>&nbsp;&nbsp;&nbsp; applicationContext-hibernate.xml：持久层接口的配置文件。</p>
<p>3.3.2 Spring的IoC<br>&nbsp;&nbsp;&nbsp; Ioc（Inversion of Control）即反转控制。Ioc模式即Dependency Injection模式是依赖注射的意思，也就是将依赖先剥离，然后在适当时候再注射进入。<br>&nbsp;&nbsp;&nbsp;
Spring的轻量级的bean容器为业务对象（business objects）、DAO对象和资源（如：JDBC数据源或者Hibernate
SessionFactorie等）对象提供了IoC类型的装配能力。Spring使用一个xml格式的应用配置文件为开发者提供了一种通过解析定制的属
性文件来手动管理单实例对象或者工厂对象的选择性。由于Spring将非入侵性做为一个重要的目标，因此可以由Spring配置管理的bean对象均不需
要依赖Spring自有的接口和类就可以通过它们的bean属性完成配置。<br>&nbsp;&nbsp;&nbsp; 就实现上来讲Spring采取了配置文件的形式来实现依赖的注射，并且支持Type2 IOC（Setter Injection）以及Type3 IOC（Constructor Injection）。 <br>&nbsp;&nbsp;&nbsp; 在Model层，使用Spring提供的Setter Injection(type2)注入方式。以User为例，下面是其用法。<br>&nbsp;&nbsp;&nbsp; 在applicationContext- hibernate.xml中定义<br>&nbsp;&nbsp;&nbsp; &lt;bean id="userDAO" class="org.appfuse.dao.hibernate.UserDAOHibernate"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="sessionFactory"&gt;&lt;ref local="sessionFactory"/&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br>&nbsp;&nbsp;&nbsp; 在UserManager类中有一句： <br>&nbsp;&nbsp;&nbsp; public void setUserDAO(UserDAO dao);<br>&nbsp;&nbsp;&nbsp; 这就是一个DAO Object设置方法（注射器）。UserDAO将被调用，和持久层通信。以这种方式创建UserDAO的实例，同样达到了由UserManager创建UserDao的目的。避免了直接实例化UserDAO的实现而使业务层和持久层紧密耦合。<br>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; 在控制层调用业务层方法时，使用服务定位器返回给Spring context，Spring的BeanFactory提供了getBean方法。BeanFactory是一个通用的Factory，它使对象能够按名称获取，并且能管理对象之间的关系。<br>&nbsp;&nbsp;&nbsp; 在applicationContext-service.xml中配置<br>&nbsp;&nbsp;&nbsp; &lt;bean id="userManager" parent="txProxyTemplate"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="target"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean class="org.appfuse.service.impl.UserManagerImpl"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="userDAO"&gt;&lt;ref bean="userDAO"/&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br>&nbsp;&nbsp;&nbsp; 在控制层BaseAction定义通用方法： <br>&nbsp;&nbsp;&nbsp; private static ApplicationContext ctx = null;<br>&nbsp;&nbsp;&nbsp; public Object getBean(String name) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ctx == null) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ctx = WebApplicationContextUtils<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .getRequiredWebApplicationContext(servlet.getServletContext());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return ctx.getBean(name);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; 在UserAction中创建UserManager的实例：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UserManager mgr = (UserManager) getBean("userManager");<br>&nbsp;&nbsp;&nbsp; 这样，通过BeanFactory的getBean方法，以及xml配置文件，避免了在UserAction类中直接实例化UserManager，消除了控制层与业务层及业务层与持久层之间的耦合，实现了依赖的注射。<br>&nbsp;&nbsp;&nbsp; ApplicationContext 是BeanFactory的子接口，为下列东西提供支持： <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 信息查找，支持着国际化 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 事件机制，允许发布应用对象以及可选的注册以接收到事件 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可移植的文件和资源访问 </p>
<p>3.3.3 Spring的事务管理<br>&nbsp;&nbsp;&nbsp; 在数据持久层的杰出贡献，可能是Spring最为闪亮的优点。<br>&nbsp;&nbsp;&nbsp;
Spring提供了通过容器的集约式参数化事务机制，实现事务的外部管理。容器管理的参数化事务为程序开发提供了相当的灵活性，同时因为将事务委托给容器
进行管理，应用逻辑中无需再编写事务代码，大大节省了代码量（特别是针对需要同时操作多个事务资源的应用），从而提高了生产率。<br>&nbsp;&nbsp;&nbsp; AppFuse在applicationContext-service.xml文件中进行了对事务的配置<br>&nbsp;&nbsp;&nbsp; &lt;bean id="txProxyTemplate" abstract="true"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="transactionManager"&gt;&lt;ref bean="transactionManager"/&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="transactionAttributes"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;props&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="save*"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="remove*"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="*"&gt;PROPAGATION_REQUIRED,readOnly&lt;/prop&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/props&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<br>&nbsp;&nbsp;&nbsp;
这里定义了一个名为txProxyTemplate的TransactionProxyFactoryBean服务。它对包含实际数据逻辑的持久层对象进
行了事务的封装。在这里，通过transactionAttributes属性，我们指定了事务的管理策略，即将所有的名称以save和remove开头
的方法纳入事务管理范围。如果此方法中抛出异常，则Spring将当前事务回滚，如果方法正常结束，则提交事务。<br>&nbsp;&nbsp;&nbsp; 而对所有其它方法则以只读的事务处理机制进行处理。设为只读型事务，可以使持久层尝试对数据操作进行优化，如对于只读事务Hibernate将不执行flush操作，而某些数据库连接池和JDBC 驱动也对只读型操作进行了特别优化。<br>&nbsp;&nbsp;&nbsp; 如果有其他的方法需要进行写数据库操作，可以在相应的Manager配置中声明。如在UserManager中，就添加了属性<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="transactionAttributes"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;props&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="save*"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="remove*"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="*LoginCookie"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="*"&gt;PROPAGATION_REQUIRED,readOnly&lt;/prop&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/props&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp; 这样，以LoginCookie结尾的方法也可以写数据库了。<br>&nbsp;&nbsp;&nbsp; Spring可以将任意Java Class 纳入事务管理，而无需对其进行任何修改，因此我们的类可能完全不知道它正在被进行事务管理。</p>
<p><br>3.3.3 Spring＋Hibernate操作持久层<br>&nbsp;&nbsp;&nbsp; Spring对Hibernate有很好的支持。<br>&nbsp;&nbsp;&nbsp;
Hibernate中通过SessionFactory创建和维护Session。Spring对SessionFactory的配置进行了整合，无需再
通过Hibernate.cfg.xml对SessionFactory进行设定。SessionFactory节点的mappingResources
属性包含了映射文件的路径，list节点下可配置多个映射文件。hibernateProperties节点则容纳了所有的属性配置。可以对应传统的
Hibernate.cfg.xml文件结构对这里的SessionFactory配置进行解读。<br>&nbsp;&nbsp;&nbsp; 下面是HibernateSessionFactory 和 HibernateTransactionManager的配置：<br>&nbsp;&nbsp;&nbsp; 在applicationContext-hibernate.xml中：<br>&nbsp;&nbsp;&nbsp; &lt;bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="dataSource"&gt;&lt;ref bean="dataSource"/&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="mappingResources"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;com/mycompany/model/User.hbm.xml&lt;/value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="hibernateProperties"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;props&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="hibernate.dialect"&gt;@HIBERNATE-DIALECT@&lt;/prop&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!--prop key="hibernate.show_sql"&gt;true&lt;/prop--&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!--prop key="hibernate.hbm2ddl.auto"&gt;update&lt;/prop--&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/props&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
<p>&nbsp;&nbsp;&nbsp;
Spring 提供了一个
HibernateTransactionManager，采用面向Hibernate的TransactionManager实现：
org.springframework.orm.hibernate.HibernateTransactionManager。他用线程捆绑了一个
Hibernate Session，用它来支持transactions。<br>&nbsp;&nbsp;&nbsp; &lt;bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="sessionFactory"&gt;&lt;ref local="sessionFactory"/&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
<p>&nbsp;&nbsp;&nbsp;
sessionFactory Bean引用了HibernateSessionFactory，而transactionManager
Bean引用了HibernateTransactionManage。 transactionManager
Bean中有个sessionFactory属性。 HibernateTransactionManager有个sessionFactory
setter 和 getter方法，用来在Spring启动的时候实现&#8220;依赖注入&#8221; （dependency injection）的。
在sessionFactory 属性里引用sessionFactory Bean。这两个对象在Spring容器初始化后就被组装了起来了。<br>&nbsp; <br>&nbsp;&nbsp;&nbsp;
User使用一个TransactionProxyFactoryBean，它定义了一个setTransactionManager()。能很方便的处
理申明的事物还有Service Object。TransactionProxyFactoryBean 还有个setter.
这会被Business service object（UserManager）引用，
UserManager定义了业务层，并且它还有个属性，由setUserDAO()引用。</p>
<p>&nbsp;&nbsp;&nbsp;
系统持久层中所有的类都继承自Spring提供的HibernateDaoSupport类，HibernateSupport实现了
HibernateTemplate和SessionFactory实例的关联。HibernateTemplate对Hibernate
Session操作进行了封装，提供了一个简单的方式实现了Hibernate-based
DAO对象。借助HibernateTemplate我们可以脱离每次数据操作必须首先获得Session实例、启动事务、提交/回滚事务以及烦杂的
try/catch/finally的繁琐操作。一个简单的Hibernate访问方法就完全解决了些麻烦!
无论是在多个DAO接口还是在多方事务的情况下，Spring使得多种DAO对象无缝地协同工作。<br>&nbsp;&nbsp;&nbsp; 对于简单的单步的动作，象find,
load,
saveOrUpdate或者delete的调用，HibernateTemplate提供更为便利的选择以代替象一行的回调的执行。此外
HibernateDaoSupport类提供了setSessionFactory方法来接受一个SessionFactory，同时提供了
getSessionFactory和getHibernateTemplate方法供其继承类使用。将这些结合起来，允许对于典型的需求给出了非常简单
的DAO实现，如获得所有用户的方法：<br>&nbsp;&nbsp;&nbsp; public List getUsers(User user) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return getHibernateTemplate().find("from User u order by upper(u.username)");<br>&nbsp;&nbsp;&nbsp; }<br></p>
<br><img src ="http://www.blogjava.net/cgogg/aggbug/124988.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cgogg/" target="_blank">chenguo</a> 2007-06-18 18:09 <a href="http://www.blogjava.net/cgogg/articles/124988.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>领域服务与业务服务职责的讨论</title><link>http://www.blogjava.net/cgogg/articles/123850.html</link><dc:creator>chenguo</dc:creator><author>chenguo</author><pubDate>Wed, 13 Jun 2007 05:28:00 GMT</pubDate><guid>http://www.blogjava.net/cgogg/articles/123850.html</guid><wfw:comment>http://www.blogjava.net/cgogg/comments/123850.html</wfw:comment><comments>http://www.blogjava.net/cgogg/articles/123850.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/cgogg/comments/commentRss/123850.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cgogg/services/trackbacks/123850.html</trackback:ping><description><![CDATA[在领域设计中，划分为三种模型，分别为：实体(Entity)、值对象(Value Object)、和服务(Service)。其中Service与我们传统设计中的Service有什么不同呢？<br><br>让
我们来回忆一下，通常我们针对将读写xml、资金转账等代码放在service中，可以看出，该层包括了两种含义，一种是与业务无关的，一种是与业务紧密
关联的。领域驱动设计将这两层含义进一步划分，《Domain-Driven
Design》中的例子那样：如果银行应用可以将我们的交易转化并输出电子表格文件，那么这种输出就应该是应用服务。而负责处理资金转帐的借贷关系，应该
属于领域层，它包括了基本的业务逻辑，而技术层上的服务则根本没有任何业务含义。由此得知，将传统设计中的Service继续划分，得到应用服务与领域服
务。领域层和应用层的服务是相互合作，由应用Service指挥领域对象来解决问题。<br><br>如此划分，可以使各层的结构和职责更加清晰，技术与业务进一步分离。<br><br>以上是我个人的理解，有不对之处还请指正。<br><br>另仔细阅读<a href="http://www.jdon.com/mda/ddd.html">实战DDD(Domain-Driven Design领域驱动设计)</a>，
在该文中说到：&#8220;在JiveJdon3中，com.jdon.jivejdon.service.ForumService和Forum实体模型及其值对
象ForumState共同完成领域模型，其中ForumService属于应用服务层；而后两者属于领域层；其他服务
ForumMessageService、AccountService和UploadService等都是此类性质。&#8221;在JiveJdon3中并没有对
领域与应用Service进行明确的划分，而是由ForumService来完成。JJ3的代码我没有看过，只是从这段文字还这样判断的，有不对之处还请
包含。请问，JJ3中是否对Service进行了划分？如果没有那么您是怎样考虑的呢？
<br><br>bangq：
非常有道理，Evans DDD中的Service和我们传统的Service确实不一样，它主要划分为应用层服务和领域层服务以及基础结构层服务。<br><br>在我的JJ3案例中，我没有进行这种严格的分层分类，是以Web服务中的Service和操作Operations概念来区分的：<br><br>需要对外开放的、供客户端调用的我命名为服务，当然这个服务里也可能会区分为应用服务和领域服务；不对外开放，供业务层内部使用的普通operations就作为普通组件来看待。<br><br>个人目前感觉：应用服务和领域服务区分需要非常敏感，而且目前没有看到大大的好处。<br><br>有时，快速性 方便性确实必须注意，当然这个尺度是根据当事人的水平而定的。<br><br>无论如何，我们更应该来关注领域对象：也就是实体和值对象，当然，领域对象并不是无行为的对象，可以封装一些业务规则，不是简单的setter或getter行为。<br><br>不知你是如何想？
<br><br>版主：
应用服务与领域服务的区分确实非常敏感且难以撑控，在较为复杂的应用中，应用服务与领域服务并不一定能够完全实现分离。这就需要不断地重构来完善，但并不一定能够完美(个人观点)。<br><br>我认同领域对象不应只是简单的setter或getter，可以封装一些业务规则。但前题是这些规则一定是它本身的职责才是，否则模型就会遭到破坏(面向对象的精要处)。
<br><br>版主：
引用两段文字来说明问题：<br>&#8220;OO编程的技术是一门代理的艺术。职责划分明确，把接口定义好，把实现交给另外的类来做。首先把变化的部分和不变的部分 划分出来。不变的部分，就成了框架。变化的部分，就是程序员自己做。 &#8221;<br><br>领
域对象是可以有行为能力的，在Rod Johnson的《without
EJB》第10章《持久化》中有一段文字：&#8220;领域对象包含的逻辑，这里称之为&#8220;domain logic&#8221;，这些domain
logic应该最小化的依赖于DAO，而我们讨论的那些需要持久化操作参与的事务性逻辑，这里称之为&#8220;workflow
methods&#8221;，这些&#8220;workflow methods&#8221;应该放在&#8220;business facade&#8221;，而不应该放在domain
object里面。可重用度很高的操作可能是domain logic，应该放在domain
object里面；比较难重用的控制逻辑方法，特别是可跨越多个domain object的操作则放在business facade
object里面。&#8221;<br><br>虽然领域对象是可以有行为能力的，但除领域对象之外一定是要业务对象的。业务对象应该操控多个领域对象的协作。并不是将所有的逻辑都塞到领域对象中。
<br><br>bangq：
&gt;但前题是这些规则一定是它本身的职责才是，否则模型就会遭到破坏(面向对象的精要处)。 <br>所以，例如各种条件的查询，这些条件的筛选功能也应该属于规则，过去我们将数据筛选留给数据库SQL语句完成，现在DDD认为筛选应该在内存中完成，SQL文本也是一种规则。<br><br>&gt;除领域对象之外一定是要业务对象的<br>引用Rod 的话容易引起误解，前面我们讨论过，我们的模型分领域对象和服务，领域对象又分实体和值对象。那么Rod这个业务对象又是什么呢？如果是服务，我们一般不会将服务称为对象，服务是一种组件模型，要么是服务，要么是操作operations
<br><br>
&gt;Service层和Function层的关系应该是胃和小肠的关系一样<br>这是很形象比喻，现在确实没有这方面框架，Service层和Function层分离，从现在来看，好像更多还是分析领域的事情，必须由建模人员来指定哪些服务是应用还是领域。
<br><br><img src ="http://www.blogjava.net/cgogg/aggbug/123850.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cgogg/" target="_blank">chenguo</a> 2007-06-13 13:28 <a href="http://www.blogjava.net/cgogg/articles/123850.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>J2EE分层架构解析</title><link>http://www.blogjava.net/cgogg/articles/123842.html</link><dc:creator>chenguo</dc:creator><author>chenguo</author><pubDate>Wed, 13 Jun 2007 05:07:00 GMT</pubDate><guid>http://www.blogjava.net/cgogg/articles/123842.html</guid><wfw:comment>http://www.blogjava.net/cgogg/comments/123842.html</wfw:comment><comments>http://www.blogjava.net/cgogg/articles/123842.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/cgogg/comments/commentRss/123842.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cgogg/services/trackbacks/123842.html</trackback:ping><description><![CDATA[众多的设计书籍都推荐用分层结构，这也几乎是框架设计者的共识了。然而层是分<br>了，具体分几层，还是一个值得决策的问题。我最熟悉的《责任驱动设计》和《领域驱<br>动设计》都推荐用四层，而且两者的吻合度相当高，我受这两本书的影响也最大，所以<br>很自然我的框架也分四层。<br><br>&nbsp;&nbsp;&nbsp; 哪四层？表现层、应用层、领域层和技术层。<br><br>&nbsp;&nbsp;&nbsp; 浏览器、PHP和数据库是比较容易区分出来的物理上的三个层次。然而这只是物理<br>分层，跟逻辑分层还是有区别的。目前Ajax非常流行，而且很多应用也确实需要Ajax来<br>提供富交互功能，所以支持Ajax是相当自然的一件事。在物理层面上，Ajax属于浏览器<br>层。实际上我对在浏览器端维护一个领域模型并不感兴趣，原因是难以在两者间保持同<br>步，也无谓地增加了复杂性。我完全可以直接在Action里处理，然后直接用PHP内置的<br>JSON系列函数直接输出数据供客户端JavaScript进行eval调用。用JavaScript Helper<br>来生成Ajax，目前我还没试过，具体是否支持，还要到时看情况而定。另外在资源定位<br>方面，我觉得REST的思想不错，不过目前还不是很熟悉REST，所以这一块打算暂时先放<br>一放，到时再重构以适应REST。<br><br>&nbsp;&nbsp;&nbsp; 回到PHP这一物理层面上来。上面所说的四层架构，就是在PHP这一物理层次。<br><br>&nbsp;&nbsp;&nbsp; 先说表现层。PHP对表现层的支持非常灵活非常方便，因为PHP最初就是用来做表现<br>层的。关于页面的组织，我的方法是把一个HTML页面分成两类元素，一类是布局，另一<br>类是模板。跟现有的一些框架不一样的是，我的模板没有再细分为其它元素，如slot之<br>类。因为没必要。一个布局对应一个有HTML输出的Action，同时一个布局还对应多个模<br>板。注意，布局跟Action是一一对应的，但模板不同，模板是完全灵活的，在某个时候<br>它可以嵌入某个布局，在另一个时候可以嵌入另一个布局。这样一来，模板就成了完全<br>可以复用的单元，而且经过我的处理，每个模板可以在树结构上从树叶一直往上移到树<br>根，以在多个Action、多个Module乃至多个App间的布局里共享。如果你熟悉JavaScript<br>的对象绑定，你就很容易理解一个对象做为另一个对象的某个属性这一过程跟我的模板<br>对应布局这一过程非常相似，都是动态绑定的。<br><br>&nbsp;&nbsp;&nbsp; 表现层由Action调用，而Action则处于应用层，多个Action组成一个Module，多个<br>Module组成一个Application（这一点参考自symfony，实际上在没有接触过symfony前<br>我就想到过这种组织方式）。这里有一个取舍问题，就是是把一个Action当成一个文件<br>处理，还是多个Action集中到一个文件来处理。symfony，Zend Framework采取的是一个<br>文件多个Action的方式。我之前采用的是一个文件一个Action的方式，后来想想这种方<br>式有一个缺点：难以在多个Action间共享某些计算，如Admin Module下的多个Action，<br>它们都需要检查客户端是否有有效的会话信息以确保用户已经登录。在一个文件一个<br>Action的情况下，这些重复代码必然会在多个Action间复制，除非你把这部分代码抽取<br>出来放到某个地方，但问题是应该放在哪个地方？我想不到好的地方（你不可能把它们<br>放到抽象的Action父类，因为这些逻辑只是属于某个模块的，不是所有的Action子类都<br>有的），所以我打算采用一个文件多个Action的方法，并把这些重复代码抽象成一个预<br>处理方法和后处理方法，当调用某个模块的Action时，预先执行这些代码。在这些代码<br>里，自然是校验和计算，如果通不过，直接给Response对象发redirect消息，让客户端<br>重定向到某个地方。<br><br>&nbsp;&nbsp;&nbsp; 除了调用表现层，Action还有哪些职责？实际上，Action的职责并不多，但它仍是<br>系统的核心所在，因为你的Action必须捕获所有的用例。可能一个用例有多个Action参<br>与，也可能一个用例只有一个Action，这取决于你的用例分析。这是OOA阶段的任务，<br>这里不多讲，一个优秀的分析人员必须能编写高质量的用例（可以参考W.C的《编写有<br>效用例》一书）。捕获了用例后，Action有两个任务：一，选取某个领域服务，让该<br>领域服务处理某个逻辑事务；二，选取某个应用服务，让该服务完成应用相关的任务，<br>如发送Email，把领域对象缓存进Cache源，把领域对象保存到Session数据源，等等。<br>这里的关键，是区分领域服务和应用服务，否则很难做出正确的设计。关于服务层，我<br>推荐阅读三本书籍：一是《领域驱动设计》，这本书有一小节详细地论述了服务层的设<br>计以及领域服务和应用服务的区分；二是《企业应用架构模式》，这本书有两个地方讲<br>述了服务层，讲得比较细致；三是《J2EE核心模式》，里头的&#8220;业务服务&#8221;模式就是领<br>域服务的精确描述，简洁易懂。<br><br>&nbsp;&nbsp;&nbsp; 应用服务操作技术层对象，那么领域服务操作什么对象？领域服务操作领域模型。<br>关于领域模型，有非常多的书有详细的讲述，前面提到的三本书都有详细的讲述，除此<br>外还有《UML与模式应用》，《UML面向对象建模与设计》等书，都有对领域模型的细致<br>讲述。理解领域模型并运用领域模型是一种非常重要的技能，因为只有分析并提炼出合<br>适的领域模型，设计才不会偏离需求。需要注意的是，我个人对领域模型复杂化并没有<br>好感，因为这样一来映射到数据源将非常麻烦。我崇尚让领域模型跟数据库的E-R模型<br>一一对应，这样一来无论理解还是修改，都非常简单。另外一个可能影响设计的东西是<br>值对象。目前对这些比较细节方面的东西的处理，我还暂时没有深入考虑，等设计到那<br>时再说。<br><br>&nbsp;&nbsp;&nbsp; 需要注意的是，领域服务不能剥夺本应属于领域模型的一部分业务逻辑，否则就混<br>淆了领域服务与领域模型的区别。比如一个领域模型：用户，它有一个规格检查：年龄<br>是否达到要求，这个规格检查就不应该放到领域服务里，而应该直接加在领域模型上，<br>这也是非常自然的处理方法。此外，类似查询用户是否存在这样的任务，就应该组织到<br>一个领域服务里，由Action调用该服务。领域服务并不知道是谁调用它（这符合分层的<br>基本原则，即上层对象可以调用它下层的对象，以及更下层的对象，但下层对象不能调<br>用上层对象，如果确实需要，至少也要用观察者模式来解开耦合，以让领域服务能更好<br>地在多个Action间重用。<br><br>&nbsp;&nbsp;&nbsp; 领域层是由开发人员组织的，框架对领域层基本没有做限制，开发者可以进行任意<br>的发挥，前面我只是提出一种我认为比较合适的组织方法。下一篇将讲述框架内最重要<br>的组成部分——技术层，以及技术层里的重中之重——持久层。
<br><img src ="http://www.blogjava.net/cgogg/aggbug/123842.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cgogg/" target="_blank">chenguo</a> 2007-06-13 13:07 <a href="http://www.blogjava.net/cgogg/articles/123842.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>