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

先看看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访问成功,页面跳转。

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

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


 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值