首先要知道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. homework2软件方法论

    什么是软件工程方法论? 1.软件工程是一个方法论,就是我们在开始一个项目时,大体框架一定要有这么一个概念,而具体实施时,必须根据公司一些特点,优化项目开发的流程,这样才是有实效而方法论只是软件工程的结 ...

  2. ubuntu | virtualbox报错:不能为虚拟电脑打开一个新任务

    百度了几个办法 都不行. 还得是gxd,说在vmware虚拟机设置勾上这个就行了

  3. js 实现全屏预览(F11功能)--转

    参考文档   http://t.zoukankan.com/ghfjj-p-6322415.html HTML代码 <body> <div id="content" ...

  4. org.nutz.http.Http忽略https SSL证书验证

    访问的是一个https get请求,报错需要SSL证书验证,以下方法直接跳过 boolean check = Http.disableJvmHttpsCheck(); // 忽略https的证书检查

  5. SQL SERVER 内存优化表踩坑记

    背景 因为生产应用需要刷新大量的状态数据到数据库中用于客户端的显示,于是新建了一张状态表,表内行数固定,状态更新到列内.刚开始运行时还行,更新都很及时,查询速度也不慢.于是就这样使用了有一个月的时间. ...

  6. 【搭建】【转】PPTP

    https://blog.51cto.com/10802692/2177227?SOURCE=DRA

  7. FastDFS安装(ARM同样支持)

    一.服务器部署规划 服务器IP 部署服务 192.168.*. tracker.storage.nginx 二.数据存储目录 应用 目录 fastdfs /usr/bin nginx /usr/loc ...

  8. sed随笔

    sed [-hnV]  [-e<script>][-f<script文件>] [文本文件] 参数说明: -e<script>或--expression=<sc ...

  9. 前端基础复习之HTML

    1.web基础知识 1 1.Web基础知识 2 1.Internet 3 1.简介 4 Internet 实际上就是由计算机所组成的网络结构 5 6 服务: 7 1.Telnet 8 远程登录 9 2 ...

  10. C# async、await、Task 探讨

    test02.ProntOut(); ///*第五题*/ static class test02 { public static async void ProntOut() { Console.Wri ...