前几章我们已经分析了 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. 给WPF示例图形加上方便查看大小的格子之完善版本

    原文:给WPF示例图形加上方便查看大小的格子之完善版本 去年10月份, 我曾写过一篇"给WPF示例图形加上方便查看大小的格子"的BLOG(http://blog.csdn.net/ ...

  2. UML静态视图——类图、对象图、包图

    绘画类的最重要的图是抽象类.让我们回顾一下类的基本内容. 一.分类 1.类的概念: 面向对象编程的类是一个基本概念.类是具有相同特性的.办法.集合语义和一组对象的关系. 2.类分类: 实体类:保存要放 ...

  3. HDU 4357 String change 法冠军

    意甲冠军: 鉴于a串b串,问我们能否a变b串 办法:自选a的2快报,ascil+=1 然后交换位置,能够操作自如倍. 3个月3以上就能T^T 2法官将着眼于暴力 #include <cstdio ...

  4. wpf XMAL中隐藏控件

    原文:wpf XMAL中隐藏控件 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/a771948524/article/details/9264569 ...

  5. Android正在使用Handler实现消息分发机制(零)

    演讲前,AsyncTask文章.我们在最后谈到.AsyncTask它是利用Handler异步消息处理机制,操作结果.使用Message回到主线程,从而执行UI更新线程. 而在我们的日常开发工作,Han ...

  6. 最通俗易懂的方式让你理解 Swift 的函数式编程

    函数式编程(Functional Programming)是相对于我们常用的面向对象和面向过程编程的另外一种开发思维方式,它更加强调以函数为中心.善用函数式编程思路,可以对我们的开发工作有很大的帮助和 ...

  7. 合并 && 还原属性链

    效果 原数据 { "id": 10, "text": { "title": "title", "content ...

  8. 工具:sql server profiler(分析器)

    打开profiler新建->连接数据库进行监测 任何访问该数据库的都有记录   image 对于linq的检验:sql实际如何->运行程序,查看分析器记录   image         ...

  9. spring boot的默认配置

    # BANNER banner.charset=UTF- # Banner file encoding. banner.location=classpath:banner.txt # Banner f ...

  10. WPF 流打印

    原文:WPF 流打印 PrintDialog printDialog = new PrintDialog(); if (printDialog.ShowDialog() == true) { Syst ...