- 浏览: 1710465 次
- 性别:
- 来自: 杭州699号
-
文章分类
最新评论
-
莫莫摸:
为什么不用dubbo
RCP数据传输模型回顾 -
大胡子爸爸:
String, Class 都实现了Serializable接 ...
RPC框架几行代码就够了 -
lss598018587:
谢谢大神分享,比起新手看复杂的dubbo框架还不如看大神的这一 ...
RPC框架几行代码就够了 -
15606915740:
你好,请问一下。<dubbo:consumer filt ...
Dubbo文档 -
joqk12345:
...
一些设计上的基本常识
J2EE中基于B/S的MVC框架不少,设计思想也在不停的进步,从Struts1.x开始,每个人都站在巨人的肩膀上,一步一个脚印。
MVC框架主要以控制器为中心,简化模型与视图的交互过程,
控制器要做哪些事?
1. 页面流控制,也就是一级级forward的处理。
2. 接收数据,从表单或URL上传来的数据,需要透明化接收(即:不能让业务逻辑看到接收过程)。
3. 呈现数据,将数据传到页面,或下一forward控制器。
其它的拦截器,前端校验,模板回调,标签库封装等都是附属功能,围绕控制器转。
以业务实现者的角度看,
控制器接口需要有一个传入参数表示接收到的数据,
有两个传出参数,一个表示呈现数据,一个表示页面跳转索引。
这刚好和Java函数的设计相反,函数不允许返回多个参数,怎么办呢?
因为这样,使得控制器接口的设计,变得百家争鸣,各不相同。
Struts(1.x)给出的方案:
它将返回值作为页面流控制,数据的传入传出都用ActionForm,当然用户也可以直接往request里set。
这个设计是比较差的:
1. ActionForward的包装过程应该由框架做,而不是由用户去ActionMapping中查找,用户应该只需返回最简单的String索引号。
2. ActionMapping不应在接口暴露,它不是业务逻辑所关注的,可以将这些附属环境信息使用ThreadLocal锁定在上下文中,如:ActionContext.getContext().getActionMapping();
3. HttpServletRequest, HttpServletResponse不应对用户公开,因为接口已经拥有ActionForm作为数据模型接口,公开Request和Response,只会误导用户绕过框架,也致使框架严重依赖Servlet容器,不便于单元测试,Mock容器是非常痛苦的事情。
4. ActionForm应该为任意POJO,最好用Serializable作为接口声明,使用户可以将实体模型作为表单模型使用。
综上四点可改成:
再加上Action上下文:
struts1.x出身早,也怪不得。
我们再来看SpringMVC的改进:
Rod Johnson估计是对Struts1.x早就看不惯了,在IoC/DI容器里,搞起了重复发明轮子的事。
SpringMVC最直接了当了,你不是需要返回两个值吗?给你包装一下就是了,ModelAndView,呈现数据模型与页面控制的封装体。
但这里同样存在与Servlet容器高度耦合,测试极不方便,也没做到数据模型透明化传递。
当然,SpringMVC提供模板方法适配,以做到透明化数据接收,和隔离Servlet容器的作用,如SimpleFormController:
而WebWork(包括Struts2.0)的设计稍微合理一些:
接口函数的返回值用于页面跳转,传入数据使用setter属性,传出数据使用getter属性,
这个设计是出乎意料的,在其它框架的Action都是单实例线程安全的情况下,它却选择了非单实例线程安全的Action控制器接口,
也就是每一次请求都需要创建新的实例,每个实例只为一次请求服务,
这一选择,使得它不再为函数签名发愁,因为它可以动用整个控制器实例的所有函数,
也使得控制器演变成了命令模式,Action封装了整个执行体,而不是一个服务。
前些天写了一个适用于RCP/RIA应用的MVC框架:
http://javatar.iteye.com/blog/264509
虽然没发布,但也有个雏形了。
这两天在思考与C/S请求与B/S请求统一控制器的问题,也就想了上面所写的一通,
如果struts4rcp也接收传统B/S页面请求,应该怎么设计控制器接口?
不太想采用WebWork的向控制器注入数据的方式,希望将模型与服务域分离。
晚上,终于想到一个比较好的接口设计方案。
接口设计为什么不能动用异常?不就有两个返回值了?如:
跳转异常信息类如下:
用返回值作数据模型,用异常作页面跳转,当该问search.action,却被跳传到了login.action,这算不算一种异常情况?
按照REST的说法,每个URL代表一个资源,一个URL得到了两种结果算不算异常?
实际项目中,真正在控制器中跳转的非常少,并且通常是success和failure,
而failure,完全可以作为一个异常处理。
另外,可以加入泛型,使接口契约更强,
优化后的接口如下:
请求类型的三种情况:
1. B/S页面请求: 传入参数表示表单(或URL)数据,由框架自动注入到POJO属性中,返回值作为呈现模型,可以在JSP中直接使用。
2. B/S AJAX请求: 通过AJAX请求传入一个JSON串,由框架格式化为一个POJO,返回值同样由框架反格式化为JSON串,客户端JavaScript可以将JSON看成对象的对等形式。
3. C/S请求:客户端直接拿到接口代理进行执行,如:Action<User, Account> action = Actions.getAction("loginAction"); User user = action.execute(account);
以登录为例:
Spring配置:
下面我们分别来看三种请求的调用方式:
1. B/S页面请求:
响应页面:(可直接使用User对象的属性)
2. B/S AJAX请求:
3. C/S请求:
在泛型的支持下,客户端与服务器端都不需要做任何强制转型,语义更明确。
MVC框架主要以控制器为中心,简化模型与视图的交互过程,
控制器要做哪些事?
1. 页面流控制,也就是一级级forward的处理。
2. 接收数据,从表单或URL上传来的数据,需要透明化接收(即:不能让业务逻辑看到接收过程)。
3. 呈现数据,将数据传到页面,或下一forward控制器。
其它的拦截器,前端校验,模板回调,标签库封装等都是附属功能,围绕控制器转。
以业务实现者的角度看,
控制器接口需要有一个传入参数表示接收到的数据,
有两个传出参数,一个表示呈现数据,一个表示页面跳转索引。
这刚好和Java函数的设计相反,函数不允许返回多个参数,怎么办呢?
因为这样,使得控制器接口的设计,变得百家争鸣,各不相同。
Struts(1.x)给出的方案:
public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // ...... }
它将返回值作为页面流控制,数据的传入传出都用ActionForm,当然用户也可以直接往request里set。
这个设计是比较差的:
1. ActionForward的包装过程应该由框架做,而不是由用户去ActionMapping中查找,用户应该只需返回最简单的String索引号。
2. ActionMapping不应在接口暴露,它不是业务逻辑所关注的,可以将这些附属环境信息使用ThreadLocal锁定在上下文中,如:ActionContext.getContext().getActionMapping();
3. HttpServletRequest, HttpServletResponse不应对用户公开,因为接口已经拥有ActionForm作为数据模型接口,公开Request和Response,只会误导用户绕过框架,也致使框架严重依赖Servlet容器,不便于单元测试,Mock容器是非常痛苦的事情。
4. ActionForm应该为任意POJO,最好用Serializable作为接口声明,使用户可以将实体模型作为表单模型使用。
综上四点可改成:
public String execute(Serializable form) throws Exception { // ...... }
再加上Action上下文:
public class ActionContext { public static ActionContext getContext() {...} public ActionMapping getActionMapping(){...} public HttpServletRequest getRequest(){...} public HttpServletResponse getResponse(){...} }
struts1.x出身早,也怪不得。
我们再来看SpringMVC的改进:
Rod Johnson估计是对Struts1.x早就看不惯了,在IoC/DI容器里,搞起了重复发明轮子的事。
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { // ...... }
SpringMVC最直接了当了,你不是需要返回两个值吗?给你包装一下就是了,ModelAndView,呈现数据模型与页面控制的封装体。
但这里同样存在与Servlet容器高度耦合,测试极不方便,也没做到数据模型透明化传递。
当然,SpringMVC提供模板方法适配,以做到透明化数据接收,和隔离Servlet容器的作用,如SimpleFormController:
public ModelAndView onSubmit(Object command, BindException errors) throws Exception { // ...... }
而WebWork(包括Struts2.0)的设计稍微合理一些:
public String execute() throws Exception { // ...... } public void setXXX() { // 接收数据 } public void getYYY() { // 呈现数据 }
接口函数的返回值用于页面跳转,传入数据使用setter属性,传出数据使用getter属性,
这个设计是出乎意料的,在其它框架的Action都是单实例线程安全的情况下,它却选择了非单实例线程安全的Action控制器接口,
也就是每一次请求都需要创建新的实例,每个实例只为一次请求服务,
这一选择,使得它不再为函数签名发愁,因为它可以动用整个控制器实例的所有函数,
也使得控制器演变成了命令模式,Action封装了整个执行体,而不是一个服务。
前些天写了一个适用于RCP/RIA应用的MVC框架:
http://javatar.iteye.com/blog/264509
虽然没发布,但也有个雏形了。
这两天在思考与C/S请求与B/S请求统一控制器的问题,也就想了上面所写的一通,
如果struts4rcp也接收传统B/S页面请求,应该怎么设计控制器接口?
不太想采用WebWork的向控制器注入数据的方式,希望将模型与服务域分离。
晚上,终于想到一个比较好的接口设计方案。
接口设计为什么不能动用异常?不就有两个返回值了?如:
public Serializable execute(Serializable model) throws ForwardException { // ...... }
跳转异常信息类如下:
/** * 请求跳转异常 * @author <a href="mailto:liangfei0201@gmail.com">liangfei</a> */ public class ForwardException extends RuntimeException { private static final long serialVersionUID = -2512411385294660606L; private final String actionName; private final Serializable model; /** * @param actionName 跳转Action名称 * @param model 跳转传递数据模型 */ public ForwardException(String actionName, Serializable model) { this.actionName = actionName; this.model = model; } public String getActionName() { return actionName; } public Serializable getModel() { return model; } @Override public String toString() { return "forward to:" + actionName; } }
用返回值作数据模型,用异常作页面跳转,当该问search.action,却被跳传到了login.action,这算不算一种异常情况?
按照REST的说法,每个URL代表一个资源,一个URL得到了两种结果算不算异常?
实际项目中,真正在控制器中跳转的非常少,并且通常是success和failure,
而failure,完全可以作为一个异常处理。
另外,可以加入泛型,使接口契约更强,
优化后的接口如下:
/** * 数据传输Action接口 * @author <a href="mailto:liangfei0201@gmail.com">liangfei</a> * @param <M> 传入模型类型 * @param <R> 返回值类型 */ public interface Action<M extends Serializable, R extends Serializable> { /** * 执行Action * @param model 传入参数 * @return 传回返回值 * @throws Exception 异常均向上抛出,由框架统一处理,包括: ForwardException */ R execute(M model) throws Exception; }
请求类型的三种情况:
1. B/S页面请求: 传入参数表示表单(或URL)数据,由框架自动注入到POJO属性中,返回值作为呈现模型,可以在JSP中直接使用。
2. B/S AJAX请求: 通过AJAX请求传入一个JSON串,由框架格式化为一个POJO,返回值同样由框架反格式化为JSON串,客户端JavaScript可以将JSON看成对象的对等形式。
3. C/S请求:客户端直接拿到接口代理进行执行,如:Action<User, Account> action = Actions.getAction("loginAction"); User user = action.execute(account);
以登录为例:
/** * 登录控制器 * @author <a href="mailto:liangfei0201@gmail.com">liangfei</a> */ public class LoginAction implements Action<Account, User> { private LoginService loginService; // IoC注入 public void setLoginService(LoginService loginService) { this.loginService = loginService; } public User execute(Account account) throws Exception { // 如果需要跳转,可以用:throw new ForwardException("xxxAction", account); return loginService.login(account.getUsername(), account.getPassword()); } }
/** * 帐号信息 * @author <a href="mailto:liangfei0201@gmail.com">liangfei</a> */ public class Account implements Serializable { private static final long serialVersionUID = 1L; private String username; private String password; public Account() {} public Account(String username, String password) { this.username = username; this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
/** * 用户实体信息 * @author <a href="mailto:liangfei0201@gmail.com">liangfei</a> */ public class User implements Serializable { private static final long serialVersionUID = 4544208653256289206L; private Long id; private String username; private String password; private String email; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
Spring配置:
<bean id="loginAction" class="com.xxx.LoginAction"> <property name="loginService" ref="loginService" /> </bean>
下面我们分别来看三种请求的调用方式:
1. B/S页面请求:
<form action="loginAction" method="post"> <input type="text" name="username" /> <input type="password" name="password" /> <!--类元信息,若没有class属性,服务器端需用Map接收--> <input type="hidden" name="class" value="com.xxx.Account" /> </form>
响应页面:(可直接使用User对象的属性)
您好,${username},您的邮箱是:${email}
2. B/S AJAX请求:
var account = {username: "james", password: "123456", class: "com.xxx.Account"}; // 传入JSON数据模型,若没有class属性,服务器端需用Map接收 var loginAction = Actions.getAction("loginAction"); var user = loginAction.execute(account); // 执行,并得到JSON结果 alert(user.email);
3. C/S请求:
Account account = new Account("james", "123456"); // 传入数据模型 Action<Account, User> loginAction = Actions.getAction("loginAction"); // 看起像拿到了服务器端Action的引用(即透明化) User user = loginAction.execute(account); // 执行,并得到User对象 System.out.println(user.getEmail());
在泛型的支持下,客户端与服务器端都不需要做任何强制转型,语义更明确。
发表评论
-
能力成长模型
2012-05-09 00:28 23082最近看了温伯格1986年出版的《技术领导之路》, 很老的书,讲 ... -
以HTTL为例讲讲模块分包&领域模型&扩展框架
2011-10-09 20:08 16885注:该博客内容已加入 ... -
使用Map参数的Webx3扩展
2011-08-28 02:10 5965因Webx3是开源的,所以把这个简单的Webx3扩展发在博客上 ... -
Netty内存泄露
2011-08-02 20:09 25001转于自己在公司的Blog: ... -
Grizzly和Netty以及Mina简单性能对比
2011-07-17 02:48 29817转于自己在公司的Blog: http://pt.alibaba ... -
RPC框架几行代码就够了
2011-07-14 00:34 90384转于自己在公司的Blog: http://pt.alibaba ... -
魔鬼在细节中
2011-05-24 14:50 32255转于自己在公司的Blog: ... -
Dubbo扩展点重构
2011-05-12 22:09 38944转于自己在公司的Blog: http://pt.alibaba ... -
配置设计
2011-03-09 23:41 23658转于自己在公司的Blog: ... -
[转]HTML5设计原理
2011-03-09 22:57 7890Jeremy Keith在 Fronteers 2010 ... -
Hessian序列化不设SerializerFactory性能问题
2010-12-27 11:38 6519转于自己在公司的Blog: http://pt.alibaba ... -
动态代理方案性能对比
2010-11-17 21:38 46462转于自己在公司的Blog: http://pt.alibaba ... -
防痴呆设计
2010-11-05 18:58 17752转于自己在公司的Blog: ... -
负载均衡扩展接口重构
2010-11-05 18:53 8796转于自己在公司的Blog: ... -
分布式服务框架常被质疑的价值
2010-11-05 18:52 5763转于自己在公司的Blog: http://pt.alibaba ... -
Hessian3.2.1在序列化32.5k字符串时的问题
2010-11-05 18:49 7332转于自己在公司的Blog: http://pt.alibaba ... -
一些设计上的基本常识
2010-07-05 19:28 27881转于自己在公司的Blog: ... -
谈谈扩充式扩展与增量式扩展
2010-06-12 19:46 19518转于自己在公司的Blog: http://pt.alibaba ... -
Scaling Architecture
2010-02-25 10:31 4146Scaling Second Life: http://p ... -
EBay SOA
2010-02-23 18:23 4847EBay SOA PPT
相关推荐
在控制器方法中,我们可以使用@RequestBody注解来处理请求参数,并使用EncryptUtils工具类来进行加密和解密。例如: ```java @PostMapping("/order/save") public String saveOrder(@RequestBody String orderId, @...
在IT行业中,构建基于Java的B/S(Browser/Server)电子商务系统是一项常见的任务,而"基于java的MVC模式的B/S电子商务-当当网"项目则是这一领域的实例。在这个项目中,开发者利用了MVC(Model-View-Controller)设计...
控制器接收用户的请求,处理这些请求,并与模型和视图进行交互。 2. **Servlet的角色** 在MVC架构中,Servlet通常作为控制器来使用。当用户发送HTTP请求时,Servlet接收这些请求,处理后端逻辑,并将结果转发给...
本篇将深入探讨C# MVC控制器如何实现前后端的数据交互。 一、基础知识 1. MVC模式:这是一种设计模式,将应用程序分为三个主要部分:模型(Model)、视图(View)和控制器(Controller)。模型处理业务逻辑和数据,...
本文实例讲述了ASP.NET在MVC控制器中获取Form表单值的方法。分享给大家供大家参考,具体如下: 在MVC控制器中,如果我们想直接获取表单中某个标签元素的值,可以使用MVC中提供的FormCollection类,具体用法如下所示...
在VS2012中,视图通常由ASP.NET MVC框架的Razor视图引擎生成,用于显示来自控制器的数据。在这个仓库管理系统中,视图可能包含多个页面,如商品列表、订单详情、库存报告等,它们提供用户友好的界面,用于输入和展示...
它是Spring MVC配置中的一个便捷元素,用于自动配置基于注解的控制器(@Controller)和数据绑定、异常处理等特性。它会开启对HTTP请求方法(如GET、POST)的自动处理,并且启用像@RequestParam、@PathVariable等注解...
1. **控制器**:接收HTTP请求,调用业务逻辑,然后将结果传递给视图进行展示。 2. **Action类**:实现了具体的操作逻辑,如添加、修改、删除学生信息等。 3. **配置文件**:定义了URL映射,关联请求与Action,以及...
总结来说,控制器是MVC架构中连接模型和视图的桥梁,它处理用户请求,协调模型和视图的工作,确保应用程序的逻辑流畅且有序。在PHP中,通过精心设计的控制器,开发者可以构建出高效且结构清晰的Web应用程序。
模型对象负责处理数据,与数据库交互,并在数据改变时通知控制器。 - View(视图):显示数据给用户。视图通常是从模型获取数据并渲染成用户界面的部分。 - Controller(控制器):接收用户的输入,处理请求,更新...
在这个项目中,模型(Model)负责处理数据和业务逻辑,视图(View)显示信息,而控制器(Controller)处理用户输入并协调模型和视图。通过这种方式,MVC使得代码更易于维护和扩展。 2. ADO.NET: ADO.NET是.NET...
3. **MVC模式**:MVC模式是软件工程中一种常见的设计模式,它将应用分为模型(Model)、视图(View)和控制器(Controller)三个部分。在学生成绩管理系统中,模型负责管理数据和业务逻辑,视图负责展示数据,控制器...
在MVC模式下,模型负责存储和更新考试数据,视图负责呈现这些数据给用户,而控制器接收用户操作并协调模型和视图的更新。例如,当用户点击“提交”按钮时,控制器会将答案发送到服务器,服务器验证答案后更新模型,...
在MVC模式中,模型(Model)负责处理业务逻辑和数据管理,视图(View)负责展示用户界面,控制器(Controller)则协调模型和视图,接收用户的请求并转发到相应的处理方法。Struts框架通过配置文件和Action类来实现这...
3. Controller(控制器)作为模型和视图之间的桥梁,接收用户的请求,处理业务逻辑,并更新模型或指示视图进行相应的显示。 本系统采用C#作为后端编程语言,这是一种面向对象的、类型安全的编程语言,广泛应用于...
在Spring MVC框架中,拦截器(Interceptor)是一个强大的工具,用于在请求被控制器处理之前或之后执行特定的逻辑。它们可以用来实现权限检查、日志记录、性能统计等多种功能。本压缩包“spring mvc 拦截器获取请求...
在Spring MVC 4.1.x框架中,`mvcURL`是一个关键的概念,它涉及到控制器(Controller)与视图(View)之间的交互。本篇将详细解释`mvcURL`的使用方法,以及它如何帮助我们构建高效、灵活的Web应用程序。 首先,`mvc...
在Spring MVC框架中,`mvc:annotation-driven`和`mvc:message-converters`是两个非常重要的元素,它们在处理基于注解的控制器和数据转换方面起着关键作用。本篇文章将深入探讨这两个组件的工作原理以及如何在实际...
**注解驱动的映射**是Spring MVC最常用的方式,它通过在控制器类的方法上使用`@RequestMapping`注解来定义请求路径。例如: ```java @Controller public class MyController { @RequestMapping("/hello") public...
本文将深入探讨如何在Struts2中使用Filter作为控制器的MVC实现。 首先,理解Filter在Java Web中的作用至关重要。Filter是Servlet规范的一部分,它允许开发者在请求到达目标Servlet或JSP之前以及响应离开之后对其...