自己實現spring核心功能 一

 

聊聊spring

       spring對於java開發者來說,是最熟悉不過的框架了,我們日常開發中每天都在使用它。它有着各種各樣的好處,簡單易用,得心應手... ...

我們一說到spring就會講到ioc 、aop、依賴注入,註解等專業名詞,不少剛接觸java的人,都是一頭霧水,很難直觀的去理解這些是個什麼玩意,但使用的多了 就愛上了它給我們帶來的便利。

 

探索spring

     當我們熟練的使用它之後就會好奇,ioc怎麼實現的呢?爲什麼我只要在類的變量中加入@AutoWrited 就能使用這個變量?帶着疑惑我們就會開始翻源碼,找答案。經過一番努力,最終我們定位到了spring-context.jar包,找到了

AbstractApplicationContext對象的refresh方法,這裏面的實現和步驟,就是整個spring功能的核心,裏面的實現另我們歎爲觀止,但也相當之複雜,用到了很大設計模式,難以窺見全貌,很多地方的設計我們不知道爲啥需要這麼實現。但我們還是知道了ioc容器實際上是用的Hashmap,
依賴注入使用的是反射,aop實際上是動態代理完成的。還有很大一部分代碼看的有點暈,就是加強健壯性保證生命週期和各種特性的。不管怎麼說,那都是別人的東西,我們只會用,源碼我們看了很多,但很少自己來實現出來。

spring實現分析

  不管怎麼說,別人的實現是別人的,自己寫出來的東西是屬於我們自己的。既然下決心要自己也能實現了,那我們就需要分析分析了。

      spring怎麼做到代碼侵入量少且各層級分明的呢?

         spring採用約定大於配置,按照固定模式,層級分爲Controller 、Service、Component、Bean、Configuration來標識類型

      spring是怎麼管理依賴關係的?

          是通過註解或xml

      spring是怎麼發現哪些類需要管理的?

          是註解標記+包路徑掃描

      spring是如何實現依賴注入的?

         通過AutoWrited註解標記加反射實例化對象

      spring是如何管理Bean的?

         使用HashMap容器、Set容器實現單例Bean

      spring怎麼實現aop切面的呢?

         使用動態代理的方式,並提供了cglib和jdk默認實現兩種方式

      spring是在何時加載到內存中的呢?

         springMvc是通過web.xml配置入口觸發,springboot是通過springApplication初始化觸發

 

 本次實現spring核心功能會涉及到哪些點?

      1.註解的定義與使用

      2.容器的初始化

      3.配置文件的讀取與使用

      4.sevlet的使用

      5.註解的定義與使用

      6.反射的運用

      7.url路由與方法映射

      8.參數解析與綁定

      9.正則與轉義

 

 

 正式開始

  首先創建一個maven項目,代碼結構如下:

 

添加jar包依賴:

裏面有2個jar包加一個插件

javax.servlet-api.jar 用來啓動核心代碼和處理請求
fastjson.jar          用來做json參數綁定

jetty-maven-plugin 使用jetty作爲web容器啓動


完整maven代碼如下:
<?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.honstat</groupId>
    <artifactId>test-spring</artifactId>
    <version>1.0-SNAPSHOT</version>

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

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.56</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>9.4.5.v20170502</version>
                <configuration>
                    <scanIntervalSeconds>10</scanIntervalSeconds>
                    <webApp>
                        <contextPath>/</contextPath>
                    </webApp>
                    <httpConnector>
                        <port>8080</port>
                    </httpConnector>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
View Code

 

 創建前端控制器類

  前端控制器專門處理servlet請求,匹配到對應的方法執行後返回,前端控制器是什麼可以參考我的另一篇博客《SpringMvc請求處理流程與源碼探祕》

   這裏我們創建一個叫CJDispatcherServlet的類,它繼承HttpServlet類,並且重寫HttpServlet的init(),doGet(),doPost() 這3個方法,圖中的HomeService和StudentService可以先忽略不寫

 

 配置web.xml

 需要配置<servlet> 和<servlet-mapping> 2個標籤

<servlet>中需要

1.指定servlet名稱

 2.指定處理請求的前端控制器類

 3.設置初始化配置文件路徑

 

 完整web.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee"

         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

         version="2.5">
    <servlet>
        <servlet-name>cjservletMVC</servlet-name>
        <servlet-class>com.honstat.spring.service.CJDispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>spring/application.properties</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>cjservletMVC</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>
View Code

 

 添加註解

 我們知道,spring裏面是通過給類加註解來識別各種使用場景的,那我們就來實現幾個必用的

作用在類上的:

  JCController

  JCService

  JCComponent

 

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface JCController {
    String value() default  "";
}

 全部採用這種類型

作用在方法上和類上的:

JCRequestMapping

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface JCRequestMapping {
    String value() default "";
}

 

作用在字段上的:

JCAutoWrited

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface JCAutoWrited {
    String value() default "";
}

 

到此我們已經初步的創建完了需要準備的類,準備工作告一段落。


 由於篇幅有限,下一篇開始實現核心功能了!

 完整代碼地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章