自定义SpringMVC实现
首先要知道springmvc主要流程:
当用户,也是就是请求送达过来的时候,
1.前端控制器会获取,
2.请求处理映射器,返回执行链接
3.获取执行适配器适配,交给执行器
4.返回modelandview给其前端控制器
5.前端控制器把信息给视图解析器进行渲染
6.渲染的视图传给客户
所以根据这个流程进行以下操作:
1.在tomcat启动时加载web.xml也就要对前端控制器
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 ""; }
//文件获取
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();
}
}
}
}
//最重要的环节,完成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的框架
自定义SpringMVC实现的更多相关文章
- Spring Boot2 系列教程(十八)Spring Boot 中自定义 SpringMVC 配置
用过 Spring Boot 的小伙伴都知道,我们只需要在项目中引入 spring-boot-starter-web 依赖,SpringMVC 的一整套东西就会自动给我们配置好,但是,真实的项目环境比 ...
- 详解Springboot中自定义SpringMVC配置
详解Springboot中自定义SpringMVC配置 WebMvcConfigurer接口 这个接口可以自定义拦截器,例如跨域设置.类型转化器等等.可以说此接口为开发者提前想到了很多拦截层面的需 ...
- Spring Boot 中自定义 SpringMVC 配置,到底继承谁哪一个类或则接口?
看了这篇文章,写的非常的言简意赅,特此记录下: 1.Spring Boot 1.x 中,自定义 SpringMVC 配置可以通过继承 WebMvcConfigurerAdapter 来实现. 2.Sp ...
- 一步一步自定义SpringMVC参数解析器
随心所欲,自定义参数解析器绑定数据. 题图:from Zoommy 干货 SpringMVC解析器用于解析request请求参数并绑定数据到Controller的入参上. 自定义一个参数解析器需要实现 ...
- 自定义springmvc统一异常处理器(实现HandlerExceptionResolver接口)不起作用的一种情况
ExceptionResolverCustom 这个是自定义的异常处理器类. 在springmvc中注册 在web.xml文件中屏蔽springmvc自动注册的异常处理器 网上的资料就是这么配置的,可 ...
- 自定义springmvc参数解析器
实现spring HandlerMethodArgumentResolver接口 通过使用@JsonArg自定义注解来解析json数据(通过fastjson的jsonPath),支持多个参数(@Req ...
- 深入学习SpringMVC以及学习总结
一.优点: 1.SpringMVC简化web程序开发; 2.SpringMVC效率很好(单例模式): 3.SpringMVC提供了大量扩展点,方便程序员自定义功能: ①.DispatcherServl ...
- SpringBoot扩展SpringMVC自动配置
SpringBoot中自动配置了 ViewResolver(视图解析器) ContentNegotiatingViewResolver(组合所有的视图解析器) 自动配置了静态资源文件夹.静态首页.fa ...
- springMVC介绍
http://www.iteye.com/blogs/subjects/springMVC —————————————————————————————————————————————————————— ...
- 《四 spring源码》手写springmvc
手写SpringMVC思路 1.web.xml加载 为了读取web.xml中的配置,我们用到ServletConfig这个类,它代表当前Servlet在web.xml中的配置信息.通过web.xml ...
随机推荐
- Elasticsearch 查询小笔记
2.x 版本,组合多查询https://www.elastic.co/guide/cn/elasticsearch/guide/current/combining-queries-together.h ...
- WebStorm 2021.3 的永久激活教程
关注公众号回复 webstorm 即可获取激活脚本和教程 更新时间 2022年9月2日. 不定时更新 激活码可在公众号中回复[激活码]获取.
- python 购物小程序
要求: 1.启动程序后,让用户输入预算,然后打印商品列表 2.允许用户根据商品编号购买商品 3.用户选择商品后,检测余额够不够,够就直接付款,不够就提醒 4.可随时退出,推出时打印已购买商品和余额 ...
- 转载一个很强大的GIT存储库学习链接
https://learngitbranching.js.org/?locale=zh_CN 可以按照步骤一步步学习相关的知识点
- cmake 实现交叉编译注意事项
(1)确保安装交叉编译工具安装成功 在终端输入arm-linux-gnueabihf-g++ -v 或 arm-linux-gnueabihf-gcc -v ,能看到相应交叉C编译器和C++编译器的版 ...
- getchar()函数的详解以及使用时需要注意的一些细节-C语言基础
这篇文章要探讨的是"getchar()函数的详解以及使用时需要注意的一些细节".涉及getchar()函数的应用和需要注意的问题.属于C语言基础篇(持续更新). 在C语言的学习过程 ...
- Code UTF-8 Console GB2312 Linux 中文乱码
Linux 系统方法: LD_LIBRARY_PATH=. ./userdemo | iconv -f GB2312 -t utf8 Shell 方法 Shell 编码 改成GB2312 // 编 ...
- win10 python + selenium 环境搭建
一.安装python3 1.下载地址: https://www.python.org/downloads/windows/ 直接选择最新版,下拉 file列表中,选择win10版 64位 Windo ...
- TensorFlow 的 Graph 模式转换
定义 TensorFlow 图形并将其保存到磁盘上. 使用 TensorFlow 的 tf.Graph() 和 tf.Session() 函数来定义和运行 TensorFlow 图形,并使用 tf.t ...
- MIUI 12.5稳定版关闭充电提示音的方法
手机开启开发中模式 将手机连接电脑 打开cmd, 输入命令:adb shell settings put global power_sounds_enabled 0,即可关闭充电时的提示音 输入命令: ...