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

          Spring Boot 搭建實(shí)際項(xiàng)目開發(fā)中的腳手架

          共 18597字,需瀏覽 38分鐘

           ·

          2021-04-01 11:21

          本來已收錄到我寫的10萬字Springboot經(jīng)典學(xué)習(xí)筆記中,筆記在持續(xù)更新……文末有領(lǐng)取方式

          前面的文章中,我主要給大家講解了 Spring Boot 中常用的一些技術(shù)點(diǎn),這些技術(shù)點(diǎn)在實(shí)際項(xiàng)目中可能不會全部用得到,因?yàn)椴煌捻?xiàng)目可能使用的技術(shù)不同,但是希望大家都能掌握如何使用,并能自己根據(jù)實(shí)際項(xiàng)目中的需求進(jìn)行相應(yīng)的擴(kuò)展。

          不知道大家了不了解單片機(jī),單片機(jī)里有個最小系統(tǒng),這個最小系統(tǒng)搭建好了之后,就可以在此基礎(chǔ)上進(jìn)行人為的擴(kuò)展。這節(jié)課我們要做的就是搭建一個 “Spring Boot 最小系統(tǒng)架構(gòu)” 。拿著這個架構(gòu),可以在此基礎(chǔ)上根據(jù)實(shí)際需求做相應(yīng)的擴(kuò)展。

          從零開始搭建一個環(huán)境,主要要考慮幾點(diǎn):統(tǒng)一封裝的數(shù)據(jù)結(jié)構(gòu)、可調(diào)式的接口、json的處理、模板引擎的使用(本文不寫該項(xiàng),因?yàn)楝F(xiàn)在大部分項(xiàng)目都前后端分離了,但是考慮到也還有非前后端分離的項(xiàng)目,所以我在源代碼里也加上了 thymeleaf)、持久層的集成、攔截器(這個也是可選的)和全局異常處理。一般包括這些東西的話,基本上一個 Spring Boot 項(xiàng)目環(huán)境就差不多了,然后就是根據(jù)具體情況來擴(kuò)展了。

          結(jié)合前面的課程和以上的這些點(diǎn),本文手把手帶領(lǐng)大家搭建一個實(shí)際項(xiàng)目開發(fā)中可用的 Spring Boot 架構(gòu)。整個項(xiàng)目工程如下圖所示,學(xué)習(xí)的時候,可以結(jié)合我的源碼,這樣效果會更好。

          工程架構(gòu)

          1. 統(tǒng)一的數(shù)據(jù)封裝

          由于封裝的 json 數(shù)據(jù)的類型不確定,所以在定義統(tǒng)一的 json 結(jié)構(gòu)時,我們需要用到泛型。統(tǒng)一的 json 結(jié)構(gòu)中屬性包括數(shù)據(jù)、狀態(tài)碼、提示信息即可,構(gòu)造方法可以根據(jù)實(shí)際業(yè)務(wù)需求做相應(yīng)的添加即可,一般來說,應(yīng)該有默認(rèn)的返回結(jié)構(gòu),也應(yīng)該有用戶指定的返回結(jié)構(gòu)。如下:

          /**
           * 統(tǒng)一返回對象
           * @author shengwu ni
           * @param <T>
           */

          public class JsonResult<T{

              private T data;
              private String code;
              private String msg;

              /**
               * 若沒有數(shù)據(jù)返回,默認(rèn)狀態(tài)碼為0,提示信息為:操作成功!
               */

              public JsonResult() {
                  this.code = "0";
                  this.msg = "操作成功!";
              }

              /**
               * 若沒有數(shù)據(jù)返回,可以人為指定狀態(tài)碼和提示信息
               * @param code
               * @param msg
               */

              public JsonResult(String code, String msg) {
                  this.code = code;
                  this.msg = msg;
              }

              /**
               * 有數(shù)據(jù)返回時,狀態(tài)碼為0,默認(rèn)提示信息為:操作成功!
               * @param data
               */

              public JsonResult(T data) {
                  this.data = data;
                  this.code = "0";
                  this.msg = "操作成功!";
              }

              /**
               * 有數(shù)據(jù)返回,狀態(tài)碼為0,人為指定提示信息
               * @param data
               * @param msg
               */

              public JsonResult(T data, String msg) {
                  this.data = data;
                  this.code = "0";
                  this.msg = msg;
              }
              
              /**
               * 使用自定義異常作為參數(shù)傳遞狀態(tài)碼和提示信息
               * @param msgEnum
               */

              public JsonResult(BusinessMsgEnum msgEnum) {
                  this.code = msgEnum.code();
                  this.msg = msgEnum.msg();
              }

              // 省去get和set方法
          }

          大家可以根據(jù)自己項(xiàng)目中所需要的一些東西,合理的修改統(tǒng)一結(jié)構(gòu)中的字段信息。

          2. json的處理

          Json 處理工具很多,比如阿里巴巴的 fastjson,不過 fastjson 對有些未知類型的 null 無法轉(zhuǎn)成空字符串,這可能是 fastjson 自身的缺陷,可擴(kuò)展性也不是太好,但是使用起來方便,使用的人也蠻多的。這節(jié)課里面我們主要集成 Spring Boot 自帶的 jackson。主要是對 jackson 做一下對 null 的配置即可,然后就可以在項(xiàng)目中使用了。

          /**
           * jacksonConfig
           * @author shengwu ni
           */

          @Configuration
          public class JacksonConfig {
              @Bean
              @Primary
              @ConditionalOnMissingBean(ObjectMapper.class)
              public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder
          {
                  ObjectMapper objectMapper = builder.createXmlMapper(false).build();
                  objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
                      @Override
                      public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                          jsonGenerator.writeString("");
                      }
                  });
                  return objectMapper;
              }
          }

          這里先不測試,等下面 swagger2 配置好了之后,我們一起來測試一下。

          3. swagger2在線可調(diào)式接口

          有了 swagger,開發(fā)人員不需要給其他人員提供接口文檔,只要告訴他們一個 Swagger 地址,即可展示在線的 API 接口文檔,除此之外,調(diào)用接口的人員還可以在線測試接口數(shù)據(jù),同樣地,開發(fā)人員在開發(fā)接口時,同樣也可以利用 Swagger 在線接口文檔測試接口數(shù)據(jù),這給開發(fā)人員提供了便利。使用 swagger 需要對其進(jìn)行配置:

          /**
           * swagger配置
           * @author shengwu ni
           */

          @Configuration
          @EnableSwagger2
          public class SwaggerConfig {

              @Bean
              public Docket createRestApi() {
                  return new Docket(DocumentationType.SWAGGER_2)
                          // 指定構(gòu)建api文檔的詳細(xì)信息的方法:apiInfo()
                          .apiInfo(apiInfo())
                          .select()
                          // 指定要生成api接口的包路徑,這里把controller作為包路徑,生成controller中的所有接口
                          .apis(RequestHandlerSelectors.basePackage("com.itcodai.course18.controller"))
                          .paths(PathSelectors.any())
                          .build();
              }

              /**
               * 構(gòu)建api文檔的詳細(xì)信息
               * @return
               */

              private ApiInfo apiInfo() {
                  return new ApiInfoBuilder()
                          // 設(shè)置頁面標(biāo)題
                          .title("Spring Boot搭建實(shí)際項(xiàng)目中開發(fā)的架構(gòu)")
                          // 設(shè)置接口描述
                          .description("跟武哥一起學(xué)Spring Boot第18課")
                          // 設(shè)置聯(lián)系方式
                          .contact("倪升武," + "微信公眾號:程序員私房菜")
                          // 設(shè)置版本
                          .version("1.0")
                          // 構(gòu)建
                          .build();
              }
          }

          到這里,可以先測試一下,寫一個 Controller,弄一個靜態(tài)的接口測試一下上面集成的內(nèi)容。

          @RestController
          @Api(value = "用戶信息接口")
          public class UserController {

              @Resource
              private UserService userService;

              @GetMapping("/getUser/{id}")
              @ApiOperation(value = "根據(jù)用戶唯一標(biāo)識獲取用戶信息")
              public JsonResult<User> getUserInfo(@PathVariable @ApiParam(value = "用戶唯一標(biāo)識") Long id) {
                  User user = new User(id, "倪升武""123456");
                  return new JsonResult<>(user);
              }
          }

          然后啟動項(xiàng)目,在瀏覽器中輸入 localhost:8080/swagger-ui.html 即可看到 swagger 接口文檔頁面,調(diào)用一下上面這個接口,即可看到返回的 json 數(shù)據(jù)。

          4. 持久層集成

          每個項(xiàng)目中是必須要有持久層的,與數(shù)據(jù)庫交互,這里我們主要來集成 mybatis,集成 mybatis 首先要在 application.yml 中進(jìn)行配置。

          # 服務(wù)端口號
          server:
            port: 8080

          # 數(shù)據(jù)庫地址
          datasource:
            url: localhost:3306/blog_test

          spring:
            datasource: # 數(shù)據(jù)庫配置
              driver-class-name: com.mysql.jdbc.Driver
              url: jdbc:mysql://${datasource.url}?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&autoReconnect=true&failOverReadOnly=false&maxReconnects=10
              username: root
              password: 123456
              hikari:
                maximum-pool-size: 10 # 最大連接池數(shù)
                max-lifetime: 1770000

          mybatis:
            # 指定別名設(shè)置的包為所有entity
            type-aliases-package: com.itcodai.course18.entity
            configuration:
              map-underscore-to-camel-case: true # 駝峰命名規(guī)范
            mapper-locations: # mapper映射文件位置
              - classpath:mapper/*.xml

          配置好了之后,接下來我們來寫一下 dao 層,實(shí)際中我們使用注解比較多,因?yàn)楸容^方便,當(dāng)然也可以使用 xml 的方式,甚至兩種同時使用都行,這里我們主要使用注解的方式來集成,關(guān)于 xml 的方式,大家可以查看前面課程,實(shí)際中根據(jù)項(xiàng)目情況來定。

          public interface UserMapper {

              @Select("select * from user where id = #{id}")
              @Results({
                      @Result(property = "username", column = "user_name"),
                      @Result(property = "password", column = "password")
              })
              User getUser(Long id);

              @Select("select * from user where id = #{id} and user_name=#{name}")
              User getUserByIdAndName(@Param("id") Long id, @Param("name") String username);

              @Select("select * from user")
              List<User> getAll();
          }

          關(guān)于 service 層我就不在文章中寫代碼了,大家可以結(jié)合我的源代碼學(xué)習(xí),這一節(jié)主要帶領(lǐng)大家來搭建一個 Spring Boot 空架構(gòu)。最后別忘了在啟動類上添加注解掃描 @MapperScan("com.itcodai.course18.dao")

          5. 攔截器

          攔截器在項(xiàng)目中使用的是非常多的(但不是絕對的),比如攔截一些置頂?shù)?url,做一些判斷和處理等等。除此之外,還需要將常用的靜態(tài)頁面或者 swagger 頁面放行,不能將這些靜態(tài)資源給攔截了。首先先自定義一個攔截器。

          public class MyInterceptor implements HandlerInterceptor {

              private static final Logger logger = LoggerFactory.getLogger(MyInterceptor.class);

              @Override
              public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

                  logger.info("執(zhí)行方法之前執(zhí)行(Controller方法調(diào)用之前)");
                  return true;
              }

              @Override
              public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
                  logger.info("執(zhí)行完方法之后進(jìn)執(zhí)行(Controller方法調(diào)用之后),但是此時還沒進(jìn)行視圖渲染");
              }

              @Override
              public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
                  logger.info("整個請求都處理完咯,DispatcherServlet也渲染了對應(yīng)的視圖咯,此時我可以做一些清理的工作了");
              }
          }

          然后將自定義的攔截器加入到攔截器配置中。

          @Configuration
          public class MyInterceptorConfig implements WebMvcConfigurer {
              @Override
              public void addInterceptors(InterceptorRegistry registry) {
                  // 實(shí)現(xiàn)WebMvcConfigurer不會導(dǎo)致靜態(tài)資源被攔截
                  registry.addInterceptor(new MyInterceptor())
                          // 攔截所有url
                          .addPathPatterns("/**")
                          // 放行swagger
                          .excludePathPatterns("/swagger-resources/**");
              }
          }

          在 Spring Boot 中,我們通常會在如下目錄里存放一些靜態(tài)資源:

          classpath:/static
          classpath:/public
          classpath:/resources
          classpath:/META-INF/resources

          上面代碼中配置的 /** 是對所有 url 都進(jìn)行了攔截,但我們實(shí)現(xiàn)了 WebMvcConfigurer 接口,不會導(dǎo)致 Spring Boot 對上面這些目錄下的靜態(tài)資源實(shí)施攔截。但是我們平時訪問的 swagger 會被攔截,所以要將其放行。swagger 頁面在 swagger-resources 目錄下,放行該目錄下所有文件即可。

          然后在瀏覽器中輸入一下 swagger 頁面,若能正常顯示 swagger,說明放行成功。同時可以根據(jù)后臺打印的日志判斷代碼執(zhí)行的順序。

          6. 全局異常處理

          全局異常處理是每個項(xiàng)目中必須用到的東西,在具體的異常中,我們可能會做具體的處理,但是對于沒有處理的異常,一般會有一個統(tǒng)一的全局異常處理。在異常處理之前,最好維護(hù)一個異常提示信息枚舉類,專門用來保存異常提示信息的。如下:

          public enum BusinessMsgEnum {
              /** 參數(shù)異常 */
              PARMETER_EXCEPTION("102""參數(shù)異常!"),
              /** 等待超時 */
              SERVICE_TIME_OUT("103""服務(wù)調(diào)用超時!"),
              /** 參數(shù)過大 */
              PARMETER_BIG_EXCEPTION("102""輸入的圖片數(shù)量不能超過50張!"),
              /** 500 : 發(fā)生異常 */
              UNEXPECTED_EXCEPTION("500""系統(tǒng)發(fā)生異常,請聯(lián)系管理員!");

              /**
               * 消息碼
               */

              private String code;
              /**
               * 消息內(nèi)容
               */

              private String msg;

              private BusinessMsgEnum(String code, String msg) {
                  this.code = code;
                  this.msg = msg;
              }

              public String code() {
                  return code;
              }

              public String msg() {
                  return msg;
              }

          }

          在全局統(tǒng)一異常處理類中,我們一般會對自定義的業(yè)務(wù)異常最先處理,然后去處理一些常見的系統(tǒng)異常,最后會來一個一勞永逸(Exception 異常)。

          @ControllerAdvice
          @ResponseBody
          public class GlobalExceptionHandler {

              private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

              /**
               * 攔截業(yè)務(wù)異常,返回業(yè)務(wù)異常信息
               * @param ex
               * @return
               */

              @ExceptionHandler(BusinessErrorException.class)
              @ResponseStatus(value 
          = HttpStatus.INTERNAL_SERVER_ERROR)
              public JsonResult handleBusinessError(BusinessErrorException ex) {
                  String code = ex.getCode();
                  String message = ex.getMessage();
                  return new JsonResult(code, message);
              }

              /**
               * 空指針異常
               * @param ex NullPointerException
               * @return
               */

              @ExceptionHandler(NullPointerException.class)
              @ResponseStatus(value 
          = HttpStatus.INTERNAL_SERVER_ERROR)
              public JsonResult handleTypeMismatchException(NullPointerException ex) {
                  logger.error("空指針異常,{}", ex.getMessage());
                  return new JsonResult("500""空指針異常了");
              }

              /**
               * 系統(tǒng)異常 預(yù)期以外異常
               * @param ex
               * @return
               */

              @ExceptionHandler(Exception.class)
              @ResponseStatus(value 
          = HttpStatus.INTERNAL_SERVER_ERROR)
              public JsonResult handleUnexpectedServer(Exception ex) {
                  logger.error("系統(tǒng)異常:", ex);
                  return new JsonResult(BusinessMsgEnum.UNEXPECTED_EXCEPTION);
              }

          }

          其中,BusinessErrorException 是自定義的業(yè)務(wù)異常,繼承一下 RuntimeException 即可,具體可以看我的源代碼,文章中就不貼代碼了。在 UserController 中有個 testException 方法,用來測試全局異常的,打開 swagger 頁面,調(diào)用一下該接口,可以看出返回用戶提示信息:”系統(tǒng)發(fā)生異常,請聯(lián)系管理員!“。當(dāng)然了,實(shí)際情況中,需要根據(jù)不同的業(yè)務(wù)提示不同的信息。

          7. 總結(jié)

          本文主要手把手帶領(lǐng)大家快速搭建一個項(xiàng)目中可以使用的 Spring Boot 空架構(gòu),主要從統(tǒng)一封裝的數(shù)據(jù)結(jié)構(gòu)、可調(diào)式的接口、json的處理、模板引擎的使用(代碼中體現(xiàn))、持久層的集成、攔截器和全局異常處理。一般包括這些東西的話,基本上一個 Spring Boot 項(xiàng)目環(huán)境就差不多了,然后就是根據(jù)具體情況來擴(kuò)展了。

          該文已收錄到我寫的《10萬字Springboot經(jīng)典學(xué)習(xí)筆記》中,點(diǎn)擊下面小卡片,進(jìn)入【武哥聊編程】,回復(fù):筆記,即可免費(fèi)獲取。

          點(diǎn)贊是最大的支持 

          瀏覽 82
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <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>
                  在线观看的av | 国产日韩无码视频 | 五月丁香婷婷综合激情 | 欧美精品久久 | 色之综合天天综合色天天素质 |