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 analysisController(String packageName) throws URISyntaxException, ClassNotFoundException, InstantiationException, IllegalAccessException{
        Map controllersMap = new HashMap();
        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 analysisURIByController(Map controllersMap){
        Map uriInfosMap = new HashMap();
        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 controllersMap = null;
    //定义存储uri信息容器
    private Map 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


<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-testdisplay-name>
  <welcome-file-list>
    <welcome-file>index.htmlwelcome-file>
  welcome-file-list>
  <servlet>
    <servlet-name>dispatcherServletservlet-name>
    <servlet-class>com.fkt.core.DispatcherServletservlet-class>
    
    <init-param>
        <param-name>mvcPackageparam-name>
        <param-value>com.fkt.web.controllerparam-value>
    init-param>
    
    <init-param>
        <param-name>viewDirectoryparam-name>
        <param-value>/WEB-INF/viewsparam-value>
    init-param>
    
    <init-param>
        <param-name>characterEncodingparam-name>
        <param-value>UTF-8param-value>
    init-param>
  servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServletservlet-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


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

代码下载

下载

你可能感兴趣的:(jspservlet)