文档章节

手写简单mvc框架之xuchen-mvc-v1.0

 徐志
发布于 06/25 15:39
字数 1317
阅读 7
收藏 0

package org.mvc.framework.servlet;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRegistration;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.mvc.framework.annotation.XCcomponent;
import org.mvc.framework.annotation.XCcontroller;
import org.mvc.framework.annotation.XCrequestMapping;
import org.mvc.framework.annotation.XCrequestMethod;
import org.mvc.framework.annotation.XCrequestParam;
import org.mvc.framework.annotation.XCservice;
import org.mvc.framework.constant.CommonConstant;
import org.mvc.framework.entity.InnerHandler;
import org.mvc.framework.entity.Model;
import org.mvc.framework.entity.ModelMap;
import org.mvc.framework.handler.ClassReflectHandler;
import org.mvc.framework.handler.XmlBeanHandler;
import org.mvc.framework.utils.ContextPathUtil;
import org.mvc.framework.utils.StringCaseUtil;
import org.mvc.framework.utils.XmlUtils;
import org.mvc.framework.view.XCView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class XCDispatcherServlet extends HttpServlet {
    private static final long serialVersionUID = -5009321460671490398L;
    private static final Logger logger = LoggerFactory.getLogger(XCDispatcherServlet.class);
    private static final String CONTEXTCOMPONENTSCAN = "//" + CommonConstant.XML_CONTEXT_SCAN_PACAKAGE;
    private Map<String, Object> iocBeanMap = new HashMap<String, Object>();
    private Map<String, InnerHandler> handlerMapper = new HashMap<String, InnerHandler>();
    private Properties contextProperties = new Properties();

    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        System.out.println("[INFO ] @Override doPost 收到用户请求。。。。");
    }
    
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("[INFO ] @Override service 收到用户请求。。。。");
        response.setHeader("Content-type", "text/html;charset=UTF-8");
        String reqPath = request.getRequestURL().toString();
        String basePath = request.getScheme() + "://" + request.getServerName()
                + ":" + request.getServerPort() + request.getContextPath();
        String url = reqPath.replace(basePath, "").replaceAll("/+", "/");
        System.out.println("[INFO ] " + url);
        InnerHandler inner = handlerMapper.get(url);
        if (null == inner) {
            System.out.println("[INFO ] service 404 not Fund reuest path " + url);
            response.getWriter().write("404 not Fund reuest");
        } else {
            doService(request, response, inner);
        }
    }

    /**
     * 处理请求
     * @throws ServletException 
     */
    private void doService(HttpServletRequest request, HttpServletResponse response, 
            InnerHandler inner) throws IOException, UnsupportedEncodingException, ServletException {
        request.setCharacterEncoding("UTF-8");//客户端网页我们控制为UTF-8
        String requestMethodType = request.getMethod();
        
        Method method0 = inner.getMethod();
        if(!validateRequstMethodType(requestMethodType, method0)) {
            response.getWriter().write(requestMethodType.toUpperCase() + " method not soupport......");
            return;
        }
        
        List<Object> invokeParamVal = new ArrayList<Object>();
        List<String> methodParamNames = inner.getMethodParameterNames();
        Model model = null;
        
        if (null!=methodParamNames && !methodParamNames.isEmpty()) {
            StringBuilder body = new StringBuilder();
            Map<String, Object> postQueryParam = null;
            if (CommonConstant.REQUEST_METHOD_TYPE_POST.equals(requestMethodType)) {
                doPostRequest(request, body);
                postQueryParam = StringCaseUtil.queryParam2Map(body.toString());
            }
            for (int i = 0; i < methodParamNames.size(); i++) {
                String methodParamName = methodParamNames.get(i);
                Class<?> methodParamType = inner.getMethodParameterTypes()[i];
                if (methodParamType.isAssignableFrom(HttpServletRequest.class)) {
                    invokeParamVal.add(request);
                } else if (methodParamType.isAssignableFrom(HttpServletResponse.class)) {
                    invokeParamVal.add(response);
                } else if (methodParamType.isAssignableFrom(Model.class)) {
                    model = new ModelMap();
                    invokeParamVal.add(model);
                } else {
                    String methodParamVal = "";
                    boolean notnull = false;
                    Annotation[][] annotationParam = inner.getParameterAnnotations();
                    if (null != annotationParam[i]&& annotationParam[i].length > 0) {
                        Annotation annotation = annotationParam[i][0];
                        if (annotation instanceof XCrequestParam) {
                            notnull = true;
                            methodParamName = ((XCrequestParam) annotation).value();
                        }
                    }
                    if (CommonConstant.REQUEST_METHOD_TYPE_GET.equals(requestMethodType)) {
                        logger.info("[INFO ] GET request......");
                        methodParamVal = request.getParameter(methodParamName);
                        if (notnull && StringUtils.isEmpty(methodParamVal)) {
                            response.getWriter().write(methodParamName + " cannot be null or empty");
                            return;
                        }
                    } else {
                        System.out.println("[INFO ] POST request deal with ......");
                        if (null != postQueryParam ) { 
                            if(postQueryParam.containsKey(methodParamName)) {
                                methodParamVal = String.valueOf(postQueryParam.get(methodParamName));
                                invokeParamVal.add(ClassReflectHandler.invokeMethodParamValue(methodParamType, methodParamVal));
                            } else {
                                // 判断请求参数不是简单的数据类型,判断是某个的类
                                try {
                                    Field[] fileds = methodParamType.getDeclaredFields();
                                    Object instance = null;
                                    for (Field field : fileds) {
                                        field.setAccessible(true);
                                        String fieldName = field.getName();
                                        if(postQueryParam.containsKey(fieldName)) {
                                            if (null == instance) {
                                                instance = methodParamType.newInstance();
                                            }
                                            // (反射)如果请求的字段是该类的一个属性,那么,就设置创建一个实例并且设置值
                                            String setMethodName = "set" + StringCaseUtil.upperFirstCase(fieldName);
                                            Method method = methodParamType.getMethod(setMethodName, new Class<?>[]{field.getType()});
                                            
                                            String fieldValue = String.valueOf(postQueryParam.get(fieldName));
                                            method.invoke(instance, ClassReflectHandler.invokeMethodParamValue(field.getType(), fieldValue));
                                        }
                                    }
                                    invokeParamVal.add(instance);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                } 
            }
        }
        try {
            Object methodClazz = inner.getMethodClass().newInstance();
            Object result = inner.getMethod().invoke(methodClazz, invokeParamVal.toArray());
            // 反射执行方法后 先去判断model 中是否有值,有就放到request
            if (null != model && model instanceof ModelMap) {
                @SuppressWarnings("unchecked")
                Map<String, Object> modelMap = (Map<String, Object>) model;
                copyModel2Request(request, modelMap);
            }
            
            if (result instanceof XCView) {
                XCView view = XCView.valueOf(result);
                String path = view.getPath();
                if (StringUtils.isNotEmpty(path)) {
                    if (path.startsWith("/")) {
                        response.sendRedirect(request.getContextPath() + path);
                    } else {
                        Map<String, Object> modelMap = view.getModel();
                        copyModel2Request(request, modelMap);
                        request.getRequestDispatcher(CommonConstant.JSP_BASE_PATH + path).forward(request, response);
                    }
                }
            } else if (result instanceof JSONObject) {
                response.setContentType("application/json; charset=utf-8");  
                response.getWriter().write(JSON.toJSONString(result));
            } else if (result instanceof String) {
                String path = "";
                if (String.valueOf(result).startsWith("/")) {
                    path = request.getContextPath() + String.valueOf(result);
                    response.sendRedirect(path.replaceAll("/+", "/"));
                } else {
                    path = CommonConstant.JSP_BASE_PATH + String.valueOf(result);
                    request.getRequestDispatcher(path.replaceAll("/+", "/")).forward(request, response);
                }
            }
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | InstantiationException e) {
            e.printStackTrace();
        }
    }

    private boolean validateRequstMethodType(String requestMethodType, Method method0) {
        if (null == method0) {
            return false;
        }
        if (method0.isAnnotationPresent(XCrequestMapping.class)) {
            XCrequestMapping requestMapping = method0.getAnnotation(XCrequestMapping.class);
            List<XCrequestMethod> requestMethodlist = Arrays.asList(requestMapping.method());
            if (requestMethodType.toUpperCase().equals("GET")) {
                if (requestMethodlist.contains(XCrequestMethod.GET)) {
                    return true;
                }
            } else {
                if (requestMethodlist.contains(XCrequestMethod.POST)) {
                    return true;
                }
            }
        }
        return false;
    }

    private void copyModel2Request(HttpServletRequest request, Map<String, Object> modelMap) {
        if (null != modelMap) {
            for (Map.Entry<String, Object> entry : modelMap.entrySet()) {
                request.setAttribute(entry.getKey(), entry.getValue());
            }
        }
    }

    private void doPostRequest(HttpServletRequest request, StringBuilder body)
            throws IOException {
        Enumeration<String> contentType = request.getHeaders("Content-Type");
        System.out.println("[INFO ] POST request...contentType " + JSON.toJSONString(contentType));
        ServletInputStream servletInputStream = request.getInputStream();
        ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream();
        try {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = servletInputStream.read(buffer))!= -1) {
                byteOutStream.write(buffer, 0, len);
            }
            byteOutStream.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
        InputStream inputStream = null;
        BufferedReader bufferedReader = null;
        try {
            inputStream = new ByteArrayInputStream(byteOutStream.toByteArray());
            bufferedReader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = bufferedReader.readLine()) != null) {
                body.append(StringCaseUtil.urlChinseEncode(line));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != inputStream) {
                inputStream.close();
            }
            if (null != bufferedReader) {
                bufferedReader.close();
            }
        }
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        System.out.println("[INFO ] initailize XCDispatcherServlet start ...");

        // 初始化 context 容器
        doInitContext(config);
        // 扫描bean 初始化 IOC
        String scanPack = contextProperties.getProperty(CommonConstant.XML_CONTEXT_SCAN_PACAKAGE);
        doInitIocScanPack(scanPack);
        // DI 依赖注入
        doInitDIBean();
        // 初始化 hanldeMapping
        doInitHandlerMapping();
        handlerMapper.forEach((key, value)->{
            System.out.println("[INFO ] " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS").format(new Date())) + " XCDispatcherServlet " + key);
        });
        // 注册servlet
        ServletContext servletContext = config.getServletContext();
        // 注册jsp servlet
        ServletRegistration jspRegistration = servletContext.getServletRegistration("jsp");
        jspRegistration.addMapping(CommonConstant.JSP_BASE_PATH + "*");
        
        ServletRegistration defaultRegistration = servletContext.getServletRegistration("default");
        defaultRegistration.addMapping(CommonConstant.STATIC_FILE_BASE_PATH + "*");
    }

    private void doInitHandlerMapping() {
        System.out.println("[INFO ] ------------ bean init handler mapping start -------------");
        if (null == iocBeanMap || iocBeanMap.isEmpty()) {
            return;
        }
        for (Entry<String, Object> entry : iocBeanMap.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            if (clazz.isAnnotationPresent(XCcontroller.class)) {
                ClassReflectHandler.doBeanHandlerMapping(clazz, handlerMapper);
            }
        }
    }

    private void doInitDIBean() {
        System.out.println("[INFO ] ------------ bean di start -------------");
        try {
            if (null == iocBeanMap || iocBeanMap.isEmpty()) {
                return;
            }
            for (Entry<String, Object> entry : iocBeanMap.entrySet()) {
                Class<?> claxx = entry.getValue().getClass();
                if (claxx.isAnnotationPresent(XCcontroller.class) || claxx.isAnnotationPresent(XCservice.class)
                        || claxx.isAnnotationPresent(XCcomponent.class)) {
                    ClassReflectHandler.doBeanDiInstation(entry, claxx,iocBeanMap);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 空格=%20 ; !=%21 ; "=%22 ; #=%23 ; $=%24 ; %=%25 ; & = %26 ; ' = %27 ( =%28
     * ; )=%29 ; *=%2A ; +=%2B ; ,=%2C ; -=%2D ; .=%2E ; /=%2F
     * 
     * @param scanPackPath
     */
    private void doInitIocScanPack(String scanPackPath) {
        if (StringUtils.isEmpty(scanPackPath)) {
            return;
        }
        try {
            String scanRootPath = scanPackPath.replace(".", "/");
            Enumeration<URL> rootUrl = this.getClass().getClassLoader().getResources(scanRootPath);
            while (rootUrl.hasMoreElements()) {
                URL url = rootUrl.nextElement();
                String urlProtocol = url.getProtocol();
                if (CommonConstant.PACKAGE_PROTOCOL_FILE.equals(urlProtocol)) {
                    scanCurrentFile(scanPackPath, url);
                } else {
                    // 扫描 jar 包
                    if (CommonConstant.PACKAGE_PROTOCOL_JAR.equals(urlProtocol)) {
                        JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
                        if (null != jarURLConnection) {
                            JarFile jarFile = jarURLConnection.getJarFile();
                            if (null != jarFile) {
                                Enumeration<JarEntry> jarEntries = jarFile.entries();
                                while (jarEntries.hasMoreElements()) {
                                    JarEntry jarEntry = (JarEntry) jarEntries.nextElement();
                                    String jarEntryName = jarEntry.getName();
                                    if (jarEntryName.endsWith(".class")) {
                                        String className = jarEntryName.substring(0,jarEntryName.lastIndexOf(".")).replaceAll("/", ".");
                                        iocBeanMap.put(className,ClassReflectHandler.loadClass(className, false));
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void scanCurrentFile(String scanPackPath, URL url)
            throws ClassNotFoundException, InstantiationException,
            IllegalAccessException {
        String clazzPath = url.getPath().replaceAll("%20", "");
        File[] files = new File(clazzPath).listFiles(new FileFilter() {
            @Override
            public boolean accept(File filePath) {
                return (filePath.isFile()&& filePath.getName().endsWith(".class") 
                        || filePath.isDirectory());
            }
        });
        if (null == files || files.length <= 0) {
            return;
        }
        for (File file : files) {
            String fileName = file.getName();
            if (file.isDirectory()) {
                String subScanPackPath = scanPackPath + "." + fileName;
                doInitIocScanPack(subScanPackPath);
            } else {
                String clazzName = scanPackPath + "." + fileName.substring(0, fileName.lastIndexOf("."));
                Class<?> claxx = Class.forName(clazzName);
                String instanceName = "";
                if (claxx.isAnnotationPresent(XCcontroller.class)
                        || claxx.isAnnotationPresent(XCcomponent.class)) {
                    instanceName = StringCaseUtil.lowerFirstCase(claxx
                            .getSimpleName());
                } else if (claxx.isAnnotationPresent(XCservice.class)) {
                    XCservice xCservice = claxx.getAnnotation(XCservice.class);
                    instanceName = xCservice.value();
                    if (StringUtils.isEmpty(instanceName)) {
                        instanceName = claxx.getInterfaces()[0].getSimpleName();
                    }
                }
                if (StringUtils.isNotEmpty(instanceName)) {
                    iocBeanMap.put(instanceName, claxx.newInstance());
                }
            }
        }
    }

    private void doInitContext(ServletConfig config) {
        String contextPath = config.getInitParameter("applicationLocalContext");
        List<String> contextlist = ContextPathUtil.splitContextPath(contextPath);
        if (null == contextlist || contextlist.isEmpty()) {
            throw new RuntimeException("dispacherservlet init context exception ...");
        }
        contextlist.stream().forEach(destPath -> {
            if (destPath.toUpperCase().endsWith("xml".toUpperCase())) {
                XmlBeanHandler.initXmlBean(XmlUtils.dealWithXml(this.getClass().getClassLoader(), 
                        destPath, CONTEXTCOMPONENTSCAN),iocBeanMap, contextProperties);
            } else {
                InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(destPath);
                try {
                    contextProperties.load(new InputStreamReader(inputStream));
                } catch (IOException e) {
                    logger.error("init servletconfig exception {}",e);
                    e.printStackTrace();
                }
            }
        });
    }
}
 

© 著作权归作者所有

共有 人打赏支持
粉丝 1
博文 22
码字总数 21059
作品 0
崇明
轻量级PHP框架--DoitPHP

DoitPHP是一个简单易用,易于扩展的轻量级PHP框架。运行高效,操作灵活。基于BSD开源协议发布,适用于MVC项目开发。 自2010年5月4日正式发布TommyFramework V1.0(DoitPHP前身)以来,如今Doi...

dotiphp
2011/07/06
5.9K
1
Java程序员,最常用的30%技术有哪些?

两个月,这让我想到了我当年第一份工作,也是两个月从零学JAVA。 当时就买了3本书,强啃了6周。记住一点,时间有限,别听网上很多大牛的,他们说的太杂。你要抓重点,你只要让人家知道,你足...

2017/12/24
0
0
Java程序员,最常用的20%技术有哪些?

两个月,这让我想到了我当年第一份工作,也是两个月从零学JAVA。当时就买了3本书,强啃了6周。记住一点,时间有限,别听网上很多大牛的,他们说的太杂。你要抓重点,你只要让人家知道,你足够...

嘿你好夏天
2017/12/24
0
2
DoitPHP v2.5 正式发布

DoitPHP 是一个简单易用,易于扩展的轻量级 PHP 框架。运行高效,操作灵活。基于 BSD 开源协议发布,适用于 MVC 项目开发。 自 2010 年 5 月 4 日正式发布 TommyFrameworkV1.0(DoitPHP 前身...

dotiphp
2016/12/27
1K
10
三流程序员与一流程序员之间的区别,看看你是属于哪一类?

源码系列 手写spring mvc框架 基于Spring JDBC手写ORM框架 实现自己的MyBatis Spring AOP实战之源码分析 Spring IOC高级特性应用分析 ORM框架底层实现原理剖析 手写Spring MVC框架实现 手把手...

茶轴的青春
04/17
0
0

没有更多内容

加载失败,请刷新页面

加载更多

web打印控件 LODOP的详细api

web打印控件 LODOP的详细api

wangxujun59
22分钟前
1
0
从一次小哥哥与小姐姐的转账开始, 浅谈分布式事务从理论到实践

分布式事务是个业界难题,在看分布式事务方案之前,先从单机数据库事务开始看起。 什么是事务 事务(Transaction)是数据库系统中一系列操作的一个逻辑单元,所有操作要么全部成功要么全部失...

中间件小哥
24分钟前
5
0
荣登Github日榜!微信最新开源MMKV

MMKV 开源当日即登Github Trending日榜,三日后荣登周榜。MMKV 在腾讯内部开源半年,得到公司内部团队的广泛应用和一致好评。 MMKV 是基于 mmap 内存映射的移动端通用 key-value 组件,底层序...

腾讯开源
33分钟前
2
0
前端取色工具:jcpicker

http://annystudio.com/software/colorpicker/#jcp-download

轻量级赤影
35分钟前
1
0
Swift - 将图片保存到相册

import Photos func loadImage(image:UIImage) { UIImageWriteToSavedPhotosAlbum(image, self, #selector(saveImage(image:didFinishSavingWithError:contextInfo:)), ni......

west_zll
42分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部