自己手写一个简易版Spring MVC主要是为了强化自己对Spring框架的理解
1.首先新建一个项目:

2.编辑pom.xml文件,主要是增加了servlet-api依赖和jetty插件

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.wlj.myframework</groupId><artifactId>mvcframework</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><name>mvcframework Maven Webapp</name><!-- FIXME change it to the project's website --><url>http://www.example.com</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.7</maven.compiler.source><maven.compiler.target>1.7</maven.compiler.target><servlet.api.version>2.4</servlet.api.version></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>${servlet.api.version}</version><scope>provided </scope></dependency></dependencies><build><finalName>mvcframework</finalName><plugins><plugin><groupId>org.eclipse.jetty</groupId><artifactId>jetty-maven-plugin</artifactId><version>9.3.7.v20160115</version><configuration><scanIntervalSeconds>10</scanIntervalSeconds><httpConnector><port>8080</port></httpConnector><webApp><contextPath>/wlj</contextPath></webApp></configuration></plugin></plugins><pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --><plugins><plugin><artifactId>maven-clean-plugin</artifactId><version>3.0.0</version></plugin><!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging --><plugin><artifactId>maven-resources-plugin</artifactId><version>3.0.2</version></plugin><plugin><artifactId>maven-compiler-plugin</artifactId><version>3.7.0</version></plugin>.............</plugins></pluginManagement></build>
</project>

3.新建java、resources等目录:

demo目录下是模拟业务代码,framework是框架目录

4.在servlet目录下新建DispatherServlet类:


public class WLJDispatherServlet extends HttpServlet{private Properties contextConfig=new Properties();private List<String> classNames=new ArrayList<>();//IOC容器private Map<String,Object> ioc=new HashMap<>();private List<Handler> handlerMapping=new ArrayList<>();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {try {dispatch(req, resp);} catch (IOException e) {e.printStackTrace();resp.getWriter().write("500 Exception");}}private void dispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException {Handler handler = getHandler(req);if(handler==null){resp.getWriter().write("404 NOT FOUND!!");return;}//获取匹配的方法的参数类型列表Class<?>[] parameterTypes = handler.methed.getParameterTypes();//保存所有需要自动赋值的参数值Object[] paramValues = new Object[parameterTypes.length];//客户端请求的参数mapMap<String,String[]> params = req.getParameterMap();for (Map.Entry<String, String[]> entry : params.entrySet()) {System.out.print("dispatch:param[]->"+Arrays.toString(entry.getValue()));System.out.println();String value = Arrays.toString(entry.getValue()).replaceAll("\\[|\\]","").replaceAll(",\\s",",");if(!handler.paramIndexMapping.containsKey(entry.getKey())){ continue;}//若找到匹配参数则开始填充参数值Integer index = handler.paramIndexMapping.get(entry.getKey());paramValues[index]=convert(parameterTypes[index],value);}//设置方法中的request和responseInteger reqIndex = handler.paramIndexMapping.get(HttpServletRequest.class.getName());paramValues[reqIndex]=req;Integer respIndex = handler.paramIndexMapping.get(HttpServletResponse.class.getName());paramValues[respIndex]=resp;try {handler.methed.invoke(handler.controller,paramValues);} catch (Exception e) {e.printStackTrace();}}private void doLoadConfig(String contextConfigLocation){InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);try {contextConfig.load(is);} catch (IOException e) {e.printStackTrace();}finally {if(null!=is){try {is.close();} catch (IOException e) {e.printStackTrace();}}}}private void doScanner(String scanPackage){System.out.println("scanPackage:"+scanPackage);//文件路径转化为包路径URL url= this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));System.out.printf("url:"+url.toString());File classDir = new File(url.getFile());for(File file:classDir.listFiles()){//是文件夹if(file.isDirectory()){doScanner(scanPackage+"."+file.getName());}else{//存扫描到的类名String className=scanPackage+"."+file.getName().replace(".class","");classNames.add(className);}}}//实例化扫描到的类private void doInstance() throws IllegalAccessException, InstantiationException {if(classNames.isEmpty()){return;}try {for (String name : classNames) {Class<?> clazz = Class.forName(name);//不是所有的类都要初始化,只认加了注解的类if(clazz.isAnnotationPresent(WLJController.class)){//key默认类名首字母小写String beanName = lowerFirstCase(clazz.getName());ioc.put(beanName,clazz.newInstance());}else if(clazz.isAnnotationPresent(WLJService.class)){//1.默认采用首字母小写//2.如果自定义了名字,优先使用自定义的名字//3.根据接口类型来赋值WLJService annotation = clazz.getAnnotation(WLJService.class);String beanName = annotation.value();//未定义自定义名字if(!"".equals(beanName.trim())){beanName=lowerFirstCase(beanName);}//某接口实现类的entryioc.put(beanName,clazz.newInstance());//某接口的entry,不过其实例就是上面的实例for(Class<?> i:clazz.getInterfaces()){ioc.put(i.getName(),clazz.newInstance());}}else{continue;}}} catch (ClassNotFoundException e) {e.printStackTrace();}}private void doAutowired(){if(ioc.isEmpty()){return;}//循环ioc容器,对需要注入的属性进行注入for (Map.Entry<String, Object> entry : ioc.entrySet()) {//依赖注入,无论该属性的访问规则怎样,因为是反射Field[] fields = entry.getValue().getClass().getDeclaredFields();for (Field field : fields) {if(!field.isAnnotationPresent(WLJAutowired.class)){continue;}WLJAutowired autowired = field.getAnnotation(WLJAutowired.class);String beanName=autowired.value().trim();if("".equals(beanName)){beanName=field.getType().getName();}//暴力访问(只要加了注解,我就访问)field.setAccessible(true);try {//field.set的第二个参数为: entry.getValue()set方法的参数field.set(entry.getValue(),ioc.get(beanName));} catch (IllegalAccessException e) {e.printStackTrace();continue;}}}}private void initHandlerMapping(){if(ioc.isEmpty()){return;}for (Map.Entry<String, Object> entry : ioc.entrySet()) {Class<?> clazz=entry.getValue().getClass();if(!clazz.isAnnotationPresent(WLJController.class)){continue;}String baseUrl="";if(clazz.isAnnotationPresent(WLJRequestMapping.class)){WLJRequestMapping annotation = clazz.getAnnotation(WLJRequestMapping.class);baseUrl=annotation.value().trim();}//扫描所有公共方法for (Method method : clazz.getMethods()) {if(!method.isAnnotationPresent(WLJRequestMapping.class)){continue;}WLJRequestMapping annotation = method.getAnnotation(WLJRequestMapping.class);String regex=("/"+baseUrl+annotation.value()).replaceAll("/+","/");Pattern compile = Pattern.compile(regex);handlerMapping.add(new Handler(entry.getValue(),method,compile));System.out.println("Mapping:"+regex+","+method);}}}private String lowerFirstCase(String str){char[] chars = str.toCharArray();chars[0]+=32;return String.valueOf(chars);}@Overridepublic void init(ServletConfig config){//从这里启动//1.加载配置文件System.out.println("contextConfigLocation:"+config.getInitParameter("contextConfigLocation"));doLoadConfig(config.getInitParameter("contextConfigLocation"));//2.扫描所有相关的类doScanner(contextConfig.getProperty("scanPackage"));//3.初始化所有相关的类try {doInstance();} catch (Exception e) {e.printStackTrace();}//4.自动注入doAutowired();//================以下mvc内容======================//5.初始化handlerMappinginitHandlerMapping();System.out.println("WLJ Spring init completed..");}private Handler getHandler(HttpServletRequest req){if(handlerMapping.isEmpty()){return null;}String url=req.getRequestURI();String contextPath = req.getContextPath();url=url.replace(contextPath,"").replaceAll("/+","/");for (Handler handler : handlerMapping) {Matcher matcher = handler.pattern.matcher(url);//没有匹配成功,下一个if(!matcher.matches()){continue;}return handler;}return null;}private Object convert(Class<?> type,String value){if(type==Integer.class){return Integer.valueOf(value);}return value;}private class Handler{protected Object controller;    //保存方法对应的实例protected Method methed;        //保存映射的方法protected Pattern pattern;protected Map<String,Integer> paramIndexMapping;    //参数顺序protected Handler(Object controller, Method methed, Pattern pattern) {this.controller=controller;this.methed = methed;this.pattern = pattern;this.paramIndexMapping=new HashMap<>();putParamIndexMapping(methed);}private void putParamIndexMapping(Method methed){//提取方法中加了注解的参数//为什么是二维数组呢?因为每个参数可以有多个注解修饰Annotation[][] params = methed.getParameterAnnotations();//params.length=3  虽然前两个参数没有注解修饰,但是他们的params[i]无元素//params[0][]空  params[1][]空  params[2][0]=WLJRequestParamfor (int i=0;i<params.length;i++) {for (Annotation a : params[i]) {if(a instanceof WLJRequestParam){String paramName = ((WLJRequestParam) a).value();if(!"".equals(paramName.trim())){paramIndexMapping.put(paramName,i);}}}}Class<?>[] parameterTypes = methed.getParameterTypes();for(int i=0;i<parameterTypes.length;i++){Class<?> type = parameterTypes[i];if(type==HttpServletRequest.class||type==HttpServletResponse.class){paramIndexMapping.put(type.getName(),i);}}}}
}

5.在WEB-INF的web.xml中配置该servlet:

(红色字样不用管)
在Resources目录下新建application.properties文件:
文件内容只有 :
scanPackage=com.wlj.demo

6.自定义注解
@WLJAutowired:

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WLJAutowired {String value() default "";
}

@WLJController:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WLJController {String value() default "";
}

@WLJRequestMapping:

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WLJRequestMapping {String value() default "";
}

@WLJRequestParam:

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WLJRequestParam {String value() default "";
}

@WLJService:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WLJService {String value() default "";
}

==================至此结束,框架代码结束=========================
业务代码:
在action目录下新建DemoAction类:

@WLJController
@WLJRequestMapping("/demo")
public class DemoAction {@WLJAutowiredprivate IDemoService demoService;@WLJRequestMapping("/query")public void query(HttpServletRequest req, HttpServletResponse resp,@WLJRequestParam("name") String name){String res=demoService.get(name);try {resp.getWriter().write(res);}catch (IOException e){e.printStackTrace();}}
}

新建service接口以及实现类:

public interface IDemoService {String get(String name);
}@WLJService
public class DemoServce implements IDemoService {@Overridepublic String get(String name) {return "My Name is "+name;}
}

================================================
启动jetty容器

在浏览器中输入地址测试:

OK,Mission Complete!!!!!!!!!!!!

手写简易版spring MVC框架相关推荐

  1. 解鞍卸甲——手写简易版Spring框架(终):使用三级缓存解决循环依赖问题

    什么是三级缓存 按照目前我们实现的 Spring 框架,是可以满足一个基本需求的,但如果你配置了A.B两个Bean对象互相依赖,那么立马会抛出 java.lang.StackOverflowError ...

  2. 仿照源码,手写一个自定义 Spring MVC 框架

    毫无疑问,Spring 框架目前已经成为 Java 开发的行业标准,Spring MVC 作为其 Web 解决方案,是所有 Java 开发者都必须掌握的基本技能,理解其底层原理,才能更好地应用它进行实 ...

  3. 手写简易版Spring框架(七):定义标记类型接口Aware,实现感知容器相关的信息

    文末有惊喜 目标 目前已实现的 Spring 框架,在 Bean 操作上能提供出的能力,包括:Bean 对象的定义和注册,以及在操作 Bean 对象过程中执行的,BeanFactoryPostProc ...

  4. 手写简易版链表及原理分析

    好多人都觉得为什么要自己写这样的数据结构,变成里面不是有吗?为什么要去写,有这个疑问,其实这个疑问这我的脑海中也存在了很长一段时间,本人是学习java编程的,直接看java的集合框架不行吗?这个时候如 ...

  5. 手写一个迷你版Spring MVC框架

    前期准备 我这里要写的是一个迷你版的Spring MVC,我将在一个干净的web工程开始开发,不引入Spring,完全通过JDK来实现. 我们先来看一眼工程: 工程代码结构 第一:在annotatio ...

  6. 手写简易版web框架

    Web框架 Web应用框架(Web application framework)是一种开发框架,用来支持动态网站.网络应用程序及网络服务的开发.Web应用框架有助于减轻网页开发时共通性活动的工作负荷, ...

  7. 手写简版spring --10--容器事件和事件监听器

    一.降低耦合 解耦场景在互联网开发的设计中使用的也是非常频繁,如:这里需要一个注册完成事件推送消息.用户下单我会发送一个MQ.收到我的支付消息就可以发货了等等,都是依靠事件订阅和发布以及MQ消息这样的 ...

  8. 手写简版spring --9--对象作用域和FactoryBean

    一.目标 交给 Spring 管理的 Bean 对象,一定就是我们用类创建出来的 Bean 吗?创建出来的 Bean 就永远是单例的吗,没有可能是原型模式吗?在集合 Spring 框架下,我们使用的 ...

  9. 手写简版spring --2--实现Bean的定义、注册、获取

    一.目标 在上一章节我们初步依照 Spring Bean 容器的概念,实现了一个粗糙版本的代码实现.那么本章节我们需要结合已实现的 Spring Bean 容器进行功能完善,实现 Bean 容器关于 ...

  10. 5 拦截器拦截请求路由_手写简易版axios拦截器,实现微信小程序wx.request的封装与拦截...

    前言: axios是一个功能强大的网络请求库,其中拦截器又是axios的精髓.在小程序的开发或者需要手动实现ajax的时候,没有实现对请求的拦截,开发的时候非常不方便,因此手写一个简易版的axios拦 ...

最新文章

  1. 编程之美初赛第一场--焦距
  2. 易评:软银收购ARM会扼住中国芯发展的咽喉吗?
  3. 100级大橙武升级流程_DNF:女气功升级100级无暇手套,前后伤害对比。
  4. 两个网口芯片接一个变压器_一看就会:详细讲解网络变压器作用
  5. 【论文阅读】Universal Domain Adaptation
  6. 我的15年操作系统开源路——RT-Thread 创始人熊谱翔
  7. hadoop 如何连beeline_impala为hadoop续命
  8. 贪心算法及几个常用的例题
  9. python飞机大战怎么将图片保存_Python飞机大战完整素材包(字体音乐图片)
  10. MTK 6737平台RILD的实现
  11. vue有纵向和横向表头表格
  12. 视频编码器接入指挥调度平台的一种可行方法
  13. 未来10年计算机专业会不会淘汰,未来10年不会“被淘汰”的4个专业,发展潜力较大,就业前景可观...
  14. [MB855]变砖解决
  15. 《身边的礼仪》视频全文
  16. 迭代法求解非线性方程组(含python代码)
  17. 完全否定联想需谨慎,加工贸易对中国制造起到了巨大推动作用
  18. 芝加哥大学计算机博士年薪,专排TOP6芝加哥大学统计学博士录取
  19. mot文件解析成bin
  20. 3D动作绑定_游戏建模大佬教你九招轻轻松松学会三维动画制作绑定技术

热门文章

  1. Python元类---道生一,一生二,二生三
  2. Navicat执行sql文件没反应
  3. Minecraft mod制作简易教程目录
  4. android A problem occurred starting process
  5. Java日期时间主要API:java.time.Duration类和java.time.Period类
  6. 如何观看网页flash视频以及下载视频
  7. word 的使用 —— 快捷键(分节符、分页符、分栏符)
  8. spring security集成jwt
  9. 记一次Spark中 Container killed by YARN for exceeding memory limits的解决过程
  10. js+Canvas 利用js 实现浏览器保存图片到本地