springmvc是spring的一部分,也是一个优秀的mvc框架,其执行原理如下:

  (1)浏览器提交请求经web容器(比如tomcat)转发到中央调度器dispatcherServlet。

  (2)中央调度器调用处理器映射器handerMapping,处理器映射器根据请求的url找到处理该请求对应的处理器hander及相关的拦截器intercepter,将它们封装成一个处理器执行链并返回给中央调度器

  (3)中央调度器根据处理器执行链中的处理器找到对应的处理器适配器handerAdaptor

  (4)处理适配器调用处理器执行对应的方法并将返回的结果封装为一个对象modelAndView中返回给中央处理器,当然在处理器执行方法前如果方法有拦截器的话会先依次执行拦截器的prehander方法,方法执行结束后会依次执行拦截器的posthander方法。

  (5)中央调度器获取到modelAndView对象后,调用视图解析器viewResolver,将modelAndView封装为视图对象

  (6)中央调度器获取到视图对象后,进行渲染,生成最后的响应返回给浏览器。

  

  下面就参照springmvc的原理,写一个我们自己的mvc框架,springmvc是依赖于servlet容器的,所以我们也要依赖javax.servlet-api-3.1.0.jar这个包,另外需要在web.xml中配置我们自己定义的中央处理器,以便web容器在启动时初始化mvc上下文。web.xml配置如下

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">
<servlet>
<servlet-name>MySpringMvc</servlet-name>
<servlet-class>io.powerx.servlet.MyDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MySpringMvc</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

  简单期间,我们就不去解析xml,使用springmvc.properties来代替,springmvc.properties的配置很简单,就是配置了一下扫描类的路径:basePackage=io.powerx。MyDispatcherServlet代码如下,具体代码里我都有注释

package io.powerx.servlet;

import io.powerx.annotation.*;
import io.powerx.util.ParseUtil; 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.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; public class MyDispatcherServlet extends HttpServlet{ private static final long serialVersionUID = -2661065041652722638L; //存自动扫描包下的所有类全名
private List<String> classNames = new ArrayList<>(); //存controller和 service 实例,key:实例名 valu:实例对象
private Map<String,Object> instanceMap = new HashMap<>(); //存 url 和对应的method
private Map<String,Method> handerMaps = new HashMap<>(); //存controller 名字和对象
private Map<String,Object> controllerMap = new HashMap<>(); @Override
public void init(ServletConfig config) throws ServletException { try {
//1、读取配置文件,获取扫描包信息
String mvcConfig = config.getInitParameter("contextConfigLocation").replace("classpath:","");
String packageName = ParseUtil.getBasePackageName(mvcConfig); //2、自动扫描包下所有类,获取所有的类全名
scanBasePackage(packageName); //3、利用反射机制,获取实例,存储在instanceMapzhing
reflectBeanInstance(); //4、依赖注入
doIoc(); //5、初始化handerMapping,建立url和method的映射关系
initHanderMapping();
} catch (Exception e) {
e.printStackTrace();
}
} private void initHanderMapping() throws Exception{ if(instanceMap.isEmpty()){
throw new Exception("实例对象为空");
}
for (Map.Entry<String,Object> entry : instanceMap.entrySet()) {
Class<?> aclazz = entry.getValue().getClass();
if(aclazz.isAnnotationPresent(MyController.class)){
String classUrl ="";
if(aclazz.isAnnotationPresent(MyRequestMapping.class)){
classUrl = aclazz.getAnnotation(MyRequestMapping.class).value();
}
Method[] methods = aclazz.getMethods();
for (Method method : methods) {
if(method.isAnnotationPresent(MyRequestMapping.class)){
String methodUrl = method.getAnnotation(MyRequestMapping.class).value();
String url = classUrl + methodUrl;
handerMaps.put(url,method);
controllerMap.put(url,entry.getValue());
}
}
}
} } private void doIoc() throws Exception{ if(instanceMap.isEmpty()) {
throw new Exception("无可注入的实例");
}
for (Map.Entry<String, Object> entry : instanceMap.entrySet()) {
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for (Field field : fields) {
if(field.isAnnotationPresent(MyAotuwired.class)){
String insKey = field.getAnnotation(MyAotuwired.class).value();
if("".equals(insKey)){
insKey = ParseUtil.toLowerFirstName(field.getType().getSimpleName());
}
field.setAccessible(true);
//注入实例
field.set(entry.getValue(),instanceMap.get(insKey));
}
}
} } private void reflectBeanInstance() throws Exception{ if(classNames.isEmpty()){
return;
}
for(String className: classNames){
Class<?> aclazz = Class.forName(className);
if(aclazz.isAnnotationPresent(MyController.class)){
MyController myController = aclazz.getAnnotation(MyController.class);
String beanName = myController.value();
if("".equals(beanName)){
beanName = ParseUtil.toLowerFirstName(aclazz.getSimpleName());
}
instanceMap.put(beanName,aclazz.newInstance()); }else if(aclazz.isAnnotationPresent(MyService.class)){
MyService myService = aclazz.getAnnotation(MyService.class);
String beanName = myService.value();
if("".equals(beanName)){
beanName = ParseUtil.toLowerFirstName(aclazz.getSimpleName());
}
instanceMap.put(beanName,aclazz.newInstance());
} } } private void scanBasePackage(String basePackName) throws Exception{
System.out.println(basePackName); String path = "/" + ParseUtil.tranferQualifiedToPath(basePackName);
System.out.println(path);
URL url = this.getClass().getClassLoader().getResource(path);
System.out.println(url);
File dir = new File(url.getFile());
File[] files = dir.listFiles();
for (File file: files) {
if(file.isDirectory()){
scanBasePackage(basePackName +"."+file.getName());
}else if(file.isFile()){
classNames.add(basePackName +"." + file.getName().replace(".class",""));
System.out.println("扫描到的类有" + basePackName +"." + file.getName().replace(".class",""));
}
}
} @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
doDispatch(req,resp);
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 处理请求
* @param req
* @param resp
*/
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception{ resp.setContentType("text/html;charset=UTF-8");
String uri = req.getRequestURI();
System.out.println(uri);
String contextPath = req.getContextPath();
System.out.println(contextPath);
uri = uri.replace(contextPath,"");
Method method = handerMaps.get(uri);
PrintWriter pw = resp.getWriter();
if(method == null){
pw.print("404,请求路径不存在");
return;
}
Parameter[] parameters = method.getParameters();
Object[] paramValues = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
if(ServletRequest.class.isAssignableFrom(parameters[i].getType())){
paramValues[i] = req;
}else if (ServletResponse.class.isAssignableFrom(parameters[i].getType())){
paramValues[i] = resp;
}else{
String bindingParam = parameters[i].getName();
if(parameters[i].isAnnotationPresent(MyRequestParam.class)){
bindingParam = parameters[i].getAnnotation(MyRequestParam.class).value();
}
String paramValue = req.getParameter(bindingParam);
paramValues[i] = paramValue;
if(Integer.class.isAssignableFrom(parameters[i].getType())){
paramValues[i] = Integer.parseInt(paramValue);
}else if(Float.class.isAssignableFrom(parameters[i].getType())){
paramValues[i] = Float.parseFloat(paramValue);
}else if(Double.class.isAssignableFrom(parameters[i].getType())){
paramValues[i] = Double.parseDouble(paramValue);
}
} }
Object result = method.invoke(controllerMap.get(uri),paramValues);
pw.print(result);
}
}

  MyDispatcherServlet中依赖了我们自定义的注解类,和ParseUtil工具类,代码如下

package io.powerx.annotation;

import java.lang.annotation.*;

/**
* Created by Administrator on 2019/1/2.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface MyAotuwired { String value() default "";
}
package io.powerx.annotation;

import java.lang.annotation.*;

/**
* Created by Administrator on 2019/1/2.
*/
@Target(ElementType.TYPE )
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyController { String value() default "";
}
package io.powerx.annotation;

import java.lang.annotation.*;

/**
* Created by Administrator on 2019/1/2.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@Documented
public @interface MyRequestMapping { String value() default "";
}
package io.powerx.annotation;

import java.lang.annotation.*;

/**
* Created by Administrator on 2019/1/2.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@Documented
public @interface MyRequestParam { String value() default "";
}
package io.powerx.annotation;

import java.lang.annotation.*;

/**
* Created by Administrator on 2019/1/2.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface MyService { String value() default "";
}
package io.powerx.util;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties; /**
* Created by Administrator on 2019/1/2.
*/
public class ParseUtil { /**
* 获取配置文件的扫描包
* @param contextConfigLocation
* @return
*/
public static String getBasePackageName(String contextConfigLocation){
InputStream in = ParseUtil.class.getClassLoader().getResourceAsStream(contextConfigLocation);
Properties pp = new Properties();
try {
pp.load(in);
} catch (IOException e) {
e.printStackTrace();
}
return pp.getProperty("basePackage");
} /**
* 将限定名转换为路径,如pers.hdh -> pers/hdh
* @param qualifiedName
* @return
*/
public static String tranferQualifiedToPath(String qualifiedName)throws Exception{
if(qualifiedName==null){
throw new Exception("空字符串不能转换");
}
return qualifiedName.replace(".","/");
} /**
* 转换第一个字母为小写
* @param name
* @return
*/
public static String toLowerFirstName(String name){
char[] charArray = name.toCharArray();
charArray[0] += 32;
return String.valueOf(charArray);
} public static void main(String[] args) throws Exception { System.out.println(tranferQualifiedToPath("io.powerx"));
}
}

  测试的controller和service

package io.powerx.controller;

import io.powerx.annotation.MyAotuwired;
import io.powerx.annotation.MyController;
import io.powerx.annotation.MyRequestMapping;
import io.powerx.annotation.MyRequestParam;
import io.powerx.service.TestService; /**
* Created by Administrator on 2019/1/3.
*/
@MyController
@MyRequestMapping("/test")
public class TestController { @MyAotuwired
private TestService testService; @MyRequestMapping("/a01")
public void test01(){
testService.mvcTest();
} @MyRequestMapping("/a02")
public String test02(@MyRequestParam("age") Integer age){
return testService.mvcTest2(age);
} }
package io.powerx.service;

import io.powerx.annotation.MyService;

/**
* Created by Administrator on 2019/1/3.
*/
@MyService
public class TestService { public void mvcTest(){
System.out.println("执行了测试方法");
} public String mvcTest2(Integer a){
System.out.println(a);
return "我的响应";
}
}

  编译打包,发布到tomcat中,可以看到控制台打印了我们扫描类和初始化mvc容器的过程

  打开浏览器,访问http://localhost:8888/myMvc/test/a02?age=12,可以看到后台收到浏览器的请求并执行,返回了结果

springmvc执行原理及自定义mvc框架的更多相关文章

  1. Java Web自定义MVC框架详解 (转)

    转自:http://blog.csdn.net/jackfrued/article/details/42774459 最近给学生讲Java Web,希望他们能够在学完这部分内容后自己实现一个MVC框架 ...

  2. 自定义MVC框架

    我们在学习自定义MVC框架的时候常常会听到Model1 ,Model2和MVC.那么什么是Model1 什么是Model2什么又是MVC呢? 什么是Model1? Model1就是一种纯jsp开发技术 ...

  3. Struts2 自定义MVC框架

    一.Model1与Model2: Model1:就是一种纯jsp开发技术,将业务逻辑代码和视图渲染代码杂糅在一起. Model2:Model2是在Model1的基础上,将业务逻辑的代码分离开来,单独形 ...

  4. 第一章 自定义MVC框架

    第一章  自定义MVC框架1.1 MVC模式设计    组成:Model:模型,用于数据和业务的处理          View :视图,用于数据的显示          Controller:控制器 ...

  5. 自定义MVC框架之工具类-模型类

    截止目前已经改造了5个类: ubuntu:通过封装验证码类库一步步安装php的gd扩展 自定义MVC框架之工具类-分页类的封装 自定义MVC框架之工具类-文件上传类 自定义MVC框架之工具类-图像处理 ...

  6. JSP学习笔记(6)—— 自定义MVC框架

    仿照SpringMVC,实现一个轻量级MVC框架,知识涉及到了反射机制.注解的使用和一些第三方工具包的使用 思路 主要的总体流程如下图所示 和之前一样,我们定义了一个DispatchServlet,用 ...

  7. springmvc执行原理

    大家是否遇到过被面试官问了这样一句话:"来聊聊springmvc执行原理".是的,springmvc的执行流程是面试的高频点,今天我就来浅谈它! 一.下面通过一个简单的spring ...

  8. 自定义MVC框架之工具类-图像处理类

    截止目前已经改造了4个类: ubuntu:通过封装验证码类库一步步安装php的gd扩展 自定义MVC框架之工具类-分页类的封装 自定义MVC框架之工具类-文件上传类 图像处理类: 1,图片加水印处理( ...

  9. 自定义MVC框架之工具类-文件上传类

    截止目前已经改造了3个类: ubuntu:通过封装验证码类库一步步安装php的gd扩展 自定义MVC框架之工具类-分页类的封装 该文件上传类功能如下: 1,允许定制上传的文件类型,文件mime信息,文 ...

随机推荐

  1. Oracle物化视图的一般使用

    普通视图和物化视图根本就不是一个东西,说区别都是硬拼到一起的,首先明白基本概念,普通视图是不存储任何数据的,他只有定义,在查询中是转换为对应的定义SQL去查询,而物化视图是将数据转换为一个表,实际存储 ...

  2. Centos 7 搭建wordpress

    1.安装mysql 详情见:http://www.cnblogs.com/jw35/p/6044170.html 2.关闭firewalld与selinux systemctl stop firewa ...

  3. DATASNAP远程方法返回TSTREAM正解

    DATASNAP远程方法返回TSTREAM正解 DATASNAP远程方法返回TSTREAM,如果数据大小超过32K是会报错的.许多DELPHIER栽在这个上头,甚至开始怀疑TSTREAM返回数据的可行 ...

  4. C# 实用小类

    /// <summary> /// 汉字转换拼音 /// </summary> /// <param name="PinYin"></pa ...

  5. 微信小程序web-view之动态加载html页面

    官方推出的web-view方便了很多开发人员. 我们在做的时候,经常会想到写一个小程序的page然后通过动态加载web-view的形式来完成其他功能页面的开发. 之前研究web-view的时候发现网上 ...

  6. C#中==操作符存在的缺陷

    ==操作符因为语法简洁而备受欢迎,但它本身也存在着局限性,比如继承或泛型问题.下面让我们依次来看看吧. 1.==和继承性问题 关于==运算符在继承时存在的问题,我们以String类型为例进行说明. s ...

  7. HTTP Error 502.5 - Process Failure Win10 VS可以正常访问,部署本地IIS报错

    最近本core得升级导致各种问题,之前刚解决了server2012的502.5问题 今天本机又出现这个问题. 情况描述:VS可以正常调试查看,部署本地IIS访问 错误502.5 分析:VS可以使用II ...

  8. 回去看linux的指令2

    SYNC CL : MSM8953 @ CL#:12212299 PROJECT PATH : // Platform / N / NILE / COMBINATION / MSM8953 Cross ...

  9. GO学习笔记 - 基本数据类型

    官方教程:https://tour.go-zh.org/basics/11 Go 的基本类型有Basic types bool string int int8 int16 int32 int64 ui ...

  10. C指针 【温故】

    概念 1 指针也是一个变量,做为指针变量的值是另一个变量的地址.指针存放的内容是一个地址,该地址指向一块内存空间 其一般形式为: 类型说明符 *变量名: 其中,*表示这是一个指针变量,变量名即为定义的 ...