您的位置:首页 > 编程语言 > ASP

asp.net和j2ee的三层结构代码比较

2007-05-31 00:47 477 查看
1 前言
j2ee曾提出多层结构的开发框架,但实际项目中三层结构仍旧是主流。一般划分为数据展现层、业务逻辑层、数据访问层。数据展现层只管数据的显示,完全不关心具体的业务逻辑;业务逻辑层负责业务逻辑处理,它位于数据展现层和数据访问层中间,其主要任务为调用数据访问层获取数据,以便交给展现层进行显示;数据访问层一般就负责访问数据库,如通过jdbc/odbc/ado.net等手段访问数据库。
日常的三层结构开发中,大部分精力耗费在CRUD(增删改查)操作上。笔者分别在j2ee和asp.net 2.0上进行了小试验,比较两者之间的开发特征。

2 j2ee和asp.net三层开发代码结构

2.1 asp.net 2.0平台
展现层:GridView / FormView / DetailView / DataList / Reapter, MasterPage以及自定义控件等asp服务器端控件对传统HTML标签进行了封装。
业务层:BLL(Business Login Layer),对CRUD操作进行封装,并采用ObjectDataStore封装要返回给展现层的数据对象。
数据访问层:DAL(Data Access Layer),采用ADO.net(SqlConnection或OracleConnection等)来访问数据库。

2.1.1 展现层代码
利用GridView,CRUD操作可以集中在一个asp页面中。EditItemTemplate对应修改;增加记录时,可以将输入表单显示到脚注FooterTemplate中;删除不需要界面;查询显示用ItemTemplate,这4个方面合成到一个TemplateField中,针对不同的操作显示相应的控件。
<asp:TemplateField>
<ItemTemplate>
<asp:Label ID="lblProductName" runat="Server"><%# Eval("ProductName") %></asp:Label>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox ID="txtProductName" runat="Server" Text='<%# Bind("ProductName") %>'></asp:TextBox>
</EditItemTemplate>
<FooterTemplate>
<asp:Button ID="btnInsert" Text="新增" OnClick="btnInsert_Click" runat="server" />
<asp:Button ID="btnCancel" Text="取消" OnClick="btnCancel_Click" runat="server" />
<asp:TextBox ID="txtProductID" runat="Server" Text=''></asp:TextBox>
<asp:TextBox ID="txtProductName" runat="Server" Text=''></asp:TextBox>
</FooterTemplate>
</asp:TemplateField>

2.1.2 业务层代码
实现业务逻辑(比如参数值校验),并调用DAL对象提供的CRUD操作,比如GetProducts(),UpdateProduct(),DeleteProduct(), InsertProduct().
public SqlDataReader GetProducts();
public int UpdateProduct(int productID, string productName, double unitPrice, Int16 unitsInStock);
public int DeleteProduct(int productID);
public int InsertProduct(int ProductID,
string ProductName,
int SupplierID,
int CategoryID,
string QuantityPerUnit,
double UnitPrice,
int UnitsInStock,
int UnitsOnOrder,
int ReorderLevel,
int Discontinued);
当然还有另外一种asp.net接受的函数声明方法,是将增删改的参数统一改成一个Product对象。
public SqlDataReader GetProducts();
public int UpdateProduct(Product prod);
public int DeleteProduct(Product prod);
public int InsertProduct(Product Prod);

2.1.3 数据访问层
调用ADO.net访问数据库(利用SqlConnection, SqlCommand, SqlDataReader等对象),注意不同数据库Connection的写法有不同。比如OracleConnection的参数绑定上与SqlConnection有区别。
// 返回DataSet或DataReader或者泛型列表List<Product>
public SqlDataReader GetProducts()
{
SqlConnection conn = new SqlConnection(m_ConnStr);
string sql = "select * from CatProd";
SqlCommand cmd = new SqlCommand(sql, conn);

conn.Open();
SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);

return reader;
}

public int UpdateProduct(int productID, string productName, double unitPrice, Int16 unitsInStock)
{
int cnt = 0;
SqlConnection conn = new SqlConnection(m_ConnStr);
string sql = @"update CatProd set ProductName = @ProductName, UnitPrice = @unitPrice, UnitsInStock = @unitsInStock
where ProductID = @ProductID";
SqlCommand cmd = new SqlCommand(sql, conn);

cmd.Parameters.AddWithValue("@ProductID", productID);
cmd.Parameters.AddWithValue("@ProductName", productName);
cmd.Parameters.AddWithValue("@unitPrice", unitPrice);
cmd.Parameters.AddWithValue("@unitsInStock", unitsInStock);

conn.Open();
cnt = cmd.ExecuteNonQuery();
conn.Close();

return cnt;
}

2.2 j2ee平台
展现层:struts框架显示数据。
业务层:Service/Manager等业务封装对象。(可选用Spring,EJB等框架)
数据访问层:DAO模式通过jdbc(或jdbc的轻量级封装工具Hibernate,ibatis,SpringJDBC等)访问底层数据库。

2.2.1 展现层代码
采用struts框架,MVC模式简化了展现层的代码,几乎完全消除了以往jsp代码中的嵌入式java代码。在jsp页面中采用了标签库
<%@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"%>

一个典型的jsp列表页面(比如listbook.jsp书籍清单)为
<table align="left" border="1" width="70%">
<tr bgcolor="#COCOCO">
<td align="center">
<bean:message key="book.id" />
</td>
<td align="center">
<bean:message key="book.name" />
</td>
<td align="center">
<bean:message key="book.author" />
</td>
<td align="center">
<bean:message key="book.publish" />
</td>
<td align="center">
<bean:message key="book.price" />
</td>
<td align="center">
<bean:message key="book.operator" />
</td>
</tr>
<logic:iterate id="book" name="books" scope="session">
<tr>
<td width="10%">
<bean:write name="book" property="bookId" />
</td>
<td width="25%">
<bean:write name="book" property="bookName" />
</td>
<td width="10%">
<bean:write name="book" property="author" />
</td>
<td width="25%">
<bean:write name="book" property="publish" />
</td>
<td width="10">
<bean:write name="book" property="price" />
</td>
<td width="25%">
<a
href="operatorAction.do?operator=showModify&bookid=<bean:write name="book" property="bookId"/>">
<bean:message key="link.modify" /> </a>   
<a
href="operatorAction.do?operator=showDelete&bookid=<bean:write name="book" property="bookId"/>">
<bean:message key="link.delete" /> </a>
</td>
<bean:define id="bookid" name="book" property="bookId" />
</tr>
</logic:iterate>
</table>

上述用bean:message,并将字符串保存到ApplicationResources.properties文件中,根据locale的不同,可以实现国际化的要求(比如一键切换中英文版本)

在action处理代码中,实现execute方法,调用业务逻辑层的代码,取得值对象(VO, Value Object)的列表,保存到jsp内建对象(如request或session)中后,进行页面跳转。
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
List books = null;

BookDAO jdbc = new BookJdbcDAO();
books = jdbc.findAllBooks();

// request.setAttribute("books", books);
request.getSession().setAttribute("books", books);

return mapping.findForward("success");
}

2.2.2 业务层代码
业务层常见的方式是定义Service或Manger的接口以及相关实现类,比如UserService(UserManager, IUserService)接口,并提供对应实现类UserServiceImpl(UserManagerImpl, UserService),一般这些实现类都是直接调用相应的DAO中的方法。
public class UserServiceImpl implements UserService {
private UserDAO userDAO = null;

public void setUserDAO(UserDAO dao) {
// TODO Auto-generated method stub
this.userDAO = dao;
}

public int insert(User user) {
// TODO Auto-generated method stub
return userDAO.insert(user);
}

public int update(User user) {
// TODO Auto-generated method stub
return userDAO.update(user);
}

public int delete(User user) {
// TODO Auto-generated method stub
return userDAO.delete(user);
}

public int[] batchInsert(List users) {
// TODO Auto-generated method stub
return userDAO.batchInsert(users);
}

public User findById(int id) {
// TODO Auto-generated method stub
return userDAO.findById(id);
}

public List findByName(String name) {
// TODO Auto-generated method stub
return userDAO.findByName(name);
}
}

2.2.3 数据访问层
具体访问数据库,提供CRUD的方法,一般可以借助于SpringJDBC,Hibernate / iBatis等工具来简化编写数据库访问的代码。下面给出以SpringJDBC为数据访问方式的代码。
public class UserDAOImpl implements UserDAO {
private JdbcTemplate jdbcTemplate;

public void setJdbcTemplate(JdbcTemplate jdbcTemp) {
this.jdbcTemplate = jdbcTemp;
}

public int insert(User user) {
String sql = "insert into user(id, name, password) values(null, ?, ?)";
Object[] values = new Object[] { user.getName(), user.getPassword() };

return jdbcTemplate.update(sql, values);
}

public int update(User user) {
String sql = "update user set name = ?, password = ? where id = ?";
Object[] values = new Object[] { user.getName(), user.getPassword(),
new Integer(user.getId()) };

return jdbcTemplate.update(sql, values);
}

public int[] batchInsert(final List users) {
String sql = "insert into user(id, name, password) values(null, ?, ?)";

BatchPreparedStatementSetter setter = new BatchPreparedStatementSetter() {
public int getBatchSize() {
return users.size();
}

public void setValues(PreparedStatement pstmt, int index)
throws SQLException {
User user = (User) users.get(index);

pstmt.setString(1, user.getName());
pstmt.setString(2, user.getPassword());
}
};

return jdbcTemplate.batchUpdate(sql, setter);
}

public int delete(User user) {
String sql = "delete from user where id = ?";
Object[] values = new Object[] { new Integer(user.getId()) };

return jdbcTemplate.update(sql, values);
}

public User findById(int id) {
String sql = "select * from user where id = ?";
Object[] values = new Object[] { new Integer(id) };

final User user = new User();

jdbcTemplate.query(sql, values, new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setPassword(rs.getString("password"));
}
});

return user;
}

public List findByName(String name) {
String sql = "select * from user where name = ?";
Object[] values = new Object[] { name };

//final List users = new ArrayList();
return (List) jdbcTemplate.query(sql, values, new ResultSetExtractor() {
public Object extractData(ResultSet rs) throws SQLException,
DataAccessException {
List users = new ArrayList();
while (rs.next()) {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setPassword(rs.getString("password"));

users.add(user);
}
return users;
}
});

//return users;
}
}

3 总结
3.1 粗浅的比较
在数据展现层方面,asp.net实现了代码和HTML标签元素的彻底分离(比如filename.aspx, filename.aspx.cs),通过回发(postback)对鼠标、键盘等事件进行处理。配套VS2005的可视化编辑功能,代码编写的工作量较小。而这方面J2EE平台上略嫌不足,Struts的配置以及jsp页面上的标签代码编写稍显繁琐(不知JSF对展现层的代码简化程度如何?)。

在业务逻辑方面,asp.net隔离为BLL层,编写业务逻辑控制的相关代码。在J2EE平台上,产生了各种业务逻辑层的开发框架,比如Spring的容器,管理各种业务逻辑对象的生命周期,使得程序员可以更加专注于业务逻辑,同时增加了代码注入和对象组装和灵活配置的能力(AOP,代码反转注入等)

在数据访问层,asp.net采用ADO.net的方式,对SQLServer支持很好,通过System.data.OracleClient亦对Oracle进行了支持。J2EE可以采用jdbc或各种jdbc的封装框架(SpringJdbc, Hibernate/iBatis等),可选择的范围很广,故可根据项目需要进行搭配。

两者之间的差别,很大程度上是由于一个是商业软件,一个是开源社区推动型的两大阵营的差异造成的。从asp.net的三层结构开发的设计思想来看,应该是吸取并借鉴了很多开源框架的优秀设计思想,但总体来说,开源社区更具有活力。

3.2 其它需考虑的问题
关于事务的处理:即将事务控制放在数据访问层还是业务逻辑层?取决于业务的复杂程度,若分布式跨越多个数据库,则在J2EE平台上用JTA,因为JDBC的事务控制局限于当前连接,而一个连接只属于一个数据库。

层之间的接口以及数据传输:ORM映射后,对象和关系(数据库表)建立起了对应关系,j2ee平台用值对象(VO, PO, javabean)来实现数据封装和层之间的传递,asp.net上通常用DataSet,当然也可以用值对象来传输。

如何基于项目需求来选择合适的开发框架:项目的规模,需求特征(如增删改操作和查询操作分别所占的比例),是否数据密集型(使用普通SQL语句或调用存储过程的决定),框架的开发成本和人力成本等各种因素的考虑。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: