手写 Spring

手写 Spring

2019年07月20日 20:11:00 肖朋伟 阅读数 11023更多

分类专栏: # Spring 学习整理

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/qq_40147863/article/details/96505433

学习自《Spring 5核心原理与30个类手写实战》作者 Tom 老师

手写 Spring

不多说,简历装 X 必备。不过练好还是需要求一定的思维能力。

一、整体思路

思路要熟练背下来

1)配置阶段

  • 配置 web.xml:
    • XDispatchServlet
    • 设定 init-param: contextConfigLocation = applicationContext.properties
    • 设定 url-pattern: /*
  • 配置 Annotation: @XController @XService @XAutowired @XRequestMapping

2)初始化阶段

  • IOC:
    • 调用 init() 方法: 加载配置文件
    • IOC 容器初始化: Map
    • 扫描相关的类: scan-package=“com.xiaopengwei”
    • 创建实例化并保存到容器: 同过反射机制将类实例化放入 IOC 容器中
  • DI:
    • 进行 DI 操作: 扫描 IOC 容器中的实例,给没有赋值的属性自动赋值
  • MVC:
    • 初始化 HandlerMapping: 将一个 URL 和一个 Method 进行一对一的关联映射 Map

3)运行阶段

  • 调用 doGet() / doPost() 方法: Web 容器调用 doGet() / doPost() 方法,获得 request/response 对象
  • 匹配 HandleMapping: 从 request 对象中获得用户输入的 url,找到其对应的 Method
  • 反射调用 method.invoker(): 利用反射调用方法并返回结果
  • response.getWrite().write(): 将返回结果输出到浏览器

二、源代码

GitHub:https://github.com/xpwi/spring-custom

项目结构:

手写 Spring_第1张图片

源代码:

(1)在 pom.xml 引入一个 jar 包


    javax.servlet
    javax.servlet-api
    3.1.0

  • 1
  • 2
  • 3
  • 4
  • 5

(2)web.xml 文件:




    Archetype Created Web Application
    
        xmvc
        com.xiaopengwei.xspring.servlet.XDispatchServlet
        
            contextConfigLocation
            
            application.properties
        
    
    
        xmvc
        /*
    

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

(3)application.properties 文件:

scan-package=com.xiaopengwei
  • 1

(4)自定义注解 XAutowired:

package com.xiaopengwei.xspring.annotation;
import java.lang.annotation.*;
/**
 * 

* * @author XiaoPengwei * @since 2019-07-19 */ @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface XAutowired { String value() default ""; }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

(5)自定义注解 XController:

package com.xiaopengwei.xspring.annotation;
import java.lang.annotation.*;
/**
 * 

* * @author XiaoPengwei * @since 2019-07-19 */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface XController { String value() default ""; }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

(6)自定义注解 XRequestMapping:

package com.xiaopengwei.xspring.annotation;
import java.lang.annotation.*;
/**
 * 

* * @author XiaoPengwei * @since 2019-07-19 */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface XRequestMapping { String value() default ""; }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

(7)自定义注解 XService:

package com.xiaopengwei.xspring.annotation;
import java.lang.annotation.*;
/**
 * 

* * @author XiaoPengwei * @since 2019-07-19 */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface XService { String value() default ""; }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

(8)核心 XDispatchServlet:

package com.xiaopengwei.xspring.servlet;
import com.xiaopengwei.xspring.annotation.XAutowired;
import com.xiaopengwei.xspring.annotation.XController;
import com.xiaopengwei.xspring.annotation.XRequestMapping;
import com.xiaopengwei.xspring.annotation.XService;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
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.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * 

* XSpring * * @author XiaoPengwei * @since 2019-07-19 */ public class XDispatchServlet extends HttpServlet { /** * 属性配置文件 */ private Properties contextConfig = new Properties(); private List classNameList = new ArrayList<>(); /** * IOC 容器 */ Map iocMap = new HashMap(); Map handlerMapping = new HashMap(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //7、运行阶段 try { doDispatch(req, resp); } catch (Exception e) { e.printStackTrace(); resp.getWriter().write("500 Exception Detail:\n" + Arrays.toString(e.getStackTrace())); } } /** * 7、运行阶段,进行拦截,匹配 * * @param req 请求 * @param resp 响应 */ private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws InvocationTargetException, IllegalAccessException { String url = req.getRequestURI(); String contextPath = req.getContextPath(); url = url.replaceAll(contextPath, "").replaceAll("/+", "/"); System.out.println("[INFO-7] request url-->" + url); if (!this.handlerMapping.containsKey(url)) { try { resp.getWriter().write("404 NOT FOUND!!"); return; } catch (IOException e) { e.printStackTrace(); } } Method method = this.handlerMapping.get(url); System.out.println("[INFO-7] method-->" + method); String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName()); System.out.println("[INFO-7] iocMap.get(beanName)->" + iocMap.get(beanName)); // 第一个参数是获取方法,后面是参数,多个参数直接加,按顺序对应 method.invoke(iocMap.get(beanName), req, resp); System.out.println("[INFO-7] method.invoke put {" + iocMap.get(beanName) + "}."); } @Override public void init(ServletConfig servletConfig) throws ServletException { //1、加载配置文件 doLoadConfig(servletConfig.getInitParameter("contextConfigLocation")); //2、扫描相关的类 doScanner(contextConfig.getProperty("scan-package")); //3、初始化 IOC 容器,将所有相关的类实例保存到 IOC 容器中 doInstance(); //4、依赖注入 doAutowired(); //5、初始化 HandlerMapping initHandlerMapping(); System.out.println("XSpring FrameWork is init."); //6、打印数据 doTestPrintData(); } /** * 6、打印数据 */ private void doTestPrintData() { System.out.println("[INFO-6]----data------------------------"); System.out.println("contextConfig.propertyNames()-->" + contextConfig.propertyNames()); System.out.println("[classNameList]-->"); for (String str : classNameList) { System.out.println(str); } System.out.println("[iocMap]-->"); for (Map.Entry entry : iocMap.entrySet()) { System.out.println(entry); } System.out.println("[handlerMapping]-->"); for (Map.Entry entry : handlerMapping.entrySet()) { System.out.println(entry); } System.out.println("[INFO-6]----done-----------------------"); System.out.println("====启动成功===="); System.out.println("测试地址:http://localhost:8080/test/query?username=xiaopengwei"); System.out.println("测试地址:http://localhost:8080/test/listClassName"); } /** * 5、初始化 HandlerMapping */ private void initHandlerMapping() { if (iocMap.isEmpty()) { return; } for (Map.Entry entry : iocMap.entrySet()) { Class clazz = entry.getValue().getClass(); if (!clazz.isAnnotationPresent(XController.class)) { continue; } String baseUrl = ""; if (clazz.isAnnotationPresent(XRequestMapping.class)) { XRequestMapping xRequestMapping = clazz.getAnnotation(XRequestMapping.class); baseUrl = xRequestMapping.value(); } for (Method method : clazz.getMethods()) { if (!method.isAnnotationPresent(XRequestMapping.class)) { continue; } XRequestMapping xRequestMapping = method.getAnnotation(XRequestMapping.class); String url = ("/" + baseUrl + "/" + xRequestMapping.value()).replaceAll("/+", "/"); handlerMapping.put(url, method); System.out.println("[INFO-5] handlerMapping put {" + url + "} - {" + method + "}."); } } } /** * 4、依赖注入 */ private void doAutowired() { if (iocMap.isEmpty()) { return; } for (Map.Entry entry : iocMap.entrySet()) { Field[] fields = entry.getValue().getClass().getDeclaredFields(); for (Field field : fields) { if (!field.isAnnotationPresent(XAutowired.class)) { continue; } System.out.println("[INFO-4] Existence XAutowired."); // 获取注解对应的类 XAutowired xAutowired = field.getAnnotation(XAutowired.class); String beanName = xAutowired.value().trim(); // 获取 XAutowired 注解的值 if ("".equals(beanName)) { System.out.println("[INFO] xAutowired.value() is null"); beanName = field.getType().getName(); } // 只要加了注解,都要加载,不管是 private 还是 protect field.setAccessible(true); try { field.set(entry.getValue(), iocMap.get(beanName)); System.out.println("[INFO-4] field set {" + entry.getValue() + "} - {" + iocMap.get(beanName) + "}."); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } /** * 3、初始化 IOC 容器,将所有相关的类实例保存到 IOC 容器中 */ private void doInstance() { if (classNameList.isEmpty()) { return; } try { for (String className : classNameList) { Class clazz = Class.forName(className); if (clazz.isAnnotationPresent(XController.class)) { String beanName = toLowerFirstCase(clazz.getSimpleName()); Object instance = clazz.newInstance(); // 保存在 ioc 容器 iocMap.put(beanName, instance); System.out.println("[INFO-3] {" + beanName + "} has been saved in iocMap."); } else if (clazz.isAnnotationPresent(XService.class)) { String beanName = toLowerFirstCase(clazz.getSimpleName()); // 如果注解包含自定义名称 XService xService = clazz.getAnnotation(XService.class); if (!"".equals(xService.value())) { beanName = xService.value(); } Object instance = clazz.newInstance(); iocMap.put(beanName, instance); System.out.println("[INFO-3] {" + beanName + "} has been saved in iocMap."); // 找类的接口 for (Class i : clazz.getInterfaces()) { if (iocMap.containsKey(i.getName())) { throw new Exception("The Bean Name Is Exist."); } iocMap.put(i.getName(), instance); System.out.println("[INFO-3] {" + i.getName() + "} has been saved in iocMap."); } } } } catch (Exception e) { e.printStackTrace(); } } /** * 获取类的首字母小写的名称 * * @param className ClassName * @return java.lang.String */ private String toLowerFirstCase(String className) { char[] charArray = className.toCharArray(); charArray[0] += 32; return String.valueOf(charArray); } /** * 2、扫描相关的类 * * @param scanPackage properties --> scan-package */ private void doScanner(String scanPackage) { // package's . ==> / URL resourcePath = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/")); if (resourcePath == null) { return; } File classPath = new File(resourcePath.getFile()); for (File file : classPath.listFiles()) { if (file.isDirectory()) { System.out.println("[INFO-2] {" + file.getName() + "} is a directory."); // 子目录递归 doScanner(scanPackage + "." + file.getName()); } else { if (!file.getName().endsWith(".class")) { System.out.println("[INFO-2] {" + file.getName() + "} is not a class file."); continue; } String className = (scanPackage + "." + file.getName()).replace(".class", ""); // 保存在内容 classNameList.add(className); System.out.println("[INFO-2] {" + className + "} has been saved in classNameList."); } } } /** * 1、加载配置文件 * * @param contextConfigLocation web.xml --> servlet/init-param */ private void doLoadConfig(String contextConfigLocation) { InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation); try { // 保存在内存 contextConfig.load(inputStream); System.out.println("[INFO-1] property file has been saved in contextConfig."); } catch (IOException e) { e.printStackTrace(); } finally { if (null != inputStream) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372

(9)示例:TestController:

package com.xiaopengwei.demo.xcontroller;
import com.xiaopengwei.demo.xservice.ITestXService;
import com.xiaopengwei.xspring.annotation.XAutowired;
import com.xiaopengwei.xspring.annotation.XController;
import com.xiaopengwei.xspring.annotation.XRequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * 

* 前置控制器 * * @author XiaoPengwei * @since 2019-07-19 */ @XController @XRequestMapping("/test") public class TestController { @XAutowired ITestXService testXService; /** * 测试方法 /test/query * * @param req 请求体 * @param resp 响应体 */ @XRequestMapping("/query") public void query(HttpServletRequest req, HttpServletResponse resp) { if (req.getParameter("username") == null) { try { resp.getWriter().write("param username is null"); } catch (IOException e) { e.printStackTrace(); } } else { String paramName = req.getParameter("username"); try { resp.getWriter().write("param username is " + paramName); } catch (IOException e) { e.printStackTrace(); } System.out.println("[INFO-req] New request param username-->" + paramName); } } /** * 测试方法 /test/listClassName * * @param req 请求体 * @param resp 响应体 */ @XRequestMapping("/listClassName") public void listClassName(HttpServletRequest req, HttpServletResponse resp) { String str = testXService.listClassName(); System.out.println("testXService----------=-=-=>" + str); try { resp.getWriter().write(str); } catch (IOException e) { e.printStackTrace(); } } }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

(10)示例接口:ITestXService:

package com.xiaopengwei.demo.xservice;
/**
 * 

* 接口 * * @author XiaoPengwei * @since 2019-07-19 */ public interface ITestXService { String listClassName(); }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

(11)示例实现类 TestXServiceImpl:

package com.xiaopengwei.demo.xservice.impl;
import com.xiaopengwei.demo.xservice.ITestXService;
import com.xiaopengwei.xspring.annotation.XService;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;

/**
 * 

* 业务实现类 * * @author XiaoPengwei * @since 2019-07-19 */ @XService public class TestXServiceImpl implements ITestXService { @Override public String listClassName() { // 假装来自数据库 return "123456TestXServiceImpl"; } }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

(12)测试:

配置 Tomcat 后,访问:

http://localhost:8080/test/query?username=xiaopengwei
  • 1

http://localhost:8080/test/listClassName
  • 1

手写 Spring_第2张图片

手写 Spring_第3张图片

你可能感兴趣的:(后端框架)