最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MybatisPlus?BaseMapper?實(shí)現(xiàn)對(duì)數(shù)據(jù)庫增刪改查源碼

 更新時(shí)間:2023年01月31日 15:48:22   作者:myboy  
MybatisPlus?是一款在?Mybatis?基礎(chǔ)上進(jìn)行的增強(qiáng)?orm?框架,可以實(shí)現(xiàn)不寫?sql?就完成數(shù)據(jù)庫相關(guān)的操作,這篇文章主要介紹了MybatisPlus?BaseMapper?實(shí)現(xiàn)對(duì)數(shù)據(jù)庫增刪改查源碼解析,需要的朋友可以參考下

MybatisPlus 是一款在 Mybatis 基礎(chǔ)上進(jìn)行的增強(qiáng) orm 框架,可以實(shí)現(xiàn)不寫 sql 就完成數(shù)據(jù)庫相關(guān)的操作。普通的 mapper 接口通過繼承 BaseMapper 接口,即可獲得增強(qiáng),如下所示:

public interface UserMapper extends BaseMapper<User> {
}

接下來就對(duì)其源碼一探究竟,看看他到底是如何實(shí)現(xiàn)的

環(huán)境搭建

1、使用 h2 數(shù)據(jù)庫,方便測(cè)試,導(dǎo)入相關(guān)依賴

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web:2.7.1'
    implementation 'com.baomidou:mybatis-plus-boot-starter:3.5.3.1'
    implementation 'org.projectlombok:lombok:1.18.24'
    implementation 'com.h2database:h2:1.4.200'
}

2、springboot 配置文件

spring:
  datasource:
    driver-class-name: org.h2.Driver
    username: root
    password: test
  sql:
    init:
      schema-locations: classpath:db/schema-h2.sql
      data-locations: classpath:db/data-h2.sql

3、resources 目錄下新建 db 目錄,創(chuàng)建 sql 文件

schema-h2.sql

DROP TABLE IF EXISTS demo_user;

CREATE TABLE demo_user
(
    id int primary key,
    name varchar,
    age int,
    email varchar
);

data-h2.sql

DELETE
FROM demo_user;

INSERT INTO demo_user (id, name, age, email)
VALUES (1, 'Jone', 18, 'test1@baomidou.com'),
       (2, 'Jack', 20, 'test2@baomidou.com'),
       (3, 'Tom', 28, 'test3@baomidou.com'),
       (4, 'Sandy', 21, 'test4@baomidou.com'),
       (5, 'Billie', 24, 'test5@baomidou.com');

4、編寫 mapper 文件

public interface UserMapper extends BaseMapper<User> {
}

5、啟動(dòng)測(cè)試

@MapperScan("org.example.mapper")
@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Main.class, args);
        UserMapper userMapper = context.getBean(UserMapper.class);
        System.out.println(userMapper.selectList(null));
    }
}

結(jié)果如下

[User(id=1, name=Jone, age=18, email=test1@baomidou.com), User(id=2, name=Jack, age=20, email=test2@baomidou.com), User(id=3, name=Tom, age=28, email=test3@baomidou.com), User(id=4, name=Sandy, age=21, email=test4@baomidou.com), User(id=5, name=Billie, age=24, email=test5@baomidou.com)]

從 @MapperScan 入手

@MapperScan 注解的作用是掃描指定 mapper 接口所在的包,并生成接口的代理對(duì)象,注入到 ioc 容器中,接口定義如下

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
}

可以看到 Import 了個(gè) MapperScannerRegistrar,點(diǎn)進(jìn)去看看這個(gè)類做了什么

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    AnnotationAttributes mapperScanAttrs = AnnotationAttributes
        .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    if (mapperScanAttrs != null) {
      // 注冊(cè)一個(gè) beanDefinition
      registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
          generateBaseBeanName(importingClassMetadata, 0));
    }
}

void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
  BeanDefinitionRegistry registry, String beanName) {

    // 注冊(cè)MapperScannerConfigurer的BeanDefinition
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
    // ......
    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}

這個(gè) importRegister 注冊(cè)了一個(gè) MapperScannerConfigurer,這個(gè)類是個(gè) BeanDefinitionRegistryPostProcessor,核心邏輯就是在這個(gè)類中,即掃描指定 mapper 接口所在的包,并生成接口的代理對(duì)象,注入到 ioc 容器中,查看該類的 postProcessBeanDefinitionRegistry() 方法

@Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
      processPropertyPlaceHolders();
    }

    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    // 設(shè)置一些scanner參數(shù)
    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
    // ......
    // 掃描mapper接口
    scanner.scan(
        StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }

進(jìn)入父類 scan 方法,發(fā)現(xiàn)核心方法是子類的 doScan(), 來到 MapperScannerConfigurer.doScan()

@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    // 拿到掃描到的 beanDefinition
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

    if (beanDefinitions.isEmpty()) {
      LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
          + "' package. Please check your configuration.");
    } else {
      // 處理 mapper beanDefinition
      processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
}

核心在 processBeanDefinitions(beanDefinitions) 中

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    AbstractBeanDefinition definition;
    BeanDefinitionRegistry registry = getRegistry();
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (AbstractBeanDefinition) holder.getBeanDefinition();

      // 設(shè)置該BeanDefinition的beanClass是 MapperFactoryBean
      definition.setBeanClass(this.mapperFactoryBeanClass);

      // ......

      // 設(shè)置該MapperFactoryBean 中的 sqlSessionTemplateBeanName
      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
        definition.getPropertyValues().add("sqlSessionTemplate",
            new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
        explicitFactoryUsed = true;
      }

      // ......

    }
}

通過這一系列源碼,可以知道,@MapperScan 指定的包在 MapperScannerConfigurer 被掃描成 BeanDefinition, 并且修改了 BeanDefinition 的 beanClass 屬性為 MapperFactory,這樣 spring 實(shí)例化 UserMapper 單例 bean 時(shí),會(huì)生成對(duì)應(yīng)的 MapperFactory

看看這個(gè) MapperFactory 是什么鬼

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {

  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
  
  public SqlSession getSqlSession() {
    return this.sqlSessionTemplate;
  }

}

這個(gè)類是個(gè) FactoryBean,那么它的 getObject() 方法就是調(diào)用 sqlSessionTemplate 的 getMapper() 方法獲取代理對(duì)象,關(guān)于這個(gè) getMapper() 方法的解析,可以參考我之前寫的《Mybatis 通過接口實(shí)現(xiàn) sql 執(zhí)行原理解析

到這里,MapperFactory 生成的 bean 被放到了 ioc 容器中,結(jié)束了嗎?我們忽略了 MapperFactory 的父類 SqlSessionDaoSupport,下面一節(jié)來看看這個(gè)父類 SqlSessionDaoSupport 做了什么

SqlSessionDaoSupport

這個(gè)類看名字是給 Dao 做支持的,Dao 指的就是那個(gè) mapper 接口,做什么支持?其實(shí)給就是給 BaseMapper 里定義的方法生成對(duì)應(yīng)的 Statemnet,注冊(cè)到 MybatisMapperRegistry 中,這樣調(diào)用 BaseMapper 方法時(shí),代理類就會(huì)從 MybatisMapperRegistry 中找到 Statemnet,這樣可以取出 sql 執(zhí)行了,來看源碼,其他都是抽象方法,只有一個(gè)初始化方法

@Override
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
    // 讓子類處理
    checkDaoConfig();

    // Let concrete implementations initialize themselves.
    try {
            initDao();
    }
    catch (Exception ex) {
            throw new BeanInitializationException("Initialization of DAO failed", ex);
    }
}

調(diào)用了抽象方法,子類實(shí)現(xiàn)了 checkDaoConfig(),來看下 MapperFactoryBean.checkDaoConfig()

protected void checkDaoConfig() {
    super.checkDaoConfig();
    Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
    Configuration configuration = this.getSqlSession().getConfiguration();
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
        try {
            // 解析這個(gè) mapper 方法
            configuration.addMapper(this.mapperInterface);
        } catch (Exception var6) {
            this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6);
            throw new IllegalArgumentException(var6);
        } finally {
            ErrorContext.instance().reset();
        }
    }

}

看到 configuration.addMapper(this.mapperInterface) 方法,相信看過 mybatis 源碼的小伙伴們已經(jīng)知道要干什么了吧。就是解析這個(gè) mapper 類方法,找到對(duì)應(yīng)的 sql,并封裝成 statemnet,下面看看這個(gè) configuration.addMapper(this.mapperInterface) 的實(shí)現(xiàn)邏輯吧

MybatisConfiguration.addMapper()

因?yàn)槭?MybatisPlus,所以源碼內(nèi)部的 Configuration 類是 MybatisConfiguration,查看他的 addMapper() 方法源碼

@Override
public <T> void addMapper(Class<T> type) {
    mybatisMapperRegistry.addMapper(type);
}

再進(jìn)入 mybatisMapperRegistry.addMapper(type) 源碼

@Override
public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
        if (hasMapper(type)) {
            // TODO 如果之前注入 直接返回
            return;
        }
        boolean loadCompleted = false;
        try {
            // TODO 注冊(cè)mapper類對(duì)應(yīng)的代理工廠類,用于生成代理對(duì)象
            knownMappers.put(type, new MybatisMapperProxyFactory<>(type));
            MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);
            // 解析mapper類,生成 statement
            parser.parse();
            loadCompleted = true;
        } finally {
            if (!loadCompleted) {
                knownMappers.remove(type);
            }
        }
    }
}

進(jìn)入 parse() 方法查看

@Override
public void parse() {
    String resource = type.toString();
    if (!configuration.isResourceLoaded(resource)) {
        // 解析mapper.xml
        loadXmlResource();
        configuration.addLoadedResource(resource);
        String mapperName = type.getName();
        assistant.setCurrentNamespace(mapperName);
        // 解析緩存
        parseCache();
        parseCacheRef();
        IgnoreStrategy ignoreStrategy = InterceptorIgnoreHelper.initSqlParserInfoCache(type);
        for (Method method : type.getMethods()) {
            if (!canHaveStatement(method)) {
                continue;
            }
            if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()
                && method.getAnnotation(ResultMap.class) == null) {
                parseResultMap(method);
            }
            try {
                // TODO 加入 注解過濾緩存
                InterceptorIgnoreHelper.initSqlParserInfoCache(ignoreStrategy, mapperName, method);
                parseStatement(method);
            } catch (IncompleteElementException e) {
                // TODO 使用 MybatisMethodResolver 而不是 MethodResolver
                configuration.addIncompleteMethod(new MybatisMethodResolver(this, method));
            }
        }
        // TODO 注入 CURD 動(dòng)態(tài) SQL , 放在在最后, because 可能會(huì)有人會(huì)用注解重寫sql
        try {
            // https://github.com/baomidou/mybatis-plus/issues/3038
            if (GlobalConfigUtils.isSupperMapperChildren(configuration, type)) {
                parserInjector();
            }
        } catch (IncompleteElementException e) {
            configuration.addIncompleteMethod(new InjectorResolver(this));
        }
    }
    parsePendingMethods();
}

關(guān)注最后注釋,注入 CRUD 動(dòng)態(tài) SQL,其實(shí)就是給 BaseMapper 里的方法創(chuàng)建對(duì)應(yīng)的 Statement,查看內(nèi)部邏輯:

void parserInjector() {
    // DefaultSqlInjector.inspectInject();
    GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type);
}

這里先獲取到默認(rèn)的 Sql 注入器 DefaultSqlInjector,再調(diào)用其 inspectInject() 方法注入 sql

@Override
public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
    Class<?> modelClass = ReflectionKit.getSuperClassGenericType(mapperClass, Mapper.class, 0);
    if (modelClass != null) {
        String className = mapperClass.toString();
        Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
        if (!mapperRegistryCache.contains(className)) {
            // 根據(jù)實(shí)體類,根據(jù)注解解析出表的信息
            TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
            // 拿到所有的AbstractMethod實(shí)現(xiàn)類
            List<AbstractMethod> methodList = this.getMethodList(mapperClass, tableInfo);
            if (CollectionUtils.isNotEmpty(methodList)) {
                // 循環(huán)注入自定義方法
                methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));
            } else {
                logger.debug(mapperClass.toString() + ", No effective injection method was found.");
            }
            mapperRegistryCache.add(className);
        }
    }
}

這里面的 AbstractMethod 的實(shí)現(xiàn)類有很多,如下

可以說,BaseMapper 中每個(gè)方法都有一個(gè)對(duì)應(yīng)的 AbstractMethod 實(shí)現(xiàn)類,以 selectList() 為例,可以找到 SelectList 類

在下面循環(huán)注入的地方:methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo)), 進(jìn)入 AbstractMethod.inject() 方法

public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
    this.configuration = builderAssistant.getConfiguration();
    this.builderAssistant = builderAssistant;
    this.languageDriver = configuration.getDefaultScriptingLanguageInstance();
    /* 注入自定義方法 */
    injectMappedStatement(mapperClass, modelClass, tableInfo);
}

子類實(shí)現(xiàn)了 injectMappedStatement 方法,還是以 SelectList 為例

@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
    // selectList sql 模版
    SqlMethod sqlMethod = SqlMethod.SELECT_LIST;
    // 格式化sql
    String sql = String.format(sqlMethod.getSql(), sqlFirst(), sqlSelectColumns(tableInfo, true), tableInfo.getTableName(),
        sqlWhereEntityWrapper(true, tableInfo), sqlOrderBy(tableInfo), sqlComment());
    // 封裝成 sqlSource 
    SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
    // 注冊(cè) mapperStatement
    return this.addSelectMappedStatementForTable(mapperClass, methodName, sqlSource, tableInfo);
}

其中 sqlSelectColumns(tableInfo, true) 方法是構(gòu)造出 select 的所有列名,并加上動(dòng)態(tài)sql標(biāo)簽

<choose>
    <when test="ew != null and ew.sqlSelect != null">
    ${ew.sqlSelect}
    </when>
    <otherwise>id,name,age,email</otherwise>
</choose>

其中 sqlWhereEntityWrapper(true, tableInfo) 方法是構(gòu)造出 where 后面的條件語句,并加上動(dòng)態(tài)sql標(biāo)簽

<if test="ew != null">
<where>
    <if test="ew.entity != null">
    <if test="ew.entity.id != null">id=#{ew.entity.id}</if>
    <if test="ew.entity['name'] != null"> AND name=#{ew.entity.name}</if>
    <if test="ew.entity['age'] != null"> AND age=#{ew.entity.age}</if>
    <if test="ew.entity['email'] != null"> AND email=#{ew.entity.email}</if>
    </if>
    <if test="ew.sqlSegment != null and ew.sqlSegment != '' and ew.nonEmptyOfWhere">
    <if test="ew.nonEmptyOfEntity and ew.nonEmptyOfNormal"> AND</if> ${ew.sqlSegment}
    </if>
</where>
<if test="ew.sqlSegment != null and ew.sqlSegment != '' and ew.emptyOfWhere">
 ${ew.sqlSegment}
</if>
</if>

最后 format 后的 sql 語句是

<script>
    <if test="ew != null and ew.sqlFirst != null">
        ${ew.sqlFirst}
        </if> SELECT <choose>
        <when test="ew != null and ew.sqlSelect != null">
        ${ew.sqlSelect}
        </when>
        <otherwise>id,name,age,email</otherwise>
        </choose> FROM demo_user 
        <if test="ew != null">
        <where>
        <if test="ew.entity != null">
        <if test="ew.entity.id != null">id=#{ew.entity.id}</if>
        <if test="ew.entity['name'] != null"> AND name=#{ew.entity.name}</if>
        <if test="ew.entity['age'] != null"> AND age=#{ew.entity.age}</if>
        <if test="ew.entity['email'] != null"> AND email=#{ew.entity.email}</if>
        </if>
        <if test="ew.sqlSegment != null and ew.sqlSegment != '' and ew.nonEmptyOfWhere">
        <if test="ew.nonEmptyOfEntity and ew.nonEmptyOfNormal"> AND</if> ${ew.sqlSegment}
        </if>
        </where>
        <if test="ew.sqlSegment != null and ew.sqlSegment != '' and ew.emptyOfWhere">
         ${ew.sqlSegment}
        </if>
        </if>  <if test="ew != null and ew.sqlComment != null">
        ${ew.sqlComment}
        </if>
</script>

最后是把 sql 封裝成了 SqlSource,并構(gòu)造 MapperStatement 存入 configuration.mappedStatements 中,后面 mapper 調(diào)用 selectList 方法時(shí),會(huì)從 mappedStatements 中找到對(duì)應(yīng)的 statement,并取出 sql 語句執(zhí)行,就能拿到數(shù)據(jù)了

小結(jié)

到此,MybatisPlus BaseMapper 實(shí)現(xiàn)對(duì)數(shù)據(jù)庫增刪改查源碼解析完畢,相信通過源碼的閱讀能對(duì) mybatisPlus 有更深的了解

到此這篇關(guān)于MybatisPlus BaseMapper 實(shí)現(xiàn)對(duì)數(shù)據(jù)庫增刪改查源碼解析的文章就介紹到這了,更多相關(guān)MybatisPlus BaseMapper 數(shù)據(jù)庫增刪改查內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java中optional的一些常用方法總結(jié)

    java中optional的一些常用方法總結(jié)

    Java8引入了一個(gè)非常強(qiáng)大的特性就是Optional類,其主要解決的問題就是我們編程時(shí)常常遇到的空指針異常,下面這篇文章主要給大家介紹了關(guān)于java中optional的一些常用方法,需要的朋友可以參考下
    2023-04-04
  • Java中invokedynamic字節(jié)碼指令問題

    Java中invokedynamic字節(jié)碼指令問題

    這篇文章主要介紹了Java中invokedynamic字節(jié)碼指令問題,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-04-04
  • Java編程基于快速排序的三個(gè)算法題實(shí)例代碼

    Java編程基于快速排序的三個(gè)算法題實(shí)例代碼

    這篇文章主要介紹了Java編程基于快速排序的三個(gè)算法題實(shí)例代碼,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • SpringBoot項(xiàng)目實(shí)現(xiàn)MyBatis流式查詢的教程詳解

    SpringBoot項(xiàng)目實(shí)現(xiàn)MyBatis流式查詢的教程詳解

    這篇文章主要介紹了SpringBoot項(xiàng)目如何實(shí)現(xiàn)MyBatis的流式查詢,mybatis的流式查詢,有點(diǎn)冷門,實(shí)際用的場(chǎng)景比較少,但是在某些特殊場(chǎng)景下,卻是十分有效的一個(gè)方法,感興趣的同學(xué)可以參考一下
    2023-06-06
  • java基礎(chǔ)二叉搜索樹圖文詳解

    java基礎(chǔ)二叉搜索樹圖文詳解

    二叉樹是一種非常重要的數(shù)據(jù)結(jié)構(gòu),它同時(shí)具有數(shù)組和鏈表各自的特點(diǎn),下面這篇文章主要給大家介紹了關(guān)于java基礎(chǔ)二叉搜索樹的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-03-03
  • springmvc使用JSR-303進(jìn)行數(shù)據(jù)校驗(yàn)實(shí)例

    springmvc使用JSR-303進(jìn)行數(shù)據(jù)校驗(yàn)實(shí)例

    本篇文章主要介紹了詳解springmvc使用JSR-303進(jìn)行數(shù)據(jù)校驗(yàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-02-02
  • 深入解析Java的包(package)

    深入解析Java的包(package)

    這篇文章主要介紹了深入解析Java的包(package),是Java入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-09-09
  • 帶你入門Java的集合

    帶你入門Java的集合

    Java的集合類型都是對(duì)java.util包中Collection接口的繼承,這里我們主要介紹依賴于collection的一些主分支,一起來看一下Java中的collection集合類型總結(jié)
    2021-07-07
  • 使用java實(shí)現(xiàn)銀行家算法

    使用java實(shí)現(xiàn)銀行家算法

    這篇文章主要為大家詳細(xì)介紹了如何使用java實(shí)現(xiàn)銀行家算法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • Java中啟動(dòng)jar包命令舉例詳解

    Java中啟動(dòng)jar包命令舉例詳解

    這篇文章主要介紹了Java中啟動(dòng)jar包命令的相關(guān)資料,文中介紹了在Windows和Linux環(huán)境下啟動(dòng)和運(yùn)行Java程序的方法,包括使用`java?-jar`和`javaw`命令啟動(dòng),以及如何在后臺(tái)運(yùn)行程序、重定向輸出和錯(cuò)誤信息、以及如何停止程序,需要的朋友可以參考下
    2024-12-12

最新評(píng)論

新民市| 深水埗区| 巴塘县| 镇巴县| 友谊县| 连平县| 开平市| 确山县| 会同县| 通州市| 仙居县| 博乐市| 合川市| 阿拉善左旗| 上蔡县| 贡觉县| 铅山县| 玉树县| 乐至县| 峡江县| 尖扎县| 黄大仙区| 巢湖市| 宜良县| 晋江市| 乌鲁木齐市| 莎车县| 闻喜县| 万安县| 深州市| 济南市| 光山县| 洛宁县| 南安市| 勐海县| 镇远县| 百色市| 新竹县| 剑河县| 宜宾市| 永定县|