人生需要总结

手写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:\n");
        for(String uri : URI_MAP.keySet()) {
            logSb.append(uri).append("\n");
        }
        logSb.append("\n").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

 

posted @ 2019-09-08 16:32  水木桶  阅读(1329)  评论(1编辑  收藏  举报