《四 spring源码》手写springmvc
手写SpringMVC思路
1.web.xml加载
为了读取web.xml中的配置,我们用到ServletConfig这个类,它代表当前Servlet在web.xml中的配置信息。通过web.xml中加载我们自己写的MyDispatcherServlet和读取配置文件。
2、初始化阶段
在前面我们提到DispatcherServlet的initStrategies方法会初始化9大组件,但是这里将实现一些SpringMVC的最基本的组件而不是全部,按顺序包括:
- 加载配置文件
- 扫描用户配置包下面所有的类
- 拿到扫描到的类,通过反射机制,实例化。并且放到ioc容器中(Map的键值对 beanName-bean) beanName默认是首字母小写
- 初始化HandlerMapping,这里其实就是把url和method对应起来放在一个k-v的Map中,在运行阶段取出
3、运行阶段
每一次请求将会调用doGet或doPost方法,所以统一运行阶段都放在doDispatch方法里处理,它会根据url请求去HandlerMapping中匹配到对应的Method,然后利用反射机制调用Controller中的url对应的方法,并得到结果返回。按顺序包括以下功能:
- 异常的拦截
- 获取请求传入的参数并处理参数
- 通过初始化好的handlerMapping中拿出url对应的方法名,反射调用
手写SpringMVC基本实现
/**
* 手写SpringMVC框架 作者:每特教育-余胜军<br>
*
* @QQ644064779 1.自定义DispatcherServlet<br>
* 2.servlet init()方法初始化###只会执行一次<br>
* ######2.1获取当前包下所有的类<br>
* ######2.2初始化当前包下所有的类,使用Java反射机制初始化对象存放在SpringMVC容器中key(beanId)-
* value( 当前实例对象) <br>
* ######2.3初始化HandlerMapping方法,将url和方法对应上 <br>
* ########2.3.1使用Java反射技术读取类的信息,存放在map集合中key为url请求地址,value为对应方法
* <br>
* ########2.3.2使用Java反射技术读取类的信息,存放在map集合中key为url请求地址,value为对应实例对象
* <br>
* 3.servlet get或者post请求<br>
* ######## 3.1.1获取请求地址,使用Java反射技术找到对应的方法和实例对象进行执行 <br>
*/
public class ExtDispatcherServlet extends HttpServlet {
// mvc bean key=beanid ,value=对象
private ConcurrentHashMap<String, Object> mvcBeans = new ConcurrentHashMap<String, Object>();
// mvc 请求方法 key=requestUrl,value=对象
private ConcurrentHashMap<String, Object> mvcBeanUrl = new ConcurrentHashMap<String, Object>();
// mvc 请求方法 key=requestUrl,value=方法
private ConcurrentHashMap<String, String> mvcMethodUrl = new ConcurrentHashMap<String, String>(); /**
* 初始化自定义SpringMVC容器
*/
public void init() throws ServletException {
try {
// 1.获取当前包下所有的类
List<Class<?>> classes = ClassUtil.getClasses("com.itmayiedu.ext.controller");
// 2.初始化当前包下所有的类,使用Java反射机制初始化对象存放在SpringMVC容器中key(beanId)-value(
// 当前实例对象)
findClassMVCBeans(classes);
// 3.初始化HandlerMapping方法,将url和方法对应上
handlerMapping(mvcBeans); } catch (Exception e) { }
} // 2.初始化当前包下所有的类,使用Java反射机制初始化对象存放在SpringMVC容器中key(beanId)-value(
// 当前实例对象)
public void findClassMVCBeans(List<Class<?>> classes)
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
mvcBeans = new ConcurrentHashMap<String, Object>();
for (Class<?> classInfo : classes) {
ExtController extController = classInfo.getDeclaredAnnotation(ExtController.class);
if (extController != null) {
// 默认类名小写 作为bean的名称
String beanId = ClassUtil.toLowerCaseFirstOne(classInfo.getSimpleName());
mvcBeans.put(beanId, ClassUtil.newInstance(classInfo));
}
} } // 3.初始化HandlerMapping方法,将url和方法对应上
public void handlerMapping(ConcurrentHashMap<String, Object> mvcBeans) {
// 遍历mvc bean对象
for (Map.Entry<String, Object> entry : mvcBeans.entrySet()) {
// springmvc 注入object对象
Object mvcObject = entry.getValue();
// 判断类上是否有@ExtRequestMapping注解
Class<? extends Object> classInfo = mvcObject.getClass();
String requestBaseUrl = null;
ExtRequestMapping classExtRequestMapping = classInfo.getAnnotation(ExtRequestMapping.class);
if (classExtRequestMapping != null) {
requestBaseUrl = classExtRequestMapping.value();
}
// 遍历当前类的所有方法,判断方法上是否有注解
Method[] declaredMethods = classInfo.getDeclaredMethods();
for (Method method : declaredMethods) {
ExtRequestMapping methodExtRequestMapping = method.getDeclaredAnnotation(ExtRequestMapping.class);
if (methodExtRequestMapping != null) {
String httpRequestUrl = methodExtRequestMapping.value();
mvcBeanUrl.put(requestBaseUrl + httpRequestUrl, mvcObject);
mvcMethodUrl.put(requestBaseUrl + httpRequestUrl, method.getName());
}
}
}
} @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) {
// TODO: handle exception
}
} public void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
// 1.获取请求url地址
String requestUrl = req.getRequestURI();
// 2.使用请求url查找对应mvc 控制器bean
Object object = mvcBeanUrl.get(requestUrl);
if (object == null) {
resp.getWriter().println("http ext not found controller 404");
return;
}
// 3.获取对应的请求方法
String methodName = mvcMethodUrl.get(requestUrl);
if (StringUtils.isEmpty(methodName)) {
resp.getWriter().println("http ext not found Method 404");
return;
}
// 4.使用java反射技术执行方法
Class<? extends Object> classInfo = object.getClass();
String resultPage = (String) methodInvoke(classInfo, object, methodName);
// 5.视图展示
viewdisplay(resultPage, req, resp);
} // 执行方法
public Object methodInvoke(Class<? extends Object> classInfo, Object object, String methodName) {
try {
Method method = classInfo.getMethod(methodName);
Object result = method.invoke(object);
return result;
} catch (Exception e) {
e.printStackTrace();
return null;
}
} // 视图展示
public void viewdisplay(String pageName, HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
// 获取后缀信息
String suffix = ".jsp";
// 页面目录地址
String prefix = "/";
req.getRequestDispatcher(prefix + pageName + suffix).forward(req, res);
} }
《四 spring源码》手写springmvc的更多相关文章
- spring源码学习之springMVC(一)
个人感觉<Spring技术内幕:深入解析Spring架构与设计原理(第2版)>这本书对spring的解读要优于<Spring源码深度解析(第2版)>这本书的,后者感觉就是再陈述 ...
- 《四 spring源码》手写springioc框架
手写SpringIOCXML版本 /** * 手写Spring专题 XML方式注入bean * * * */ public class ClassPathXmlApplicationContext { ...
- 《四 spring源码》利用TransactionManager手写spring的aop
事务控制分类 编程式事务控制 自己手动控制事务,就叫做编程式事务控制. Jdbc代码: Conn.setAutoCommite(false); // 设置手动控制事务 Hibern ...
- 史上最完整promise源码手写实现
史上最完整的promise源码实现,哈哈,之所以用这个标题,是因为开始用的标题<手写promise源码>不被收录 promise自我介绍 promise : "君子一诺千金,承诺 ...
- 【spring源码学习】springMVC之映射,拦截器解析,请求数据注入解析,DispatcherServlet执行过程
[一]springMVC之url和bean映射原理和源码解析 映射基本过程 (1)springMVC配置映射,需要在xml配置文件中配置<mvc:annotation-driven > ...
- 《四 spring源码》spring的事务注解@Transactional 原理分析
先了解什么是注解 注解 Jdk1.5新增新技术,注解.很多框架为了简化代码,都会提供有些注解.可以理解为插件,是代码级别的插件,在类的方法上写:@XXX,就是在代码上插入了一个插件. 注解不会也不能影 ...
- spring源码学习之springMVC(二)
接着上一篇.继续来看springMVC中最和我们开发中接近的一部分内容: DispatcherServlet的逻辑处理 作者写到在DispatcherServlet类中存在doGet.doPost之类 ...
- Spring源码追踪4——SpringMVC View解析
这次的议题是返回json和返回普通view经过的路线差异. ---------------------------------------------------------------------- ...
- Ubuntu搭建Spring源码环境常见问题
在一心想要学习Spring框架源码时,我们会遇到很多麻烦的问题.开始本文前,你只需要拥有一个装好IDEA的Ubuntu系统就可以愉快启程了.如果还没有IDEA,可以参考在Ubuntu上安装Intell ...
随机推荐
- TF-IFD算法及python实现关键字提取
TF-IDF算法: TF:词频(Term Frequency),即在分词后,某一个词在文档中出现的频率. IDF:逆文档频率(Inverse Document Frequency).在词频的基础上给每 ...
- linux C++ scandir 的使用
() 头文件 #include <dirent.h> () 函数定义 int scandir(const char *dir,struct dirent **namelist,int (* ...
- sangfor-AF 地址转换以及各种模式理解(路由,透明,虚拟网线,混合模式)
目的地址转换: 1.路由其实很简单的,其实你可以理解为路由器就好了2.透明和虚拟网线的区别:虚拟网线不对数据做任何的处理,你可以理解为不封装不拆包,直接丢给对端.而透明不一样,透明你可以把设备当做是交 ...
- poj1637 Sightseeing tour[最大流+欧拉回路]
混合图的欧拉回路定向问题. 顺便瞎说几句,有向图定欧拉回路的充要条件是每个点入度等于出度,并且图联通.无向图的话只要联通无奇点即可. 欧拉路径的确定应该是无向图联通且奇点数0个或2个,有向图忘了,好像 ...
- 「SHOI2002」「LuoguP1291」百事世界杯之旅(UVA10288 Coupons)(期望,输出
题目描述 “……在2002年6月之前购买的百事任何饮料的瓶盖上都会有一个百事球星的名字.只要凑齐所有百事球星的名字,就可参加百事世界杯之旅的抽奖活动,获得球星背包,随声听,更克赴日韩观看世界杯.还不赶 ...
- Stamps
链接 分析:dp[i][j]表示前i个数,组成j,最少需要多少个.dp[i][j]=min(dp[i-1][j],dp[i-1][j-k*v[i]]+k),则可以转化为完全背包问题,同样的方法进行降维 ...
- ogg日常运维命令
1.查看历史记录.快捷执行历史中的一条命令 GGSCI (11g) 32> h 23: view param exta24: info all25: lag exta.... GGSCI (11 ...
- bzoj 3680(洛谷1337) 吊打XXX——模拟退火
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3680 https://www.luogu.org/problemnew/show/P1337 ...
- CS231n 2016 通关 第六章 Training NN Part2
本章节讲解 参数更新 dropout ================================================================================= ...
- chromium浏览器开发系列第五篇:Debugging with WinDBG
Windbg 相信windows开发的人都知道,有些人用的溜儿溜儿的,有个crash,直接拿这个工具一分析,就定位出来了.非常好用.以前有个同事,做sdk开发 的,会各种命令.来北京后,还去过微软面试 ...