手写 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:
- <sevlet>
- XDispatchServlet
- 设定 init-param: contextConfigLocation = applicationContext.properties
- <servlet-mapping>
- 设定 url-pattern: /*
- 配置 Annotation: @XController @XService @XAutowired @XRequestMapping
2)初始化阶段
- IOC:
- 调用 init() 方法: 加载配置文件
- IOC 容器初始化: Map<String, Object>
- 扫描相关的类: scan-package=“com.xiaopengwei”
- 创建实例化并保存到容器: 同过反射机制将类实例化放入 IOC 容器中
- DI:
- 进行 DI 操作: 扫描 IOC 容器中的实例,给没有赋值的属性自动赋值
- MVC:
- 初始化 HandlerMapping: 将一个 URL 和一个 Method 进行一对一的关联映射 Map<String, Method>
3)运行阶段
- 调用 doGet() / doPost() 方法: Web 容器调用 doGet() / doPost() 方法,获得 request/response 对象
- 匹配 HandleMapping: 从 request 对象中获得用户输入的 url,找到其对应的 Method
- 反射调用 method.invoker(): 利用反射调用方法并返回结果
- response.getWrite().write(): 将返回结果输出到浏览器
GitHub:https://github.com/xpwi/spring-custom
项目结构:
源代码:
(1)在 pom.xml 引入一个 jar 包
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
- 1
- 2
- 3
- 4
- 5
(2)web.xml 文件:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>xmvc</servlet-name>
<servlet-class>com.xiaopengwei.xspring.servlet.XDispatchServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!--you can't use classpath*: -->
<param-value>application.properties</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>xmvc</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
- 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.*;
/**
* <p>
*
* @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.*;
/**
* <p>
*
* @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.*;
/**
* <p>
*
* @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.*;
/**
* <p>
*
* @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.*;
/**
* <p>
* XSpring
*
* @author XiaoPengwei
* @since 2019-07-19
*/
public class XDispatchServlet extends HttpServlet {
/**
* 属性配置文件
*/
private Properties contextConfig = new Properties();
private List<String> classNameList = new ArrayList<>();
/**
* IOC 容器
*/
Map<String, Object> iocMap = new HashMap<String, Object>();
Map<String, Method> handlerMapping = new HashMap<String, Method>();
@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<String, Object> entry : iocMap.entrySet()) {
System.out.println(entry);
}
System.out.println("[handlerMapping]-->");
for (Map.Entry<String, Method> 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<String, Object> 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<String, Object> 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;
/**
* <p>
* 前置控制器
*
* @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;
/**
* <p>
* 接口
*
* @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;
/**
* <p>
* 业务实现类
*
* @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