当初看了《从零开始写一个Java Web框架》,也跟着写了一遍,但当时学艺不精,真正进脑子里的并不是很多,作者将依赖注入框架和MVC框架写在一起也给我造成了不小的困扰。最近刚好看了一遍springMVC的官方文档,对过去一段时间的使用做了一下总结,总结了一些MVC的使用需求,打算自己开坑写一个MVC框架,虽然是重复造轮子的过程,但也是学习提高的过程。

1.我们可能需要一个什么样的MVC框架
(1)用户一:我讨厌配置文件,最好能用注解的全用注解注解,能扫描直接扫描
(2)用户二:最好我导入一个jar包,有默认的servlet配置,也可以按自己的需要配置,然后就直接写这样的代码就可以处理请求了

@Controller
public class Test {
  @MapURL(value = "/as.do")
public String main(HttpServletRequest req,HttpServletResponse resp,ModelMap model,String name) throws IOException, ServletException {
System.out.println("as1");
model.put("time",new Date(System.currentTimeMillis()));
return "test";
}
}

(3)用户三:返回的话,直接返回一个字符串,在配置中写明页面根路径,直接返回页面名字就好了

(4)用户四:springMVC里想用什么参数都可以直接写到函数里,好方便,最好也实现一下
每个新出的框架都会说自己简单,性能好不会像已经成熟的框架那样复杂,冗余,而且用不到的功能很多,但会降低系统性能,但随着需求越来越多,每个框架都会越来越复杂,功能越来越多,只有最适合自己的才是最好的框架。

2.从annotation说起
  annotation是从jdk1.5起引入的新机制,annotation旨在将类,方法,变量与特定的信息或是元数据相关联,注解可以理解为一个框架可以识别的注释或是标签,当我在类上标了@Controller时,框架就知道了这是个控制器,扫描类的时候遇到有这个注解的就放进来。
  一个最简单的演示就是我定义一个注解类,使用@interface,设置对应的元数据属性,然后创建一个类,在类上面标上@Controller,

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {}

  

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MapURL {
String value();
String method() default "GET";
}

  

  然后我获得这个类,可以new完了获取class,也可以直接Class.forName,调用class.isAnnotationPresent(Controller.class),通过得到的布尔值来判断这个类的注解是不是Controller。对于方法级别的注释,采用@MapURL标签,设置2个属性value和method,分别代表短url和http的请求方式,默认是get,不是的话需要单独设置。在实际的项目中,Controller的类一定会有很多,我们需要在初始化的时候把这些类从全部类中拿出来,放到集合中,然后在这个class的集合中拿出所有的被注解的方法,将url作为key,方法作为value存到map中(http请求方式暂未考虑,以后再重构),这个map是整个框架的核心。

  在拿出全部类的过程中,用户可以在config.ini中设置要扫描的包,程序会通过文件操作不停的递归找到所有的class文件,然后从这个包的所有类中挑选出Controller的类。

public static void  scanClassSetByPackage(String packageName)
{
methodMap=new HashMap<String, MethodPro>();
classMap=new HashMap<String, Class<?>>();
classSet=new HashSet<Class<?>>();
String filePath=Config.getProPath()+ StringUtils.modifyPackagePath(packageName);
FileUtils.getClassSet(filePath,classSet,packageName);
for(Class<?> clazz:classSet)
{
if(clazz.isAnnotationPresent(Controller.class))
{
Method[] methods=clazz.getDeclaredMethods();
for(Method method:methods)
{
if(method.isAnnotationPresent(MapURL.class))
{
MapURL mapURL=method.getAnnotation(MapURL.class);
MethodPro mp=new MethodPro(method,mapURL.value(),mapURL.method());
methodMap.put(mapURL.value(),mp);
classMap.put(mapURL.value(),clazz);
}
}
}
}
}

  

3.建立转发servlet
  框架归根到底还是servlet这一套东西,请求进来,处理请求,返回结果。现在框架只是屏蔽了servlet的一些东西,让开发者能够使用更友好的,更简单的方式来实现业务逻辑。框架需要servlet,一个就够了,这个servlet要把传进来的请求交给别人处理,交给那些别标记了@Controller中的被标记了@MapURL的方法来处理,这里就用到了刚才用到的map,map什么时候生成,可是放在静态块里加载的时候生成,也可以放在servlet的init方法中初始化生成。在servlet的service中,会通过request的getPathInfo(),或是getServletPath(),来获得当前请求的短路径,通过map拿到这个路径url对应的method,反射,将request和response传入,然后就可以调用任何@Controller的任何方法,一个最简单的MVC框架到这也就算完成了。这个框架配置简单,只需要一个servlet就可以处理各种不同的请求,开发者不必再写一大堆servlet,只需要在@Controller中加一大堆方法就好了。现在的问题就是即使我不必写servlet,但我在方法中写的代码太servlet化了,简直没什么区别,我写个跳转jsp的页面还得request.getDispatcher("test.jsp").forward(request.response)要找一个传进来的值还得去request中拿,想传出去还得再放到request中。

4.实现一个springMVC式的参数填充
  在最开始使用springMVC的时候,十分惊讶于这种设计,一直在想这是怎么做到的,我为什么可以使用任意多个参数,在form提交的表单中为什么同名的会直接被赋值,为什么把模型类写到参数里会自动填充类中的属性,还没来得及看springMVC这块的实现代码,就先把自己理解的和用到的整理了一下逻辑,实现了一遍。
public Object invoke(Object obj, Object... args),在method的反射调用中采用的是可变长参数,这个机制很有意思,我可以在反射中使用很多参数比如invoke(obj,req,resp,model,name);或是把后面几项放到数组中invoke(obj,obj[]);因为在@Controller的函数的长度是任意的,所以需要先得到对应method的信息,得到参数的个数,并生成相应长度的数组供调用。然后就得到了2个问题:
(1)怎么获得method 的信息,为了保证名字相同的参数的替换(或是注入)以及不同类型的转换,我们需要先知道method的参数的类型以及method参数的名字,类型还是很简单的,只需要method.getParameterTypes()就能得到method中各个参数的类型,获得参数的名字就比较复杂了,为了解决这个问题需要另外一个黑科技:asm。我并没有过于深入的研究框架本身,找到了一个Demo,能够直接获得函数中参数的名字,将asm整合进入框架中,得到该method的2个classNames,paraNames分别为类型名和参数名的集合。

          List<String> paraNames= MethodResolver.getMethodNames(clazz.getName(),methodPro.getName());
List<String> classNames= CollectionUtils.classArrToStringList(method.getParameterTypes());
Object[] args=MethodResolver.makeArgs(paraNames,classNames,req,resp,model);

  

 public static List<String> getMethodNames(String className,String methodName) throws IOException {
List<String> list=new ArrayList<String>();
String cn=Config.getProPath()+className.replace(".", "/")+".class";
InputStream is=new FileInputStream(new File(cn));
ClassReader cr = new ClassReader(is);
ReadMethodArgNameClassVisitor classVisitor = new ReadMethodArgNameClassVisitor();
cr.accept(classVisitor, 0);
for(Entry<String, List<String>> entry : classVisitor.nameArgMap.entrySet()) {
if(entry.getKey().equals(methodName)) {
for (String s : entry.getValue()) {
list.add(s);
}
}
}
return list;
}

  

(2)怎么将刚才得到的method信息,通过已有的request和response生成一个Object[] args来供反射调用。这里要分成几个策略来进行。遍历classNames,paraNames,对于每一组 参数类型[参数名],如果这个类型是javax.servlet.http.HttpServletRequest,或javax.servlet.http.HttpServletResponse,或ModelMap,直接加入到args中,否则继续判断;如果参数类型为String且参数名在request的ParameterNames中有相同的名字,加入args;不满足的话:判断参数名在request的ParameterNames中有相同的名字,有的话此时肯定不是String,转换类型,不能转换类型的话自动报错,不必处理;不满足的话判断这个类型能否被加载且属性中存在与当前参数名相同的属性:如果满足条件,使用反射生成这个类,并把类中的属性与相同的request的ParameterNames那部分赋值,实现模型类的自动装配并加入args。如果不满足上述所有条件,args加入null。遍历完所有的classNames,paraNames后就得到method的参数的被调用数组。

5.页面路径设置

  在@Controller的代码中会返回页面的名称字符串,在配置文件中配置页面路径和页面后缀就能简单的实现页面的转发。

Object result=method.invoke(clazz.newInstance(),args);
Map<String,Object> map=model.getMap();
for(String key:map.keySet())
{
req.setAttribute(key,map.get(key));
}
if(result instanceof String)
{
req.getRequestDispatcher(Config.getConfig("pagePath")+File.separator+result.toString()+Config.getConfig("suffix")).forward(req, resp);
}

  

6.部署

  在框架完成后可以使用IDE自带的功能打成jar包,然后使用 mvn install命令放到本地仓库,新建一个web项目,导入jar包,servlet在程序中写死了拦截*.do请求,因此不必设置web.xml,只需要最基本的内容,后面会改,实现可配置。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>11</title>
</head>
<body>
当前时间:${time}
</body>
</html>

  后台的代码就是最上面那一段代码,这个为jsp的代码,启动tomcat,最后效果为

后:

  这个框架前前后后写了3,4天,加深了annotation,asm,可变长参数,反射,泛型函数以及对MVC本身框架的理解,还是很有收获的。

gitHub: https://github.com/Asens/new-AsMVC

AsMVC:一个简单的MVC框架的Java实现的更多相关文章

  1. 自己动手写一个简单的MVC框架(第一版)

    一.MVC概念回顾 路由(Route).控制器(Controller).行为(Action).模型(Model).视图(View) 用一句简单地话来描述以上关键点: 路由(Route)就相当于一个公司 ...

  2. 自己动手写一个简单的MVC框架(第二版)

    一.ASP.NET MVC核心机制回顾 在ASP.NET MVC中,最核心的当属“路由系统”,而路由系统的核心则源于一个强大的System.Web.Routing.dll组件. 在这个System.W ...

  3. 一个简单的MVC框架的实现-基于注解的实现

    1.@Action注解声明 package com.togogo.webtoservice.annotations; import java.lang.annotation.Documented; i ...

  4. 一个简单的MVC框架的实现

    1.Action接口 package com.togogo.webtoservice; import javax.servlet.http.HttpServletRequest; import jav ...

  5. [.NET] 一步步打造一个简单的 MVC 网站 - BooksStore(一)

    一步步打造一个简单的 MVC 网站 - BooksStore(一) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore 简介 主 ...

  6. [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(一)

    一步步打造一个简单的 MVC 电商网站 - BooksStore(一) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore &l ...

  7. [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(一) (转)

    http://www.cnblogs.com/liqingwen/p/6640861.html 一步步打造一个简单的 MVC 电商网站 - BooksStore(一) 本系列的 GitHub地址:ht ...

  8. PHP之简单实现MVC框架

    PHP之简单实现MVC框架   1.概述 MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种 ...

  9. [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(二)

    一步步打造一个简单的 MVC 电商网站 - BooksStore(二) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore 前: ...

随机推荐

  1. 关于SVD(Singular Value Decomposition)的那些事儿

    SVD简介 SVD不仅是一个数学问题,在机器学习领域,有相当多的应用与奇异值都可以扯上关系,比如做feature reduction的PCA,做数据压缩(以图像压缩为代表)的算法,还有做搜索引擎语义层 ...

  2. 简单总结焦点事件、Event事件对象、冒泡事件

    每学习一些新的东西,要学会复习,总结和记录. 今天来简单总结一下学到的几个事件:焦点事件.Event事件对象.冒泡事件 其实这几个事件应该往深的说是挺难的,但今天主要是以一个小菜的角度去尝试理解一些基 ...

  3. 【NOI2006】最大获利

    [问题描述] 新的技术正冲击着手机通讯市场,对于各大运营商来说,这既是机遇,更是挑战.THU 集团旗下的CS&T 通讯公司在新一代通讯技术血战的前夜,需要做太多的准备工作,仅就站址选择一项,就 ...

  4. hdu 2460 poj 3694 (双联通+LCA)

    在给出的两个点上加一条边,求剩下桥的数量,,不会LCA在线,就用了最普通的,先Tarjan双联通缩点,然后将缩完的图建成一棵树,树的所有边就是桥了,如果在任意两点间加一条边的话,那么从两点到最近公共祖 ...

  5. Java简介(4)-关键字

    abstract assert boolean break byte byte case catch char class const continue default do-while double ...

  6. 百度分享share.js插件

    //百度分享window._bd_share_config = { common : { bdText : '分享标题', bdDesc : '分享描述', bdUrl : '分享链接', bdPic ...

  7. 141 Linked List Cycle(判断链表是否有环Medium)

    题目意思:链表有环,返回true,否则返回false 思路:两个指针,一快一慢,能相遇则有环,为空了没环 ps:很多链表的题目:都可以采用这种思路 /** * Definition for singl ...

  8. [Struts2学习笔记] -- 环境配置

    在创建好WebProject后,就可以开始进行Struts2的环境配置,可以到Struts2官网下载,本环境使用struts-2.3.24.1版本. 首先导入必要的jar包到WebProject的/W ...

  9. Egret 入门

    居然使用 TyptScript... 先贴手册地址:http://www.typescriptlang.org/docs/tutorial.html. 先要接受一个诡异的写法: private loa ...

  10. h.264 FMO

    在H.264之前的标准中,比如H.263,其比特流中的数据是按照一个宏块接一个宏块的方式排列的,一旦发生丢包,很多相邻宏块信息都会丢失,很难进行错误隐藏处理.在H.264中加入了一项新特性:把宏块在比 ...