自己开发一个简单的mvc框架(类似spring mvc)

原创 2016年06月01日 11:24:31

先看看controller,这里写了一个UserController

package com.tanlei.controller;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import tmvc.framework.annotation.Controller;
import tmvc.framework.annotation.RequestMapping;
import tmvc.framework.annotation.RequestParameter;

@Controller(url = "/user")
public class UserController {

	@RequestMapping(url ="/login.html", method="POST")
	public String login(@RequestParameter(value="userName") String userName,@RequestParameter(value="request") HttpServletRequest request, @RequestParameter(value="password") String password) {
		Map<String, Object> map = new HashMap<String, Object>();
		Map<String, Object> returnMap = new HashMap<String, Object>();
		map.put("a", "aaaaa");
		System.out.println("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); 
		request.setAttribute("userName", "tanlei");
		return "index.jsp";
	}
}


这里定义了三个注解:@Controller和@RequestMapping和@RequestParameter

package tmvc.framework.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestParameter {
	String value() default "";
}


 

package tmvc.framework.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
	String url() default ""; //描述路径,例如: /article/1,则这里写成 url = /1
	String method() default "GET";  //用于描述请求方式
}


 

package tmvc.framework.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
	String url() default "";
}


核心流程:

服务器启动,执行ControllerHandlerListener,扫描Contrtoller所在包:com.tanlei.controller,生成MethodHandlerMap并放入ServletContext中:

package tmvc.framework.web;

import java.util.Map;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import tmvc.framework.util.ControllerHandler;

public class ControllerHandleListener implements ServletContextListener {
	
	@Override
	public void contextDestroyed(ServletContextEvent servletContextEvent) {
		
		
	}

	@Override
	public void contextInitialized(ServletContextEvent servletContextEvent) {
		try {
			String controllerPackageName = servletContextEvent.getServletContext().getInitParameter("controllerPackageName");
			Map<String, Map<String, Object>> methodHandlerMap = ControllerHandler.handle(controllerPackageName);
			servletContextEvent.getServletContext().setAttribute("methodHandlerMap", ControllerHandler.handle(controllerPackageName));
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

}

这里的核心是methodHandlerMap这个数据结构。

来看看methodHandlerMap生成方法:

package tmvc.framework.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import tmvc.framework.annotation.Controller;
import tmvc.framework.annotation.RequestMapping;
import tmvc.framework.annotation.RequestParameter;

/**
 * 控制层处理器 反射controller层的包 得到url与controller对应的map,例如 对ShowArticleController处理
 * handlerMap.put("/article/1", map<String, Object>); map.put("1"(二级url),
 * methodName); map.put("1"(二级url), parameterList<Map<"parameterName",方法参数名>,Map<"parameterType",方法参数类型>>);
 * 
 * @author Administrator
 *
 */
public class ControllerHandler {

	public static String[] getMethodParameterNamesByAnnotation(Method method) {
		Annotation[][] parameterAnnotations = method.getParameterAnnotations();
		if (parameterAnnotations == null || parameterAnnotations.length == 0) {
			return null;
		}
		String[] parameterNames = new String[parameterAnnotations.length];
		int i = 0;
		for (Annotation[] parameterAnnotation : parameterAnnotations) {
			for (Annotation annotation : parameterAnnotation) {
				if (annotation instanceof RequestParameter) {
					RequestParameter param = (RequestParameter) annotation;
					parameterNames[i++] = param.value();
				}
			}
		}
		return parameterNames;
	}


 

        public static Map<String, Map<String, Object>> handle(String controllerPackageName) throws ClassNotFoundException {
		// 控制层的数据封装
		Map<String, Map<String, Object>> handlerMap = new HashMap<String, Map<String, Object>>();

		// 根据包名反射控制层所有controller
		Set<String> clazzs = ClassUtil.getClassName(controllerPackageName, false);
		for (String clazz : clazzs) {
			Class<?> c = Class.forName(clazz);
			// 得到头部注解@Controller
			Controller ct = c.getAnnotation(Controller.class);
			String url = null;
			String headerUrl = ct.url();


 

                        // 得到方法上的注解@RequestMapping,方法名和方法内的参数(有序)
			for (Method method : c.getDeclaredMethods()) {
				Map<String, Object> methodMap = new HashMap<String, Object>();
				RequestMapping r = method.getAnnotation(RequestMapping.class);
				String methodUrl = r.url();
				url = headerUrl + methodUrl;
				Class<?> paramTypes[] = method.getParameterTypes();
				String[] paramNames = getMethodParameterNamesByAnnotation(method);
				List<Map<String , Object>> params = new ArrayList<Map<String, Object>>();
				for (int i = 0; i < paramTypes.length; i++) {
					Map<String, Object> paramMap = new HashMap<String, Object>();
					paramMap.put("paramName", paramNames[i]);
					paramMap.put("paramType", paramTypes[i]);
					params.add(paramMap);
				}
				methodMap.put("url", clazz);
				methodMap.put("params", params);
				methodMap.put("methodName", method.getName());
				handlerMap.put(url, methodMap);
				
			}
		}
		return handlerMap;
	}


}


ControllerHandler这个类负责扫描Controller所在包的所有类,生成类的反射信息封装在map中。

以UserController为例:

Map<String, Map<String, Object>>的内层map负责 innermap.put("url", com.tanlei.controller.UserController), innermap.put("params", params),

params为一个List<Map<String , Object>> params = new ArrayList<Map<String, Object>>();类型。里面存放的是方法的参数名和类型的信息

例如UserController的login方法参数信息。

innermap.put("methodName", method.getName());

然后外层outermap.put(url, methodMap); url为: /user/login.html,即方法controller和requestMaping的映射信息。

 

核心dispatcherServlet类负责转发请求给controller和返回到页面:

package tmvc.framework.web;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import tmvc.framework.util.ClassUtil;
import tmvc.framework.util.UriUtil;

/**
 * Servlet implementation class DispatcherServlet
 */
@WebServlet("/DispatcherServlet")
public class DispatcherServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;    
    
	public void init() throws ServletException {

	}


 

         public void service(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
			throws ServletException, IOException, RuntimeException {
    	//反射得到对应的controller 
    	Map<String, Map<String, Object>> methodHandlerMap = (Map<String, Map<String, Object>>) this.getServletContext().getAttribute("methodHandlerMap");
    	String uri = httpServletRequest.getRequestURI().toString();

    	Map<String, Object> methodMap = methodHandlerMap.get(UriUtil.getControllerUri(httpServletRequest,uri));
    	String queryStr = httpServletRequest.getQueryString();
    	System.out.println(uri+ ":" +  httpServletRequest.getContextPath().length());
    	List<Map<String , Object>> params = (List<Map<String, Object>>) methodMap.get("params"); //反射方法得到的参数信息的list
    	Object[] paramObjs = new Object[params.size()];//method的参数值的集合


 

Class[] paramTypes = new Class[params.size()];//method的参数类型的集合
    	int index = 0;
    	//List<Map<String , String>> urlParams = RequestParameterHandler.generateParameterListMap(httpServletRequest); //url里面传来的参数的list
    	for(int i = 0; i < params.size(); i++) {
    		Map<String, Object> paramMap = params.get(i); //反射得到的参数map
    		paramTypes[i] = (Class) paramMap.get("paramType");
    		String paramName = (String) paramMap.get("paramName");
    		if(paramTypes[i].isInstance(httpServletRequest)) {
    			paramObjs[i] = httpServletRequest;
    		} else {
    			paramObjs[i] = httpServletRequest.getParameter(paramName);
    		}
    	}


 

try {
			Class<?> clazz = Class.forName((String)methodMap.get("url"));
			Method method = clazz.getDeclaredMethod((String)methodMap.get("methodName"), paramTypes);
			Object obj = clazz.newInstance();
			Object ob = method.invoke(obj, paramObjs);
			System.out.println(ob.toString());
			httpServletRequest.getRequestDispatcher("/" + ob.toString()).forward(httpServletRequest, httpServletResponse);
			return;
			
		} catch (ClassNotFoundException | NoSuchMethodException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace(); 
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
    }

urlutil:

package tmvc.framework.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

public class UriUtil {
	
	public static String getControllerUri(HttpServletRequest request, String uri) {
		if(uri.contains("?")) {
    		uri = uri.substring(request.getContextPath().length(), uri.indexOf("?"));
    	} else {
    		uri = uri.substring(request.getContextPath().length(), uri.length());
    	}
		return uri;
	}
	
	

	
}


classutil核心方法:

public static Set<String> getClassName(String packageName, boolean isRecursion) {
        Set<String> classNames = null;
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        String packagePath = packageName.replace(".", "/");

        URL url = loader.getResource(packagePath);
        if (url != null) {
            String protocol = url.getProtocol();
            if (protocol.equals("file")) {
                classNames = getClassNameFromDir(url.getPath(), packageName, isRecursion);
            } else if (protocol.equals("jar")) {
                JarFile jarFile = null;


 

try{
                    jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
                } catch(Exception e){
                    e.printStackTrace();
                }
                
                if(jarFile != null){
                    getClassNameFromJar(jarFile.entries(), packageName, isRecursion);
                }
            }
        } else {
            /*从所有的jar包中查找包名*/
            classNames = getClassNameFromJars(((URLClassLoader)loader).getURLs(), packageName, isRecursion);
        }
        
        return classNames;
    }



 

web.xml的配置:

 

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:jsp="http://java.sun.com/xml/ns/javaee/jsp" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <display-name>test</display-name>

  <context-param>
    <param-name>controllerPackageName</param-name>
    <param-value>com.tanlei.controller</param-value>
  </context-param>

  <listener>
  	<listener-class>tmvc.framework.web.ControllerHandleListener</listener-class>
  </listener>


 

<servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>tmvc.framework.web.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>controllerPackageName</param-name>
      <param-value>com.tanlei.controller</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>*.html</url-pattern>
  </servlet-mapping>
</web-app>


 

访问:http://localhost:8080/user/login.html?userName=a&password=b访问成功,页面跳转。

只是实现了一个简单的流程,还有很多细节没有实现。

由于公司限制复制内容大小,限制网络等原因,代码格式不好,以后完善后给出下载链接。


 

 

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

一个很小的用C++写的MVC架构例子

#include #include //get namespace related stuff using std::cin; using std::cout; using std::endl; us...

从MVC架构到C++的多态实现

学习可以是一件很快乐的事,特别是当你发现以前所学的点点滴滴慢慢地能够串起来或者变成了一个环,这种感觉真好。这篇文章就这么来的。 从MVC架构开始说起吧。这两天系统了解了一下MVC架构的内容,主要参考于...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

C++实现一个简单的双线程MVC框架

MVC框架实现,原理如下: 两个线程一个控制线程,一个视图使用线程(tyg)

代码重构-MVC框架

1、什么是mvc框架 MVC是当前比较流行的框架,随便Google下,就可以发现几乎所有的应用程序开发中都采用了MVC框架,例如:.NET,Java Spring,Java Struts,PHP...

自己动手模仿 springmvc 写一个 mvc框架

spring的知识那么多,看源码也不知从何看起,那应该怎么学呢?我觉得,完整的自己动手去实现一个,是最好的方式。 bfmvc模仿springmvc的轻量级web框架,适合学习和搭建小型web项目使用,...

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

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

Spring.NET在.NET新时代的尴尬

本文介绍Spring.NET继承Java版Spring的衣钵,在一些.NET项目中已经被采用,并且已经被部分企业用作其开发框架的标准组成部分,但对于更大规模或者更小规模的.NET项目而言他处处给人以高...

写出一个你自己的MVC框架-基于对springMVC源码实现和理解(1):入口所在

在开始之前,我们必须对springMVC有一个明确的认识。一个软件项目的完成少不了各种设计文档,对整个项目进行一个规划,而在这里,我们写自己的MVC只是为了对springMVC有更深的认识,我们不妨跟...

《Spring设计思想》AOP设计基本原理

Spring 提供了AOP(Aspect Oriented Programming) 的支持, 那么,什么是AOP呢?本文将通过一个另外一个角度来诠释AOP的概念,帮助你更好地理解和使用Spring ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:自己开发一个简单的mvc框架(类似spring mvc)
举报原因:
原因补充:

(最多只允许输入30个字)