最近在复习框架 在快看小说网搜了写资料 和原理 今天总结一下 希望能加深点映像  不足之处请大家指出

我就不画流程图了 直接通过代码来了解springmvc的运行机制和原理

回想用springmvc用到最多的是什么?当然是controller和RequestMapping注解啦

首先我们来看怎样定义注解的

首先来定义@Controller

@Target表示该注解运行在什么地方
1、public static final ElementTypeTYPE      类、接口(包括注释类型)或枚举声明
2、public static final ElementTypeFIELD     字段声明(包括枚举常量)
3、public static final ElementTypeMETHOD    方法声明
4、public static final ElementTypePARAMETER     参数声明
5、public static final ElementTypeCONSTRUCTOR    构造方法声明
6、public static final ElementTypeLOCAL_VARIABLE     局部变量声明
7、public static final ElementTypeANNOTATION_TYPE     注释类型声明
8、public static final ElementTypePACKAGE    包声明

@Retention :用来说明该注解类的生命周期。它有以下三个参数:

     RetentionPolicy.SOURCE  : 注解只保留在源文件中

     RetentionPolicy.CLASS  : 注解保留在class文件中,在加载到JVM虚拟机时丢弃

     RetentionPolicy.RUNTIME  : 注解保留在程序运行期间,此时可以通过反射获得定义在某个类上的所有注解。

@Target(ElementType.TYPE)//表示注解运行在哪里
@Retention(RetentionPolicy.RUNTIME)//用来说明该注解类的生命周期
public @interface Controller {
}

其次定义@RequestMapping

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
public String value();//定义一个字符数组来接收路径值
}

定义好了注解 怎样让其产生效果呢?

假设程序启动运行中 我们如何去知道类上面是否存在注解了?叱咤风云林云 这就需要用到java的反射机制了。在运行状态中对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,我们都可以调用它任意的一个方法。

项目目录结构如下:

比如说在运行状态中 我们知道springmvc扫描的包为com.czmec.Controller.IndexController这个包下面  我们通过反射就可以获取改包下的信息

public class Test {
public static void main(String[] args) {
Class clazz= IndexController.class;
//判断这个类是否存在@Controller 是否标记Controller注解
if (clazz.isAnnotationPresent(Controller.class)){
System.out.println(clazz.getName()+",被标记为控制器!");
//吧标记了@Controller注解的类管理起来
String path="";
//判断标记了Controller类上是否存在@RequestMapper
if (clazz.isAnnotationPresent(RequestMapping.class)){
//如果存在 就获取注解上的路径值
RequestMapping reqAnno= (RequestMapping) clazz.getAnnotation(RequestMapping.class);
path=reqAnno.value();
}

//获取类上路径过后 再获取该类的所有公开方法进行遍历(快看小说网) 并且判断哪些方法上有@RequestMapping
Method[] ms=clazz.getMethods();
for (Method method:ms){
//如果不存在RequestMapping注解 进入下一轮循环
if(!method.isAnnotationPresent(RequestMapping.class)){
continue;
}
System.out.println("映射对外路径"+path+method.getAnnotation(RequestMapping.class).value());
}
}
}
}

运行结果如下:

通过以上测试类 反射机制去获取加了注解的信息(超级王者个人理解:注解是一种标记 在代码做标记 然后在特定的管理下 可以通过某种动态方式找到想要的信息)

开发人员通过自己的业务需求去添加注解类 那么框架的设计就应该去吧这些添加了注解的类管理起来。

比如在springmvc配置中需要配置扫描的包 那么我们需要写一个工具类来根据配置的扫描包来管理需要管理的类

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.*;
/**
* 用来扫描指定的包下面的类
*/
public class ClassScanner {
/**
* 用来扫描指定的包下面的类
* @param basePackg 基础包
* @return Map<String,Class<?>> Map<类,类的class实例>
*/
public static Map<String,Class<?>> scannerClass(String basePackg){
Map<String,Class<?>> results=new HashMap<String, Class<?>>();
//com.czmec.controller
//通过包名替换成 com/czmec/controller
String filePath=basePackg.replace(".","/");
//通过类加载器获取完整路径
try {
Enumeration<URL> dirs=Thread.currentThread().getContextClassLoader().getResources(filePath);
String rootPath=Thread.currentThread().getContextClassLoader().getResource(filePath).getPath();
System.out.println(rootPath);
if (rootPath!=null){
rootPath=rootPath.substring(rootPath.lastIndexOf(filePath));
}
///C:/Users/user/Desktop/%e5%ad%a6%e4%b9%a0/springmvc/out/production/springmvc/com/czmec/Controller
//获取的是类的真实的物理路径 接下来就是io啦
while (dirs.hasMoreElements()){
URL url=dirs.nextElement();
System.out.println(url);
/**
* /C:/Users/user/Desktop/%e5%ad%a6%e4%b9%a0/springmvc/out/production/springmvc/com/czmec/Controller
file:/C:/Users/user/Desktop/%e5%ad%a6%e4%b9%a0/springmvc/out/production/springmvc/com/czmec/Controller
*/
//根据url 判断是文件还是文件夹
if (url.getProtocol().equals("file")){
File file=new File(url.getPath().substring(1));//因为路径前面多了一个/所有从1开始夜夜笙香
//如果是文件夹 就需要递归找下去 找到所有文件
try {
scannerFile(file,rootPath,results);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

}
}
} catch (IOException e) {
e.printStackTrace();
}

return results;
}

//递归
private static void scannerFile(File folder,String rootPath,Map<String,Class<?>> classes) throws ClassNotFoundException {
//拿到folder里面的所有文件对象 如果文件夹为空生而为王萧阳 会返回一个Null
File[] files=folder.listFiles();
for (int i=0;files!=null&&i<files.length;i++){
File file=files[i];
//如果是文件夹就继续进行递归
if (file.isDirectory()){
if (rootPath.substring(rootPath.length()-1).equals("/")){
rootPath=rootPath.substring(0,rootPath.length()-1);
}
scannerFile(file,rootPath+"/"+file.getName()+"/",classes);
}else {
String path=file.getAbsolutePath();//获取真实路径
if (path.endsWith(".class")){
//将路径中的\替换成/
path=path.replace("\\","/");
//获取完整的类路径 比如com.czmec.IndexController
if (!rootPath.substring(rootPath.length()-1).equals("/")){
rootPath+="/";
}
String className=rootPath+path.substring(path.lastIndexOf("/")+1,path.indexOf(".class"));
className=className.replace("/",".");
System.out.println(className);
//吧类路径是实例保存起来
classes.put(className,Class.forName(className));
}
}
}
}

public static void main(String[] args) {
ClassScanner.scannerClass("com.czmec");
}
}

通过上面代码 可以看出在指定的包下 获取包下类的路径 并获取实例 保存在Map中,刘瑞阳小芳卢雪艳  吧这些类扫描出来通过反射技术获取注解值,获取控制器类等等

在springmvc中 我们会配置springmvc的核心控制器DispatcherServlet来设置路径权限

DispatcherServlet的生命周期有:init(),service,destory;

下面是生命周期图

通过这个图我们可以了解到DispatcherServlet在运行过程中所起到的作用

配置DispatcherServlet有两种方式 一种是基于xml 一种是基于注解 下面我们来看看基于注解

在DispatcherServlet类中 通过设置@WebServlet来设置拦截路径 @WebInitParam来设置设置的包

在初始化方法中去获取包下面所有的类并且迭代所有类的实例 获取有Controller注解 并通过类加载机制实例化对象超级王者

保存在Map中 再获取类中所有加了@RequestMapping注解的方法,和路径保存在methods中  并在service中解析请求路径通过invoke方法

@WebServlet(urlPatterns = {"*.do"},initParams = {@WebInitParam(name = "basePackage",value = "com.czmec")})
public class DispacherServlet extends javax.servlet.http.HttpServlet {
// protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
//
// }
//
// protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
//
// }
//存储Controller实例
private Map<String,Object> controllers=new HashMap<String,Object>();
//被反射调用的method
private Map<String,Method> methods=new HashMap<String,Method>();
public DispacherServlet(){
super();
}

@Override
public void init(ServletConfig config) throws ServletException {
//获取包名
String basePackage=config.getInitParameter("basePackage");
//扫描
Map<String ,Class<?>> cons= ClassScanner.scannerClass(basePackage);
//迭代
Iterator<String> itro=cons.keySet().iterator();
while (itro.hasNext()){
String className=itro.next();
Class clazz=cons.get(className);
if (clazz.isAnnotationPresent(Controller.class)){
System.out.println(clazz.getName()+",被标记为控制器!");
//吧标记了@Controller注解的类管理起来
String path="";
//判断标记了Controller类上是否存在@RequestMapper
if (clazz.isAnnotationPresent(RequestMapping.class)){
//如果存在 就获取注解上的路径值
RequestMapping reqAnno= (RequestMapping) clazz.getAnnotation(RequestMapping.class);
path=reqAnno.value();
}
try {
controllers.put(className,clazz.newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
//获取类上路径过后 再获取该类的所有公开方法进行遍历 并且判断哪些方法上有@RequestMapping
Method[] ms=clazz.getMethods();
for (Method method:ms){
//如果不存在RequestMapping注解 进入下一轮循环快看小说网
if(!method.isAnnotationPresent(RequestMapping.class)){
continue;
}
System.out.println("映射对外路径"+path+method.getAnnotation(RequestMapping.class).value());
methods.put(path+method.getAnnotation(RequestMapping.class).value(),method);
}
}
}
}

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(req.getRequestURI());
///abc.do
String uri=req.getRequestURI();
String contextPath=req.getContextPath();
//获取映射路径
String mappingPath=uri.substring(uri.indexOf(contextPath)+contextPath.length(),uri.indexOf(".do"));
//
Method method=methods.get(mappingPath);
//获取实例对象
Object controller=controllers.get(method.getDeclaringClass().getName());
try {
method.invoke(controller);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}

配上tomcat运行

输入地址:http://localhost:8080/czmec/delete.do

通过以上代码的说明 可以了解基本springmvc的运行机制和原理

至此最基本的springmvc就完成了极品摄影师

有哪里错误的地方请指出

源代码下载地址:https://github.com/dcg123/springmvc

springmvc原理详解(手写springmvc)的更多相关文章

  1. (二)springMvc原理和手写springMvc框架

    我们从两个方面了解springmvc执行原理,首先我们去熟悉springmvc执行的过程,然后知道原理后通过手写springmvc去深入了解代码中执行过程. (一)SpringMVC流程图 (二)Sp ...

  2. 手写SpringMVC 框架

    手写SpringMVC框架 细嗅蔷薇 心有猛虎 背景:Spring 想必大家都听说过,可能现在更多流行的是Spring Boot 和Spring Cloud 框架:但是SpringMVC 作为一款实现 ...

  3. SpringMVC RequestMapping 详解

    SpringMVC RequestMapping 详解 RequestMapping这个注解在SpringMVC扮演着非常重要的角色,可以说是随处可见.它的知识点很简单.今天我们就一起学习Spring ...

  4. 《四 spring源码》手写springmvc

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

  5. 利用Intellij+MAVEN搭建Spring+Mybatis+MySql+SpringMVC项目详解

    http://blog.csdn.net/noaman_wgs/article/details/53893948 利用Intellij+MAVEN搭建Spring+Mybatis+MySql+Spri ...

  6. 手写SpringMVC框架(三)-------具体方法的实现

    续接前文 手写SpringMVC框架(二)结构开发设计 本节我们来开始具体方法的代码实现. doLoadConfig()方法的开发 思路:我们需要将contextConfigLocation路径读取过 ...

  7. 手写SpringMVC框架(二)-------结构开发设计

    续接前文, 手写SpringMVC框架(一)项目搭建 本节我们来开始手写SpringMVC框架的第二阶段:结构开发设计. 新建一个空的springmvc.properties, 里面写我们要扫描的包名 ...

  8. Spring框架系列(9) - Spring AOP实现原理详解之AOP切面的实现

    前文,我们分析了Spring IOC的初始化过程和Bean的生命周期等,而Spring AOP也是基于IOC的Bean加载来实现的.本文主要介绍Spring AOP原理解析的切面实现过程(将切面类的所 ...

  9. Spring框架系列(11) - Spring AOP实现原理详解之Cglib代理实现

    我们在前文中已经介绍了SpringAOP的切面实现和创建动态代理的过程,那么动态代理是如何工作的呢?本文主要介绍Cglib动态代理的案例和SpringAOP实现的原理.@pdai Spring框架系列 ...

随机推荐

  1. k8s学习 - API

    k8s学习 - API 之前对k8s并没有很深入的了解,最近想把手头一个项目全部放到k8s上,以方便部署,需要研究.这里记录一下自己研究过程中头脑中的理解. k8s 和 docker 首先,需要先理解 ...

  2. 【烂笔头】常用adb命令记录

    前言    Android的adb提供了很多命令,功能很强大,可以为开发和调试带来很大的便利.当然本文并不是介绍各种命令的文章,而是用于记录在平时工作中需要经常使用的命令,方便平时工作时使用,所以以后 ...

  3. Linux下安装docker与kubernetes(k8s)

    环境 安装是使用Vmware虚拟机下进行,操作系统是CentOS7 64位.规划是使用三台虚拟机搭建k8s的集群,网络使用NAT模式.三台的ip分别为: k8s-master:192.168.91.1 ...

  4. RabbitMQ从入门到精通(三)

    目录 1. 自定义消费者使用 自定义消费端演示 2.消费端的限流策略 2.1 限流的场景与机制 2.2 限流相关API 2.3 限流演示 3. 消费端ACK与重回队列机制 3.1 ACK与NACK 3 ...

  5. c语言进阶5-递归算法

    一.  什么是递归 程序调用自身的编程技巧称为递归( recursion). 递归做为一种算法在程序设计语言中广泛应用. 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型 ...

  6. 2017day1

    http://www.cnblogs.com/alex3714/articles/5465198.html 四.Python安装 windows 1 2 3 4 5 6 7 1.下载安装包     h ...

  7. c#六大设计原则(以仪器代码为例)

    [有格式的原文请到https://www.cnc6.cn/c六大设计原则/文末下载] 软件设计原则常见的有6大原则,分别为: ①单一职责原则: ②开闭原则: ③依赖倒置原则: ④里氏替换原则: ⑤接口 ...

  8. python课堂整理14---函数式编程

    一.分类 当下主流的编程方法大体分为三类 1. 面向过程 2. 函数式 3. 面向对象 二.函数式编程:函数式 = 编程语言定义的函数 + 数学意义的函数 特征:1. 不可变数据 2. 第一类对象 3 ...

  9. 面试中的 ThreadLocal 原理和使用场景

    相信大家不管是在网上做题还是在面试中都经常被问过 ThreadLocal 的原理和用法,虽然一直知道这个东西的存在但是一直没有好好的研究一下原理,没有自己的知识体系.今天花点时间好好学习了一下,分享给 ...

  10. Java编程思想,初学者推荐看看

    这是一本介绍Java编程思想,如何从面向过程的编程思想转换为面向对象的编程思想.我个人是比较建议新手看一下的,思想掌握了,学起来自然也就会方便很多的,我还有一些Java基础的数,有需要的可以找我要,都 ...