手寫MVC框架(二)-代碼實現和使用示例

--------上一篇:手寫MVC框架(一)-再出發-----html

背景

書接上文,以前整理了實現MVC框架須要寫哪些東西。這周粗看了一下,感受也沒多少工做量,因此就計劃一天時間來完成。週末的時間,哪會那麼老實的坐在電腦前寫代碼呢?看電影的時候應該是老實的。爲了避免給本身留遺憾,因此今天就接着寫了,而後就寫完了。java

1、主要代碼結構

.
├── 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

2、主要流程

一、服務啓動,加載XDispatcherServletgit

二、XDispatcherServlet初始化,調用InstanceManager進行對象初始化、依賴注入github

三、調用ServletMapper掃描編寫的各個URIweb

四、請求到達XDispatcherServlet時,經過ServletMapper匹配到對應的方法apache

五、執行匹配到的方法json

3、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); } }

 

4、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);
    }
    
}

 

5、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);
        }
    }
}

 

6、源碼分享

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

依賴:https://gitee.com/simpleha/gutil.gitapp

7、使用示例

一、編譯打包gutil

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

二、編譯打包gmvc

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

三、新建webapp,引入pom

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

 

四、修改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>

 

五、編寫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();
    }

}

 

六、編寫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);
    }

}

 

七、編寫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());
    }
}

 

八、啓動服務

九、示例代碼地址

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

相關文章
相關標籤/搜索