手写 Spring

手写 Spring

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

一、整体思路

思路要熟练背下来

1)配置阶段

  • 配置 web.xml: XDispatchServlet
  • 设定 init-param: contextConfigLocation = classpath:application.xml
  • 设定 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(): 将返回结果输出到浏览器

二、源代码

项目结构:

源代码:

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

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
</dependency>

(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>

(3)application.properties 文件:

scan-package=com.xiaopengwei

(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 "";
}

(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 "";
}

(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 "";
}

(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 "";
}

(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();
                }
            }
        }
    }
}

(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();
        }
    }
}

(10)示例接口:ITestXService:

package com.xiaopengwei.demo.xservice;
/**
 * <p>
 * 接口
 *
 * @author XiaoPengwei
 * @since 2019-07-19
 */
public interface ITestXService {
    String listClassName();
}

(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";
    }
}

(12)测试:

配置 Tomcat 后,访问:

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

http://localhost:8080/test/listClassName

原文地址:https://www.cnblogs.com/xpwi/p/11219083.html

时间: 2024-10-01 02:18:36

手写 Spring的相关文章

我是这样手写Spring的,麻雀虽小,五脏俱全

人见人爱的Spring已然不仅仅只是一个框架了.如今,Spring已然成为了一个生态.但深入了解Spring的却寥寥无几.这里,我带大家一起来看看,我是如何手写Spring的.我将结合对Spring十多年的研究经验,用不到400行代码来描述Spring IOC.DI.MVC的精华设计思想,并保证基本功能完整.首先,我们先来介绍一下Spring的三个阶段,配置阶段.初始化阶段和运行阶段(如图):配置阶段:主要是完成application.xml配置和Annotation配置.初始化阶段:主要是加载

一个老程序员是如何手写Spring MVC的

人见人爱的Spring已然不仅仅只是一个框架了.如今,Spring已然成为了一个生态.但深入了解Spring的却寥寥无几.这里,我带大家一起来看看,我是如何手写Spring的.我将结合对Spring十多年的研究经验,用不到400行代码来描述SpringIOC.DI.MVC的精华设计思想,并保证基本功能完整. 首先,我们先来介绍一下Spring的三个阶段,配置阶段.初始化阶段和运行阶段(如图): 配置阶段:主要是完成application.xml配置和Annotation配置. 初始化阶段:主要是

记录一次阿里架构师全程手写Spring MVC 原

人见人爱的Spring已然不仅仅只是一个框架了.如今,Spring已然成为了一个生态.但深入了解Spring的却寥寥无几.这里,我带大家一起来看看,我是如何手写Spring的.我将结合对Spring十多年的研究经验,用不到400行代码来描述SpringIOC.DI.MVC的精华设计思想,并保证基本功能完整. 首先,我们先来介绍一下Spring的三个阶段,配置阶段.初始化阶段和运行阶段(如图): 配置阶段:主要是完成application.xml配置和Annotation配置. 初始化阶段:主要是

手写 Spring 事务、IOC、DI 和 MVC

Spring AOP 原理 什么是 AOP? AOP 即面向切面编程,利用 AOP 可以对业务进行解耦,提高重用性,提高开发效率 应用场景:日志记录,性能统计,安全控制,事务处理,异常处理 AOP 底层实现原理是采用代理实现的 Spring 事务 基本特性: 原子性 隔离性 一致性 持久性 事务控制分类: 编程式事务:手动控制事务操作 声明式事务:通过 AOP 控制事务 编程式事务实现 使用编程事务实现手动事务 @Component @Scope("prototype") public

手写Spring框架,加深对Spring工作机制的理解!

在我们的日常工作中,经常会用到Spring.Spring Boot.Spring Cloud.Struts.Mybatis.Hibernate等开源框架,有了这些框架的诞生,平时的开发工作量也是变得越来越轻松,我们用 Spring Boot 分分钟可以新建一个Web项目. 记得自己刚开始工作的时候还是在用Servlet写Web项目,自己写数据库连接池,用原生JDBC操作数据库,好了不发散了.回到这篇文章的主题,今天通过手写Spring框架,帮大家深入了解一下Spring的工作机制,文中涉及的代码

spring事物(1)-----手写spring的事物框架

一,区别声明式事物和编程式事物 所谓编程式事务指的是通过编码方式实现事务,即类似于JDBC编程实现事务管理.管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager.对于编程式事务管理,spring推荐使用TransactionTemplate. 声明式事物其实就是编程式事物+spring的AOP代理,在里面我们是见不到手动的begin commit  和rollback的. 管理建立在AOP之上的.其本质是对方法前后进行拦截,然后在目

携程系统架构师带你手写spring mvc,解读spring核心源码!

讲师简介: James老师 系统架构师.项目经理 十余年Java经验,曾就职于携程.人人网等一线互联网公司,专注于java领域,精通软件架构设计,对于高并发.高性能服务有深刻的见解, 在服务化基础架构和微服务技术有大量的建设和设计经验. 课程内容: 1.为什么读Spring源码? 如果你是一名JAVA开发人员,你一定用过Spring Framework. 作为一款非常经典的开源框架,从2004年发布的1.0版本到现在的5.0版本,经历了14年的洗礼, 持久不衰 与其说现在是JAVA的天下, 不如

手写Spring事务框架

Spring事务基于AOP环绕通知和异常通知 编程事务 声明事务 Spring事务底层使用编程事务+AOP进行包装的   = 声明事务 AOP应用场景:  事务 权限 参数验证 什么是AOP技术 AOP技术应用场景 面向切面编程  解决代码复用问题 AOP编程核心点: 在方法之前或者之后处理事情 AOP底层实现原理:代理设计模式 Spring事务基于AOP的环绕通知 为什么用AOP: 复用 解耦 AOP: 静态代需要生成目标代理对象 动态代理不需要生成目标代理对象 动态代理分为:JDK动态代理 

阿里微服务专家手写Spring Boot 实现一个简单的自动配置模块

为了更好的理解 Spring Boot 的 自动配置和工作原理,我们自己来实现一个简单的自动配置模块. 假设,现在项目需要一个功能,需要自动记录项目发布者的相关信息,我们如何通过 Spring Boot 的自动配置,更好的实现功能呢? 实战的开端 – Maven搭建 先创建一个Maven项目,我来手动配置下 POM 文件. 参数的配置 - 属性参数类 首先,我们定义一个自定义前缀,叫做 custom 吧.之前说到,这里的配置参数,可以通过 application.properties 中直接设置