javaweb-servlet实现简单的mvc分发请求

自定义servlet请求分发,模拟mvc,视图采用freemarker2.3.27

1).创建注解

标记控制器注解Controller

仅仅起到标记的作用,没有什么具体的作用_

package com.fkt.core.annotation;

import static java.lang.annotation.ElementType.TYPE;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 标记控制器
 * @author xuzhen
 *
 */
@Target(TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {

}

标记映射注解RequestMapping

value就是用来存储请求Url,用来分发请求用的

package com.fkt.core.annotation;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 标记url
 * @author xuzhen
 *
 */
@Target({ TYPE, METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
	/**
	 * 路径映射值
	 * @return
	 */
	String value() default "";
}

2).定义实体类

存储视图和数据的实体类ModelAndView

package com.fkt.core.entity;

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

/**
 * 包含视图和数据的实体类,主要用于freemarker渲染使用
 * @author xuzhen
 *
 */
@SuppressWarnings({"rawtypes","unchecked"})
public class ModelAndView {
	//视图名称
	private String viewName = null;
	//模板根容器
	private Map root = null;
	public ModelAndView() {
		root = new HashMap();
	}
	public ModelAndView(String viewName) {
		this();
		this.viewName=viewName;
	}
	public String getViewName() {
		return viewName;
	}
	public void setViewName(String viewName) {
		this.viewName = viewName;
	}
	public Map getRoot() {
		return root;
	}
	/**
	 * 增加数据
	 * @param key
	 * @param value
	 */
	public void addObject(Object key,Object value) {
		root.put(key, value);
	}
}

路径映射具体信息的实体类URIInfo

Method是反射包里的一个类,代表类的方法,有点类似c++的方法指针,想具体了解的话,就去看看java的反射吧_!

package com.fkt.core.entity;

import java.lang.reflect.Method;

/**
 * uri的实体类,包含uri对应的控制器和控制器方法
 * @author xuzhen
 *
 */
public class URIInfo {
	//控制器键
	private String controllerId;
	//控制器方法
	private Method uriMethod;
	public URIInfo() {}
	public URIInfo(String controllerId,Method uriMethod) {
		this.controllerId=controllerId;
		this.uriMethod=uriMethod;
	}
	public String getControllerId() {
		return controllerId;
	}
	public void setControllerId(String controllerId) {
		this.controllerId = controllerId;
	}
	public Method getUriMethod() {
		return uriMethod;
	}
	public void setUriMethod(Method uriMethod) {
		this.uriMethod = uriMethod;
	}

}

3). 编写反射工具类

根据注解把控制器添加到容器,根据注解存储映射的具体信息(映射路径,控制器方法)

package com.fkt.core.tool;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import com.fkt.core.annotation.Controller;
import com.fkt.core.annotation.RequestMapping;
import com.fkt.core.entity.URIInfo;

/**
 * 反射相关工具类
 * @author xuzhen
 *
 */
public abstract class ReflexUtils {
	/**
	 * 根据包名反射解析所有的控制器
	 * @param packageName
	 * @return
	 * @throws URISyntaxException
	 * @throws ClassNotFoundException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static Map<String,Object> analysisController(String packageName) throws URISyntaxException, ClassNotFoundException, InstantiationException, IllegalAccessException{
		Map<String,Object> controllersMap = new HashMap<String,Object>();
		ClassLoader classLoader = ReflexUtils.class.getClassLoader();
		String packageDirectory = packageName.replace(".", "/");
		File directory = new File(classLoader.getResource(packageDirectory).toURI());
		for(File file : directory.listFiles()) {
			String fName = file.getName();
			if(!fName.endsWith(".class")) {
				continue;
			}
			fName = fName.substring(0, fName.length() - 6);
			String className = packageName+"."+fName;
			Class cl = classLoader.loadClass(className);
			//检查解析包中的类是否带有Controller注解,没有注解不往容器中添加实例
			Annotation controllerAnnotation = cl.getAnnotation(Controller.class);
			if(controllerAnnotation!=null) {
				controllersMap.put(UUID.randomUUID().toString(), cl.newInstance());
			}
		}
		return controllersMap;
	}
	/**
	 * 解析控制器上和方法上的RequestMapping注解,并存储对应的方法到容器
	 * @param controllersMap
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static Map<String,URIInfo> analysisURIByController(Map<String,Object> controllersMap){
		Map<String,URIInfo> uriInfosMap = new HashMap<String,URIInfo>();
		for(String controllerId : controllersMap.keySet()) {
			Object controller = controllersMap.get(controllerId);
			Class controllerClass = controller.getClass();
			String uriPrefix = "";
			//解析类controller上的ReqestMapping注解
			Annotation cRequestMappingAnnotation = controllerClass.getAnnotation(RequestMapping.class);
			if(cRequestMappingAnnotation!=null) {
				RequestMapping requestMapping = (RequestMapping)cRequestMappingAnnotation;
				uriPrefix = requestMapping.value();
			}
			Method[] controllerMethods = controllerClass.getMethods();
			for(Method controllerMethod : controllerMethods) {
				Annotation mRequestMappingAnnotation = controllerMethod.getAnnotation(RequestMapping.class);
				if(mRequestMappingAnnotation!=null) {
					RequestMapping requestMapping = (RequestMapping)mRequestMappingAnnotation;
					String uri = uriPrefix+requestMapping.value();
					URIInfo uriInfo = new URIInfo(controllerId, controllerMethod);
					uriInfosMap.put(uri, uriInfo);
				}
			}
		}
		return uriInfosMap;
	}
}

4). 定义视图解析器,解析freemarker页面

package com.fkt.core.resolver;

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.net.URI;

import javax.servlet.http.HttpServletResponse;

import com.fkt.core.entity.ModelAndView;

import freemarker.core.ParseException;
import freemarker.template.Configuration;
import freemarker.template.MalformedTemplateNameException;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import freemarker.template.TemplateNotFoundException;

/**
 * 简单的freemarker模板处理引擎
 * @author xuzhen
 *
 */
public class FreemarkerViewResolver {
	private Configuration cfg = null;
	private String defaultEncoding = "UTF-8";

	public FreemarkerViewResolver() {

	}

	public FreemarkerViewResolver(String defaultEncoding) {
		this.defaultEncoding=defaultEncoding;
	}
	/**
	 * 初始化freemarker引擎
	 * @param directory
	 * @throws IOException
	 */
	public void init(URI viewDirectory) throws IOException {
		cfg = new Configuration(Configuration.VERSION_2_3_27);
		cfg.setDefaultEncoding(defaultEncoding);
		cfg.setDirectoryForTemplateLoading(new File(viewDirectory));
		cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
		cfg.setLogTemplateExceptions(false);
		cfg.setWrapUncheckedExceptions(true);
	}
  //视图解析方法
	public void resolver(ModelAndView mav,HttpServletResponse response) throws TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException, TemplateException {
		Template template = cfg.getTemplate(mav.getViewName());
		Writer out = response.getWriter();
		template.process(mav.getRoot(), out);
		out.flush();
	}
}

5). 定义请求分发servlet

package com.fkt.core;

import java.io.IOException;
import java.util.Map;

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

import com.fkt.core.entity.ModelAndView;
import com.fkt.core.entity.URIInfo;
import com.fkt.core.resolver.FreemarkerViewResolver;
import com.fkt.core.tool.ReflexUtils;
/**
 * freemarker测试核心分发控制器
 * @author xuzhen
 *
 */
public class DispatcherServlet extends HttpServlet {
	private static final long serialVersionUID = 1762792866529445537L;
	//定义存储所有的控制器的容器
	private Map<String,Object> controllersMap = null;
	//定义存储uri信息容器
	private Map<String,URIInfo> uriInfosMap = null;
	//创建模板引擎
	private FreemarkerViewResolver viewResolver = null;
	//编码默认使用utf-8
	private String defaultCharacterEncoding = "UTF-8";
	@Override
	public void init() throws ServletException {
		defaultCharacterEncoding = getInitParameter("characterEncoding");
		String mvcPackage = getInitParameter("mvcPackage");
		String viewDirectory = getInitParameter("viewDirectory");
		try {
			controllersMap = ReflexUtils.analysisController(mvcPackage);
			uriInfosMap = ReflexUtils.analysisURIByController(controllersMap);
			viewResolver = new FreemarkerViewResolver(defaultCharacterEncoding);
			viewResolver.init(getServletContext().getResource(viewDirectory).toURI());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doPost(req, resp);
	}
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		req.setCharacterEncoding(defaultCharacterEncoding);
		resp.setCharacterEncoding(defaultCharacterEncoding);
		String uri = req.getRequestURI().replace(req.getServletContext().getContextPath(), "");
		URIInfo uriInfo = uriInfosMap.get(uri);
		try {
			Object mavObject = uriInfo.getUriMethod().invoke(controllersMap.get(uriInfo.getControllerId()), req,resp);
			if(mavObject!=null) {
				viewResolver.resolver((ModelAndView) mavObject, resp);
				return;
			}
		} catch (Exception e) {
			//处理最外层捕捉错误,并抛到前台
			e.printStackTrace(resp.getWriter());
			return;
		}
		//没有正确的处理时的输出
		resp.getWriter().println("^_^ error!");
		resp.getWriter().flush();
	}
	@Override
	public void destroy() {

	}
}

6). 使用

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>freemarker-test</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
  <servlet>
  	<servlet-name>dispatcherServlet</servlet-name>
  	<servlet-class>com.fkt.core.DispatcherServlet</servlet-class>
    <!-- 要扫描的控制器包 -->
  	<init-param>
  		<param-name>mvcPackage</param-name>
  		<param-value>com.fkt.web.controller</param-value>
  	</init-param>
    <!-- 视图所在路径 -->
  	<init-param>
  		<param-name>viewDirectory</param-name>
  		<param-value>/WEB-INF/views</param-value>
  	</init-param>
    <!-- 制定编码 -->
  	<init-param>
  		<param-name>characterEncoding</param-name>
  		<param-value>UTF-8</param-value>
  	</init-param>
  </servlet>
  <servlet-mapping>
  	<servlet-name>dispatcherServlet</servlet-name>
  	<url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

TestController

package com.fkt.web.controller;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.fkt.core.annotation.Controller;
import com.fkt.core.annotation.RequestMapping;
import com.fkt.core.entity.ModelAndView;

@Controller
public class TestController {
	@RequestMapping("/")
	public ModelAndView index(HttpServletRequest request,HttpServletResponse response) throws IOException {
		ModelAndView mav = new ModelAndView("index.ftl");
		mav.addObject("name", "欢迎进入首页!");
		return mav;
	}

}

index.ftl

<!DOCTYPE HTML>
<html>
	<head>
		<title>首页</title>
		<meta charset="utf-8"/>
	</head>
	<body>
		<h1>${name!""}</h1>
	</body>
</html>

代码下载

下载

posted @ 2017-12-12 14:16  Bug的梦魇  阅读(836)  评论(0编辑  收藏  举报