在设计完API后,我们就需要实现这个MVC框架。MVC框架的核心是一个DispatcherServlet,用于接收所有的HTTP请求,并根据URL选择合适的Action对其进行处理。在这里,和Struts不同的是,所有的组件均被IoC容器管理,因此,DispatcherServlet需要实例化并持有Guice IoC容器,此外,DispatcherServlet还需要保存URL映射和Action的对应关系,一个Interceptor拦截器链,一个ExceptionResolver处理异常。DispatcherServlet定义如下: package com.javaeedev.lightweight.mvc; /** private Log log = LogFactory.getLog(getClass()); private Map<String, ActionAndMethod> actionMap; private Injector injector = null; // Guice IoC容器 ... Guice的配置完全由Java 5注解完成,而在DispatcherServlet中,我们需要主动从容器中查找某种类型的Bean,相对于客户端被动地使用IoC容器(客户端甚至不能感觉到IoC容器的存在),DispatcherServlet需要使用ServiceLocator模式主动查找Bean,写一个通用方法: private List<Key<?>> findKeysByType(Injector inj, Class<?> type) { DispatcherServlet初始化时就要首先初始化Guice IoC容器: public void init(ServletConfig config) throws ServletException { 然后,从IoC容器中查找Action和URL的映射关系: private Map<String, ActionAndMethod> getUrlMapping(List<Key<?>> actionKeys) { 我们假定客户端是以如下方式配置Action和URL映射的: public class MyModule implements Module { public void configure(Binder binder) { 即通过Guice提供的一个注解Names.named()指定URL。当然还可以用其他方法,比如标注一个@Url注解可能更方便,下一个版本会加上。 Interceptor,ExceptionResolver和ViewResolver也是通过查找获得的。 下面讨论DispatcherServlet如何真正处理用户请求。第一步是根据URL查找对应的Action: String contextPath = request.getContextPath(); 没找到Action就直接给个404 Not Found,找到了进行下一步,实例化一个Action并填充参数: // init ActionContext: ActionContext.setActionContext(request, response, session, context); // 每次创建一个新的Action实例: 注意,为了提高速度,所有的set方法已经预先缓存了,因此避免每次请求都用反射重复查找Action的set方法。 然后要应用所有的Interceptor以便拦截Action: InterceptorChainImpl chains = new InterceptorChainImpl(interceptors); 实现InterceptorChain看上去复杂,其实就是一个简单的递归,大家看InterceptorChainImpl代码就知道了: package com.javaeedev.lightweight.mvc; /** private final Interceptor[] interceptors; InterceptorChainImpl(Interceptor[] interceptors) { ModelAndView getModelAndView() { public void doInterceptor(Action action) throws Exception { 把上面的代码用try ... catch包起来,就可以应用ExceptionResolver了。 如果得到了ModelAndView,最后一步就是渲染View了,这个过程极其简单: // render view: 最简单的JspViewResolver的实现如下: package com.javaeedev.lightweight.mvc.view; /** /** /** 至此,MVC框架的核心已经完成。 评 1。在Action里现在的属性set支持是基本类型和数组,但是在一般的应用中,可能是希望直接得到一个PO, 这个怎么实现
2。一个action怎么实现处理多个请求,打个比方,用户注册,有一个方法doRegister,还有一个方法,验证这个用户名是否存在,在前台用ajax调用一次,doValidateUserName,这两个方法都在一个signupAction里,我在例子和sourcecode里都没有找到比较好的解决办法,请教楼主。 3。Example提供的例子非常好,但是Transaction在action的这个粒度上interceptor,感觉不是很好,从分层的角度讲,不利于封装;是不是在business层会更好一点 xuefeng发表于07-12-24 09:42
1.得到所有属性后,就可以在execute()方法中构造该Bean,不想设计成Struts的FormBean,配置特别麻烦
2.写一个MultiAction,根据某个参数action=register用反射调用doRegister()或doValidate() 3.为了演示Interceptor的用法,和Filter类似,但是仅限于Action层,不包括Render View,事务具体在哪里开要根据具体应用确定 bus387发表于07-12-24 11:19
1.得到所有属性后,就可以在execute()方法中构造该Bean,不想设计成Struts的FormBean,配置特别麻烦
在execute里再构造Bean,当然也是可以,如可以用BeanUtils.setProperties(request, po); 要是框架能做到这些事情,就更完美了。我相信你一定能做到0配置,不需要再像Struts一样有FormBean, 我当时为了0配置到Google搜索就找到这个框架的。 “2.写一个MultiAction,根据某个参数action=register用反射调用doRegister()或doValidate()” Good Idea! 这样做的话,doRegister和doValidate需要传入的参数和表单提交的数据是不一样的,如果form里有20几个参数,这20几个参数都放在action里作为属性,就不好区分doRegister和doValidate方法需要的了。 xuefeng发表于07-12-24 13:33
java作为静态语言和ruby还是不一样的,ruby可以来个execute(MyBean bean),但是java如果这样搞就必须用反射,而且不能利用接口调用了,比较好的方式是定义一个setBean(MyBean bean),效果和多个setBeanProperty1(), setBeanProperty2()效果类似
下个版本可以考虑在一个Action中定义多个executeXxx()方法,每个execute方法可以对应一个setXxx(MyBean bean),url默认映射为/baseUrl/xxx?params freeren发表于07-12-28 14:53
楼主,今天一直在研读你的代码
然后跟我同事谈了这事,我想问下,velocity是不是不再发布了,听说现地freemarker更多人使用,是不是这样? mdream发表于07-12-28 14:55
velocity 最近不是已经发了1.5版本了.貌似现在进度比以前快很多.APACHE顶级项目.
xuefeng发表于07-12-28 15:43
freemarker就是比velocity多一个格式化功能,其他都差不多,主要是velocity配置比较简单
freeren发表于07-12-28 18:20
谢谢两位的分享,看来小弟还有很多东西需要学的。今天下午小弟测试了代码结果,tomcat启动时总是出现这个错误:
2007-12-28 17:57:35,781 [main] ERROR org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[ /light] - Servlet /light threw load() exception java.lang.ClassNotFoundException: com.lightmvc.sample.SampleModule --小弟把目录改了,在class目录下也有SampleModule的class文件,但为什么总是说找不到呢? freeren发表于07-12-28 18:23
对了,还有web.xml的
<filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>*.do</url-pattern> <dispatcher>REQUEST</dispatcher> </filter-mapping> 也出现了以下的报错信息: Multiple annotations found at this line: - The content of element type "filter-mapping" must match "(filter-name, (url-pattern|servlet-name))". - Missing end tag "filter-mapping" 小弟把<dispatcher>REQUEST</dispatcher>注释了就没问题了。请问这如何解决! |
|