首先要知道springmvc主要流程:

当用户,也是就是请求送达过来的时候,

1.前端控制器会获取,

2.请求处理映射器,返回执行链接

3.获取执行适配器适配,交给执行器

4.返回modelandview给其前端控制器

5.前端控制器把信息给视图解析器进行渲染

6.渲染的视图传给客户

所以根据这个流程进行以下操作:

1.在tomcat启动时加载web.xml也就要对前端控制器

DispatcherServlet进行初始化,也就是更具加载的springMVC.xml进行初始化
2.定义相关的注解,完成相关位置标记比如ioc和controller等标记(
我建议模仿Spring做一个init方法完成初始化包括以下
  public void init(ServletConfig config) throws ServletException {
//1.加载配置文件 springmvc.properties
String contextConfigLocation = config.getInitParameter("contextConfigLocation");
doLoadConfig(contextConfigLocation);
//2.扫描相关类
System.out.println(properties.getProperty("scanPackge"));
doScan(properties.getProperty("scanPackge"));
//3.初始化bean对象(ioc)
doInstance();
//4.依赖注入(di)
doAutoWired();
//5构造一个Handler
ininHandlerMapping(); System.out.println("badfisher mvc 初始化 成功!!!");
}
 
我是几个注解放在一个框里,但是一个注解其实是一个类
/**
* author: Badfsiher(yhy)
* date 2020/6/17 21:12
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD})
@Documented
public @interface MyAutowired {
String value() default ""; } /**
* author: Badfsiher(yhy)
* date 2020/6/28 20:21
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE})
@Documented
public @interface MyController {
String value() default "";
} /**
* author: Badfsiher(yhy)
* date 2020/6/17 21:12
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE,ElementType.METHOD})
@Documented
public @interface MyRequestMapping {
String value() default "";
} /**
* author: Badfsiher(yhy)
* date 2020/6/17 20:51
*/ @Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE})
@Documented
public @interface MyService {
String value() default ""; }
对于注解的自定义可以看我spring内的随笔。
 
3.加载过程中我们同时需要实现spring的核心ioc和aop。
完成自定义ioc容器初始化以及类的注入。
ioc部分
    //文件获取
private void doLoadConfig(String contextConfigLocation) { InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try {
properties.load(resourceAsStream);
} catch (IOException e) {
e.printStackTrace();
} } // 扫描
// pathPackage 会是全类名 com.badfisher.demo这样的递归扫描-->替换磁盘路径 private void doScan(String pathPackage){
String scanPathPackage = Thread.currentThread().getContextClassLoader().getResource("").getPath()+ pathPackage.replaceAll("\\.","/");
File pack = new File(scanPathPackage);
System.out.println(scanPathPackage);
File[] files = pack.listFiles();
for (File file :files) {
if (file.isDirectory()){
doScan(pathPackage +"."+ file.getName());
}else if (file.getName().endsWith(".class")){
String className = pathPackage +"."+file.getName().replaceAll(".class","");
classNames.add(className);
} } } //反射完成对对象创建
private void doInstance(){
if(classNames.size() == 0) return;
try{
for (int i =0 ; i < classNames.size();i++){
String className = classNames.get(i); //反射
Class<?> myClazz = Class.forName(className);
//区分注解
if(myClazz.isAnnotationPresent(MyController.class)){
//MyController获取类名并直接那类名小写
String simpleName = lowerFirst(myClazz.getSimpleName());
Object o = myClazz.getDeclaredConstructor().newInstance();
iocMap.put(simpleName,o); }else if (myClazz.isAnnotationPresent(MyService.class)){
MyService myService = myClazz.getAnnotation(MyService.class);
//MyService 获取注解的值
String beanName = myService.value();
Object o = myClazz.getDeclaredConstructor().newInstance();
if (!"".equals(beanName.trim())){
iocMap.put(beanName,o); }else {
String simpleName = lowerFirst(myClazz.getSimpleName());
iocMap.put(simpleName,o);
}
//service 往往存在接口,再以接口名称放入一份id到iocMap
Class<?>[] interfaces = myClazz.getInterfaces();
for(int j = 0;j < interfaces.length; j++){
Class<?> ainterface = interfaces[j];
//以接口名称放入
iocMap.put(ainterface.getName(),o); }
}else{
continue;
} }
}catch (Exception e){
System.out.println("注解类加载失败!!!");
e.printStackTrace(); } }

接下来是对注解解析实现我们要的注解功能

   private void doAutoWired() {
if (iocMap.isEmpty()) return; for (Map.Entry<String,Object> entry:iocMap.entrySet()) {
//注入对象
Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();
//便利并判断
for (int i = 0; i < declaredFields.length; i++) {
Field declaredField = declaredFields[i];
//不存在跳出
if(!declaredField.isAnnotationPresent(MyAutowired.class)){
continue;
} MyAutowired autowired = declaredField.getAnnotation(MyAutowired.class);
String beanName = autowired.value();
if ("".equals(beanName.trim())){
beanName = declaredField.getType().getName();
}
//赋值
declaredField.setAccessible(true);
try {
declaredField.set(entry.getValue(),iocMap.get(beanName));
} catch (IllegalAccessException e) {
System.out.println("MyAutowired 注入失败!!!");
e.printStackTrace();
} } } }
4.初始化springMVC的组件,建立处理url和method的联系
也即对HandlerMapping的初始化。
//最重要的环节,完成url和methrd建立联系
private void ininHandlerMapping() {
if (iocMap.isEmpty()) return;
for (Map.Entry<String,Object> entry:iocMap.entrySet()) {
//获取ioc当前便利对象的class类型
Class<?> myClass = entry.getValue().getClass(); if(!myClass.isAnnotationPresent(MyController.class)){
continue;
}
String baseUrl = "";
if (myClass.isAnnotationPresent(MyRequestMapping.class)){
MyRequestMapping myRequestMapping = myClass.getAnnotation(MyRequestMapping.class);
baseUrl = myRequestMapping.value();//"/demo" }
//获取方法
Method[] methods = myClass.getMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if (!method.isAnnotationPresent((MyRequestMapping.class))){
continue;
}
MyRequestMapping myRequestMapping = method.getAnnotation(MyRequestMapping.class);
String methodUrl = myRequestMapping.value(); String URL = baseUrl + methodUrl;
//建立联系
//封装自定的hadler
Handler handler = new Handler(entry.getValue(),method,Pattern.compile(URL));
// handlerMap.put(URL,method);
//计算参数位置
Parameter[] parameters = method.getParameters();
for (int j = 0; j < parameters.length; j++) {
Parameter parameter = parameters[j]; if (parameter.getType() == HttpServletResponse.class || parameter.getType() == HttpServletRequest.class){
// //HttpServletResponse HttpServletRequest 获取类名
handler.getParaIndexMapping().put(parameter.getType().getSimpleName(),j);
}else{
handler.getParaIndexMapping().put(parameter.getName(),j);
} }
handlerMap.add(handler); } } }

完成绑定之后实现dopost逻辑

   protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//需求处理根据url找到对应的method方法进行调取
//获取url
// String requestURI = req.getRequestURI();
// Method method = handlerMap.get(requestURI);
//反射调用需要传入对象,需要传入参数,此处无法调用,没有缓存对象
// method.invoke(); Handler handler = getHandler(req);
if (handler == null){
resp.getWriter().write("404 not found !");
return;
}
//参数绑定
//获取所有参数
Class<?>[] parameterTypes =handler.getMethod().getParameterTypes();
//参数数组反射调用
Object[] paraValues = new Object[parameterTypes.length];
//参数数组放值
Map<String,String[]> parameterMap = req.getParameterMap();
//遍历request中的参数
for (Map.Entry<String,String[]> param:parameterMap.entrySet()) {
//name=1&name=2 name[1.2]
String value = StringUtils.join(param.getValue(),","); //1,2
//如果匹配成功,填充
if (!handler.getParaIndexMapping().containsKey(param.getKey())){continue;}
Integer index = handler.getParaIndexMapping().get(param.getKey());
paraValues[index] = value; } //放入 indexHttpServletRequestr indexHttpServletResponse
int indexHttpServletRequestr = handler.getParaIndexMapping().get(HttpServletRequest.class.getSimpleName()); //0
paraValues[indexHttpServletRequestr] = req;
int indexHttpServletResponse = handler.getParaIndexMapping().get(HttpServletResponse.class.getSimpleName());//1
paraValues[indexHttpServletResponse] = resp; // 最后的handler事物·method属性
try {
handler.getMethod().invoke(handler.getController(),paraValues);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} } private Handler getHandler(HttpServletRequest req) {
if (handlerMap.isEmpty()){ return null; }
String requestURI = req.getRequestURI();
for (Handler handler : handlerMap) {
Matcher matcher = handler.getPattern().matcher(requestURI);
if (!matcher.matches()){continue;}
return handler; }
return null;
}

我的handler定义

package com.badfisher.mvcFramework.pojo;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern; /**
* author: Badfsiher(yhy)
* date 2020/6/29 23:17
* 封装handler相关信息
*/
public class Handler {
private Object controller;//method.invoke private Method method; private Pattern pattern;//url 支持正则 private Map<String,Integer> paraIndexMapping;//参数顺序 参数绑定 public Handler(Object controller, Method method, Pattern pattern) {
this.controller = controller;
this.method = method;
this.pattern = pattern;
this.paraIndexMapping = new HashMap<>();
} public Object getController() {
return controller;
} public void setController(Object controller) {
this.controller = controller;
} public Method getMethod() {
return method;
} public void setMethod(Method method) {
this.method = method;
} public Pattern getPattern() {
return pattern;
} public void setPattern(Pattern pattern) {
this.pattern = pattern;
} public Map<String, Integer> getParaIndexMapping() {
return paraIndexMapping;
} public void setParaIndexMapping(Map<String, Integer> paraIndexMapping) {
this.paraIndexMapping = paraIndexMapping;
}
}

如此就完成我们整体的简易SpringMVC的框架

照例我也会给上我的项目git地址  https://github.com/yhybadfisher/badfisherOwer     badfisherMVC.rar

自定义SpringMVC实现的更多相关文章

  1. Spring Boot2 系列教程(十八)Spring Boot 中自定义 SpringMVC 配置

    用过 Spring Boot 的小伙伴都知道,我们只需要在项目中引入 spring-boot-starter-web 依赖,SpringMVC 的一整套东西就会自动给我们配置好,但是,真实的项目环境比 ...

  2. 详解Springboot中自定义SpringMVC配置

    详解Springboot中自定义SpringMVC配置 WebMvcConfigurer接口 ​ 这个接口可以自定义拦截器,例如跨域设置.类型转化器等等.可以说此接口为开发者提前想到了很多拦截层面的需 ...

  3. Spring Boot 中自定义 SpringMVC 配置,到底继承谁哪一个类或则接口?

    看了这篇文章,写的非常的言简意赅,特此记录下: 1.Spring Boot 1.x 中,自定义 SpringMVC 配置可以通过继承 WebMvcConfigurerAdapter 来实现. 2.Sp ...

  4. 一步一步自定义SpringMVC参数解析器

    随心所欲,自定义参数解析器绑定数据. 题图:from Zoommy 干货 SpringMVC解析器用于解析request请求参数并绑定数据到Controller的入参上. 自定义一个参数解析器需要实现 ...

  5. 自定义springmvc统一异常处理器(实现HandlerExceptionResolver接口)不起作用的一种情况

    ExceptionResolverCustom 这个是自定义的异常处理器类. 在springmvc中注册 在web.xml文件中屏蔽springmvc自动注册的异常处理器 网上的资料就是这么配置的,可 ...

  6. 自定义springmvc参数解析器

    实现spring HandlerMethodArgumentResolver接口 通过使用@JsonArg自定义注解来解析json数据(通过fastjson的jsonPath),支持多个参数(@Req ...

  7. 深入学习SpringMVC以及学习总结

    一.优点: 1.SpringMVC简化web程序开发; 2.SpringMVC效率很好(单例模式): 3.SpringMVC提供了大量扩展点,方便程序员自定义功能: ①.DispatcherServl ...

  8. SpringBoot扩展SpringMVC自动配置

    SpringBoot中自动配置了 ViewResolver(视图解析器) ContentNegotiatingViewResolver(组合所有的视图解析器) 自动配置了静态资源文件夹.静态首页.fa ...

  9. springMVC介绍

    http://www.iteye.com/blogs/subjects/springMVC —————————————————————————————————————————————————————— ...

  10. 《四 spring源码》手写springmvc

    手写SpringMVC思路 1.web.xml加载  为了读取web.xml中的配置,我们用到ServletConfig这个类,它代表当前Servlet在web.xml中的配置信息.通过web.xml ...

随机推荐

  1. Java 中的内存分配

    Java 中的内存分配 Java 程序运行时,需要在内存中分配空间.为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式. 一.栈:储存局部变量 局部变量 ...

  2. 4组-Beta冲刺-4/5

    一.基本情况 队名:摸鲨鱼小队 组长博客:https://www.cnblogs.com/smallgrape/p/15604878.html github链接:https://github.com/ ...

  3. torch直接更改参数

    使用model.layer1.weight.data.copy_(w1) 其中model是自定义的参数名字,layer1是某个具体的层,使用某个具体的w1来修改

  4. python+基本3D显示

    想要将双目照片合成立体图实现三维重建,完全没有头绪.但是对三维理解是必须的.所以将以前在单片机上运行的 3D画图 程序移植到python上.效果如下: 没有用numpy.openGL等,只用了纯mat ...

  5. 11、jmeter配置元件--计数器

    前面学过csv和变量 csv里面的数据是固定的  如果里面的数据不够  线程要么就停止要么就需要重头再来 不太灵活 用到固定化的数据,比如说多少个用户等等 如果有一些是变动的 随着线程数增加   数据 ...

  6. Java简单认识及环境下载

    Java的特性和优势 简单性 面向对象 可移植性 跨平台性 write once run anywhere 高性能 分布式 动态性 反射 多线程 安全性 健壮性 Java三大版本 JavaSE:标准版 ...

  7. 深入理解css 笔记(6)

    网格布局:flexbox 彻底改变了网页布局方式,但这只是开始.它还有一个大哥:另一个称作网格布局模块的新规范.这两个规范提供了一种前所未有的全功能布局引擎.跟 flexbox 类似,网格布局也是作用 ...

  8. 单向链表&有关类和对象

    // Test515.cpp: 定义控制台应用程序的入口点.// #include "stdafx.h"#include <iostream>using namespa ...

  9. node+appium安装

      node是什么: node全称Node.js,是一个基于Chrome V8引擎的JavaScript运行环境,一个让JavaScript 运行在服务端的开发平台:它让JavaScript成为与PH ...

  10. Linux完全卸载mysql的方式

    //rpm包安装方式卸载查包名:rpm -qa|grep -i mysql删除命令:rpm -e –nodeps 包名 //yum安装方式下载1.查看已安装的mysql命令:rpm -qa | gre ...