前几章我们已经分析了 Spring 的源码并且手动实现了一个 IOC/DI 容器。

这章我们在自己实现的 Spring 框架的基础上实现一个 SpringMVC 框架。

我们自己实现的 Spring 框架的快速入口:Spring 框架 里面的 MySpring 是我们 IOC/DI 框架的源码。

下面我们在这个基础上实现一个 MVC 框架。首先 @Controller,@Service,@Repository 注解已经在我们自己的 Spring 框架里面定义了,可以实现将被这些注解标记的类交给 Spring 管理,并且实现了依赖注入。

先看整体架构

因为我们这个 MVC 框架要依赖 IOC/DI 容器,所以我们在 pom 文件里要将自己的 Spring 框架引入进来。

实现 MVC 的整体功能

首先我们定义两个 MVC 专用的注解,RequestMapping 用来做 url 匹配,RequestParam 做参数转换:

package com.paul.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
String value() default "";
}
package com.paul.annotation;

import java.lang.annotation.*;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
String value() default "";
}

我们知道 SpringMVC 的核心是 DispatcherServlet,用来做核心路由控制,我们也定义这样一个类,并且在初始化方法里初始化一个 IOC/DI 容器,看过前面文章的同学应该知道,初始化容器后我们已经将 Bean 放到容器中而且完成了依赖注入。

package com.paul.servlet;

import com.paul.annotation.RequestMapping;
import com.paul.annotation.RequestParam;
import org.springframework.ioc.annotation.Controller;
import org.springframework.ioc.bean.AnnotationApplicationContext;
import org.springframework.ioc.bean.ApplicationContext; import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; //实现 servlet,和我们以前使用 servlet 一样。
public class DispatcherServlet extends HttpServlet { // 完整路径和 方法的 mapping
private Map<String,Object> handleMapping = new ConcurrentHashMap<>(); // 类路径和controller 的 mapping
private Map<String,Object> controllerMapping = new ConcurrentHashMap<>(); private Map<String,Object> beanFactory = new ConcurrentHashMap<>(); @Override
public void init(ServletConfig config) throws ServletException {
super.init(config); //实例化 IOC 容器
ApplicationContext ctx = new AnnotationApplicationContext("applicationContext.xml");
this.beanFactory = ((AnnotationApplicationContext) ctx).beanFactory;
//上一步已经完成了 Controller,service,respostry,autowired 等注解的扫描和注入
//遍历容器,将 requestmapping 注解的路径和对应的方法以及 contoller 实例对应起来 for(Map.Entry<String,Object> entry:beanFactory.entrySet()){
Object instance = entry.getValue();
Class<?> clazz = instance.getClass();
if(clazz.isAnnotationPresent(Controller.class)){
RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
String classPath = requestMapping.value();
Method[] methods = clazz.getMethods();
for(Method method:methods){
if(method.isAnnotationPresent(RequestMapping.class)){
RequestMapping requestMapping2 = method.getAnnotation(RequestMapping.class);
String methodPath = requestMapping2.value();
String requestPath = classPath + methodPath;
handleMapping.put(requestPath,method);
controllerMapping.put(requestPath,instance);
}else{
continue;
} }
}else{
continue;
}
}
} @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
} //根据上一步获取到的 mapping,根据 url 找到对应的 controller 和方法去执行。
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uri = req.getRequestURI(); // /paul-mvc/com.paul.controller/method-com.paul.controller
String context = req.getContextPath(); // /paul-vmc
String path = uri.replace(context,""); // /com.paul.controller/method-com.paul.controller
Method m = (Method) handleMapping.get(path); //从容器里拿到controller 实例
Object instance = controllerMapping.get(path); Object[] args = handle(req,resp,m);
for (Object a:args){
System.out.println("Object:"+a);
} try {
m.invoke(instance,args);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} } private static Object[] handle(HttpServletRequest req, HttpServletResponse resp,Method method){
//拿到当前执行的方法有哪些参数
Class<?>[] paramClazzs = method.getParameterTypes();
//根据参数的个数,new 一个参数的数据
Object[] args = new Object[paramClazzs.length]; int args_i = 0;
int index = 0;
for(Class<?> paramClazz:paramClazzs){
if(ServletRequest.class.isAssignableFrom(paramClazz)){
args[args_i++] = req;
}
if(ServletResponse.class.isAssignableFrom(paramClazz)){
args[args_i++] = resp;
} //判断requestParam 注解
Annotation[] paramAns = method.getParameterAnnotations()[index];
if(paramAns.length > 0){
System.out.println("my");
for(Annotation paramAn:paramAns){
if(RequestParam.class.isAssignableFrom(paramAn.getClass())){
System.out.println("13mj");
RequestParam rp = (RequestParam) paramAn;
args[args_i++] = req.getParameter(rp.value());
}
}
}
index ++;
}
return args;
} }

测试代码

新建一个 WebDemo,Maven web 项目。

先来看我们需要测试的 Controller 和 Service。

@Controller
@RequestMapping("/user")
public class UserController { @Autowired
private UserService userService; @RequestMapping("/query")
public void get(HttpServletRequest request, HttpServletResponse response, @RequestParam("name") String name,@RequestParam("age") String age){
try {
PrintWriter pw = response.getWriter();
String res = userService.query(name,age);
pw.write(res);
} catch (IOException e) {
e.printStackTrace();
} }
}
import org.springframework.ioc.annotation.Service;

@Service("userService")
public class UserServiceImpl implements UserService {
@Override
public String query(String name, String age) {
return "name="+name+"age="+age;
}
}

在 resources 目录我们需要写一个名字为 applicationContext 的配置文件来指明扫描包路径。

<?xml version="1.0" encoding="UTF-8"?>
<beans>
<package-scan component-scan="com.paul.demo" />
</beans>

在 web.xml 中配置我们自己的 DispatcherServlet。

<!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>DispatcherServlet</servlet-name>
<servlet-class>com.paul.servlet.DispatcherServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet> <servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

在浏览器中测试结果:

结果和我们想的一样。

整个项目的源码在:源码,希望大家多 star 一起改进。

手动实现 SpringMVC的更多相关文章

  1. 手动搭建SpringMVC报错

    猜测这个是由于自己在搭建时缺少包造成的,后来将按照自己之前的项目将包补齐,tomcat就不报错了,看来还是要学习maven 这样就不会缺少包了

  2. 手码两万余字,SpringMVC 包教包会

    1. SpringMVC 简介 1.1 Spring Web MVC是什么 Spring Web MVC 是一种基于 Java 的实现了 Web MVC 设计模式的请求驱动类型的轻量级 Web 框架, ...

  3. SpringMVC框架——原理及使用

    核心组件 1. DispatcherServlet:前端控制器器,负责调度其他组件的执行,可降低不同组件之间的耦合性,是Spring MVC 的核心模块. 2. Handler:处理器,完成具体的业务 ...

  4. SSM手把手整合教程&测试事务

    自打来了博客园就一直在看帖,学到了很多知识,打算开始记录的学习到的知识点 今天我来写个整合SpringMVC4 spring4 mybatis3&测试spring事务的教程,如果有误之处,还请 ...

  5. ssm回顾笔记(一)

    这两天来到了农银,这边即将进行的一个项目是将ssh框架的电商项目迁移到springboot+ssm框架上,所以我基本上是三门技术在同时进行学习,当然以前学过ssm,现在只是回顾. spring 注解 ...

  6. SSM简单整合教程&测试事务

    自打来了博客园就一直在看帖,学到了很多知识,打算开始记录的学习到的知识点 今天我来写个整合SpringMVC4 spring4 mybatis3&测试spring事务的教程,如果有误之处,还请 ...

  7. Spring学习(二)——使用Gradle构建一个简单的Spring MVC Web应用程序

    1.新建一个Gradle工程(Project) 在新建工程窗口的左侧中选择 [Gradle],右侧保持默认选择,点击next,模块命名为VelocityDemo. 2.在该工程下新建一个 module ...

  8. Spring - RestTemplate执行原理分析

    什么是RestTemplate Synchronous client to perform HTTP requests, exposing a simple, template method API ...

  9. 4-2 Spring MVC框架-01

    Spring MVC框架-01 Ⅰ.接收客户端请求 1. 关于Spring MVC框架 Spring MVC是基于Spring框架基础之上的 作用: 接收请求,响应结果,处理异常 主要解决了后端服务器 ...

随机推荐

  1. 概率图模型(PGM) —— 贝叶斯网络(Bayesian Network)

    概率图模型是图论与概率方法的结合产物.Probabilistic graphical models are a joint probability distribution defined over ...

  2. WPF中的3D特性和常见的几个类

    原文:WPF中的3D特性和常见的几个类 WPF 3D 常用的几个类及其关系 1.  Visual 类      所有二维可视化元素的基类,为 WPF 中的呈现提供支持,其中包括命中测试.坐标转换和边界 ...

  3. WPF中Auto与*的区别

    原文:WPF中Auto与*的区别 Auto 表示自动适应显示内容的宽度, 如自动适应文本的宽度,文本有多长,控件就显示多长. * 则表示按比例来分配宽度. <ColumnDefinition W ...

  4. StreamDM:基于Spark Streaming、支持在线学习的流式分析算法引擎

    StreamDM:基于Spark Streaming.支持在线学习的流式分析算法引擎 streamDM:Data Mining for Spark Streaming,华为诺亚方舟实验室开源了业界第一 ...

  5. Robot Framework 快速入门_中文版

    目录 介绍 概述 安装 运行demo 介绍样例应用程序 测试用例 第一个测试用例 高级别测试用例 数据驱动测试用例 关键词keywords 内置关键词 库关键词 用户定义关键词 变量 定义变量 使用变 ...

  6. ASP.NET Core 配置 EF 框架服务 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core 配置 EF 框架服务 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 配置 EF 框架服务 上一章节中我们了解了 Entity ...

  7. WPF使用DynamicDataDisplay.dll显示CPU及内存使用曲线

    原文:WPF使用DynamicDataDisplay.dll显示CPU及内存使用曲线 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/wangshub ...

  8. 构建自己的PHP框架(MVC)

    完整项目地址:https://github.com/Evai/Aier 规划文件夹 新建 MFFC/app 文件夹,在 app 中创建 controllers.models.views 三个文件夹,开 ...

  9. malloc()与calloc差异

    Both the malloc() and the calloc() functions are used to allocate dynamic memory. Each operates slig ...

  10. 从零开始学习 asp.net core 2.1 web api 后端api基础框架(二)-创建项目

    原文:从零开始学习 asp.net core 2.1 web api 后端api基础框架(二)-创建项目 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.ne ...