05. 手写Spring核心框架

Posted IT BOY

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了05. 手写Spring核心框架相关的知识,希望对你有一定的参考价值。

目录

05 手写Spring核心框架

Pt1 手写IoC/DI

Pt1.1 流程设计

Pt1.2 基础配置

application.properties

pom.xml

web.xml

Pt1.3 注解定义

@MyController

@MyService

@MyAutowired

@MyRequestMapping

@MyRequestParam

Pt1.4 核心代码

DispatcherServlet

ApplicationContext

BeanDefinition

BeanWrapper

Pt1.5 功能验证

Pt2 手写MVC

Pt2.1 流程设计

Pt2.2 MVC九大组件

Pt2.3 基础配置

Pt2.4 核心代码

DispatcherServlet

HandlerMapping

HandlerAdapter

ModelAndView

ViewResolver

View

Pt2.5 功能验证

Pt3 手写AOP

Pt3.1 流程设计

Pt3.2 基础配置

application.properties

Pt3.3 核心代码

AopProxy

JdkDynamicAopProxy

AdvisedSupport

Advice

AopConfig

ApplicationContext

Pt3.4 功能验证


05 手写Spring核心框架

在我们还没有开始Spring源码分析之前,先尝试模仿Spring手写一套类似Spring的核心骨架,这套代码能够帮助我们更好的理解Spring源码的实现架构,帮助我们在源码分析时构建更加清晰的思维脉络。所以这部分其实非常重要,当你在后续的源码分析中迷失时,只需要回到这里,看看我们在手写这段框架时关注了那些组件,就可以把我们的焦点拉回到主干这条线上,沿着这条线就不会迷失。

 

这部分代码,我已经上传到github上,可以参考。

https://github.com/ChenMingMing821/myspring5-action.git

 


Pt1 手写IoC/DI

Pt1.1 流程设计

IoC + DI是在启动的时候初始化的,负责管理Bean的生命周期和依赖关系。

IoC和DI主要涉及核心类:

  • DispatcherServlet:在web.xml定义的启动类Servlet。负责Web容器初始化,以及拦截客户端请求并完成调度和分发;

  • ApplicationContext:Spring运行上下文。负责读取Spring配置,扫描Bean,保存IoC容器。

  • BeanDefinition:保存Spring Bean的定义信息。

  • BeanWrapper:Spring对BeanDefinition的代理,包含了Bean定义和实例化对象信息。

  • BeanDefinitionReader:负责加载Spring配置,读取Bean定义。

 

 

IoC和DI是在Spring启动的过程中完成的,其中DispatcherServlet是入口,初始化整个流程是在ApplicationContext进行控制的。 过程大体分为以下几个步骤:

  1. 加载/解析Spring配置文件,扫描Bean;

  2. 将读取的Bean定义封装成BeanDefinition;

  3. 将Bean注册到IoC容器(未实例化);

  4. 完成依赖注入(自动);

 


Pt1.2 基础配置

application.properties

Spring IoC的自动扫描需要配置scanPackage,即扫描根路径,Spring会扫描根路径下所有Bean定义。当然在Spring中,该配置是在Spring的xml中配置的,这里我们直接以properties文件的形式进行配置,简化代码读取的逻辑,本质上是一样的。

 # 配置类扫描包路径
 scanPackage=com.demo.spring.simulation.v5.test

 

pom.xml

从依赖上可以看出,核心只有Servlet,引入日志、Lombok和Junit是用来简化开发和输出一些验证信息。没有任何的Spring组件依赖,我们要自己手写Spring的核心流程,实现上还是尽量干净一些。

 <?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>org.spring5</groupId>
     <artifactId>myspring5-action</artifactId>
     <version>1.0-SNAPSHOT</version>
     <packaging>war</packaging>
 ​
     <name>myspring5-action 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.8</maven.compiler.source>
         <maven.compiler.target>1.8</maven.compiler.target>
     </properties>
 ​
     <dependencies>
         <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
             <version>3.1.0</version>
         </dependency>
 ​
         <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
             <version>1.18.12</version>
         </dependency>
 ​
         <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
             <version>1.7.25</version>
         </dependency>
 ​
         <!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
         <dependency>
             <groupId>ch.qos.logback</groupId>
             <artifactId>logback-classic</artifactId>
             <version>1.2.3</version>
         </dependency>
 ​
         <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter</artifactId>
             <version>RELEASE</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
 ​
     <build>
         <finalName>myspring5-action</finalName>
         <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.1.0</version>
                 </plugin>
                 <plugin>
                     <artifactId>maven-resources-plugin</artifactId>
                     <version>3.0.2</version>
                 </plugin>
                 <plugin>
                     <artifactId>maven-compiler-plugin</artifactId>
                     <version>3.8.0</version>
                 </plugin>
                 <plugin>
                     <artifactId>maven-surefire-plugin</artifactId>
                     <version>2.22.1</version>
                 </plugin>
                 <plugin>
                     <artifactId>maven-war-plugin</artifactId>
                     <version>3.2.2</version>
                 </plugin>
                 <plugin>
                     <artifactId>maven-install-plugin</artifactId>
                     <version>2.5.2</version>
                 </plugin>
                 <plugin>
                     <artifactId>maven-deploy-plugin</artifactId>
                     <version>2.8.2</version>
                 </plugin>
             </plugins>
         </pluginManagement>
     </build>
 </project>

 


web.xml

配置非常简单,定义了Web容器启动Servlet(MyDispatcherServlet)和配置路径。

 <?xml version="1.0" encoding="UTF-8"?>
 <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
          version="3.1">
 ​
     <display-name>myspring5-action</display-name>
     <welcome-file-list>
         <welcome-file>index.jsp</welcome-file>
     </welcome-file-list>
     
     <servlet>
         <servlet-name>springMVC</servlet-name>
         <servlet-class>com.demo.spring.simulation.v5.servlet.MyDispatcherServlet</servlet-class>
         <init-param>
             <param-name>contextConfigLocation</param-name>
             <param-value>classpath:application.properties</param-value>
         </init-param>
         <load-on-startup>1</load-on-startup>
         <async-supported>true</async-supported>
     </servlet>
     <servlet-mapping>
         <servlet-name>springMVC</servlet-name>
         <url-pattern>/*</url-pattern>
     </servlet-mapping>
 </web-app>

 


Pt1.3 注解定义

Spring注解比较多,根据模拟过程中的需要选择性的实现部分。

 

@MyController

 package com.demo.spring.simulation.v5.annotation;
 ​
 import java.lang.annotation.*;
 ​
 /**
  * 自定义Controller注解
  */
 @Target({ElementType.TYPE})
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface MyController {
     String value() default "";
 }

 

@MyService

 package com.demo.spring.simulation.v5.annotation;
 ​
 import java.lang.annotation.*;
 ​
 /**
  * 自定义Service注解
  */
 @Target({ElementType.TYPE})
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface MyService {
     String value() default "";
 }

 

@MyAutowired

 package com.demo.spring.simulation.v5.annotation;
 ​
 import java.lang.annotation.*;
 ​
 /**
  * 自定义Autowired注解
  */
 @Target({ElementType.FIELD})
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface MyAutowired {
     String value() default "";
 }

 

@MyRequestMapping

 package com.demo.spring.simulation.v5.annotation;
 ​
 import java.lang.annotation.*;
 ​
 /**
  * 自定义RequestMapping注解
  */
 @Target({ElementType.TYPE, ElementType.METHOD})
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface MyRequestMapping {
     String value() default "";
 }

 

@MyRequestParam

 package com.demo.spring.simulation.v5.annotation;
 ​
 import java.lang.annotation.*;
 ​
 /**
  * 自定义RequestParam注解
  */
 @Target({ElementType.PARAMETER})
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface MyRequestParam {
     String value() default "";
 }

 


Pt1.4 核心代码

DispatcherServlet

DispatcherServlet作为启动的整个入口,init()负责初始化IoC、DI、MVC和AOP的环境。这里先介绍IoC和DI的加载过程,从代码可以看出,初始化过程是在ApplicationContext中完成的。

DispatherServlet#init是入口,我们从这里开始看整个流程的处理。

/**
 * DispatcherServlet负责请求调度和分发。
 */
public class MyDispatcherServlet extends HttpServlet {

    // Spring配置文件路径
    private static final String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";

    // Spring上下文,Spring IoC容器
    private MyApplicationContext applicationContext;

    @Override
    public void init(ServletConfig config) throws ServletException {
        log.info("DispatcherServlet -> Create Web Server Starting.");

        // 1、初始化ApplicationContext。ApplicationContext包含了Spring核心IoC容器,完成Bean扫描、初始化和DI。
        log.info("DispatcherServlet -> Init Spring IoC/DI Starting.");
        applicationContext = new MyApplicationContext(config.getInitParameter(CONTEXT_CONFIG_LOCATION));
        log.info("DispatcherServlet -> Init Spring IoC/DI Finished.");

		// TODO
        
        log.info("DispatcherServlet -> Create Web Server Finished.");
    }

    @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 {
		// TODO
    }
}

 


ApplicationContext

DispatcherServlet#init直接调用ApplicationContext的构造器执行IoC初始化,根据ApplicationContext构造器的逻辑一步一步来看代码逻辑。

package com.demo.spring.simulation.v5.context;


import com.demo.spring.simulation.v5.annotation.MyAutowired;
import com.demo.spring.simulation.v5.annotation.MyController;
import com.demo.spring.simulation.v5.annotation.MyService;
import com.demo.spring.simulation.v5.aop.MyJdkDynamicAopProxy;
import com.demo.spring.simulation.v5.aop.config.MyAopConfig;
import com.demo.spring.simulation.v5.aop.support.MyAdvisedSupport;
import com.demo.spring.simulation.v5.beans.MyBeanWrapper;
import com.demo.spring.simulation.v5.beans.config.MyBeanDefinition;
import com.demo.spring.simulation.v5.beans.support.MyBeanDefinitionReader;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 完成Bean的扫描、创建和DI。
 */
@Slf4j
public class MyApplicationContext {

    // 负责读取Bean配置
    private MyBeanDefinitionReader reader;

    // 存储注册Bean定义的IoC容器
    private Map<String, MyBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, MyBeanDefinition>();

    // 存放单例的IoC容器
    private Map<String, Object> factoryBeanObjectCache = new HashMap<String, Object>();

    // 通用的IoC容器
    private Map<String, MyBeanWrapper> factoryBeanInstanceCache = new HashMap<String, MyBeanWrapper>();

    /**
     * Spring上下文环境初始化
     *
     * @param configLocations 配置文件路径
     */
    public MyApplicationContext(String... configLocations) {

        // 1、加载、解析配置文件,扫描相关的类。
        reader = new MyBeanDefinitionReader(configLocations);
        log.info("ApplicationContext -> 1、加载、解析配置文件,扫描相关的类。");

        try {
            // 2、将扫描的Bean封装成BeanDefinition。
            List<MyBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
            log.info("ApplicationContext -> 2、将扫描的Bean封装成BeanDefinition。");

            // 3、注册,把BeanDefintion缓存到容器。
            doRegistBeanDefinition(beanDefinitions);
            log.info("ApplicationContext -> 3、注册,把BeanDefintion缓存到容器。");

            // 4、完成自动依赖注入。
            doAutowrited();
            log.info("ApplicationContext -> 4、完成自动依赖注入。");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 完成Bean的实例化和自动依赖注入(非延迟加载的场景)。
     */
    private void doAutowrited() {
        // 到这步,所有的Bean并没有真正的实例化,还只是配置阶段。
        for (Map.Entry<String, MyBeanDefinition> beanDefinitionEntry : this.beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            // getBean才真正完成依赖注入
            getBean(beanName);
        }
    }

    /**
     * 把BeanDefintion缓存起来
     *
     * @param beanDefinitions 通过扫描配置文件获取的Bean定义
     * @throws Exception
     */
    private void doRegistBeanDefinition(List<MyBeanDefinition> beanDefinitions) throws Exception {
        log.info("ApplicationContext -> 缓存BeanDefinition信息。");

        for (MyBeanDefinition beanDefinition : beanDefinitions) {

            // Bean在IoC容器中名称必须唯一
            if (this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
                throw new Exception("The " + beanDefinition.getFactoryBeanName() + "is exists");
            }

            // 分别用两种名称存储,便于查找
            beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
            beanDefinitionMap.put(beanDefinition.getBeanClassName(), beanDefinition);
        }
    }

    /**
     * Bean的实例化和DI是从这个方法开始的。
     *
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        log.info("ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。");

        // 1、先拿到BeanDefinition配置信息
        MyBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);

        // 2、反射实例化newInstance();
        Object instance = instantiateBean(beanName, beanDefinition);

        // 3、封装成一个叫做BeanWrapper
        MyBeanWrapper beanWrapper = new MyBeanWrapper(instance);

        // 4、保存到IoC容器
        factoryBeanInstanceCache.put(beanName, beanWrapper);

        // 5、执行依赖注入
        populateBean(beanName, beanDefinition, beanWrapper);

        // 6、返回对象
        return beanWrapper.getWrapperInstance();
    }

    public Object getBean(Class<?> beanClass) {
        return getBean(beanClass.getName());
    }

    /**
     * DI核心逻辑。
     *
     * @param beanName
     * @param beanDefinition
     * @param beanWrapper
     */
    private void populateBean(String beanName, MyBeanDefinition beanDefinition, MyBeanWrapper beanWrapper) {
        log.info("ApplicationContext -> 完成依赖注入核心逻辑。");

        // TODO 可能涉及到循环依赖待解决,如果依赖对象还未实例化,注入的示例为Null引发后续问题,这里需要考虑如何解决。

        // 1、拿到当前Bean实例化对象
        Object instance = beanWrapper.getWrapperInstance();

        // 2、拿到当前Bean的类信息
        Class<?> clazz = beanWrapper.getWrapperClass();

        // 3、只有注解的类,才执行依赖注入
        if (!(clazz.isAnnotationPresent(MyController.class) || clazz.isAnnotationPresent(MyService.class))) {
            return;
        }

        // 把所有的包括private/protected/default/public 修饰字段都取出来
        // TODO 这里只考虑接口注入的方式,实际还要考虑构造器注入和Setter注入。
        for (Field field : clazz.getDeclaredFields()) {

            // 是否被Autowired标记为自动注入
            if (!field.isAnnotationPresent(MyAutowired.class)) {
                continue;
            }

            MyAutowired autowired = field.getAnnotation(MyAutowired.class);
            // 如果用户没有自定义的beanName,就默认根据类型注入
            String autowiredBeanName = autowired.value().trim();
            if ("".equals(autowiredBeanName)) {

                // field.getType().getName() 获取字段的类型的全限定名
                autowiredBeanName = toLowerFirstCase(field.getType().getSimpleName());
            }

            // 暴力访问
            field.setAccessible(true);

            try {
                // 获取对应名称的bean实例对象 TODO
                // 此处没有考虑Bean的实例化顺序,可能需要注入的对象此时还没有完成实例化,在IoC容器中无法正确获取。不过除了在初始化时触发DI,
                // 在实际调用的时候,通过getBean()获取对象时,仍然会触发DI操作。
                if (this.factoryBeanInstanceCache.get(autowiredBeanName) == null) {
                    continue;
                }

                // ioc.get(beanName) 相当于通过接口的全名拿到接口的实现的实例
                field.set(instance, this.factoryBeanInstanceCache.get(autowiredBeanName).getWrapperInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                continue;
            }
        }
    }

    /**
     * 创建真正的实例对象
     *
     * @param beanName
     * @param beanDefinition
     * @return
     */
    private Object instantiateBean(String beanName, MyBeanDefinition beanDefinition) {
        log.info("ApplicationContext -> 通过反射创建Bean实例。");

        String className = beanDefinition.getBeanClassName();
        Object instance = null;
        try {
            Class<?> clazz = Class.forName(className);
            instance = clazz.newInstance();

            // 默认的类名首字母小写
            this.factoryBeanObjectCache.put(beanName, instance);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    /**
     * 已注册所有Bean的名称
     *
     * @return
     */
    public String[] getBeanDefinitionNames() {
        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
    }

    /**
     * 已注册Bean的数量
     *
     * @return
     */
    public int getBeanDefinitionCount() {
        return this.beanDefinitionMap.size();
    }

    public Properties getConfig() {
        return this.reader.getConfig();
    }

    /**
     * 将大写字母转换为小写
     *
     * @param simpleName
     * @return
     */
    private String toLowerFirstCase(String simpleName) {
        char[] chars = simpleName.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }
}

 


BeanDefinition

package com.demo.spring.simulation.v5.beans.config;

import lombok.Data;

/**
 * Spring Bean定义信息
 */
@Data
public class MyBeanDefinition {
    // Bean全路径类名
    private String beanClassName;

    // Bean在IoC容器中名称
    private String factoryBeanName;
}


BeanDefinitionReader

package com.demo.spring.simulation.v5.beans.support;

import com.demo.spring.simulation.v5.beans.config.MyBeanDefinition;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * 扫描配置文件,读取Bean定义
 */
@Slf4j
public class MyBeanDefinitionReader {

    // 保存扫描的结果
    private List<String> regitryBeanClasses = new ArrayList<String>();

    // 保存配置信息
    private Properties contextConfig = new Properties();

    public MyBeanDefinitionReader(String... configLocations) {
        log.info("BeanDefinitionReader -> 构造器执行开始。");

        // 1、读取配置信息。
        doLoadConfig(configLocations[0]);
        log.info("BeanDefinitionReader -> 1、读取配置信息。");

        // 2、扫描配置文件中的配置的相关的类。
        doScanner(contextConfig.getProperty("scanPackage"));
        log.info("BeanDefinitionReader -> 2、扫描配置文件中的配置的相关的类。");

        log.info("BeanDefinitionReader -> 构造器执行完成。");
    }

    /**
     * 将Bean封装为BeanDefinition
     *
     * @return
     */
    public List<MyBeanDefinition> loadBeanDefinitions() {
        log.info("BeanDefinitionReader -> 将扫描的Bean信息封装成BeanDefinition。");

        List<MyBeanDefinition> result = new ArrayList<MyBeanDefinition>();
        try {
            for (String className : regitryBeanClasses) {
                Class<?> beanClass = Class.forName(className);

                // 接口不能实例化
                if (beanClass.isInterface()) {
                    continue;
                }

                // BeanName有三种情况:
                // 1、默认是类名首字母小写
                // 2、自定义名称
                // 3、接口注入
                result.add(doCreateBeanDefinition(toLowerFirstCase(beanClass.getSimpleName()), beanClass.getName()));

                // 如果是多个实现类,只能覆盖
                for (Class<?> i : beanClass.getInterfaces()) {
                    result.add(doCreateBeanDefinition(i.getName(), beanClass.getName()));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }

    private MyBeanDefinition doCreateBeanDefinition(String beanName, String beanClassName) {
        MyBeanDefinition beanDefinition = new MyBeanDefinition();
        beanDefinition.setFactoryBeanName(beanName);
        beanDefinition.setBeanClassName(beanClassName);
        return beanDefinition;
    }

    /**
     * 从配置文件中加载Spring配置信息
     *
     * @param contextConfigLocation
     */
    private void doLoadConfig(String contextConfigLocation) {
        log.info("BeanDefinitionReader -> 加载Spring配置文件。");

        InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation.replaceAll("classpath:", ""));
        try {
            contextConfig.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 根据配置的basePackage扫描获取Bean定义
     *
     * @param scanPackage
     */
    private void doScanner(String scanPackage) {
        log.info("BeanDefinitionReader -> 根据scanPackage路径逐层扫描,获取Bean定义。");

        //jar 、 war 、zip 、rar
        URL url = this.getClass().getClassLoader().getResource(scanPackage.replaceAll("\\\\.", "/"));
        File classPath = new File(url.getFile());

        //当成是一个ClassPath文件夹
        for (File file : classPath.listFiles()) {
            if (file.isDirectory()) {
                doScanner(scanPackage + "." + file.getName());
            } else {
                if (!file.getName().endsWith(".class")) {
                    continue;
                }
                //全类名 = 包名.类名
                String className = (scanPackage + "." + file.getName().replace(".class", ""));
                //Class.forName(className);
                regitryBeanClasses.add(className);
            }
        }
    }

    /**
     * 将大写字母转换为小写
     *
     * @param simpleName
     * @return
     */
    private String toLowerFirstCase(String simpleName) {
        char[] chars = simpleName.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

    /**
     * 获取配置信息
     *
     * @return
     */
    public Properties getConfig() {
        return this.contextConfig;
    }
}

 


BeanWrapper

package com.demo.spring.simulation.v5.beans;

/**
 * Spring IoC容器对Bean生成的代理类
 */
public class MyBeanWrapper {
    // Bean的实例化对象
    private Object wrappedInstance;

    // Bean的Class信息
    private Class<?> wrapperClass;

    public MyBeanWrapper(Object wrappedInstance) {
        this.wrappedInstance = wrappedInstance;
        this.wrapperClass = wrappedInstance.getClass();
    }

    public Object getWrapperInstance() {
        return this.wrappedInstance;
    }

    // 返回代理Class
    public Class<?> getWrapperClass() {
        return this.wrapperClass;
    }
}

 


Pt1.5 功能验证

IoC测试我们直接以main启动ApplicationContext的模式来验证,推荐使用DEBUG模式看每一步数据的处理,这里为了简单描述,我直接输出测试代码和结果。

 

  • 测试启动类

/**
 * Spring IoC和DI测试类。
 */
public class MyApplicationContextTest {

    public static void main(String[] args) {
        MyApplicationContext applicationContext = new MyApplicationContext("classpath:application.properties");
        // IoC容器初始化时,通过执行getBean完成DI。此处再次调用getBean防止有未被注入的属性。
        DemoController demoController = (DemoController) applicationContext.getBean(DemoController.class);
        demoController.say();
    }
}

 

  • 业务处理入口

@MyController
public class DemoController {

    @MyAutowired()
    private DemoService demoService;

    public void say() {
        demoService.say();
    }
}

 

  • 业务处理核心类

@MyService
public class DemoService {
    public void say() {
        System.out.println("执行自定义Service方法。");
    }
}

 

  • 测试结果输出

启动测试类,看输出结果。

17:02:30.638 [main] INFO com.demo.spring.simulation.v5.beans.support.MyBeanDefinitionReader - BeanDefinitionReader -> 构造器执行开始。
17:02:30.645 [main] INFO com.demo.spring.simulation.v5.beans.support.MyBeanDefinitionReader - BeanDefinitionReader -> 加载Spring配置文件。
17:02:30.648 [main] INFO com.demo.spring.simulation.v5.beans.support.MyBeanDefinitionReader - BeanDefinitionReader -> 1、读取配置信息。
17:02:30.648 [main] INFO com.demo.spring.simulation.v5.beans.support.MyBeanDefinitionReader - BeanDefinitionReader -> 根据scanPackage路径逐层扫描,获取Bean定义。
17:02:30.649 [main] INFO com.demo.spring.simulation.v5.beans.support.MyBeanDefinitionReader - BeanDefinitionReader -> 根据scanPackage路径逐层扫描,获取Bean定义。
17:02:30.652 [main] INFO com.demo.spring.simulation.v5.beans.support.MyBeanDefinitionReader - BeanDefinitionReader -> 根据scanPackage路径逐层扫描,获取Bean定义。
17:02:30.653 [main] INFO com.demo.spring.simulation.v5.beans.support.MyBeanDefinitionReader - BeanDefinitionReader -> 根据scanPackage路径逐层扫描,获取Bean定义。
17:02:30.653 [main] INFO com.demo.spring.simulation.v5.beans.support.MyBeanDefinitionReader - BeanDefinitionReader -> 2、扫描配置文件中的配置的相关的类。
17:02:30.653 [main] INFO com.demo.spring.simulation.v5.beans.support.MyBeanDefinitionReader - BeanDefinitionReader -> 构造器执行完成。
17:02:30.653 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 1、加载、解析配置文件,扫描相关的类。
17:02:30.653 [main] INFO com.demo.spring.simulation.v5.beans.support.MyBeanDefinitionReader - BeanDefinitionReader -> 将扫描的Bean信息封装成BeanDefinition。
17:02:30.658 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 2、将扫描的Bean封装成BeanDefinition。
17:02:30.658 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 缓存BeanDefinition信息。
17:02:30.658 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 3、注册,把BeanDefintion缓存到容器。
17:02:30.659 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.659 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.674 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.689 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.689 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.690 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.710 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.712 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.713 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.713 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.714 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.715 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.715 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.715 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.720 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.725 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.725 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.726 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.726 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.726 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.733 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.734 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.734 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.735 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.735 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.735 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.736 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.736 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.736 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.736 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.736 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.737 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.738 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.738 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.738 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.747 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.753 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.753 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.755 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.755 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.755 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.758 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.760 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.761 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.764 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.764 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.764 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.767 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.767 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.767 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.770 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.771 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.771 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.773 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.776 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.777 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.779 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.779 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.780 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.782 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
17:02:30.784 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 4、完成自动依赖注入。
17:02:30.785 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 执行getBean(),将BeanDefinition保存到IoC容器并完成自动依赖注入。
17:02:30.785 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 通过反射创建Bean实例。
17:02:30.818 [main] INFO com.demo.spring.simulation.v5.context.MyApplicationContext - ApplicationContext -> 完成依赖注入核心逻辑。
执行自定义Service方法。

Process finished with exit code 0

 

 


Pt2 手写MVC

Pt2.1 流程设计

MVC概念中,M为Model,代表数据;V为View,代表展现层,比如JSP、html等;C是控制层,负责整体业务逻辑的处理和调度。

 

在SpringMVC中,Handler是核心逻辑的处理器,即MVC中的C也就是Controller层,它的具体表现形式有很多,可以是类,也可以是方法。在Controller层中@RequestMapping标注的所有方法都可以看成是一个Handler,只要可以实际处理请求就可以是Handler。

用户在客户端发起请求,请求URL对应C层的具体Handler(处理器),Handler完成逻辑处理后,输出结果数据(即M层),然后包装成View(V层)返回给客户端完成展现。Spring MVC核心类有以下:

  • DispatcherServlet 请求调度

  • HandlerMapping 请求映射

  • HandlerAdapter 请求方法适配器

  • ModelAndView 页面数据封装

  • ViewResolver 视图解析器

  • View 自定义模板引擎

 


Pt2.2 MVC九大组件

在实现Spring MVC手写源码之前,先来介绍下Spring九大组件,这也是我们在Spring MVC初始化过程中要完成的操作。

【1. HandlerMapping】

HandlerMapping是用来查找Handler的。在SpringMVC中会有很多请求,每个请求都需要一个Handler(即Controller)处理,具体接收到一个请求之后使用哪个Handler进行处理呢?这就是HandlerMapping需要做的事。

 

【2. HandlerAdapter】

从名字上看,它就是一个适配器。因为SpringMVC中的Handler可以是任意的形式,只要能处理请求就ok,但是Servlet需要的处理方法的结构却是固定的,都是以request和response为参数的方法。如何让固定的Servlet处理方法调用灵活的Handler来进行处理呢?这就是HandlerAdapter要做的事情。

小结:Handler(即Controller)是用来干活的工具;HandlerMapping用于根据需要干的活找到相应的工具;HandlerAdapter是使用工具干活的人。

 

【3. HandlerExceptionResolver】

其它组件都是用来干活的。在干活的过程中难免会出现问题,出问题后怎么办呢?这就需要有一个专门的角色对异常情况进行处理,在SpringMVC中就是HandlerExceptionResolver。具体来说,此组件的作用是根据异常设置ModelAndView,之后再交给render方法进行渲染。

 

【4. ViewResolver】

ViewResolver用来将String类型的视图名和Locale解析为View类型的视图。View是用来渲染页面的,也就是将程序返回的参数填入模板里,生成html(也可能是其它类型)文件。这里就有两个关键问题:使用哪个模板?用什么技术(规则)填入参数?这其实是ViewResolver主要要做的工作,ViewResolver需要找到渲染所用的模板和所用的技术(也就是视图的类型)进行渲染,具体的渲染过程则交由不同的视图自己完成。

 

【5. RequestToViewNameTranslator】

ViewName是根据ViewName查找View,但有的Handler处理完后并没有设置View也没有设置ViewName,这时就需要从request获取ViewName了,如何从request中获取ViewName就是RequestToViewNameTranslator要做的事情了。RequestToViewNameTranslator在Spring MVC容器里只可以配置一个,所以所有request到ViewName的转换规则都要在一个Translator里面全部实现。

 

【6. LocaleResolver】

解析视图需要两个参数:一是视图名,另一个是Locale。视图名是处理器返回的,Locale是从哪里来的?这就是LocaleResolver要做的事情。LocaleResolver用于从request解析出Locale,Locale就是zh-cn之类,表示一个区域,有了这个就可以对不同区域的用户显示不同的结果。SpringMVC主要有两个地方用到了Locale:一是ViewResolver视图解析的时候;二是用到国际化资源或者主题的时候。

 

【7. ThemeResolver】

用于解析主题。SpringMVC中一个主题对应一个properties文件,里面存放着跟当前主题相关的所有资源、如图片、css样式等。SpringMVC的主题也支持国际化,同一个主题不同区域也可以显示不同的风格。SpringMVC中跟主题相关的类有 ThemeResolver、ThemeSource和Theme。主题是通过一系列资源来具体体现的,要得到一个主题的资源,首先要得到资源的名称,这是ThemeResolver的工作。然后通过主题名称找到对应的主题(可以理解为一个配置)文件,这是ThemeSource的工作。最后从主题中获取资源就可以了。

 

【8. MultipartResolver】

用于处理上传请求。处理方法是将普通的request包装成MultipartHttpServletRequest,后者可以直接调用getFile方法获取File,如果上传多个文件,还可以调用getFileMap得到FileName->File结构的Map。此组件中一共有三个方法,作用分别是判断是不是上传请求,将request包装成MultipartHttpServletRequest、处理完后清理上传过程中产生的临时资源。

 

【9. FlashMapManager】

用来管理FlashMap的,FlashMap主要用在redirect中传递参数。

 


Pt2.3 基础配置

在配置文件中,我们配置静态资源的根路径。

# 静态资源路径
templateRoot=/webapp/WEB-INF/view

 


Pt2.4 核心代码

DispatcherServlet

在DispatcherServlet中完成了IoC、DI和MVC的初始化动作。

package com.demo.spring.simulation.v5.servlet;

import com.demo.spring.simulation.v5.annotation.MyController;
import com.demo.spring.simulation.v5.annotation.MyRequestMapping;
import com.demo.spring.simulation.v5.context.MyApplicationContext;
import lombok.extern.slf4j.Slf4j;

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.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * DispatcherServlet负责请求调度和分发。
 */
@Slf4j
public class MyDispatcherServlet extends HttpServlet {

    // Spring配置文件路径
    private static final String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";

    // Spring上下文,Spring IoC容器
    private MyApplicationContext applicationContext;

    // 保存请求URL和处理方法的映射关系
    private List<MyHandlerMapping> handlerMappings = new ArrayList<MyHandlerMapping>();

    // 保存请求映射和处理Handler的关系
    private Map<MyHandlerMapping, MyHandlerAdapter> handlerAdapters = new HashMap<MyHandlerMapping, MyHandlerAdapter>();

    // 保存所有View解析器
    private List<MyViewResolver> viewResolvers = new ArrayList<MyViewResolver>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        log.info("DispatcherServlet -> Create Web Server Starting.");

        // 1、初始化ApplicationContext。ApplicationContext包含了Spring核心IoC容器,完成Bean扫描、初始化和DI。
        log.info("DispatcherServlet -> Init Spring IoC/DI Starting.");
        applicationContext = new MyApplicationContext(config.getInitParameter(CONTEXT_CONFIG_LOCATION));
        log.info("DispatcherServlet -> Init Spring IoC/DI Finished.");

        // 2、初始化Spring MVC九大组件
        log.info("DispatcherServlet -> Init Spring MVC Starting.");
        initStrategies(applicationContext);
        log.info("DispatcherServlet -> Init Spring MVC Finished.");

        log.info("DispatcherServlet -> Create Web Server Finished.");
    }

    @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 {
        log.info("DispatcherServlet -> Receive client request.");

        try {
            // 3、委派,根据URL去找到一个对应的Method并通过response返回
            doDispatch(req, resp);
        } catch (Exception e) {
            try {
                processDispatchResult(req, resp, new MyModelAndView("500"));
            } catch (Exception e1) {
                e1.printStackTrace();
                resp.getWriter().write("500 Exception,Detail : " + Arrays.toString(e.getStackTrace()));
            }
        }

        log.info("DispatcherServlet -> Return client response.");
    }

    /**
     * 完成Spring MVC组件的初始化。
     *
     * @param context
     */
    private void initStrategies(MyApplicationContext context) {
        // 1、多文件上传的组件 TODO
        // initMultipartResolver(context);
        // log.info("DispatcherServlet -> 1、多文件上传的组件");

        // 2、初始化本地语言环境 TODO
        // initLocaleResolver(context);
        // log.info("DispatcherServlet -> 2、初始化本地语言环境");

        // 3、初始化模板处理器 TODO
        // initThemeResolver(context);
        // log.info("DispatcherServlet -> 3、初始化模板处理器");

        // 4、初始化HandlerMapping,必须实现。
        initHandlerMappings(context);
        log.info("DispatcherServlet -> 4、初始化HandlerMapping,必须实现。");

        // 5、初始化参数适配器,必须实现。
        initHandlerAdapters(context);
        log.info("DispatcherServlet -> 5、初始化参数适配器,必须实现。");

        // 6、初始化异常拦截器 TODO
        // initHandlerExceptionResolvers(context);
        // log.info("DispatcherServlet -> 6、初始化异常拦截器");

        // 7、初始化视图预处理器 TODO
        // initRequestToViewNameTranslator(context);
        // log.info("DispatcherServlet -> 7、初始化视图预处理器");

        // 8、初始化视图转换器,必须实现。
        initViewResolvers(context);
        log.info("DispatcherServlet -> 8、初始化视图转换器,必须实现。");

        // 9、初始化FlashMap管理器 TODO
        // initFlashMapManager(context);
        // log.info("DispatcherServlet -> 9、初始化FlashMap管理器");
    }

    /**
     * HandlerMapping:保存请求URL和处理方法的映射关系。
     *
     * @param context
     */
    private void initHandlerMappings(MyApplicationContext context) {
        log.info("DispatcherServlet -> 解析和缓存HandlerMapping");

        if (this.applicationContext.getBeanDefinitionCount() == 0) {
            return;
        }

        for (String beanName : this.applicationContext.getBeanDefinitionNames()) {
            Object instance = applicationContext.getBean(beanName);
            Class<?> clazz = instance.getClass();

            // 1、Controller注解的类才具备URL配置
            if (!clazz.isAnnotationPresent(MyController.class)) {
                continue;
            }

            // 2、提取 class上配置的base_url
            String baseUrl = "";
            if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
                baseUrl = clazz.getAnnotation(MyRequestMapping.class).value();
            }

            // 3、获取public的方法
            for (Method method : clazz.getMethods()) {
                if (!method.isAnnotationPresent(MyRequestMapping.class)) {
                    continue;
                }
                // 4、提取每个方法上面配置的url
                MyRequestMapping requestMapping = method.getAnnotation(MyRequestMapping.class);

                // 5、拼接URL
                String regex = ("/" + baseUrl + "/" + requestMapping.value().replaceAll("\\\\*", ".*")).replaceAll("/+", "/");
                Pattern pattern = Pattern.compile(regex);


                // 6、保存HandlerMapping映射关系
                handlerMappings.add(new MyHandlerMapping(pattern, instance, method));
            }
        }
    }

    /**
     * 初始化参数适配器。
     *
     * @param context
     */
    private void initHandlerAdapters(MyApplicationContext context) {
        log.info("DispatcherServlet -> 创建HandlerAdapter处理类。");

        // HandlerAdapter调用具体的方法对用户发来的请求来进行处理,所以每个HandlerMapping都对应一个HandlerAdapter。
        for (MyHandlerMapping handlerMapping : handlerMappings) {
            this.handlerAdapters.put(handlerMapping, new MyHandlerAdapter());
        }
    }

    /**
     * 根据请求URL找对对应处理Handler完成请求,并返回Response。
     *
     * @param req
     * @param resp
     * @throws Exception
     */
    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        log.info("DispatcherServlet -> 请求分发");

        // 1、通过从Request获得请

以上是关于05. 手写Spring核心框架的主要内容,如果未能解决你的问题,请参考以下文章

从零开始手写 spring ioc 框架,深入学习 spring 源码

从零开始手写 spring ioc 框架,深入学习 spring 源码

手写Spring事务框架

:开篇介绍

手写迷你Spring框架

30个类手写Spring核心原理之AOP代码织入