zoukankan      html  css  js  c++  java
  • 手写MVC框架(二)-代码实现和使用示例

    --------上一篇:手写MVC框架(一)-再出发-----

    背景

    书接上文,之前整理了实现MVC框架需要写哪些东西。这周粗看了一下,感觉也没多少工作量,所以就计划一天时间来完成。周末的时间,哪会那么老实的坐在电脑前写代码呢?看电影的时候应该是老实的。为了不给自己留遗憾,所以今天就接着写了,然后就写完了。

    一、主要代码结构

    .
    ├── annotation 
    │   ├── XAutowired.java //用于依赖注入
    │   ├── XComponent.java //资源管理
    │   ├── XController.java //资源管理-controller
    │   ├── XRepository.java //资源管理-资源层
    │   ├── XRequestMapping.java //资源uri
    │   └── XService.java //资源管理-service
    ├── bean
    │   ├── EntityBean.java //存储实例化的资源
    │   └── SystemConst.java
    ├── handler
    │   └── XDispatcherServlet.java //核心调度类
    ├── mapper
    │   ├── InstanceManager.java //资源实例管理
    │   └── ServletMapper.java //请求路径-资源映射
    └── util
    ├── ClazzUtil.java
    ├── CommonUtil.java
    └── RequestResolveUtil.java

    二、主要流程

    1、服务启动,加载XDispatcherServlet

    2、XDispatcherServlet初始化,调用InstanceManager进行对象初始化、依赖注入

    3、调用ServletMapper扫描编写的各个URI

    4、请求到达XDispatcherServlet时,通过ServletMapper匹配到对应的方法

    5、执行匹配到的方法

    三、InstanceManager实现

    package com.shuimutong.gmvc.mapper;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Field;
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Map;
    import java.util.Set;
    
    import org.apache.commons.lang3.StringUtils;
    import org.reflections.Reflections;
    
    import com.shuimutong.gmvc.annotation.XAutowired;
    import com.shuimutong.gmvc.annotation.XComponent;
    import com.shuimutong.gmvc.annotation.XController;
    import com.shuimutong.gmvc.annotation.XRepository;
    import com.shuimutong.gmvc.annotation.XService;
    import com.shuimutong.gmvc.bean.EntityBean;
    import com.shuimutong.gmvc.bean.SystemConst;
    import com.shuimutong.gmvc.util.ClazzUtil;
    
    /**
     * 实例管理
     * @ClassName:  InstanceManager   
     * @Description:(这里用一句话描述这个类的作用)   
     * @author: 水木桶
     * @date:   2019年9月7日 下午10:08:27     
     * @Copyright: 2019 [水木桶]  All rights reserved.
     */
    public class InstanceManager {
        /**被注解的类**/
        private static Map<String, EntityBean> CLASS_ENTITY_MAP = new HashMap();
        /**被XController注解的类**/
        private static Set<EntityBean> CONTROLLER_CLASS_ENTITY_MAP = new HashSet();
        
        /**
         * 初始化
         * @param conf
         * @throws InstantiationException
         * @throws IllegalAccessException
         */
        public static void init(Map<String, String> conf) throws InstantiationException, IllegalAccessException {
            String basePackageStr = conf.get(SystemConst.BASE_PACKAGE);
            //扫描通过框架管理的资源
    scanAnnotationedResources(basePackageStr);
         //实例化通过框架管理的资源 generateAnnotationedEntity(); }
    /** * 获取controller类 * @return */ public static Set<EntityBean> getControllerClazzes() { return CONTROLLER_CLASS_ENTITY_MAP; } /** * 根据类(被框架管理的类)获取对应的实例对象 * @param clazz * @return */ public static EntityBean getEntityByClazz(Class clazz) { String className = ClazzUtil.getClazzName(clazz); return CLASS_ENTITY_MAP.get(className); } /** * 扫描需要框架管理的类 * @param basePackageStr */ private static void scanAnnotationedResources(String basePackageStr) { if(StringUtils.isBlank(basePackageStr)) { return; } String[] basePackages = basePackageStr.split(","); Reflections reflections = new Reflections(basePackages); Class<?>[] annotations = {XController.class, XService.class, XRepository.class, XComponent.class}; for(Class<?> annotation : annotations) { Set<Class<?>> resourceClazzes = reflections .getTypesAnnotatedWith((Class<? extends Annotation>) annotation); for(Class<?> resourceClazz : resourceClazzes) { String className = ClazzUtil.getClazzName(resourceClazz); CLASS_ENTITY_MAP.put(className, new EntityBean(className, resourceClazz)); if(resourceClazz.isAnnotationPresent(XController.class)) { CONTROLLER_CLASS_ENTITY_MAP.add(new EntityBean(className, resourceClazz)); } } } } /** * 对通过框架管理的类进行实例化 * @throws IllegalAccessException * @throws InstantiationException */ private static void generateAnnotationedEntity() throws InstantiationException, IllegalAccessException { //先根据构造方法初始化bean initBeanInstance(CLASS_ENTITY_MAP.values()); Set<String> clazzNames = CLASS_ENTITY_MAP.keySet(); for(String clazzName : clazzNames) { EntityBean entityBean = CLASS_ENTITY_MAP.get(clazzName); initBeanAutowired(entityBean); } } /** * 初始化实例对象 * @param classEntityMap * @throws IllegalAccessException * @throws InstantiationException */ private static void initBeanInstance(Collection<EntityBean> entityBeans) throws InstantiationException, IllegalAccessException { for(EntityBean entityBean : entityBeans) { if(entityBean.getO() == null) { Class<?> destClazz = entityBean.getClazz(); entityBean.setO(destClazz.newInstance()); } } } /** * 初始化bean中注入的类 * @param entityBean * @throws IllegalArgumentException * @throws IllegalAccessException * @throws InstantiationException */ private static void initBeanAutowired(EntityBean entityBean) throws IllegalArgumentException, IllegalAccessException, InstantiationException { if(entityBean.isFullAutowired()) { return; } Class<?> destClazz = entityBean.getClazz(); Field[] fields = destClazz.getDeclaredFields(); Object entityInstance = entityBean.getO(); Collection<EntityBean> entityBeans = CLASS_ENTITY_MAP.values(); for(Field field : fields) { if(!field.isAnnotationPresent(XAutowired.class)) { continue; } field.setAccessible(true); Object fieldVal = field.get(entityInstance); if(fieldVal != null) { continue; } Class<?> fieldClazz = field.getType(); EntityBean relayEntity = getEntityByClazz(fieldClazz); //依赖的对象能够直接查到 if(relayEntity != null) { field.set(entityInstance, relayEntity.getO()); } else { boolean find = false; for(EntityBean otherEntityBean : entityBeans) { //判断子类 if(fieldClazz.isAssignableFrom(otherEntityBean.getClazz())) { field.set(entityInstance, otherEntityBean.getO()); find = true; break; } } if(!find) { throw new IllegalArgumentException("autowiredEntityNotFoundException"); } } } entityBean.setFullAutowired(true); } }

    四、ServletMapper实现

    package com.shuimutong.gmvc.mapper;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    
    import com.shuimutong.gmvc.annotation.XRequestMapping;
    import com.shuimutong.gmvc.bean.EntityBean;
    import com.shuimutong.gmvc.util.CommonUtil;
    import com.shuimutong.gutil.common.GUtilCommonUtil;
    
    /**
     * servlet映射
     * @ClassName:  ServletMapper   
     * @Description:(这里用一句话描述这个类的作用)   
     * @author: 水木桶
     * @date:   2019年9月7日 下午6:22:19     
     * @Copyright: 2019 [水木桶]  All rights reserved.
     */
    public class ServletMapper {
        /**uri-method映射**/
        private static Map<String, Method> URI_MAP = new HashMap();
        
        public static void init() {
            generateUriMap(InstanceManager.getControllerClazzes());
            StringBuilder logSb = new StringBuilder("ServletMapper,scanUriPath:
    ");
            for(String uri : URI_MAP.keySet()) {
                logSb.append(uri).append("
    ");
            }
            logSb.append("
    ").append("---scanUriPath-----end----");
            System.out.println(logSb.toString());
        }
        
        /**
         * 生成uri-方法映射
         * @param controllerClazz
         */
        private static void generateUriMap(Set<EntityBean> controllerClazzBeans) {
            if(GUtilCommonUtil.checkListEmpty(controllerClazzBeans)) {
                return;
            }
            Class<? extends Annotation> requestMappingClazz = XRequestMapping.class;
            for(EntityBean eb : controllerClazzBeans) {
                Class<?> controllerClazz = eb.getClazz();
                String rootUri = "";
                if(controllerClazz.isAnnotationPresent(requestMappingClazz)) {
                    XRequestMapping xrm = (XRequestMapping) controllerClazz.getAnnotation(XRequestMapping.class);
                    rootUri = xrm.value();
                }
                Method[] methods = controllerClazz.getDeclaredMethods();
                for(Method method : methods) {
                    if(method.isAnnotationPresent(requestMappingClazz)) {
                        XRequestMapping xrm = (XRequestMapping) method.getAnnotation(XRequestMapping.class);
                        String methodUri = xrm.value();
                        String fullUri = rootUri + "/" + methodUri;
                        URI_MAP.put(CommonUtil.formatUri(fullUri), method);
                    }
                }
            }
        }
        
        /**
         * 获取uri对应的方法
         * @param uri
         * @return
         */
        public static Method getMethodByUri(String uri) {
            return URI_MAP.get(uri);
        }
        
    }

    五、XDispatcherServlet-核心调度实现

    package com.shuimutong.gmvc.handler;
    
    import java.io.IOException;
    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.Map;
    
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import com.shuimutong.gmvc.bean.EntityBean;
    import com.shuimutong.gmvc.bean.SystemConst;
    import com.shuimutong.gmvc.mapper.InstanceManager;
    import com.shuimutong.gmvc.mapper.ServletMapper;
    
    /**
     * 调度servlet
     * @ClassName:  XDispatcherServlet   
     * @Description:(这里用一句话描述这个类的作用)   
     * @author: 水木桶
     * @date:   2019年9月8日 上午11:58:37     
     * @Copyright: 2019 [水木桶]  All rights reserved.
     */
    public class XDispatcherServlet extends HttpServlet {
        private static final Logger log = LoggerFactory.getLogger(XDispatcherServlet.class);
        
        @Override
        public void init() throws ServletException {
            super.init();
            //获取ServletConfig对象
            ServletConfig config = this.getServletConfig();
            //根据参数名获取参数值
    //        String basePackage = config.getInitParameter(SystemConst.BASE_PACKAGE);
            Map<String, String> confMap = new HashMap();
            confMap.put(SystemConst.BASE_PACKAGE, config.getInitParameter(SystemConst.BASE_PACKAGE));
            try {
                InstanceManager.init(confMap);
            } catch (Exception e) {
                throw new ServletException(e);
            }
            ServletMapper.init();
        }
    
        /**
         * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
         */
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doPost(request, response);
        }
    
        /**
         * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
         */
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //        System.out.println("Hello");
            String requestUri = request.getRequestURI().replace(request.getContextPath(), "");
    //        System.out.println("requestUri:" + requestUri);
            Method resolveMethod = ServletMapper.getMethodByUri(requestUri);
            EntityBean entityBean = InstanceManager.getEntityByClazz(resolveMethod.getDeclaringClass());
            if(entityBean == null) {
                throw new ServletException("uriNotFoundException");
            }
            try {
                resolveMethod.invoke(entityBean.getO(), request, response);
            } catch (Exception e) {
                log.error("execute" + resolveMethod.getName() + "Exception", e);
                throw new ServletException(e);
            }
        }
    }

    六、源码分享

    gmvc:https://gitee.com/simpleha/gmvc.git

    依赖:https://gitee.com/simpleha/gutil.git

    七、使用示例

    1、编译打包gutil

    https://gitee.com/simpleha/gutil.git

    2、编译打包gmvc

    https://gitee.com/simpleha/gmvc.git

    3、新建webapp,引入pom

    <dependency>
        <groupId>com.shuimutong</groupId>
        <artifactId>gmvc</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>

    4、修改web.xml

    <!DOCTYPE web-app PUBLIC
     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd" >
    
    <web-app>
      <display-name>Archetype Created Web Application</display-name>
      <servlet>
          <servlet-name>gmvc</servlet-name>
          <servlet-class>com.shuimutong.gmvc.handler.XDispatcherServlet</servlet-class>
          <init-param>
            <param-name>basePackage</param-name>
            <param-value>com.shuimutong.testgmvc</param-value> //框架扫描的包名,多个路径以“,”连接
        </init-param>
          <load-on-startup>2</load-on-startup>
      </servlet>
    
      <servlet-mapping>
          <servlet-name>gmvc</servlet-name>
          <url-pattern>/</url-pattern>
      </servlet-mapping>
    
    </web-app>

    5、编写dao层

    package com.shuimutong.testgmvc.dao;
    
    import com.shuimutong.testgmvc.bean.Person;
    //接口
    public interface TestDao {
        Person findPerson();
    }
    
    
    package com.shuimutong.testgmvc.dao.impl;
    
    import com.shuimutong.gmvc.annotation.XRepository;
    import com.shuimutong.testgmvc.bean.Person;
    import com.shuimutong.testgmvc.dao.TestDao;
    //实现类,需加注解
    @XRepository
    public class TestDaoImpl implements TestDao {
    
        @Override
        public Person findPerson() {
            return new Person();
        }
    
    }

    6、编写service

    package com.shuimutong.testgmvc.service;
    //接口
    public interface Test2Service {
        void speak();
        String convertString(String s);
    }
    
    
    package com.shuimutong.testgmvc.service.impl;
    
    import com.alibaba.fastjson.JSONObject;
    import com.shuimutong.gmvc.annotation.XAutowired;
    import com.shuimutong.gmvc.annotation.XService;
    import com.shuimutong.testgmvc.bean.Person;
    import com.shuimutong.testgmvc.dao.TestDao;
    import com.shuimutong.testgmvc.service.Test2Service;
    //实现类
    @XService
    public class Test2ServiceImpl implements Test2Service {
        @XAutowired
        private TestDao testDao;
    
        @Override
        public void speak() {
            System.out.println("----Test2ServiceImpl-----speak----");
        }
    
        @Override
        public String convertString(String s) {
            Person p = testDao.findPerson();
            p.setName(p.getName() + s);
            return JSONObject.toJSONString(p);
        }
    
    }

    7、编写controller

    package com.shuimutong.testgmvc.controller;
    
    import java.util.Map;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.alibaba.fastjson.JSONObject;
    import com.shuimutong.gmvc.annotation.XAutowired;
    import com.shuimutong.gmvc.annotation.XController;
    import com.shuimutong.gmvc.annotation.XRequestMapping;
    import com.shuimutong.gmvc.util.RequestResolveUtil;
    import com.shuimutong.testgmvc.service.Test2Service;
    import com.shuimutong.testgmvc.service.TestService;
    
    @XController
    @XRequestMapping("/test")
    public class TestController {
        @XAutowired
        private Test2Service test2Service;
        @XAutowired
        private TestService testService;
    
        @XRequestMapping("/testA")
        public void testA(HttpServletRequest request, HttpServletResponse reponse) {
            System.out.println("Hi, this is TestA");
        }
        
        @XRequestMapping("/testB")
        public void testB(HttpServletRequest request, HttpServletResponse reponse) {
            System.out.println("Hi, this is TestA");
            JSONObject res = new JSONObject();
            String tmpMsg = null;
            Map<String, String[]> map = request.getParameterMap();
            for(String k : map.keySet()) {
                res.put(k, map.get(k));
                if(tmpMsg == null) {
                    tmpMsg = map.get(k)[0];
                }
            }
            System.out.println("----------testService.speak()------------");
            testService.speak();
            System.out.println("----------test2Service.convertString()------------");
            String person = test2Service.convertString(tmpMsg);
            res.put("person", person);
            RequestResolveUtil.returnJson(request, reponse, res.toJSONString());
        }
    }

    8、启动服务

    9、示例代码地址

    https://github.com/shuimutong/useDemo/tree/master/gmvc_demo

  • 相关阅读:
    Eclipse中支持js提示
    数据库命名规则
    JavaWeb 命名规则
    Ajax&json
    js中,var 修饰变量名和不修饰的区别
    javaScript知识点
    Bootstrap 栅格系统
    文本框如果不输入任何内容提交过后是一个空字符串还是null
    根据汇总数量依次扣减的SQL新语法
    asp.net中使用forms验证
  • 原文地址:https://www.cnblogs.com/shuimutong/p/11487124.html
Copyright © 2011-2022 走看看