<iframe align="center" marginwidth="0" marginheight="0" src="http://www.zealware.com/csdnblog336280.html" frameborder="0" width="336" scrolling="no" height="280"></iframe>
在设计完API后,我们就需要实现这个MVC框架。MVC框架的核心是一个DispatcherServlet,用于接收所有的HTTP请求,并根据URL选择合适的Action对其进行处理。在这里,和Struts不同的是,所有的组件均被IoC容器管理,因此,DispatcherServlet需要实例化并持有Guice IoC容器,此外,DispatcherServlet还需要保存URL映射和Action的对应关系,一个Interceptor拦截器链,一个ExceptionResolver处理异常。DispatcherServlet定义如下:
package com.javaeedev.lightweight.mvc;
/**
* Core dispatcher servlet.
*
* @author Xuefeng
*/
public class DispatcherServlet extends HttpServlet {
private Log log = LogFactory.getLog(getClass());
private Map<string actionandmethod> actionMap;<br> private Interceptor[] interceptors = null;<br> private ExceptionResolver exceptionResolver = null;<br> private ViewResolver viewResolver = null;</string>
private Injector injector = null; // Guice IoC容器
...
}
Guice的配置完全由Java 5注解完成,而在DispatcherServlet中,我们需要主动从容器中查找某种类型的Bean,相对于客户端被动地使用IoC容器(客户端甚至不能感觉到IoC容器的存在),DispatcherServlet需要使用ServiceLocator模式主动查找Bean,写一个通用方法:
private List<key>> findKeysByType(Injector inj, Class> type) {<br> Map<key>, Binding>> map = inj.getBindings();<br> List<key>> keyList = new ArrayList<key>>();<br> for(Key> key : map.keySet()) {<br> Type t = key.getTypeLiteral().getType();<br> if(t instanceof Class>) {<br> Class> clazz = (Class>) t;<br> if(type==null || type.isAssignableFrom(clazz)) {<br> keyList.add(key);<br> }<br> }<br> }<br> return keyList;<br>}</key></key></key></key>
DispatcherServlet初始化时就要首先初始化Guice IoC容器:
public void init(ServletConfig config) throws ServletException {
String moduleClass = config.getInitParameter("module");
if(moduleClass==null || moduleClass.trim().equals(""))
throw new ServletException("Cannot find init parameter in web.xml: <servlet>"<br> + "<servlet-name>?</servlet-name><servlet-class>"<br> + getClass().getName()<br> + "</servlet-class><init-param><param-name>module</param-name><param-value>"<br> + "put-your-config-module-full-class-name-here</param-value></init-param></servlet>");
ServletContext context = config.getServletContext();
// init guice:
injector = Guice.createInjector(Stage.PRODUCTION, getConfigModule(moduleClass.trim(), context));
...
}
然后,从IoC容器中查找Action和URL的映射关系:
private Map<string actionandmethod> getUrlMapping(List<key>> actionKeys) {<br> Map<string actionandmethod> urlMapping = new HashMap<string actionandmethod>();<br> for(Key> key : actionKeys) {<br> Object obj = safeInstantiate(key);<br> if(obj==null)<br> continue;<br> Class<action> actionClass = (Class<action>) obj.getClass();<br> Annotation ann = key.getAnnotation();<br> if(ann instanceof Named) {<br> Named named = (Named) ann;<br> String url = named.value();<br> if(url!=null)<br> url = url.trim();<br> if(!"".equals(url)) {<br> log.info("Bind action [" + actionClass.getName() + "] to URL: " + url);<br> // link url with this action:<br> urlMapping.put(url, new ActionAndMethod(key, actionClass));<br> }<br> else {<br> log.warn("Cannot bind action [" + actionClass.getName() + "] to *EMPTY* URL.");<br> }<br> }<br> else {<br> log.warn("Cannot bind action [" + actionClass.getName() + "] because no @Named annotation found in config module. Using: binder.bind(MyAction.class).annotatedWith(Names.named(\"/url\"));");<br> }<br> }<br> return urlMapping;<br>}</action></action></string></string></key></string>
我们假定客户端是以如下方式配置Action和URL映射的:
public class MyModule implements Module {
public void configure(Binder binder) {
// bind actions:
binder.bind(Action.class)
.annotatedWith(Names.named("/start.do"))
.to(StartAction.class);
binder.bind(Action.class)
.annotatedWith(Names.named("/register.do"))
.to(RegisterAction.class);
binder.bind(Action.class)
.annotatedWith(Names.named("/signon.do"))
.to(SignonAction.class);
...
}
}
即通过Guice提供的一个注解Names.named()指定URL。当然还可以用其他方法,比如标注一个@Url注解可能更方便,下一个版本会加上。
Interceptor,ExceptionResolver和ViewResolver也是通过查找获得的。
下面讨论DispatcherServlet如何真正处理用户请求。第一步是根据URL查找对应的Action:
String contextPath = request.getContextPath();
String url = request.getRequestURI().substring(contextPath.length());
if(log.isDebugEnabled())
log.debug("Handle for URL: " + url);
ActionAndMethod am = actionMap.get(url);
if(am==null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404 Not Found
return;
}
没找到Action就直接给个404 Not Found,找到了进行下一步,实例化一个Action并填充参数:
// init ActionContext:
HttpSession session = request.getSession();
ServletContext context = session.getServletContext();
ActionContext.setActionContext(request, response, session, context);
// 每次创建一个新的Action实例:
Action action = (Action) injector.getInstance(am.getKey());
// 把HttpServletRequest的参数自动绑定到Action的属性中:
List<string> props = am.getProperties();<br>for(String prop : props) {<br> String value = request.getParameter(prop);<br> if(value!=null) {<br> am.invokeSetter(action, prop, value);<br> }<br>}</string>
注意,为了提高速度,所有的set方法已经预先缓存了,因此避免每次请求都用反射重复查找Action的set方法。
然后要应用所有的Interceptor以便拦截Action:
InterceptorChainImpl chains = new InterceptorChainImpl(interceptors);
chains.doInterceptor(action);
ModelAndView mv = chains.getModelAndView();
实现InterceptorChain看上去复杂,其实就是一个简单的递归,大家看InterceptorChainImpl代码就知道了:
package com.javaeedev.lightweight.mvc;
/**
* Used for holds an interceptor chain.
*
* @author Xuefeng
*/
class InterceptorChainImpl implements InterceptorChain {
private final Interceptor[] interceptors;
private int index = 0;
private ModelAndView mv = null;
InterceptorChainImpl(Interceptor[] interceptors) {
this.interceptors = interceptors;
}
ModelAndView getModelAndView() {
return mv;
}
public void doInterceptor(Action action) throws Exception {
if(index==interceptors.length)
// 所有的Interceptor都执行完毕:
mv = action.execute();
else {
// 必须先更新index,再调用interceptors[index-1],否则是一个无限递归:
index++;
interceptors[index-1].intercept(action, this);
}
}
}
把上面的代码用try ... catch包起来,就可以应用ExceptionResolver了。
如果得到了ModelAndView,最后一步就是渲染View了,这个过程极其简单:
// render view:
private void render(ModelAndView mv, HttpServletRequest reqest, HttpServletResponse response) throws ServletException, IOException {
String view = mv.getView();
if(view.startsWith("redirect:")) {
// 重定向:
String redirect = view.substring("redirect:".length());
response.sendRedirect(redirect);
return;
}
Map<string object> model = mv.getModel();<br> if(viewResolver!=null)<br><font color="#ff0000">viewResolver.resolveView</font>(view, model, reqest, response);<br>}</string>
最简单的JspViewResolver的实现如下:
package com.javaeedev.lightweight.mvc.view;
/**
* Let JSP render the model returned by Action.
*
* @author Xuefeng
*/
public class JspViewResolver implements ViewResolver {
/**
* Init JspViewResolver.
*/
public void init(ServletContext context) throws ServletException {
}
/**
* Render view using JSP.
*/
public void resolveView(String view, Map<string object> model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {<br> if(model!=null) {<br> Set<string> keys = model.keySet();<br> for(String key : keys) {<br> request.setAttribute(key, model.get(key));<br> }<br> }<br> request.getRequestDispatcher(view).<font color="#ff0000">forward</font>(request, response);<br> }<br>}</string></string>
至此,MVC框架的核心已经完成。
相关推荐
"超轻量级MVC框架的设计和实现(源码)"是针对这个概念的一个具体实例,它强调了框架的简洁性和无XML配置特性。开发者通常会避免使用XML配置,因为它们可能增加代码的阅读和维护难度。相反,使用Java 5注解可以将配置...
总的来说,"C# MVC+layui.js超轻量级框架(包含数据库)"提供了一个开箱即用的解决方案,适合初学者和有一定经验的开发者快速搭建功能完善的Web应用。通过C# MVC的后端控制和layui.js的前端呈现,结合SQLSugar的...
其中,Biny脱颖而出,它是一款由腾讯开源的高性能、超轻量级PHP框架,专为快速构建现代Web应用程序而设计。Biny以其简洁优雅的代码和易于理解的架构,吸引了许多开发者的眼球。 **框架概述** Biny遵循经典的MVC...
这个框架的核心特性体现在其超轻量级的设计,使得它在资源消耗和性能优化方面表现出色。在这款框架中,你可以发现许多现代Web开发的最佳实践,例如MVC(模型-视图-控制器)架构模式、路由系统、依赖注入等。 1. MVC...
9. **性能优化**:BroPHP的超轻量级设计意味着它有较低的内存占用和较快的执行速度。同时,框架允许开发者通过缓存策略进一步提升性能,例如使用文件缓存或Memcached、Redis等内存缓存。 10. **社区与文档**:作为...
2. **MVC模式**:BroPHP遵循常见的Model-View-Controller架构,将业务逻辑、数据处理和用户界面分离,提高了代码的可维护性和可扩展性。开发者可以通过控制器处理HTTP请求,模型管理数据,而视图则负责展示结果。 3...
2. **MVC架构**:遵循Model-View-Controller(模型-视图-控制器)设计模式,BroPHP将业务逻辑、数据处理和用户界面分离,提高代码可读性和可维护性。Model负责数据处理,View负责展示,Controller协调两者并处理请求...
2. **高效**:由于其精简的设计,框架运行效率高,加载速度快,适合构建中小型Web应用。 3. **数据库支持**:尽管轻量,MiniFramework仍提供了数据库操作接口,支持常见的SQL查询,方便开发者进行数据存取。 4. **...
Sparkweb 是一个超轻量级的简易高效的 Java WEB 开发框架,其设计思想结合了目前主流的 Spring、Struts2、Playframework、Nodejs-Expressjs、Ruby On Rails 等框架的优秀地方,完美支持 RESTful设计。 二、Sparkweb ...
StartMvc 是一个针对初学者和小型项目设计的超轻量级 PHP 框架。它的主要目标是简化 Web 应用程序的开发过程,同时保持足够的灵活性,使得开发者能够快速构建高效能的应用。在 StartMvc v2.1.2 版本中,我们看到了这...
可以快速搭建出一个项目,作为一个超轻量级的PHP框架,完美支持MVC,完全面向对象。作为一个PHPer,你还需要什么呢? 我们的口号是: 给力的超轻量级PHP框架。 原因: 用了的人都知道! DuoLamPHP框架功能概述: ...
迷你框架MiniFramework是一种常规的Apache2开源协议发布的,支持MVC和RESTful的超轻量级PHP开发框架。MiniFramework能够帮助开发者用最小的学习成本快速构建Web应用程序,在满足开发者最基础的分层开发,数据库和...
LMVC(Lightweight MVC)框架是一个超轻量级的MVC(Model-View-Controller)框架,旨在提供快速、高效且几乎无需配置的开发体验。该框架由webcat创建,首次发布于2008年9月26日。 #### 入门:Hello World 示例 L...
#jQM-Lazy-Loading-MVC ###目录 ##Purpose 轻量级 jQuery Mobile MVC-ish 延迟加载库。 使项目创建快速简便。 延迟加载控制器和视图。 模型留给开发人员。 辅助方法提供快速简便的 UI 功能。 ####Notes 这并...
根据提供的资料,“LMVC”被定义为一种“超轻量级MVC框架”。MVC(Model-View-Controller)架构模式是软件工程中一种常用的架构设计模式,尤其适用于Web应用程序的开发。它将应用程序分为三个核心部分:模型(Model...
可以快速搭建出一个项目,作为一个超轻量级的PHP框架,完美支持MVC,完全面向对象。作为一个PHPer,你还需要什么呢? 我们的口号是: 给力的超轻量级PHP框架。 原因: 用了的人都知道! DuoLamPHP框架功能...
JDiy是一个超轻量级的java极速开发框架,内置一个建站平台(不用写代码,生成后台管理界面),有了JDiy,您无需再折腾其它庞大的框架(如SSH) JDiy同时支持javaEE/WEB和javaSE开发环境 JDiy利用本地化Locale,中文显示...
LightningPHP是一个用于PHP的FAST超轻量级MVC(模型,视图,控制器)框架。 与大多数其他可用框架不同,LightningPHP默认情况下几乎不包含任何内容。 这是一个最小的准系统MVC框架,没有绒毛,没有复杂性并且没有...
JunePHP是一个超轻量级的PHP框架,采用MVC架构,基于PHP5.3开发。 关于数据库:数据库操作采用Medoo,支持MySQL, SQLite, MariaDB, PostgreSQL等多种数据库。 关于模板:模板引擎使用原生PHP,高效灵活,支持替换...