手写spring(一)------------------------------------------------------------------基本的加载流程

项目地址:https://github.com/gongxianshengjiadexiaohuihui/noobspring

准备工作:

这个项目是一个maven项目,pom文件内容

<?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>com.ggp.noobspring</groupId>
    <artifactId>noobspring</artifactId>
    <version>1.0-SNAPSHOT</version>


    <properties>
        <servlet.api.version>2.4</servlet.api.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>${servlet.api.version}</version>
            <scope>provided</scope>
        </dependency>
        <!--日志文件配置-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.2.2</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.7</version>
        </dependency>
        <!--Cglib动态代理-->
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm</artifactId>
            <version>5.2</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib-nodep</artifactId>
            <version>3.2.2</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.mortbay.jetty</groupId>
                <artifactId>maven-jetty-plugin</artifactId>
                <version>6.1.26</version>
                <configuration>
                    <contextPath>noobspring</contextPath>
                    <connectors>
                        <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
                            <port>8090</port>
                        </connector>
                    </connectors>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

关于spring的包就一个,java.servlet的api

然后是日志文件的包,至于为什么这样写,可以参考我的一篇博客设计模式学习笔记---------------------------------------------门面模式(外观模式)和日志框架(sl4j、logback、log4j

后面是cglib代理需要的包在aop中会用到

然后是jetty的包,我们的web容器是jetty

接着是配置maven的编译插件,因为这个项目需要用到方法的形参,所以需要设置一个编译参数 -parameters 如果你不用maven编译的话,需要在IDE中设置这个编译参数IDEA中的位置

我们知道spring项目首先会加载web.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_4.xsd"
         version="2.4">

    <display-name>Noob Web Application</display-name>

    <servlet>
        <servlet-name>nbmvc</servlet-name>
        <servlet-class>com.ggp.framework.servlet.NBDispatcherServlet</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>
    </servlet>

    <servlet-mapping>
        <servlet-name>nbmvc</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

实现spring的第一步是重写DispatcherServlet 继承HttpServlet,我们重写它的名字是NBDispatcherServlet,并且把它配置到了web.xml中去,并且配置了它的一个初始化参数contextConfigLocation参数,参数的值是我们配置文件所在的位置,我们配置文件的

scanPackage=com.ggp

我们看到下面还配置了一个servlet-mapping从字面的意思我们知道它是一个映射,而且通过servlet-name属性可以知道这个映射的属性指向nbmvc这个我们自己写的servlet,而url-pattern这个属性是一个正则表达式把符合表达式的请求路径分配到此servlet中去。

重写DispatcherServlet时,我们需要重写两个重要的方法 

一个是init,这个方法会在DispatcherServlet初始化的时候首先加载

@Override
    public void init(ServletConfig config) throws ServletException {
        logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>开始初始化noobspring");
        try{
        /**
         * 加载配置文件 config来自父类
         */
        doLoadConfig(config.getInitParameter(LOCATION));
        /**
         * 扫描相关的类 (从配置文件中读取扫描包的范围)把扫描到的类名保存到className中
         */
        logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>开始扫描类");

        doScanner(p.getProperty("scanPackage"));

        logger.debug("扫描到的类:{}",classNames.toString());
        logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>扫描成功");
        /**
         * 通过反射初始化className中的所有类,保存到ioc容器中(ioc容器的key默认是类名字母小写)
         */
        doInstance();
        /**
         * 织入切点
         */
        doAspect();
        /**
         * 依赖注入
         */
        doAutowired();
        /**
         * 构造handlerMapping
         */
        initHandlerMapping();
        }catch (Exception e){
            logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>初始化noobspring失败");
            e.printStackTrace();
            throw e;

        }
        logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>初始化noobspring完成");
    }

我们看一下加载的流程

首先是加载配置文件doLoadConfig(),通过获取servlet的初始化参数contextConfigLocation,获取配置所在的路径,然后进行解析并保存

    /**
     * 保存所有配置信息
     */
    private Properties p = new Properties();

接着是扫描相关的类

doScanner(),通过配置文件信息p获取扫描包的路径,然后对应路径下的所有类的名字保存起来,保存位置

    private List<String> classNames = new ArrayList<String>();

接着就是spring的核心IOC容器

doInstance(),通过我们扫描到的类名,有选择的根据java的反射机制,生成类实例,并保存起来,保存位置

private Map<String,Object> ioc = new HashMap<String, Object>();

接着就是AOP织入切点,doAspect(),这里要提及的一点,包括前面的ioc很关键的一点就是通过注解来进行标记,标记哪些类需要生成实例,那些方法需要织入通知,以及那些字段需要进行依赖注入。

DI依赖注入,doAutowired(),也是通过反射的方法,进行注入。

接着就是进行initHandlerMapping方法了,将请求路径和处理方法映射关联在一起,保存在

private Map<String,Method> handlerMapping = new HashMap<String, Method>();

猜你喜欢

转载自blog.csdn.net/qq_33543634/article/details/86767390