让域对象(Domain Object)纵穿整个系统

传统B/S业务系统数据转换复杂,引入无关对象增加系统复杂度。近年,OGNL和WebWork2解决了UI与业务模块交互问题,Hibernate解决了对象系统与关系数据库的不协调。本文介绍结合Webwork2和Hibernate的实现方式,避免数据多次转换,提高开发效率。

——结合使用Webwork2Hibernate简化系统结构、提高开发效率

引子

如果狭隘的理解,很多时候,一个运行于B/S平台的业务系统的工作是接受用户从浏览器端输入的信息,经过处理后存储到数据库,或者,从数据库提取数据处理并格式化后传回浏览器显示给用户。然而,让应用设计者很沮丧的是:浏览器功能很弱,只能提交复杂度极低的信息,如携带着文本信息的表单(form);主流的数据库是关系数据库,并不能直接存储面向对象的领域模型数据;而应用系统往往是用面向对象的方式来设计、开发的。从只能传递简单数据类型的form,到面向对象的系统,再到关系数据库,都要跨越恼人的系统边界,也就是,我们都要对同一数据进行多次形态转换才能实现系统功能。为了解决这个问题,系统设计先辈提出了很多解决方案,如:用FormBean封装从浏览器传递过来的简单类型数据(也可能进行一些常见类形状换,但说到底,还是简单类型的堆砌,并不是领域模型domain model),用VO(不同的设计叫法不同,但思想是一样的)来对关系数据库做tables—JavaBeans的一对一映射等。

这种方式对浏览器的Form和关系数据库的Table进行了面向对象的封装,从某种意义上讲,起到了一个适配器的作用,在一定程度上隐藏了浏览器的form和数据库的table的底层信息,简化了系统设计,并在相当长的一段时期内取得了很好的效果。但是,随着业务系统需求复杂度的增加,这种解决方案显示出了诸多缺陷:

由于在系统中引入FormBeanVO这两类与domain object无关的对象,分散了开发人员对领域对象本身的关注,增加了系统的复杂度,降低了系统的运行效率,并对系统的耦合度、复用等带来负面的影响。可以认为,这已不再是最好的、最有效的解决方案,我们有必要对这种处理方式进行改进。

 

入题

近年,已经出现了很多成熟的解决方案:

OGNL(http://www.ognl.org)等设置、获取Java Object Properties的语言的出现解决了浏览器等UI系统同核心业务模块交互效果不佳的问题,并且,很多前端控制器(Front controller)很好的实现了OGNL,如WebWork2等;Hibernate(http://www.hibernate.org)O/R Mapping的出现,解决了从对象系统到关系数据库之间的不协调。

于是,在开发中,我们不必被迫在跨越逻辑层次时在不同的JavaBeans之间进行转换。

 

实现

       建立详细细致的业务模型,如:Employee

package info.powersoft.sample;

 

import java.util.Date;

 

/**

 * <p>Description: </p>

 * <p>Copyright: Copyright (c) 2005</p>

 * @author guangzong.xu

 * @since 2005-3-17

 * @version 1.0

 */

 

/**

 * @author guangzong.xu

 *

 * TODO To change the template for this generated type comment go to

 * Window - Preferences - Java - Code Style - Code Templates

 */

public class Employee {

 

       private Long id;

       private String name;

       private Date dateOfBirth;

       private Department department;

       /**

        * @return Returns the dateOfBirth.

        */

       public Date getDateOfBirth() {

              return dateOfBirth;

       }

       /**

        * @param dateOfBirth The dateOfBirth to set.

        */

       public void setDateOfBirth(Date dateOfBirth) {

              this.dateOfBirth = dateOfBirth;

       }

       /**

        * @return Returns the department.

        */

       public Department getDepartment() {

              return department;

       }

       /**

        * @param department The department to set.

        */

       public void setDepartment(Department department) {

              this.department = department;

       }

       /**

        * @return Returns the id.

        */

       public Long getId() {

              return id;

       }

       /**

        * @param id The id to set.

        */

       public void setId(Long id) {

              this.id = id;

       }

       /**

        * @return Returns the name.

        */

       public String getName() {

              return name;

       }

       /**

        * @param name The name to set.

        */

       public void setName(String name) {

              this.name = name;

       }

}

 

Department

package info.powersoft.sample;

 

/**

 * <p>Description: </p>

 * <p>Copyright: Copyright (c) 2005</p>

 * @author guangzong.xu

 * @since 2005-3-17

 * @version 1.0

 */

 

/**

 * @author guangzong.xu

 *

 * TODO To change the template for this generated type comment go to

 * Window - Preferences - Java - Code Style - Code Templates

 */

public class Department {

 

       private Long id;

       private String name;

       private Department superDepartment;

       /**

        * @return Returns the id.

        */

       public Long getId() {

              return id;

       }

       /**

        * @param id The id to set.

        */

       public void setId(Long id) {

              this.id = id;

       }

       /**

        * @return Returns the name.

        */

       public String getName() {

              return name;

       }

       /**

        * @param name The name to set.

        */

       public void setName(String name) {

              this.name = name;

       }

       /**

        * @return Returns the superDepartment.

        */

       public Department getSuperDepartment() {

              return superDepartment;

       }

       /**

        * @param superDepartment The superDepartment to set.

        */

       public void setSuperDepartment(Department superDepartment) {

              this.superDepartment = superDepartment;

       }

}

 

       DAO

package info.powersoft.sample;

 

/**

 * <p>Description: </p>

 * <p>Copyright: Copyright (c) 2005</p>

 * @author guangzong.xu

 * @since 2005-3-17

 * @version 1.0

 */

 

/**

 * @author guangzong.xu

 *

 * TODO To change the template for this generated type comment go to

 * Window - Preferences - Java - Code Style - Code Templates

 */

public interface EmployeeDAO {

 

       Long insertEmployee(Employee employee);

}

 

       使用Webwork2作为前端控制器(front controller),并在相应的action中把该业务模型作为一个变量,设置相应的gettersetter,如

package info.powersoft.sample;

 

/**

 * <p>Description: </p>

 * <p>Copyright: Copyright (c) 2005</p>

 * @author guangzong.xu

 * @since 2005-3-17

 * @version 1.0

 */

 

/**

 * @author guangzong.xu

 *

 * TODO To change the template for this generated type comment go to

 * Window - Preferences - Java - Code Style - Code Templates

 */

public class EmployeeAction {

 

    private EmployeeDAO employeeDAO;

    private Employee employee;

   

    /**

     * @return Returns the employeeDAO.

     */

    public EmployeeDAO getEmployeeDAO() {

        return employeeDAO;

    }

    /**

     * @param employeeDAO The employeeDAO to set.

     */

    public void setEmployeeDAO(EmployeeDAO employeeDAO) {

        this.employeeDAO = employeeDAO;

    }

    /**

     * @return Returns the employee.

     */

    public Employee getEmployee() {

        return employee;

    }

    /**

     * @param employee The employee to set.

     */

    public void setEmployee(Employee employee) {

        this.employee = employee;

    }

   

    public String execute() throws Exception {

        employeeDAO.insertEmployee(employee);

        return "success";

    }

}

       其中,EmployeeDAO是用于将域对象存储到数据库中的DAO,特定实现可以用Hibernate,这个大家都很熟悉,不在此赘述。

    发送到浏览器中的网页为

<html>

<head>

<title>test</title>

</head>

 

<body>

<form name="form1" method="post" action="employee.action">

  <p>

    <input name="employee.name" type="text">

  </p>

  <p>

    <input name="employee.dateOfBirth" type="text">

  </p>

  <p>

    <input name="employee.department.id" type="text">

  </p>

  <p>

    <input type="submit" name="Submit" value="Submit">

  </p>

</form>

</body>

</html>

       我们可以注意到网页中,文本输入框(input type="text")的名字(name)为类似于这样的东西:“employee.department.id”,经Webwork2interceptor的拦截,自动把这些form中的field中的字段拼装成我们所需要的employee对象,供我们方便的调用,省去了在FormBeanDomain Object间的转换。同样的道理,我们也可以把这些域对象送到视图层去格式化显示(这其中要用一些支持特定功能的表示技术,如jsp2.0freemarker等)

 

完了的话

       至此,我们已经完成从浏览器接受输入到存储进数据库,在这过程当中,一直是同一个域对象(Domain Object)承载着应用所需要的信息,没有被迫进行数据转换,代码相对简洁,结构相对明了,同时,执行效率也较传统方式(FormBean->Domain Object->VO)有所提高。

当然,本文只是从梗概上介绍了这种实现方式,所提供的代码并不能直接运行。如果你在体验的过程中有好的想法,或者遇到问题,欢迎与我交流boyxgz@msn.com

<think>我们正在讨论对象安全(ACL)的相关介绍。根据引用[2],ACL(访问控制列表)是Spring Security中用于实现对象实例安全性的核心概念。每个对象实例都有自己的ACL,记录谁能访问该对象以及如何访问的详细信息。结合引用[1]中的技术细节,ACL通过ObjectIdentity关联对象,并存储在ACL_OBJECT_IDENTITY表中。 现在,我将系统性地介绍对象安全(ACL): 1. ACL基本概念 2. Spring Security中的ACL实现 3. ACL的核心组件 4. 使用场景 注意:回答中需要自然地添加引用标记,并在最后生成相关问题。 </think>### 对象安全(ACL)介绍 对象安全(ACL,Access Control List)是一种细粒度的权限控制机制,用于管理**特定对象实例**的访问权限。与传统基于角色的权限控制(RBAC)不同,ACL 允许为每个对象单独定义访问规则,适用于需要精确控制数据访问的场景。 #### 1. **核心概念** - **对象Domain Object)** 系统中的业务实体(如订单、用户档案、文档等),每个实例都是独立的权限控制目标[^2]。 - **ACL 结构** - 每个对象关联一个唯一的 ACL - ACL 包含多个 **ACE(Access Control Entry)**,记录「谁(主体)对当前对象有何种权限」 - 示例 ACE:`用户A 可读写 | 用户B 只读 | 组C 禁止访问` - **对象标识(ObjectIdentity)** 通过唯一标识(如数据库 ID)关联对象和 ACL,避免直接耦合[^1]。 #### 2. **ACL 在 Spring Security 的实现** Spring Security 提供完整的 ACL 框架,包含三大核心功能: 1. **ACL 存储与管理** - 使用数据库表(如 `ACL_OBJECT_IDENTITY`)存储对象与 ACL 的映射关系[^1] - 通过 `AclService` 接口实现 ACL 的查询和修改 2. **方法级安全控制** - **调用前检查**:通过 `@PreAuthorize` 验证用户是否有权操作目标对象 - **调用后检查**:通过 `@PostAuthorize` 验证返回结果是否可被访问[^2] ```java @PostAuthorize("hasPermission(returnObject, 'READ')") public Document getDocument(Long id) { ... } ``` 3. **表达式权限** 支持 SpEL 表达式动态判断权限,例如: ```html <sec:ifAnyGranted roles="ROLE_USER"> <!-- 引用[4] --> <g:include controller="adminUser" action="myUsers"/> </sec:ifAnyGranted> ``` #### 3. **ACL 的典型应用场景** - **多租户系统**:控制租户间的数据隔离(如 A 公司不可访问 B 公司的订单) - **协作平台**:精细化控制文档的编辑/查看权限(如仅创建者可修改) - **权限管理**:在 Active Directory 中限制用户对组的管理权限(如禁止普通用户修改管理员组)[^3] #### 4. **ACL 的优势与挑战** | **优势** | **挑战** | |----------|----------| | ✅ 细粒度控制到对象级别 | ❌ 存储开销大(每个对象需独立 ACL) | | ✅ 动态权限调整灵活 | ❌ 权限逻辑复杂度高 | | ✅ 支持继承(如文件夹→文件) | ❌ 性能需优化(频繁 ACL 查询) | ---
评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值