<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如果沒(méi)有了 Spring 怎么辦?

          共 39874字,需瀏覽 80分鐘

           ·

          2021-03-05 12:53

          超全面!Java核心知識(shí)總結(jié)(點(diǎn)擊查看)

          超全面!Java核心知識(shí)總結(jié)(點(diǎn)擊查看)


          隨著 Spring 的崛起以及其功能的完善,現(xiàn)在可能絕大部分項(xiàng)目的開(kāi)發(fā)都是使用 Spring(全家桶) 來(lái)進(jìn)行開(kāi)發(fā),Spring也確實(shí)和其名字一樣,是開(kāi)發(fā)者的春天,Spring 解放了程序員的雙手,而等到 SpringBoot出來(lái)之后配置文件大大減少,更是進(jìn)一步解放了程序員的雙手,但是也正是因?yàn)?/span>Spring家族產(chǎn)品的強(qiáng)大,使得我們習(xí)慣了面向 Spring 開(kāi)發(fā)。
          那么假如有一天沒(méi)有了 Spring,是不是感覺(jué)心里一空,可能一下子連最基本的接口都不會(huì)寫了,尤其是沒(méi)有接觸過(guò)Servlet編程的朋友。因?yàn)榧尤霙](méi)有了 Spring 等框架,那么我們就需要利用最原生的 Servlet 來(lái)自己實(shí)現(xiàn)接口路徑的映射,對(duì)象也需要自己進(jìn)行管理。

          Spring 能幫我們做什么

          Spring 是為解決企業(yè)級(jí)應(yīng)用開(kāi)發(fā)的復(fù)雜性而設(shè)計(jì)的一款框架,Spring 的設(shè)計(jì)理念就是:簡(jiǎn)化開(kāi)發(fā)。

          Spring 框架中,一切對(duì)象都是 bean,所以其通過(guò)面向 bean 編程(BOP),結(jié)合其核心思想依賴注入(DI)和面向切面((AOP)編程,Spring 實(shí)現(xiàn)了其偉大的簡(jiǎn)化開(kāi)發(fā)的設(shè)計(jì)理念。

          控制反轉(zhuǎn)(IOC)

          IOC 全稱為:Inversion of Control。控制反轉(zhuǎn)的基本概念是:不用創(chuàng)建對(duì)象,但是需要描述創(chuàng)建對(duì)象的方式。

          簡(jiǎn)單的說(shuō)我們本來(lái)在代碼中創(chuàng)建一個(gè)對(duì)象是通過(guò) new 關(guān)鍵字,而使用了 Spring 之后,我們不在需要自己去 new 一個(gè)對(duì)象了,而是直接通過(guò)容器里面去取出來(lái),再將其自動(dòng)注入到我們需要的對(duì)象之中,即:依賴注入。

          也就說(shuō)創(chuàng)建對(duì)象的控制權(quán)不在我們程序員手上了,全部交由 Spring 進(jìn)行管理,程序要只需要注入就可以了,所以才稱之為控制反轉(zhuǎn)。

          依賴注入(DI)

          依賴注入(Dependency Injection,DI)就是 Spring 為了實(shí)現(xiàn)控制反轉(zhuǎn)的一種實(shí)現(xiàn)方式,所有有時(shí)候我們也將控制反轉(zhuǎn)直接稱之為依賴注入。

          面向切面編程(AOP)

          AOP 全稱為:Aspect Oriented Programming。AOP是一種編程思想,其核心構(gòu)造是方面(切面),即將那些影響多個(gè)類的公共行為封裝到可重用的模塊中,而使原本的模塊內(nèi)只需關(guān)注自身的個(gè)性化行為。

          AOP編程的常用場(chǎng)景有:Authentication(權(quán)限認(rèn)證)、Auto Caching(自動(dòng)緩存處理)、Error Handling(統(tǒng)一錯(cuò)誤處理)、Debugging(調(diào)試信息輸出)、Logging(日志記錄)、Transactions(事務(wù)處理)等。

          利用 Spring 來(lái)完成 Hello World

          最原生的 Spring 需要較多的配置文件,而 SpringBoot 省略了許多配置,相比較于原始的 Spring 又簡(jiǎn)化了不少,在這里我們就以 SpringBoot 為例來(lái)完成一個(gè)簡(jiǎn)單的接口開(kāi)發(fā)。

          • 1、新建一個(gè) maven 項(xiàng)目,pom 文件中引入依賴(省略了少部分屬性):

          <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>2.4.0</version>
              <relativePath/>
          </parent>
          <dependencies>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter</artifactId>
              </dependency>

              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
              </dependency>
          </dependencies>
          • 2、新建一個(gè) HelloController 類:
          package com.lonely.wolf.note.springboot.demo;

          import org.springframework.web.bind.annotation.GetMapping;
          import org.springframework.web.bind.annotation.RequestMapping;
          import org.springframework.web.bind.annotation.RestController;

          @RestController
          @RequestMapping("/hello")
          public class HelloController {
              @GetMapping("/demo")
              public String helloWorld(String name){
                  return "Hello:" + name;
              }
          }
          • 3、最后新建一個(gè) SpringBoot 啟動(dòng)類:
          package com.lonely.wolf.note.springboot;

          import org.springframework.boot.SpringApplication;
          import org.springframework.boot.autoconfigure.SpringBootApplication;

          @SpringBootApplication(scanBasePackages = "com.lonely.wolf.note.springboot")
          class MySpringBootApplication {
              public static void main(String[] args) {
                  SpringApplication.run(MySpringBootApplication.classargs);
              }
          }
          • 4、現(xiàn)在就可以輸入測(cè)試路徑:http://localhost:8080/hello/demo?name=雙子孤狼 進(jìn)行測(cè)試,正常輸出:Hello:雙子孤狼

          我們可以看到,利用 SpringBoot 來(lái)完成一個(gè)簡(jiǎn)單的應(yīng)用開(kāi)發(fā)非常簡(jiǎn)單,可以不需要任何配置完成一個(gè)簡(jiǎn)單的應(yīng)用,這是因?yàn)?SpringBoot 內(nèi)部已經(jīng)做好了約定(約定優(yōu)于配置思想),包括容器 Tomcat 都被默認(rèn)集成,所以我們不需要任何配置文件就可以完成一個(gè)簡(jiǎn)單的 demo 應(yīng)用。

          假如沒(méi)有了 Spring

          通過(guò)上面的例子我們可以發(fā)現(xiàn),利用 Spring 來(lái)完成一個(gè) Hello World 非常簡(jiǎn)單,但是假如沒(méi)有了 Spring,我們又該如何完成這樣的一個(gè) Hello World 接口呢?

          基于 Servlet 開(kāi)發(fā)

          在還沒(méi)有框架之前,編程式基于原始的 Servlet 進(jìn)行開(kāi)發(fā),下面我們就基于原生的 Servlet 來(lái)完成一個(gè)簡(jiǎn)單的接口調(diào)用。

          • 1、pom 文件引入依賴,需要注意的是,package 屬性要設(shè)置成 war 包,為了節(jié)省篇幅,這里沒(méi)有列出 pom 完整的信息:

          <packaging>war</packaging> 
          <dependencies>
                  <dependency>
                      <groupId>javax.servlet</groupId>
                      <artifactId>servlet-api</artifactId>
                      <version>2.4</version>
                  </dependency>

                  <dependency>
                      <groupId>org.apache.commons</groupId>
                      <artifactId>commons-lang3</artifactId>
                      <version>3.7</version>
                  </dependency>

                  <dependency>
                      <groupId>com.alibaba</groupId>
                      <artifactId>fastjson</artifactId>
                      <version>1.2.72</version>
                  </dependency>
              </dependencies>
          • 2、在 src/main 下面新建文件夾 webapp/WEB-INF,然后在 WEB-INF 下面新建一個(gè) 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"
           xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
           xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
           version="2.4">

           <display-name>Lonely Wolf Web Application</display-name>
           <servlet>
            <servlet-name>helloServlet</servlet-name>
            <servlet-class>com.lonely.wolf.mini.spring.servlet.HelloServlet</servlet-class>
           </servlet>
           <servlet-mapping>
            <servlet-name>helloServlet</servlet-name>
            <url-pattern>/hello/*</url-pattern>
           </servlet-mapping>
          </web-app>

          這里面定義了 selvletservlet-mapping 兩個(gè)標(biāo)簽,這兩個(gè)標(biāo)簽必須一一對(duì)應(yīng),上面的標(biāo)簽定義了 servlet 的位置,而下面的 servlet-mapping 文件定義了路徑的映射,這兩個(gè)標(biāo)簽通過(guò) servlet-name 標(biāo)簽對(duì)應(yīng)。

          • 3、新建一個(gè) HelloServlet 類繼承 HttpServlet
          package com.lonely.wolf.mini.spring.servlet;

          import javax.servlet.ServletException;
          import javax.servlet.http.HttpServlet;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import java.io.IOException;

          /**
           * 原始Servlet接口編寫,一般需要實(shí)現(xiàn)GET和POST方法,其他方法可以視具體情況選擇性繼承
           */

          public class HelloServlet extends HttpServlet {
              @Override
              protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                  this.doPost(request,response);
              }

              @Override
              protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                  response.setContentType("text/html;charset=utf-8");
                  response.getWriter().write("Hello:" + request.getParameter("name"));
              }
          }
          • 4、執(zhí)行 maven 打包命令,確認(rèn)成功打包成 war 包:

          • 5、RUN-->Edit Configurations,然后點(diǎn)擊左上角的 + 號(hào),新建一個(gè) Tomcat Server,如果是第一次配置,默認(rèn)沒(méi)有 Tomcat Server 選項(xiàng),需要點(diǎn)擊底部的 xx more items...
          • 6、點(diǎn)擊右邊的 Deployment,然后按照下圖依次點(diǎn)擊,最后在彈框內(nèi)找到上面打包好的 war 包文件:
          • 7、選中之后,需要注意的是,下面 Application Context 默認(rèn)會(huì)帶上 war 包名,為了方便,我們需要把它刪掉,即不用上下文路徑,只保留一個(gè)根路徑 / (當(dāng)然上下文也可以保留,但是每次請(qǐng)求都要帶上這一部分), 再選擇 Apply,點(diǎn)擊 OK,即可完成部署:
          • 8、最后我們?cè)跒g覽器輸入請(qǐng)求路徑http://localhost:8080/hello?name=雙子孤狼,即可得到返回:Hello:雙子孤狼
          上面我們就完成了一個(gè)簡(jiǎn)單的 基于Servlet 的接口開(kāi)發(fā),可以看到,配置非常麻煩,每增加一個(gè) Servlet 都需要增加對(duì)應(yīng)的配置,所以才會(huì)有許多框架的出現(xiàn)來(lái)幫我們簡(jiǎn)化開(kāi)發(fā),比如原來(lái)很流行的 Struts2 框架,當(dāng)然現(xiàn)在除了一些比較老的項(xiàng)目,一般我們都很少使用,而更多的是選擇 Spring 框架來(lái)進(jìn)行開(kāi)發(fā)。

          模仿Spring

          Spring 的源碼體系非常龐大,大部分人對(duì)其源碼都敬而遠(yuǎn)之。確實(shí),Spring 畢竟經(jīng)過(guò)了這么多年的迭代,功能豐富,項(xiàng)目龐大,不是一下子就能看懂的。雖然 Spring 難以理解,但是其最核心的思想仍然是我們上面介紹的幾點(diǎn),接下來(lái)就基于 Spring 最核心的部分來(lái)模擬,自己動(dòng)手實(shí)現(xiàn)一個(gè)超級(jí)迷你版本的 Spring(此版本并不包含 AOP 功能)。
          • 1、pom 依賴和上面保持不變,然后 web.xml 作如下改變,這里會(huì)攔截所有的接口 /*,然后多配置了一個(gè)參數(shù),這個(gè)參數(shù)其實(shí)也是為了更形象的模擬 Spring
          <servlet>
              <servlet-name>myDispatcherServlet</servlet-name>
              <servlet-class>com.lonely.wolf.mini.spring.v1.MyDispatcherServlet</servlet-class>
              <init-param>
                  <param-name>defaultConfig</param-name>
                  <param-value>application.properties</param-value>
              </init-param>
          </servlet>

          <servlet-mapping>
              <servlet-name>myDispatcherServlet</servlet-name>
              <url-pattern>/*</url-pattern>
          </servlet-mapping>
          • 2、在 respurces 下面新建一個(gè)配置文件 application.properties,用來(lái)定義掃描的基本路徑:
          basePackages=com.lonely.wolf.mini.spring
          • 3、創(chuàng)建一些相關(guān)的注解類:
          package com.lonely.wolf.mini.spring.annotation;

          import java.lang.annotation.*;

          @Target({ElementType.FIELD})
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          public @interface WolfAutowired {
              String value() default "";
          }
          package com.lonely.wolf.mini.spring.annotation;

          import java.lang.annotation.*;

          @Target({ElementType.TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          public @interface WolfController {
              String value() default "";
          }
          package com.lonely.wolf.mini.spring.annotation;

          import java.lang.annotation.*;

          @Target({ElementType.METHOD})
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          public @interface WolfGetMapping {
              String value() default "";
          }
          package com.lonely.wolf.mini.spring.annotation;

          import java.lang.annotation.*;

          @Target({ElementType.PARAMETER})
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          public @interface WolfRequestParam {
              String value() default "";
          }
          package com.lonely.wolf.mini.spring.annotation;

          import java.lang.annotation.*;

          @Target({ElementType.TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          public @interface WolfService {
              String value() default "";
          }
          • 4、這個(gè)時(shí)候最核心的邏輯就是 MyDispatcherServlet 類了:
          package com.lonely.wolf.mini.spring.v1;

          import com.lonely.wolf.mini.spring.annotation.*;
          import com.lonely.wolf.mini.spring.v1.config.MyConfig;
          import org.apache.commons.lang3.StringUtils;

          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.annotation.Annotation;
          import java.lang.reflect.Field;
          import java.lang.reflect.Method;
          import java.net.URL;
          import java.util.*;

          public class MyDispatcherServlet extends HttpServlet {
              private MyConfig myConfig = new MyConfig();
              private List<String> classNameList = new ArrayList<String>();

              private Map<String,Object> iocContainerMap = new HashMap<>();
              private Map<String,HandlerMapping> handlerMappingMap = new HashMap<>();

              @Override
              protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                  this.doPost(request,response);
              }

              @Override
              protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                  try {
                      this.doDispatch(request, response);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }

              private void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception{
                  String requestUrl = this.formatUrl(request.getRequestURI());
                  HandlerMapping handlerMapping = handlerMappingMap.get(requestUrl);
                  if (null == handlerMapping){
                      response.getWriter().write("404 Not Found");
                      return;
                  }

                  //獲取方法中的參數(shù)類型
                  Class<?>[] paramTypeArr = handlerMapping.getMethod().getParameterTypes();
                  Object[] paramArr = new Object[paramTypeArr.length];

                  for (int i=0;i<paramTypeArr.length;i++){
                      Class<?> clazz = paramTypeArr[i];
                      //參數(shù)只考慮三種類型,其他不考慮
                      if (clazz == HttpServletRequest.class){
                          paramArr[i] = request;
                      }else if (clazz == HttpServletResponse.class){
                          paramArr[i] = response;
                      } else if (clazz == String.class){
                          Map<Integer,String> methodParam = handlerMapping.getMethodParams();
                          paramArr[i] = request.getParameter(methodParam.get(i));
                      }else{
                          System.out.println("暫不支持的參數(shù)類型");
                      }
                  }
                  //反射調(diào)用controller方法
                  handlerMapping.getMethod().invoke(handlerMapping.getTarget(), paramArr);
              }

              private String formatUrl(String requestUrl) {
                  requestUrl = requestUrl.replaceAll("/+","/");
                  if (requestUrl.lastIndexOf("/") == requestUrl.length() -1){
                      requestUrl = requestUrl.substring(0,requestUrl.length() -1);
                  }
                  return requestUrl;
              }


              @Override
              public void init(ServletConfig config) throws ServletException {
                  //1.加載配置文件
                  try {
                      doLoadConfig(config.getInitParameter("defaultConfig"));
                  } catch (Exception e) {
                      System.out.println("加載配置文件失敗");
                      return;
                  }

                  //2.根據(jù)獲取到的掃描路徑進(jìn)行掃描
                  doScanPacakge(myConfig.getBasePackages());

                  //3.將掃描到的類進(jìn)行初始化,并存放到IOC容器
                  doInitializedClass();

                  //4.依賴注入
                  doDependencyInjection();

                  System.out.println("DispatchServlet Init End..." );
              }


              private void doDependencyInjection() {
                  if (iocContainerMap.size() == 0){
                      return;
                  }
                  //循環(huán)IOC容器中的類
                  Iterator<Map.Entry<String,Object>> iterator = iocContainerMap.entrySet().iterator();

                  while (iterator.hasNext()){
                      Map.Entry<String,Object> entry = iterator.next();
                      Class<?> clazz = entry.getValue().getClass();
                      Field[] fields = clazz.getDeclaredFields();

                      //屬性注入
                      for (Field field : fields){
                          //如果屬性有WolfAutowired注解則注入值(暫時(shí)不考慮其他注解)
                          if (field.isAnnotationPresent(WolfAutowired.class)){
                              String value = toLowerFirstLetterCase(field.getType().getSimpleName());//默認(rèn)bean的value為類名首字母小寫
                              if (field.getType().isAnnotationPresent(WolfService.class)){
                                  WolfService wolfService = field.getType().getAnnotation(WolfService.class);
                                  value = wolfService.value();
                              }
                              field.setAccessible(true);
                              try {
                                  Object target = iocContainerMap.get(beanName);
                                  if (null == target){
                                      System.out.println(clazz.getName() + "required bean:" + beanName + ",but we not found it");
                                  }
                                  field.set(entry.getValue(),iocContainerMap.get(beanName));//初始化對(duì)象,后面注入
                              } catch (IllegalAccessException e) {
                                  e.printStackTrace();
                              }
                          }
                      }

                      //初始化HanderMapping
                      String requestUrl = "";
                      //獲取Controller類上的請(qǐng)求路徑
                      if (clazz.isAnnotationPresent(WolfController.class)){
                          requestUrl = clazz.getAnnotation(WolfController.class).value();
                      }

                      //循環(huán)類中的方法,獲取方法上的路徑
                      Method[] methods = clazz.getMethods();
                      for (Method method : methods){
                          //假設(shè)只有WolfGetMapping這一種注解
                          if(!method.isAnnotationPresent(WolfGetMapping.class)){
                              continue;
                          }
                          WolfGetMapping wolfGetMapping = method.getDeclaredAnnotation(WolfGetMapping.class);
                          requestUrl = requestUrl + "/" + wolfGetMapping.value();//拼成完成的請(qǐng)求路徑

                          //不考慮正則匹配路徑/xx/* 的情況,只考慮完全匹配的情況
                          if (handlerMappingMap.containsKey(requestUrl)){
                              System.out.println("重復(fù)路徑");
                              continue;
                          }

                          Annotation[][] annotationArr = method.getParameterAnnotations();//獲取方法中參數(shù)的注解

                          Map<Integer,String> methodParam = new HashMap<>();//存儲(chǔ)參數(shù)的順序和參數(shù)名
                          retryParam:
                          for (int i=0;i<annotationArr.length;i++){
                              for (Annotation annotation : annotationArr[i]){
                                  if (annotation instanceof WolfRequestParam){
                                      WolfRequestParam wolfRequestParam = (WolfRequestParam) annotation;
                                      methodParam.put(i,wolfRequestParam.value());//存儲(chǔ)參數(shù)的位置和注解中定義的參數(shù)名
                                      continue retryParam;
                                  }
                              }
                          }

                          requestUrl = this.formatUrl(requestUrl);//主要是防止路徑多了/導(dǎo)致路徑匹配不上
                          HandlerMapping handlerMapping = new HandlerMapping();
                          handlerMapping.setRequestUrl(requestUrl);//請(qǐng)求路徑
                          handlerMapping.setMethod(method);//請(qǐng)求方法
                          handlerMapping.setTarget(entry.getValue());//請(qǐng)求方法所在controller對(duì)象
                          handlerMapping.setMethodParams(methodParam);//請(qǐng)求方法的參數(shù)信息
                          handlerMappingMap.put(requestUrl,handlerMapping);//存入hashmap
                      }
                  }
              }


              /**
               * 初始化類,并放入容器iocContainerMap內(nèi)
               */

              private void doInitializedClass() {
                  if (classNameList.isEmpty()){
                      return;
                  }
                  for (String className : classNameList){
                      if (StringUtils.isEmpty(className)){
                          continue;
                      }
                      Class clazz;
                      try {
                          clazz = Class.forName(className);//反射獲取對(duì)象
                          if (clazz.isAnnotationPresent(WolfController.class)){
                              String value = ((WolfController)clazz.getAnnotation(WolfController.class)).value();
                              //如果直接指定了value則取value,否則取首字母小寫類名作為key值存儲(chǔ)類的實(shí)例對(duì)象
                              iocContainerMap.put(StringUtils.isBlank(value) ? toLowerFirstLetterCase(clazz.getSimpleName()) : value,clazz.newInstance());
                          }else if(clazz.isAnnotationPresent(WolfService.class)){
                              String value = ((WolfService)clazz.getAnnotation(WolfService.class)).value();
                              iocContainerMap.put(StringUtils.isBlank(value) ? toLowerFirstLetterCase(clazz.getSimpleName()) : value,clazz.newInstance());
                          }else{
                              System.out.println("不考慮其他注解的情況");
                          }
                      } catch (Exception e) {
                          e.printStackTrace();
                          System.out.println("初始化類失敗,className為" + className);
                      }
                  }

              }

              /**
               * 將首字母轉(zhuǎn)換為小寫
               * @param className
               * @return
               */

              private String toLowerFirstLetterCase(String className) {
                  if (StringUtils.isBlank(className)){
                      return "";
                  }
                  String firstLetter = className.substring(0,1);
                  return firstLetter.toLowerCase() + className.substring(1);
              }


              /**
               * 掃描包下所有文件獲取全限定類名
               * @param basePackages
               */

              private void doScanPacakge(String basePackages) {
                  if (StringUtils.isBlank(basePackages)){
                      return;
                  }
                  //把包名的.替換為/
                  String scanPath = "/" + basePackages.replaceAll("\\.","/");
                  URL url = this.getClass().getClassLoader().getResource(scanPath);//獲取到當(dāng)前包所在磁盤的全路徑
                  File files = new File(url.getFile());//獲取當(dāng)前路徑下所有文件
                  for (File file : files.listFiles()){//開(kāi)始掃描路徑下的所有文件
                      if (file.isDirectory()){//如果是文件夾則遞歸
                          doScanPacakge(basePackages + "." + file.getName());
                      }else{//如果是文件則添加到集合。因?yàn)樯厦媸峭ㄟ^(guò)類加載器獲取到的文件路徑,所以實(shí)際上是class文件所在路徑
                          classNameList.add(basePackages + "." + file.getName().replace(".class",""));
                      }
                  }

              }


              /**
               * 加載配置文件
               * @param configPath - 配置文件所在路徑
               */

              private void doLoadConfig(String configPath) {
                  InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(configPath);
                  Properties properties = new Properties();
                  try {
                      properties.load(inputStream);
                  } catch (IOException e) {
                      e.printStackTrace();
                      System.out.println("加載配置文件失敗");
                  }

                  properties.forEach((k, v) -> {
                      try {
                          Field field = myConfig.getClass().getDeclaredField((String)k);
                          field.setAccessible(true);
                          field.set(myConfig,v);
                      } catch (Exception e) {
                          e.printStackTrace();
                          System.out.println("初始化配置類失敗");
                          return;
                      }
                  });
              }
          }
          • 5、這個(gè) Servlet 相比較于上面的 HelloServlet 多了一個(gè) init 方法,這個(gè)方法中主要做了以下幾件事情:

          (1)初始化配置文件,拿到配置文件中配置的參數(shù)信息(對(duì)應(yīng)方法:doLoadConfig)。

          (2)拿到第 1 步加載出來(lái)的配置文件,獲取到需要掃描的包路徑,然后將包路徑進(jìn)行轉(zhuǎn)換成實(shí)際的磁盤路徑,并開(kāi)始遍歷磁盤路徑下的所有 class 文件,最終經(jīng)過(guò)轉(zhuǎn)換之后得到掃描路徑下的所有類的全限定類型,存儲(chǔ)到全局變量 classNameList 中(對(duì)應(yīng)方法:doScanPacakge)。

          (3)根據(jù)第 2 步中得到的全局變量 classNameList 中的類通過(guò)反射進(jìn)行初始化(需要注意的是只會(huì)初始化加了指定注解的類)并將得到的對(duì)應(yīng)關(guān)系存儲(chǔ)到全局變量 iocContainerMap 中(即傳說(shuō)中的 IOC 容器),其中 key 值為注解中的 value 屬性,如 value 屬性為空,則默認(rèn)取首字母小寫的類名作為 key 值進(jìn)行存儲(chǔ)(對(duì)應(yīng)方法:doInitializedClass)。

          (4)這一步比較關(guān)鍵,需要對(duì) IOC 容器中的所有類的屬性進(jìn)行賦值并且需要對(duì) Controller 中的請(qǐng)求路徑進(jìn)行映射存儲(chǔ),為了確保最后能順利調(diào)用 Controller 中的方法,還需要將方法的參數(shù)進(jìn)行存儲(chǔ) 。對(duì)屬性進(jìn)行映射時(shí)只會(huì)對(duì)加了注解的屬性進(jìn)行映射,映射時(shí)會(huì)從 IOC 容器中取出第 3 步中已經(jīng)初始化的實(shí)例對(duì)象進(jìn)行賦值,最后將請(qǐng)求路徑和 Controller 中方法的映射關(guān)系存入變量 handlerMappingMapkey 值為請(qǐng)求路徑,value 為方法的相關(guān)信息 (對(duì)應(yīng)方法:doDependencyInjection)。

          • 6、存儲(chǔ)請(qǐng)求路徑和方法的映射關(guān)系時(shí),需要用到 HandlerMapping 類來(lái)進(jìn)行存儲(chǔ):

          package com.lonely.wolf.mini.spring.v1;

          import java.lang.reflect.Method;
          import java.util.Map;

          //省略了getter/setter方法
          public class HandlerMapping {
              private String requestUrl;
              private Object target;//保存方法對(duì)應(yīng)的實(shí)例
              private Method method;//保存映射的方法
              private Map<Integer,String> methodParams;//記錄方法參數(shù)
          }
          • 7、初始化完成之后,因?yàn)閿r截了 /* ,所以調(diào)用任意接口都會(huì)進(jìn)入 MyDispatcherServlet ,而且最終都會(huì)執(zhí)行方法 doDispatch,執(zhí)行這個(gè)方法時(shí)會(huì)拿到請(qǐng)求的路徑,然后和全局變量 handlerMappingMap 進(jìn)行匹配,匹配不上則返回 404,匹配的上則取出必要的參數(shù)進(jìn)行賦值,最后通過(guò)反射調(diào)用到 Controller 中的相關(guān)方法。
          • 8、新建一個(gè) HelloControllerHelloService 來(lái)進(jìn)行測(cè)試:
          package com.lonely.wolf.mini.spring.controller;

          import com.lonely.wolf.mini.spring.annotation.WolfAutowired;
          import com.lonely.wolf.mini.spring.annotation.WolfController;
          import com.lonely.wolf.mini.spring.annotation.WolfGetMapping;
          import com.lonely.wolf.mini.spring.annotation.WolfRequestParam;
          import com.lonely.wolf.mini.spring.service.HelloService;

          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import java.io.IOException;

          @WolfController
          public class HelloController {
              @WolfAutowired
              private HelloService helloService;

              @WolfGetMapping("/hello")
              public void query(HttpServletRequest request,HttpServletResponse response, @WolfRequestParam("name") String name) throws IOException {
                  response.setContentType("text/html;charset=utf-8");
                  response.getWriter().write("Hello:" + name);
              }
          }
          package com.lonely.wolf.mini.spring.service;

          import com.lonely.wolf.mini.spring.annotation.WolfService;

          @WolfService(value = "hello_service")//為了演示能否正常取value屬性
          public class HelloService {
          }
          • 9、輸入測(cè)試路徑:http://localhost:8080////hello?name=雙子孤狼, 進(jìn)行測(cè)試發(fā)現(xiàn)可以正常輸出:Hello:雙子孤狼

          上面這個(gè)例子只是一個(gè)簡(jiǎn)單的演示,通過(guò)這個(gè)例子只是希望在沒(méi)有任何框架的情況下,我們也能知道如何完成一個(gè)簡(jiǎn)單的應(yīng)用開(kāi)發(fā)。例子中很多細(xì)節(jié)都沒(méi)有進(jìn)行處理,僅僅只是為了體驗(yàn)一下 Spring 的核心思想,并了解 Spring 到底幫助我們做了什么,實(shí)際上 Spring 能幫我們做的事情遠(yuǎn)比這個(gè)例子中多得多,Spring 體系龐大,設(shè)計(jì)優(yōu)雅,經(jīng)過(guò)了多年的迭代優(yōu)化,是一款非常值得研究的框架。

          總結(jié)

          本文從介紹 Spring 核心功能開(kāi)始入手,從如何利用 Spring 完成一個(gè)應(yīng)用開(kāi)發(fā),講述到假如沒(méi)有 Spring 我們?cè)撊绾位?Servlet 進(jìn)行開(kāi)發(fā),最后再通過(guò)一個(gè)簡(jiǎn)單的例子體驗(yàn)了 Spring 的核心思想。

          作者:雙子孤狼

          www.cnblogs.com/lonely-wolf/p/14127957.html


          如有文章對(duì)你有幫助,

          在看”和轉(zhuǎn)發(fā)是對(duì)我最大的支持!


          推薦 GitHub 書籍倉(cāng)庫(kù)
          https://github.com/ebooklist/awesome-ebooks-list

          整理了大部分常用 技術(shù)書籍PDF,持續(xù)更新中... 你需要的技術(shù)書籍,這里可能都有...


          點(diǎn)擊文末“閱讀原文”可直達(dá)

          整理不易,麻煩各位小伙伴在GitHub中來(lái)個(gè)Star支持一下


          瀏覽 45
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  火爆达人金先生短发白领 | 俺去大香蕉 | 女人操逼 | 九九黄色小视频 | 在线观看中文字幕一区 |