搭建一个简单的Java MVC框架

  • 一 . 前言
  • 二. 代码实现
    • 1. 思路分析
    • 2. 代码实现
      • 2.1 Controller注解
      • 2.2 RequestMapping注解
      • 2.3 UserController
      • 2.4 MethodBean
      • 2.5 DispatcherServlet
      • 2.6 ClassScannerUtils
      • 2.7 web.xml文件配置
      • 2.7 pom.xml文件配置
    • 三. 测试
    • 1. 部署tomcat
    • 2. 访问测试
  • 四. 总结

一 . 前言

自定义一个简单的web MVC 框架,实现一个Controller控制处理多个请求。
在IDEA中创建Maven web项目,最终项目层级结构如下:

二. 代码实现

1. 思路分析

  1. 获得请求的URI和项目部署路径, 截取获得映射路径 eg: /user/login
  2. 扫描某个包里面的所有类的字节码对象集合List
  3. 遍历字节码对象集合List
  4. 获得类里面的所有的Method
  5. 遍历所有的Method
  6. 获得method上面的注解的value属性值
  7. 判断value属性值是否和获得映路径一致, 一致就调用method

2. 代码实现

2.1 Controller注解

这个注解打在类身上,主要是为了解决我们在总的控制器类里面解析太多类的问题以后只要解析哪个类身上打上这个注解,我们就解析哪个类。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {}

2.2 RequestMapping注解

此注解注解方法,表示映射路径

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {String value();
}

2.3 UserController

所有有关用户操作的请求,都交给这个controller来完成

  • 类上打注解 @Controller
  • 方法上打注解 @RequestMappting
@Controller
public class UserController {@RequestMapping("/user/register")public void register(HttpServletRequest req, HttpServletResponse resp){System.out.println("执行了UserController的register方法~!");}@RequestMapping("/user/login")public void login(HttpServletRequest req , HttpServletResponse resp){System.out.println("执行了UserController的login方法~!");}
}

2.4 MethodBean

用来封装 被调用的方法和 调用这个方法用到的实例对象,这里用Lombok注解生成构造方法。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class MethodBean {private Method method;//具体的方法对象private Object obj;//调用这个方法要用的实例对象
}

2.5 DispatcherServlet

  1. 它是一个servlet,要抓住指定的请求
  2. 重写两个方法 init方法和service
  3. 注册DispatcherServlet,使用xml方式来注册
public class DispatcherServlet extends HttpServlet {//在类的成员变量中定义map集合Map<String , MethodBean> map = new HashMap<>();@Overridepublic void init(ServletConfig config) throws ServletException {try {//1. 读取初始化参数String packageName = config.getInitParameter("packageName");//2. 扫描这个包List<Class<?>> classList = ClassScannerUtils.getClasssFromPackage(packageName);//3. 遍历集合中的每一个类for (Class<?> clazz : classList) {//4. 判定这个类身上有没有 @Controller注解boolean flag = clazz.isAnnotationPresent(Controller.class);if (flag) {//5. 得到这个类里面所有方法Method[] methods = clazz.getMethods();//6. 遍历每一个方法for (Method method : methods) {//7. 判断方法上是否有注解 @RequestMapping , 要注意有的方法身上没有这个注解,那么返回的是nullRequestMapping annotation = method.getAnnotation(RequestMapping.class);if(annotation != null){//获取注解身上的value值。String mapping = annotation.value();//8. 使用map集合来装映射管理  key : mapping  ,value : method &  clazz.newInstance();MethodBean methodBean = new MethodBean(method, clazz.newInstance());map.put(mapping,methodBean);}}}}} catch (Exception e) {e.printStackTrace();}}@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {try {String uri = req.getRequestURI();String path  = uri.substring(req.getContextPath().length() , uri.lastIndexOf('.'));//2. 拿着地址去map集合里面找匹配的集合MethodBean bean = map.get(path);//3. 要记得判定map返回的结果,因为这个映射地址在map集合里面并不一定有方法与之对应if(bean != null){//如果进入这个if分支,即表示找到匹配的记录,找到之后,就让方法执行即可Method method = bean.getMethod();Object object = bean.getObj();method.invoke(object ,req , resp );}else{System.out.println(path + " ,没有找到匹配的方法可以执行!");}} catch (Exception e) {e.printStackTrace();}}
}

2.6 ClassScannerUtils

这是一个工具类,用来获取包下的所有class的实例。

public static List<Class<?>> getClasssFromPackage(String packageName) {List clazzs = new ArrayList<>();// 是否循环搜索子包boolean recursive = true;// 包名对应的路径名称String packageDirName = packageName.replace('.', '/');Enumeration<URL> dirs;try {dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);while (dirs.hasMoreElements()) {URL url = dirs.nextElement();String protocol = url.getProtocol();if ("file".equals(protocol)) {String filePath = URLDecoder.decode(url.getFile(), "UTF-8");findClassInPackageByFile(packageName, filePath, recursive, clazzs);}}} catch (Exception e) {e.printStackTrace();}return clazzs;}/*** 在package对应的路径下找到所有的class*/public static void findClassInPackageByFile(String packageName, String filePath, final boolean recursive,List<Class<?>> clazzs) {File dir = new File(filePath);if (!dir.exists() || !dir.isDirectory()) {return;}// 在给定的目录下找到所有的文件,并且进行条件过滤File[] dirFiles = dir.listFiles(new FileFilter() {public boolean accept(File file) {boolean acceptDir = recursive && file.isDirectory();// 接受dir目录boolean acceptClass = file.getName().endsWith("class");// 接受class文件return acceptDir || acceptClass;}});for (File file : dirFiles) {if (file.isDirectory()) {findClassInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, clazzs);} else {String className = file.getName().substring(0, file.getName().length() - 6);try {clazzs.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + "." + className));} catch (Exception e) {e.printStackTrace();}}}}
}

2.7 web.xml文件配置

配置DispatcherServlet,这配置抓取 *.do 的请求,可任意配置。

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/javaee"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"version="2.5"><!--配置DispatcherServlet--><servlet><servlet-name>dispatcher</servlet-name><servlet-class>com.primo.servlet.DispatcherServlet</servlet-class><!--这里还需要额外的配置--><!--1. 要告诉这个DispatcherServlet扫描哪个包?--><init-param><param-name>packageName</param-name><param-value>com.primo.Controller</param-value></init-param><!--2. 让servlet初始化的时机提前--><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>dispatcher</servlet-name><url-pattern>*.do</url-pattern></servlet-mapping></web-app>

2.7 pom.xml文件配置

配置项目依赖jar包

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.primo</groupId><artifactId>myMVC</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!--Servlet jar包--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.18</version></dependency><!--junit--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency></dependencies></project>

三. 测试

1. 部署tomcat

2. 访问测试

  • 启动tomcat,在浏览器中访问
  • 控制台中执行了UserController的register方法,访问成功

四. 总结

此案例实现了一个简单的MVC框架,对学习理解Java中最常用的SpringMVC框架有一定的帮助。

在Java中搭建一个简单的MVC框架相关推荐

  1. 搭建一个简单的MVC框架

    背景 为何要用MVC框架?首先我们知道不用框架的话,在javaweb项目中每个请求都要写一个servlet,并且要在web.xml中对每个servlet类的映射作配置,不方便开发,因此引入MVC框架. ...

  2. 搭建一个简单springboot后端框架

    前言 框架知识是每个程序员都应该或多或少都要有所了解,作为后端开发更是以后进阶架构师必备的知识储备:以此为出发点,我们可以从搭建一个简单的后端框架开始,了解相关的技术点和搭建思路. 我们可以从创建项目 ...

  3. 快速搭建一个简单的SSM框架

    1.准备工作 mysql数据库 idea工具 说明:本次是搭建一个SSM框架,首先要确保电脑配置好1.8以上的JDK(因为1.8以下的jdk某些东西不支持),mysql数据库直接使用不方便可以安装一个 ...

  4. 自己动手写一个简单的MVC框架(第一版)

    一.MVC概念回顾 路由(Route).控制器(Controller).行为(Action).模型(Model).视图(View) 用一句简单地话来描述以上关键点: 路由(Route)就相当于一个公司 ...

  5. 自己动手写一个简单的MVC框架(第二版)

    一.ASP.NET MVC核心机制回顾 在ASP.NET MVC中,最核心的当属"路由系统",而路由系统的核心则源于一个强大的System.Web.Routing.dll组件. 在 ...

  6. mysql可以使用mybaties框架吗_搭建一个简单的mybatis框架

    一.Mybatis介绍 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBatis可以 ...

  7. java 线图_在Java中绘制一个简单的线图

    您的代码和建议的问题: >再次,您需要更改组件的preferredSize(这里是Graph JPanel),而不是大小 >不要设置JFrame的边界. >在添加组件之后并在调用se ...

  8. Java乐谱_如何在java中创建一个简单但结构良好的乐谱表(乐谱)?

    我正在使用非常基本的声音合成在我的游戏中创建音频和效果.基本上,我有一些方法可以发出一个频率和频率的声音.幅度和幅度持续时间. 对于短语和旋律,我想提出一个基本的符号,这样我就可以轻松地重写或添加新的 ...

  9. php实现简单的框架,PHP 实现简单的 MVC 框架

    前言: 在 PHP 的世界中,有着众多的框架,它们各有所长,各具特色.既有 Zend,Symfony,Laravel 等大型框架,也有 CodeIgniter,Slim Framework 等轻量级的 ...

最新文章

  1. iOS开发中使用[[UIApplication sharedApplication] openURL:]加载其它应用
  2. Redis中持久化的两种方法详解
  3. PHP之父评价Facebook的HipHop项目:别当作银弹
  4. 如何从心理上缓解对浑浊物的恐惧?
  5. 经典面试题(30):以下代码将输出的结果是什么?
  6. C++11 并发指南九(综合运用: C++11 多线程下生产者消费者模型详解)
  7. order by 子查询_SQL查询语法
  8. 解决linux vi报错 Can‘t open file for writing
  9. python对于设计师有什么用-好的IT产品设计师要做到哪些事
  10. SM2算法第十八篇:SM2毕设论文
  11. 通过工具XShell4生成密钥对(公钥和私钥)
  12. 联想m100耗材灯亮_联想m100加粉清零方法
  13. php mail cc,邮件cc是什么意思
  14. DVD-R、DVD+R以及DVD-RW和DVD+RW的功能区别
  15. 黑客来势汹汹,受害者能以牙还牙“黑回去”吗
  16. bzoj4238: 电压
  17. 【mpeg1】mpeg1相关资料
  18. 京冀101家饭店跻身北京2022年冬奥会官方接待签约名单
  19. Spring Boot 使用 Swagger3 生成 API 接口文档
  20. python怎么生成日志_python 生成模拟日志

热门文章

  1. POOLED指的是什么
  2. Python,写校歌
  3. uniapp使用uni.createInnerAudioContext()实现在app 小程序 h5有声书的倍速功能
  4. 申请邓白氏(D-U-N-S)编码
  5. Python爬虫案例3:爬取房天下房价等各种信息
  6. Oracle如何将GMT时间(即格林尼标准时间)转换成标准时间格式
  7. 30岁后的终极使命,是接受自己只是一个普通人
  8. 5分钟利用ChatGPT4+MindShow制作一个演讲稿PPT
  9. 风云网络工作室-专业的wow代练工作室http://ahhujing.go.nease.net
  10. img图像标签-HTML入门基础(017)