欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談MyBatis通用Mapper實(shí)現(xiàn)原理

 更新時(shí)間:2018年10月19日 14:38:11   作者:isea533  
這篇文章主要介紹了淺談MyBatis通用Mapper實(shí)現(xiàn)原理,本文會(huì)先介紹通用 Mapper 的簡(jiǎn)單原理,然后使用最簡(jiǎn)單的代碼來(lái)實(shí)現(xiàn)這個(gè)過(guò)程。感興趣的小伙伴們可以參考一下

本文會(huì)先介紹通用 Mapper 的簡(jiǎn)單原理,然后使用最簡(jiǎn)單的代碼來(lái)實(shí)現(xiàn)這個(gè)過(guò)程。

基本原理

通用 Mapper 提供了一些通用的方法,這些通用方法是以接口的形式提供的,例如。

public interface SelectMapper<T> {
  /**
   * 根據(jù)實(shí)體中的屬性值進(jìn)行查詢(xún),查詢(xún)條件使用等號(hào)
   */
  @SelectProvider(type = BaseSelectProvider.class, method = "dynamicSQL")
  List<T> select(T record);
}

接口和方法都使用了泛型,使用該通用方法的接口需要指定泛型的類(lèi)型。通過(guò) Java 反射可以很容易得到接口泛型的類(lèi)型信息,代碼如下。

Type[] types = mapperClass.getGenericInterfaces();
Class<?> entityClass = null;
for (Type type : types) {
  if (type instanceof ParameterizedType) {
    ParameterizedType t = (ParameterizedType) type;
    //判斷父接口是否為 SelectMapper.class
    if (t.getRawType() == SelectMapper.class) {
      //得到泛型類(lèi)型
      entityClass = (Class<?>) t.getActualTypeArguments()[0];
      break;
    }
  }
}

實(shí)體類(lèi)中添加的 JPA 注解只是一種映射實(shí)體和數(shù)據(jù)庫(kù)表關(guān)系的手段,通過(guò)一些默認(rèn)規(guī)則或者自定義注解也很容易設(shè)置這種關(guān)系,獲取實(shí)體和表的對(duì)應(yīng)關(guān)系后,就可以根據(jù)通用接口方法定義的功能來(lái)生成和 XML 中一樣的 SQL 代碼。動(dòng)態(tài)生成 XML 樣式代碼的方式有很多,最簡(jiǎn)單的方式就是純 Java 代碼拼字符串,通用 Mapper 為了盡可能的少的依賴(lài)選擇了這種方式。如果使用模板(如 FreeMarker,Velocity 和 beetl 等模板引擎)實(shí)現(xiàn),自由度會(huì)更高,也能方便開(kāi)發(fā)人員調(diào)整。

在 MyBatis 中,每一個(gè)方法(注解或 XML 方式)經(jīng)過(guò)處理后,最終會(huì)構(gòu)造成 MappedStatement 實(shí)例,這個(gè)對(duì)象包含了方法id(namespace+id)、結(jié)果映射、緩存配置、SqlSource 等信息,和 SQL 關(guān)系最緊密的是其中的 SqlSource,MyBatis 最終執(zhí)行的 SQL 時(shí)就是通過(guò)這個(gè)接口的 getBoundSql 方法獲取的。

在 MyBatis 中,使用@SelectProvider 這種方式定義的方法,最終會(huì)構(gòu)造成 ProviderSqlSource,ProviderSqlSource 是一種處于中間的 SqlSource,它本身不能作為最終執(zhí)行時(shí)使用的 SqlSource,但是他會(huì)根據(jù)指定方法返回的 SQL 去構(gòu)造一個(gè)可用于最后執(zhí)行的 StaticSqlSource,StaticSqlSource的特點(diǎn)就是靜態(tài) SQL,支持在 SQL 中使用#{param} 方式的參數(shù),但是不支持 <if>,<where> 等標(biāo)簽。

為了能根據(jù)實(shí)體類(lèi)動(dòng)態(tài)生成支持動(dòng)態(tài) SQL 的方法,通用 Mapper 從這里入手,利用ProviderSqlSource 可以生成正常的 MappedStatement,可以直接利用 MyBatis 各種配置和命名空間的特點(diǎn)(這是通用 Mapper 選擇這種方式的主要原因)。在生成 MappedStatement 后,“過(guò)河拆橋” 般的利用完就把 ProviderSqlSource 替換掉了,正常情況下,ProviderSqlSource 根本就沒(méi)有執(zhí)行的機(jī)會(huì)。在通用 Mapper 定義的實(shí)現(xiàn)方法中,提供了 MappedStatement 作為參數(shù),有了這個(gè)參數(shù),我們就可以根據(jù) ms 的 id(規(guī)范情況下是 接口名.方法名)得到接口,通過(guò)接口的泛型可以獲取實(shí)體類(lèi)(entityClass),根據(jù)實(shí)體和表的關(guān)系我們可以拼出 XML 方式的動(dòng)態(tài) SQL,一個(gè)簡(jiǎn)單的方法如下。

/**
 * 查詢(xún)?nèi)拷Y(jié)果
 *
 * @param ms
 * @return
 */
public String selectAll(MappedStatement ms) {
  final Class<?> entityClass = getEntityClass(ms);
  //修改返回值類(lèi)型為實(shí)體類(lèi)型
  setResultType(ms, entityClass);
  StringBuilder sql = new StringBuilder();
  sql.append(SqlHelper.selectAllColumns(entityClass));
  sql.append(SqlHelper.fromTable(entityClass, tableName(entityClass)));
  sql.append(SqlHelper.orderByDefault(entityClass));
  return sql.toString();
}

拼出的 XML 形式的動(dòng)態(tài) SQL,使用 MyBatis 的 XMLLanguageDriver 中的 createSqlSource 方法可以生成 SqlSource。然后使用反射用新的 SqlSource 替換ProviderSqlSource 即可,如下代碼。

/**
 * 重新設(shè)置SqlSource
 *
 * @param ms
 * @param sqlSource
 */
protected void setSqlSource(MappedStatement ms, SqlSource sqlSource) {
  MetaObject msObject = SystemMetaObject.forObject(ms);
  msObject.setValue("sqlSource", sqlSource);
}

MetaObject 是MyBatis 中很有用的工具類(lèi),MyBatis 的結(jié)果映射就是靠這種方式實(shí)現(xiàn)的。反射信息使用的 DefaultReflectorFactory,這個(gè)類(lèi)會(huì)緩存反射信息,因此 MyBatis 的結(jié)果映射的效率很高。

到這里核心的內(nèi)容都已經(jīng)說(shuō)完了,雖然知道怎么去替換 SqlSource了,但是!什么時(shí)候去替換呢?

這一直都是一個(gè)難題,如果不大量重寫(xiě) MyBatis 的代碼很難萬(wàn)無(wú)一失的完成這個(gè)任務(wù)。通用 Mapper 并沒(méi)有去大量重寫(xiě),主要是考慮到以后的升級(jí),也因此在某些特殊情況下,通用 Mapper 的方法會(huì)在沒(méi)有被替換的情況下被調(diào)用,這個(gè)問(wèn)題在將來(lái)的 MyBatis 3.5.x 版本中會(huì)以更友好的方式解決(目前的 ProviderSqlSource 已經(jīng)比以前能實(shí)現(xiàn)更多的東西,后面會(huì)講)。

針對(duì)不同的運(yùn)行環(huán)境,需要用不同的方式去替換。當(dāng)使用純 MyBatis (沒(méi)有Spring)方式運(yùn)行時(shí),替換很簡(jiǎn)單,因?yàn)闀?huì)在系統(tǒng)中初始化 SqlSessionFactory,可以初始化的時(shí)候進(jìn)行替換,這個(gè)時(shí)候也不會(huì)出現(xiàn)前面提到的問(wèn)題。替換的方式也很簡(jiǎn)單,通過(guò) SqlSessionFactory 可以得到 SqlSession,然后就能得到 Configuration,通過(guò) configuration.getMappedStatements() 就能得到所有的 MappedStatement,循環(huán)判斷其中的方法是否為通用接口提供的方法,如果是就按照前面的方式替換就可以了。

在使用 Spring 的情況下,以繼承的方式重寫(xiě)了 MapperScannerConfigurer 和 MapperFactoryBean,在 Spring 調(diào)用 checkDaoConfig 的時(shí)候?qū)?SqlSource 進(jìn)行替換。在使用 Spring Boot 時(shí),提供的 mapper-starter 中,直接注入 List<SqlSessionFactory> sqlSessionFactoryList 進(jìn)行替換。

下面我們按照這個(gè)思路,以最簡(jiǎn)練的代碼,實(shí)現(xiàn)一個(gè)通用方法。

實(shí)現(xiàn)一個(gè)簡(jiǎn)單的通用 Mapper

1. 定義通用接口方法

public interface BaseMapper<T> {
  @SelectProvider(type = SelectMethodProvider.class, method = "select")
  List<T> select(T entity);
}

這里定義了一個(gè)簡(jiǎn)單的 select 方法,這個(gè)方法判斷參數(shù)中的屬性是否為空,不為空的字段會(huì)作為查詢(xún)條件進(jìn)行查詢(xún),下面是對(duì)應(yīng)的 Provider。

public class SelectMethodProvider {
  public String select(Object params) {
    return "什么都不是!";
  }
}

這里的 Provider 不會(huì)最終執(zhí)行,只是為了在初始化時(shí)可以生成對(duì)應(yīng)的 MappedStatement。

2. 替換 SqlSource

下面代碼為了簡(jiǎn)單,都指定的 BaseMapper 接口,并且沒(méi)有特別的校驗(yàn)。

public class SimpleMapperHelper {
  public static final XMLLanguageDriver XML_LANGUAGE_DRIVER
      = new XMLLanguageDriver();
  /**
   * 獲取泛型類(lèi)型
   */
  public static Class getEntityClass(Class<?> mapperClass){
    Type[] types = mapperClass.getGenericInterfaces();
    Class<?> entityClass = null;
    for (Type type : types) {
      if (type instanceof ParameterizedType) {
        ParameterizedType t = (ParameterizedType) type;
        //判斷父接口是否為 BaseMapper.class
        if (t.getRawType() == BaseMapper.class) {
          //得到泛型類(lèi)型
          entityClass = (Class<?>) t.getActualTypeArguments()[0];
          break;
        }
      }
    }
    return entityClass;
  }

  /**
   * 替換 SqlSource
   */
  public static void changeMs(MappedStatement ms) throws Exception {
    String msId = ms.getId();
    //標(biāo)準(zhǔn)msId為 包名.接口名.方法名
    int lastIndex = msId.lastIndexOf(".");
    String methodName = msId.substring(lastIndex + 1);
    String interfaceName = msId.substring(0, lastIndex);
    Class<?> mapperClass = Class.forName(interfaceName);
    //判斷是否繼承了通用接口
    if(BaseMapper.class.isAssignableFrom(mapperClass)){
      //判斷當(dāng)前方法是否為通用 select 方法
      if (methodName.equals("select")) {
        Class entityClass = getEntityClass(mapperClass);
        //必須使用<script>標(biāo)簽包裹代碼
        StringBuffer sqlBuilder = new StringBuffer("<script>");
        //簡(jiǎn)單使用類(lèi)名作為包名
        sqlBuilder.append("select * from ").append(entityClass.getSimpleName());
        Field[] fields = entityClass.getDeclaredFields();
        sqlBuilder.append(" <where> ");
        for (Field field : fields) {
          sqlBuilder.append("<if test=\"")
              .append(field.getName()).append("!=null\">");
          //字段名直接作為列名
          sqlBuilder.append(" and ").append(field.getName())
               .append(" = #{").append(field.getName()).append("}");
          sqlBuilder.append("</if>");
        }
        sqlBuilder.append("</where>");
        sqlBuilder.append("</script>");
        //解析 sqlSource
        SqlSource sqlSource = XML_LANGUAGE_DRIVER.createSqlSource(
            ms.getConfiguration(), sqlBuilder.toString(), entityClass);
        //替換
        MetaObject msObject = SystemMetaObject.forObject(ms);
        msObject.setValue("sqlSource", sqlSource);
      }
    }
  }

}

changeMs 方法簡(jiǎn)單的從 msId 開(kāi)始,獲取接口和實(shí)體信息,通過(guò)反射回去字段信息,使用 <if> 標(biāo)簽動(dòng)態(tài)判斷屬性值,這里的寫(xiě)法和 XML 中一樣,使用 XMLLanguageDriver 處理時(shí)需要在外面包上 <script> 標(biāo)簽。生成 SqlSource 后,通過(guò)反射替換了原值。

3. 測(cè)試

針對(duì)上面代碼,提供一個(gè) country 表和對(duì)應(yīng)的各種類(lèi)。

實(shí)體類(lèi)。

public class Country {
 private Long  id;
 private String countryname;
 private String countrycode;
 //省略 getter,setter
} 

Mapper 接口。

public interface CountryMapper extends BaseMapper<Country> {

}

啟動(dòng) MyBatis 的公共類(lèi)。

public class SqlSessionHelper {
  private static SqlSessionFactory sqlSessionFactory;

  static {
    try {
      Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
      sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
      reader.close();
      //創(chuàng)建數(shù)據(jù)庫(kù)
      SqlSession session = null;
      try {
        session = sqlSessionFactory.openSession();
        Connection conn = session.getConnection();
        reader = Resources.getResourceAsReader("hsqldb.sql");
        ScriptRunner runner = new ScriptRunner(conn);
        runner.setLogWriter(null);
        runner.runScript(reader);
        reader.close();
      } finally {
        if (session != null) {
          session.close();
        }
      }
    } catch (IOException ignore) {
      ignore.printStackTrace();
    }
  }

  public static SqlSession getSqlSession() {
    return sqlSessionFactory.openSession();
  }

}

 配置文件。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
 <environments default="development">
  <environment id="development">
   <transactionManager type="JDBC">
    <property name="" value=""/>
   </transactionManager>
   <dataSource type="UNPOOLED">
    <property name="driver" value="org.hsqldb.jdbcDriver"/>
    <property name="url" value="jdbc:hsqldb:mem:basetest"/>
    <property name="username" value="sa"/>
   </dataSource>
  </environment>
 </environments>

 <mappers>
  <package name="tk.mybatis.simple.mapper"/>
 </mappers>

</configuration>

初始化sql。

drop table country if exists;

create table country (
 id integer,
 countryname varchar(32),
 countrycode varchar(2)
);

insert into country (id, countryname, countrycode) values(1,'Angola','AO');
insert into country (id, countryname, countrycode) values(23,'Botswana','BW');
-- 省略部分
insert into country (id, countryname, countrycode) values(34,'Chile','CL');
insert into country (id, countryname, countrycode) values(35,'China','CN');
insert into country (id, countryname, countrycode) values(36,'Colombia','CO');

測(cè)試代碼。

public class SimpleTest {

  public static void main(String[] args) throws Exception {
    SqlSession sqlSession = SqlSessionHelper.getSqlSession();
    Configuration configuration = sqlSession.getConfiguration();
    HashSet<MappedStatement> mappedStatements
        = new HashSet<MappedStatement>(configuration.getMappedStatements());
    //如果注釋下面替換步驟就會(huì)出錯(cuò)
    for (MappedStatement ms : mappedStatements) {
      SimpleMapperHelper.changeMs(ms);
    }
    //替換后執(zhí)行該方法
    CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
    Country query = new Country();
    //可以修改條件或者注釋條件查詢(xún)?nèi)?
    query.setCountrycode("CN");
    List<Country> countryList = mapper.select(query);
    for (Country country : countryList) {
      System.out.printf("%s - %s\n",
          country.getCountryname(),
          country.getCountrycode());
    }
    sqlSession.close();
  }
}

通過(guò)簡(jiǎn)化版的處理過(guò)程應(yīng)該可以和前面的內(nèi)容聯(lián)系起來(lái),從而理解通用 Mapper 的簡(jiǎn)單處理過(guò)程。

完整代碼下載:simple-mapper_jb51.rar

最新的 ProviderSqlSource

早期的 ProviderSqlSource 有個(gè)缺點(diǎn)就是定義的方法要么沒(méi)有參數(shù),要么只能是 Object parameterObject 參數(shù),這個(gè)參數(shù)最終的形式在開(kāi)發(fā)時(shí)也不容易一次寫(xiě)對(duì),因?yàn)椴煌问降慕涌诘膮?shù)會(huì)被 MyBatis 處理成不同的形式,可以參考 深入了解MyBatis參數(shù)。由于沒(méi)有提供接口和類(lèi)型相關(guān)的參數(shù),因此無(wú)法根據(jù)類(lèi)型實(shí)現(xiàn)通用的方法。

在最新的 3.4.5 版本中,ProviderSqlSource 增加了一個(gè)額外可選的 ProviderContext 參數(shù),這個(gè)類(lèi)如下。

/**
 * The context object for sql provider method.
 *
 * @author Kazuki Shimizu
 * @since 3.4.5
 */
public final class ProviderContext {

 private final Class<?> mapperType;
 private final Method mapperMethod;

 /**
  * Constructor.
  *
  * @param mapperType A mapper interface type that specified provider
  * @param mapperMethod A mapper method that specified provider
  */
 ProviderContext(Class<?> mapperType, Method mapperMethod) {
  this.mapperType = mapperType;
  this.mapperMethod = mapperMethod;
 }

 /**
  * Get a mapper interface type that specified provider.
  *
  * @return A mapper interface type that specified provider
  */
 public Class<?> getMapperType() {
  return mapperType;
 }

 /**
  * Get a mapper method that specified provider.
  *
  * @return A mapper method that specified provider
  */
 public Method getMapperMethod() {
  return mapperMethod;
 }

}

有了這個(gè)參數(shù)后,就能獲取到接口和當(dāng)前執(zhí)行的方法信息,因此我們已經(jīng)可以實(shí)現(xiàn)通用方法了。

下面是一個(gè)官方測(cè)試中的簡(jiǎn)單例子,定義的通用接口如下。

public interface BaseMapper<T> {

 @SelectProvider(type= OurSqlBuilder.class, method= "buildSelectByIdProviderContextOnly")
 @ContainsLogicalDelete
 T selectById(Integer id);

 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.METHOD)
 @interface ContainsLogicalDelete {
  boolean value() default false;
 }

 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
 @interface Meta {
  String tableName();
 }
}

接口定義了一個(gè)簡(jiǎn)單的根據(jù) id 查詢(xún)的方法,定義了一個(gè)邏輯刪除的注解、還有一個(gè)表名的元注解。

下面是 方法的實(shí)現(xiàn)。

public String buildSelectByIdProviderContextOnly(ProviderContext context) {
 //獲取方法上的邏輯刪除注解
 final boolean containsLogicalDelete = context.getMapperMethod().
      getAnnotation(BaseMapper.ContainsLogicalDelete.class) != null;
 //獲取接口上的元注解(不是實(shí)體)
 final String tableName = context.getMapperType().
      getAnnotation(BaseMapper.Meta.class).tableName();
 return new SQL(){{
  SELECT("*");
  FROM(tableName);
  WHERE("id = #{id}");
  if (!containsLogicalDelete){
   WHERE("logical_delete = ${Constants.LOGICAL_DELETE_OFF}");
  }
 }}.toString();
}

這里相比之前,可以獲取到更多的信息,SQL 也不只是固定表的查詢(xún),可以根據(jù) @Meta 注解制定方法查詢(xún)的表名,和原來(lái)一樣的是,最終還是返回一個(gè)簡(jiǎn)單的 SQL 字符串,仍然不支持動(dòng)態(tài) SQL 的標(biāo)簽。

下面是實(shí)現(xiàn)的接口。

@BaseMapper.Meta(tableName = "users")
public interface Mapper extends BaseMapper<User> {

}

上面實(shí)現(xiàn)的方法中,注解從接口獲取的,因此這里也是在 Mapper 上配置的 Meta 接口。

按照前面通用 Mapper 中的介紹,在實(shí)現(xiàn)方法中是可以獲取 User 類(lèi)型的,因此如果把注解定義在實(shí)體類(lèi)上也是可行的。

現(xiàn)在看起來(lái)已經(jīng)很不錯(cuò)了,但是還不支持動(dòng)態(tài) SQL,還不能緩存根據(jù) SQL 生成的 SqlSource,因此每次執(zhí)行都需要執(zhí)行方法去生成 SqlSource,仍然還有改進(jìn)的地方,為了解決這個(gè)問(wèn)題,我提交了兩個(gè)PR #1111,#1120,目前還在討論階段,真正實(shí)現(xiàn)可能要到 3.5.0 版本。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • java設(shè)計(jì)模式理解依賴(lài)于抽象不依賴(lài)具體的分析

    java設(shè)計(jì)模式理解依賴(lài)于抽象不依賴(lài)具體的分析

    這篇文章主要為大家介紹了java設(shè)計(jì)模式的規(guī)則,理解依賴(lài)于抽象不依賴(lài)具體的示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2021-10-10
  • IDEA高效使用設(shè)置指南

    IDEA高效使用設(shè)置指南

    本文主要為大家介紹了關(guān)于IDEA高效的設(shè)置指南,其中包含必備的一些插件推薦以及主題優(yōu)化還有IDEA源碼的閱讀技巧,干貨滿(mǎn)滿(mǎn),有需要的朋友可以借鑒參考下
    2022-01-01
  • Java中的CompletableFuture原理與用法

    Java中的CompletableFuture原理與用法

    CompletableFuture 是由Java8引入的,這讓我們編寫(xiě)清晰可讀的異步代碼變得更加容易,該類(lèi)功能比Future 更加強(qiáng)大,在Java中CompletableFuture用于異步編程,異步通常意味著非阻塞,運(yùn)行任務(wù)單獨(dú)的線程,與主線程隔離,這篇文章介紹CompletableFuture原理與用法,一起看看吧
    2024-01-01
  • Java 使用JdbcTemplate 中的queryForList發(fā)生錯(cuò)誤解決辦法

    Java 使用JdbcTemplate 中的queryForList發(fā)生錯(cuò)誤解決辦法

    這篇文章主要介紹了Java 使用JdbcTemplate 中的queryForList發(fā)生錯(cuò)誤解決辦法的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • 如何使用jakarta.json進(jìn)行json序列化和反序列化

    如何使用jakarta.json進(jìn)行json序列化和反序列化

    java里,json框架何其多,常見(jiàn)的有jackson、fastjson、gson等,本文重點(diǎn)介紹如何使用jakarta.json進(jìn)行json序列化和反序列化,需要的朋友可以參考下,
    2024-07-07
  • java selenium XPath 定位實(shí)現(xiàn)方法

    java selenium XPath 定位實(shí)現(xiàn)方法

    本文主要介紹java selenium XPath,這里整理了XPath的資料,并附實(shí)現(xiàn)方法,有需要的小伙伴可以參考下
    2016-08-08
  • SpringBoot工程啟動(dòng)順序與自定義監(jiān)聽(tīng)超詳細(xì)講解

    SpringBoot工程啟動(dòng)順序與自定義監(jiān)聽(tīng)超詳細(xì)講解

    這篇文章主要介紹了SpringBoot工程啟動(dòng)順序與自定義監(jiān)聽(tīng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2022-11-11
  • 使用hutool進(jìn)行ftp文件下載和上傳詳細(xì)代碼示例

    使用hutool進(jìn)行ftp文件下載和上傳詳細(xì)代碼示例

    在開(kāi)發(fā)Java項(xiàng)目時(shí),FTP客戶(hù)端是經(jīng)常需要使用的工具,因?yàn)镕TP協(xié)議在文件傳輸方面有著廣泛的應(yīng)用,這篇文章主要給大家介紹了關(guān)于使用hutool進(jìn)行ftp文件下載和上傳的相關(guān)資料,需要的朋友可以參考下
    2024-02-02
  • 從零搭建Spring Boot腳手架整合OSS作為文件服務(wù)器的詳細(xì)教程

    從零搭建Spring Boot腳手架整合OSS作為文件服務(wù)器的詳細(xì)教程

    這篇文章主要介紹了從零搭建Spring Boot腳手架整合OSS作為文件服務(wù)器的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Springboot項(xiàng)目打包如何將依賴(lài)的jar包輸出到指定目錄

    Springboot項(xiàng)目打包如何將依賴(lài)的jar包輸出到指定目錄

    公司要對(duì)springboot項(xiàng)目依賴(lài)的jar包進(jìn)行升級(jí),但是遇到一個(gè)問(wèn)題,項(xiàng)目打包之后,沒(méi)辦法看到他里面依賴(lài)的jar包,版本到底是不是升上去了,沒(méi)辦法看到,下面通過(guò)本文給大家分享Springboot項(xiàng)目打包如何將依賴(lài)的jar包輸出到指定目錄,感興趣的朋友一起看看吧
    2024-05-05

最新評(píng)論