手写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
手写MVC框架(二)-代码实现和使用示例的更多相关文章
- 手写MVC框架(一)-再出发
背景 前段时间把之前写的DAO框架(手写DAO框架(一)-从“1”开始)整理了一下,重构了一版.整理过程中看以前写的代码,只是为了了解实现,只是为了实现,代码写的有点粗糙.既然已经整理了DAO框架,索 ...
- 手写MQ框架(四)-使用netty改造梳理
一.背景 书接上文手写MQ框架(三)-客户端实现,前面通过web的形式实现了mq的服务端和客户端,现在计划使用netty来改造一下.前段时间学习了一下netty的使用(https://www.w3cs ...
- 手写MQ框架(二)-服务端实现
一.起航 书接上文->手写MQ框架(一)-准备启程 本着从无到有,从有到优的原则,所以计划先通过web实现功能,然后再优化改写为socket的形式. 1.关于技术选型 web框架使用了之前写的g ...
- 手写MQ框架(一)-准备启程
一.背景 很久以前写了DAO框架和MVC框架,前段时间又重写了DAO框架-GDAO(手写DAO框架(一)-从“1”开始,源码:https://github.com/shuimutong/gdao.gi ...
- 源码分析系列 | 从零开始写MVC框架
1. 前言 2. 为什么要自己手写框架 3. 简单MVC框架设计思路 4. 课程目标 5. 编码实战 5.1 配置阶段 web.xml配置 config.properties 自定义注解 5.2 初始 ...
- (二)springMvc原理和手写springMvc框架
我们从两个方面了解springmvc执行原理,首先我们去熟悉springmvc执行的过程,然后知道原理后通过手写springmvc去深入了解代码中执行过程. (一)SpringMVC流程图 (二)Sp ...
- 手写DAO框架(二)-开发前的最后准备
-------前篇:手写DAO框架(一)-从“1”开始 --------- 前言:前篇主要介绍了写此框架的动机,把主要功能点大致介绍了一下.此篇文章主要介绍开发前最后的一些准备.主要包括一些基础知识点 ...
- 手写RPC框架指北另送贴心注释代码一套
Angular8正式发布了,Java13再过几个月也要发布了,技术迭代这么快,框架的复杂度越来越大,但是原理是基本不变的.所以沉下心看清代码本质很重要,这次给大家带来的是手写RPC框架. 完整代码以及 ...
- 手写SpringMVC框架(二)-------结构开发设计
续接前文, 手写SpringMVC框架(一)项目搭建 本节我们来开始手写SpringMVC框架的第二阶段:结构开发设计. 新建一个空的springmvc.properties, 里面写我们要扫描的包名 ...
随机推荐
- NioEventLoopGroup源码分析与线程设定
我的以Netty Socket编程的代码为例, 1.EventLoopGroup 进入EventLoopGroup,这是一个特殊的EventExecutorGroup,在事件循环中,在selectio ...
- Set JAVA_HOME in windows cmd(在windows 命令行中修改JAVA_HOME)
set JAVA_HOME=jrepathset PATH=%JAVA_HOME%\bin;%PATH%注意这里没有引号.这样就不需要在我的电脑属性中修改java_home了,以及重启命令行了.对于程 ...
- 003 docker安装nginx
一:安装与运行nginx 1.查找镜像网站 https://c.163yun.com/hub#/m/home/ 2.pull 3.查看当前在运行的容器 docker ps 4.启动nginx 使用后台 ...
- MySQL悲观
//0.开始事务 begin;/begin work;/start transaction; (三者选一就可以) //1.查询出商品信息 for update; //2.根据商品信息生成订单 inse ...
- 文档工具的王者Sphinx
Sphinx https://www.sphinx.org.cn/ Sphinx是一个工具,可以轻松创建由Georg Brandl编写并根据BSD许可证授权的智能和美观文档 它最初是为Python文档 ...
- 【PHP】使用phpoffice/phpspreadsheet导入导出数据
当你在使用phpoffice/phpexcel 类库时候.composer 会给你提示一句话 Package phpoffice/phpexcel is abandoned, you should a ...
- CocosCreator TypeScript项目 (vscode 设置中文,默认调试环境设置)
版本:2.2.1 深圳好多公司用的cocoscreator,学习一下. 这篇是如何安装,然后运行一个hello world. 一 下载 cocoscreator:https://www.cocos. ...
- Python - Django - form 组件动态从数据库取 choices 数据
app01/models.py: from django.db import models class UserInfo(models.Model): username = models.CharFi ...
- 工控随笔_24_西门子TIA 博图硬件目录的更新
西门子博图软件,不但体积庞大,功能也很复杂,与经典的Step7相比,如果不是经常使用,一般都会有一种很难使用的感觉. 而且相比原来的Step7操作有点不太一样.这里简单的说一下硬件目录的更新. 有两种 ...
- FFmpeg 的bug
发现一个ffmpeg 的bug, 我用老版本的ffmpeg解码播视频,对同样的视频,音频部分得到的是6通道,一直有杂音 周末呢换了新版本的ffmpeg4.2的库,得到是4,6,8三个通道在切换,我修改 ...