├── README.md ├── pom.xml └── src └── main ├── java └── com │ └── xiaopengwei │ ├── demo │ ├── xcontroller │ │ └── TestController.java │ └── xservice │ │ ├── ITestXService.java │ │ └── impl │ │ └── TestXServiceImpl.java │ └── xspring │ ├── annotation │ ├── XAutowired.java │ ├── XController.java │ ├── XRequestMapping.java │ └── XService.java │ └── servlet │ └── XDispatchServlet.java ├── resources └── application.properties └── webapp ├── WEB-INF └── web.xml └── index.jsp /README.md: -------------------------------------------------------------------------------- 1 | # spring-custom 2 | 手写 Spring 3 | 4 | 学习自《Spring 5核心原理与30个类手写实战》作者 Tom 老师 5 | 博客地址:https://icode.blog.csdn.net/article/details/96505433 6 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.xiaopengwei 8 | spring-custom 9 | 1.0-SNAPSHOT 10 | war 11 | 12 | https://github.com/xpwi 13 | 14 | 15 | UTF-8 16 | 1.8 17 | 1.8 18 | 19 | 20 | 21 | 22 | 23 | javax.servlet 24 | javax.servlet-api 25 | 3.1.0 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/main/java/com/xiaopengwei/demo/xcontroller/TestController.java: -------------------------------------------------------------------------------- 1 | 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(); } } } -------------------------------------------------------------------------------- /src/main/java/com/xiaopengwei/demo/xservice/ITestXService.java: -------------------------------------------------------------------------------- 1 | package com.xiaopengwei.demo.xservice; /** *

* 接口 * * @author XiaoPengwei * @since 2019-07-19 */ public interface ITestXService { String listClassName(); } -------------------------------------------------------------------------------- /src/main/java/com/xiaopengwei/demo/xservice/impl/TestXServiceImpl.java: -------------------------------------------------------------------------------- 1 | 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"; } } -------------------------------------------------------------------------------- /src/main/java/com/xiaopengwei/xspring/annotation/XAutowired.java: -------------------------------------------------------------------------------- 1 | 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 ""; } -------------------------------------------------------------------------------- /src/main/java/com/xiaopengwei/xspring/annotation/XController.java: -------------------------------------------------------------------------------- 1 | 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 ""; } -------------------------------------------------------------------------------- /src/main/java/com/xiaopengwei/xspring/annotation/XRequestMapping.java: -------------------------------------------------------------------------------- 1 | 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 ""; } -------------------------------------------------------------------------------- /src/main/java/com/xiaopengwei/xspring/annotation/XService.java: -------------------------------------------------------------------------------- 1 | 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 ""; } -------------------------------------------------------------------------------- /src/main/java/com/xiaopengwei/xspring/servlet/XDispatchServlet.java: -------------------------------------------------------------------------------- 1 | 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(); } } } } } -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | scan-package=com.xiaopengwei -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | Archetype Created Web Application 7 | 8 | 9 | xmvc 10 | com.xiaopengwei.xspring.servlet.XDispatchServlet 11 | 12 | contextConfigLocation 13 | 14 | application.properties 15 | 16 | 17 | 18 | 19 | xmvc 20 | /* 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main/webapp/index.jsp: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Hello World!

4 | 5 | 6 | --------------------------------------------------------------------------------