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

来源:互联网 发布:画流程图的软件 编辑:程序博客网 时间:2024/05/22 01:40

自定义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)@Documentedpublic @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)@Documentedpublic @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;@Controllerpublic 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>

代码下载

下载