自学内容网 自学内容网

Maven教程超详细

什么是Maven

Maven的概念

Maven 是自动化构建工具。Maven 是 Apache 软件基金会组织维护的一款自动化构建工具,专注服务于 Java 平台的项目构建和依赖管理。Maven 这个单词的本意是:专家,内行。Maven 是目前最流行的自动化构建工具,对于生产环境下多框架、多模块整合开发有重要作用,Maven 是一款在大型项目开发过程中不可或缺的重要工具。

项目构建过程

构建(build)是面向过程的(从开始到结尾的多个步骤),涉及到多个环节的协同工作。
image.png
构建过程的几个主要环节

  1. 清理:删除以前的编译结果,为重新编译做好准备。
  2. 编译:将Java源程序编译为字节码文件。
  3. 测试:针对项目中的关键点进行测试,确保项目在迭代开发过程中关键点的正确性。
  4. 报告:在每一次测试后以标准的格式记录和展示测试结果。
  5. 打包:将一个包含诸多文件的工程封装为一个压缩文件用于安装或部署。Java 工程对应 jar 包,Web 工程对应war包。
  6. 安装:在Maven环境下特指将jar包安装到本地仓库中。这样该项目就可以被其他的maven项目通过依赖的方式引入。
  7. 部署:将jar包部署到私服上。

Maven的两大核心功能(重点)

项目构建

对项目进行编译,测试,打包,部署等构建。

依赖管理

对jar包的统一管理,Maven提供中央仓库,私服,本地仓库解决jar包的依赖和相关依赖的下载。
如下图所示:包括蓝、黄两个部分分别对应着依赖关系和项目构建两大核心功能。
image.png

Maven的核心概念

什么是POM

POM(Project Object Model)项目对象模型,它是Maven的核心组件。它是Maven中的基本工作单元。它是一个xml文件,以pom.xml驻留在项目的根目录中。POM不仅包含有关项目的信息及Maven用于构建项目的各种配置的详细信息,还包含目标和插件。

什么是坐标GAV

也称为gav定位。使用三个标签来唯一定位jar资源。项目的唯一的名称,创建项目时定义gav名称,引用项目时使用gav名称。相当于项目的身份证号。

  1. groupId: 组织名称,一般是公司域名的倒写 com.liming
  2. artifactId: 项目名称 maven_demo
  3. version: 版本编号 1.0-SNAPSHOT(开发时的临时版本号) 5.2.5.RELEASE(发布版本)

定义项目

<groupId>com.liming</groupId>
<artifactId>maven_demo</artifactId>  
<version>1.0.0</version>

引用项目

<dependency>
  <groupId>com.liming</groupId>
  <artifactId>maven_demo</artifactId>  
  <version>1.0.0</version>
</dependency>

什么是仓库

存放jar包的位置 。Maven中所有的jar包都在仓库中。仓库分为本地仓库远程仓库。我们依赖的jar包它从哪儿获取呢?maven下载下来才8M,我们需要的jar包有时候都几百兆甚至几个G,它从哪儿弄去呢? 其实,maven有仓库的概念。在Maven中,任何一个依赖、插件或者项目构建的输出,都可以称之为构件。Maven 核心程序仅仅定义了自动化构建项目的生命周期,但具体的构建工作是由特定的构件完成的。而且为了提高构建的效率和构件复用,maven把所有的构件统一存储在某一个位置,这个位置就叫做仓库。

本地仓库

本地仓库,存在于当前电脑上,默认存放在.m2\repository中,为本机上所有的Maven工程服务。你也可以通过Maven的配置文件Maven_home/conf/settings.xml中修改本地仓库所在的目录。

注意: gav就是仓库中一级一级的目录名称

远程仓库

  • 远程仓库,分为为全世界范围内的开发人员提供服务的中央仓库、为全世界范围内某些特定的用户提供服务的中央仓库镜像、为本公司提供服务自己架设的私服。

  • 中央仓库是maven默认的远程仓库,其地址是:http://repo.maven.apache.org/maven2/,中央仓库包含了绝大多数流行的开源Java构件,以及源码、作者信息、许可证信息等。一般来说,简单的Java项目依赖的构件都可以在这里下载得到。

  • 私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,私服代理广域网上的远程仓库,供局域网内的Maven用户使用。当Maven需要下载构件的时候,它从私服请求,如果私服上不存在该构件,则从外部的远程仓库下载,缓存在私服上之后,再为Maven的下载请求提供服务。我们还可以把一些无法从外部仓库下载到的构件上传到私服上。
    image.png

  • 程序员常用的一个仓库:http://mvnrepository.com/

    image.png

什么是依赖

一个Maven 项目正常运行需要其它项目的支持,Maven 会根据坐标自动到本地仓库中进行查找。对于程序员自己的 Maven 项目需要进行安装,才能保存到仓库中。不用maven 的时候所有的 jar 都不是你的,需要去各个地方下载拷贝,用了 maven 所有的 jar 包都是你的,想用谁,叫谁的名字就行。maven 帮你下载。除了管理当前要使用的jar包,并且同时管理与其有依赖关系的jar包,自动去下载,并添加到当前的仓库,并给项目添加引用。是通过<dependencies>大标签中的子标签<dependency>,使用gav添加依赖。

<dependencies>
  <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.11</version>
  </dependency>
</dependencies>

什么是生命周期

构建项目时按照生命周期顺序构建,每一个阶段都有特定的插件来完成。不论现在要执行生命周期中的哪个阶段,都是从这个生命周期的最初阶段开始的。使用idea后,生命周期要调用的命令被集成化一些按钮,只需要双击即可调用相应的插件来运行。

在这里插入图片描述

生命周期对应的Maven命令(了解):

  1. mvn clean 清理(会删除原来编译和测试的目录,即 target 目录,但是已经 install 到仓库里的包不会删除)
  2. mvn compile 编译主程序(会在当前目录下生成一个 target,里边存放编译主程序之后生成的字节码文件)
  3. mvn test-compile 编译测试程序(会在当前目录下生成一个 target,里边存放编译测试程序之后生成的字节码文件)
  4. mvn test 测试(会生成一个目录surefire-reports,保存测试结果)
  5. mvn package 打包主程序(会编译、编译测试、测试、并且按照 pom.xml 配置把主程序打包生成 jar 包或者 war 包)
  6. mvn install 安装主程序(会把本工程打包,并且按照本工程的坐标保存到本地仓库中)
  7. mvn deploy 部署主程序(部署到私服仓库中)

什么是插件

Maven本质上是一个插件框架,它的核心并不执行任何具体的构建任务,所有这些任务都交给插件来完成,例如编译源代码是由maven- compiler-plugin完成的。进一步说,每个任务对应了一个插件目标(goal),每个插件会有一个或者多个目标,例如maven- compiler-plugin的compile目标用来编译位于src/main/java/目录下的主源码,testCompile目标用来编译位于src/test/java/目录下的测试源码。

Maven支持极简化的插件添加.使用<plugins>大标签中添加<plugin>子标签引用插件.

<plugins>
  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
      <source>17</source>
      <target>17</target>
      <encoding>UTF-8</encoding>
    </configuration>
  </plugin>
</plugins>

Maven的应用

下载Maven

官网:http://maven.apache.org/

在这里插入图片描述

配置Maven

注意:一定要将Maven工具解压到一个没有中文的路径下!

配置环境变量

必须要配置JAVA_HOMEMAVEN_HOME的环境变量。在Maven工具的bin目录下的mvn文件中多次使用这两个环境变量。

配置步骤:

  1. 我的电脑—>右键属性—>高级系统设置—>环境变量—>系统变量
  2. 点新建—>
    1. 创建JAVA_HOME环境变量
    2. 创建MAVEN_HOME环境变量
      1. 变量名: MAVEN_HOME
      2. 变量值: D:\apache-maven-3.6.3 (Maven工具所在的目录,bin的上一级目录)
  3. 点path—>编辑—>新建
    1. %JAVA_HOME%\bin
    2. %MAVEN_HOME%\bin
  4. 打开cmd窗口,输入 mvn -v
    在这里插入图片描述

配置Maven工具参数

打开D:\apache-maven-3.6.3\conf\settings.xml文件,进行本地仓库,远程仓库和JDK参数设置。

  1. 配置本地仓库,将注释中53行的代码提取出注释,放置在第55行,设置本地仓库的地址路径。如果已有本地仓库则直接指定地址,如果没有本地仓库则指定一个目录,在idea配置后会自动生成目录。

image.png

  1. 配置远程仓库

找到结束标签,将以下代码贴在其前面。

<!--配置阿里远程仓库-->
<mirror>
  <id>alimaven</id>
  <name>aliyun maven</name>
  <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
  <mirrorOf>central</mirrorOf>        
</mirror>

image.png

  1. 配置JDK属性

在标签中进行配置,一定要小心,找到结束标签,在其前面配置以下代码。因为在标签中全部是注释,粘到哪里都在注释中,只有找到结束标签前才是注释外的,配置才会生效。

<profile>
  <id>jdk17</id>
  <activation>
    <activeByDefault>true</activeByDefault>
    <jdk>17</jdk>
  </activation>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <maven.compiler.compilerVersion>17</maven.compiler.compilerVersion>
  </properties>
</profile>

Maven的依赖管理

在JAVA开发中,项目的依赖管理是一项重要任务。通过合理管理项目的依赖关系,我们可以有效的管理第三方库,模块的引用及版本控制。而Maven作为一个强大的构建工具和依赖管理工具,为我们提供了便捷的方式来管理项目的依赖。

什么是依赖范围

Maven的依赖构件包含一个依赖范围的属性。这个属性描述的是三套classpath的控制,即编译、测试、运行。这说白了就是添加的jar包起作用的范围。 maven提供了以下几种依赖范围:compile,test,provided.runtime,system

image.png

什么是依赖传递

直接依赖和间接依赖是一个相对的概念。直接在项目中配置的依赖称为直接依赖,通过添加依赖关联进来的依赖称为间接依赖。1是项目的直接依赖,2是1的直接依赖,2是项目的间接依赖,以此类推。如图

image.png

依赖冲突

什么是依赖冲突

在 Maven 项目中,依赖通常被定义在项目的 pom.xml 文件中。当多个依赖项引入了不同版本的相同库时,就会发生依赖冲突。这可能是因为项目的直接依赖和间接依赖导致了同一库的多个版本存在于类路径中。每个显式声明的类包都会依赖于一些其它的隐式类包,这些隐式的类包会被maven间接引入进来,从而造成类包冲突。

依赖冲突的解决方案

版本锁定

在父工程中使用dependencyManagement 进行版本锁定,dependencyManagement可以统一管理整个项目的版本号,确保应用的各个项目的依赖和版本一致。 dependencyManagement只是声明依赖,并不自动实现引入,因此子项目需要显示的声明需要用的依赖,便可以忽略版本号。如果排斥父工程中定义的版本号,可以显示的进行版本号声明。

  1. 子工程使用父工程锁定的版本号

image.png

  1. 子工程使用自定义的版本号,只要重新声明即可

image.png

  1. 父工程不使用标签,则子工程跟父工程完全保持一致。子工程不需要显示依赖任何jar包。

image.png

短路径优先

引入路径短者优先,顾名思义,当一个间接依赖存在多条引入路径时,引入路径短的会被解析使用。如图
image.png

声明优先

如果存在短路径,则优先选择短路径,如果路径相同的情况下,先声明者优先,POM 文件中依赖声明的顺序决定了间接依赖会不会被解析使用,顺序靠前的优先使用。如图。
image.png

特殊优先(后来者居上)

同一个pom.xml文件中进行了多次依赖jar包不同版本的配置,后面的覆盖前面的配置。这种情况比较少见。
image.png

可选依赖

maven_03项目可选择是否传递间接依赖junit_4.13,主动权在当前项目maven_03中。如果当前项目被依赖到其它项目中,当前项目可以拒绝交出间接依赖项。例如maven_02添加了maven_03的依赖,maven_03可以自主设置其依赖项junit_4.13是否被间接传递。true 为不传递间接依赖,那么在maven_02项目中就没有junit_4.13的依赖。默认是false,是传递间接依赖。
image.png

排除依赖

是当前项目是否主动断开其依赖项目的间接依赖。也就是控制当前项目是否使用其直接依赖传递下来的接间依赖。在maven_02项目中添加maven_03项目的依赖,但不要maven_03项目中的junit_4.13的依赖,可以选择排除依赖。这样可以保证当前项目依赖的纯净性。
排除依赖使用 exclusions 元素排除依赖,说明如下:

  1. exclusions 元素下可以包含若干个 exclusion 子元素,用于排除若干个间接依赖,该元素包含两个子元素:groupId 和 artifactId,用来确定需要排除的间接依赖的坐标信息

  2. exclusion 元素中只需要设置 groupId 和 artifactId 就可以确定需要排除的依赖,无需指定版本version

    如图
    image.png

可选依赖和排除依赖的区别

排除依赖和可选依赖都能在项目中将间接依赖排除在外,但两者实现机制却完全不一样。

  1. 可选依赖是自己决定是否向外提供间接依赖(maven_03设置拒绝提供间接依赖junit)
  2. 排除依赖是主动拒绝添加直接依赖关联的间接依赖(maven_02项目设置排除maven_03的junit依赖)
  3. 可选依赖的优先级高于排除依赖
  4. 若对于同一个间接依赖同时使用排除依赖和可选依赖进行设置,那么可选依赖的取值必须为 false,否则排除依赖无法生效。

刷新依赖的8种方式(重点)

在idea中有时候会出现刷新延时的情况,那么需要进行手工刷新依赖。

  1. 点击M刷新按钮
  2. 点Maven窗口的Reload All Maven Projects
  3. Build--->ReBuild Project 重新构建项目的同时刷新所有依赖
  4. 点击本项目的pom.xml文件—>右键—>Maven—>Reload Project 刷新本项目的依赖
  5. 打开pom.xml文件,全选,拷贝,删除,关闭,打开,粘贴.物理刷新pom.xml文件
  6. Invalidate Caches—>全选—>Invalidate and Restart 清空idea的缓存并重启idea刷新依赖
  7. 打开本地仓库,搜索last,全选删除,点Maven的刷新全部依赖的按钮
  8. 在7的步骤后执行File—>settings—>Build,Execution,Deployment—>Build Tools—>Maven—>Repositories—>选中本地仓库—>update—>ok

Maven的继承和聚合

什么是继承

Maven 的依赖传递机制可以一定程度上简化 POM 的配置,但这仅限于存在依赖关系的项目或模块中。当一个项目的多个模块都依赖于相同 jar 包的相同版本,且这些模块之间不存在依赖关系,这就导致同一个依赖需要在多个模块中重复声明,这显然是不可取的,大量的前人经验告诉我们,重复往往意味着更多的劳动和更高的潜在风险

父工程示例:

<modelVersion>4.0.0</modelVersion>
<groupId>com.liming</groupId>
<artifactId>maven_parent</artifactId>
<version>1.0-SNAPSHOT</version>
<!--打包方式必须是pom,声明是父工程-->
<packaging>pom</packaging>

<!--聚合子模块-->
<modules>
  <module>maven_son</module>
  <module>maven_web</module>
</modules>

<properties>
  <!--定义属性,便于版本的管理-->
  <spring-version>5.3.24</spring-version>
  <servlet-version>4.0.1</servlet-version>
</properties>

<!--只是定义,并没有真正的添加依赖,子工程根据需要有选择的添加依赖-->
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <!--引用定义好的属性-->
      <version>${spring-version}</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>${servlet-version}</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<build>
  <pluginManagement>
    <plugins>
      <!--只定义Tomcat插件 -->
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <port>8089</port>
          <url>/</url>
        </configuration>
      </plugin>
    </plugins>
  </pluginManagement>
</build>

子工程示例:

<modelVersion>4.0.0</modelVersion>
<parent>
  <groupId>com.liming</groupId>
  <artifactId>maven_parent</artifactId>
  <version>1.0-SNAPSHOT</version>
</parent>
<!--可以省略groupId和version,因为与父工程保持一致-->
<artifactId>maven_web</artifactId>
<packaging>war</packaging>

<!--需要什么依赖,添加什么依赖,可以省略版本号,版本由父工程统一管理 -->
<dependencies>
  <dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  </dependency>
  <dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  </dependency>
</dependencies>

<build>
  <plugins>
    <!--配置Tomcat插件 -->
    <plugin>
      <groupId>org.apache.tomcat.maven</groupId>
      <artifactId>tomcat7-maven-plugin</artifactId>
      <version>2.2</version>
      <!--子工程可以自定义端口号,不自定义使用父工程的-->
      <configuration>
        <port>8060</port>
        <url>/</url>
      </configuration>
    </plugin>
  </plugins>
</build>

什么是聚合

使用 Maven 聚合功能对项目进行构建时,需要在该项目中额外创建一个的聚合模块,然后通过这个模块构建整个项目的所有模块。聚合模块仅仅是帮助聚合其他模块的工具,其本身并无任何实质内容,因此聚合模块中只有一个 POM 文件,不包含 src 等目录。与父模块相似,聚合模块的打包方式(packaging)也是 pom,用户可以在其 POM 中通过 modules 下的 module 子元素来添加需要聚合的模块的目录路径。父模块的 pom.xml 文件的 <modules> 把子模块聚集起来.

代码示例:

<modelVersion>4.0.0</modelVersion>

<groupId>com.liming</groupId>
<artifactId>maven_ju</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>

<modules>
  <module>../maven_02</module>
  <module>../maven_03</module>
  <module>../maven_01</module>
  <module>../maven_parent</module>
</modules>

总结:一句话,聚合就是集中构建项目的。

Maven的私服

什么是私服

  • Maven 私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,用来代理位于外部的远程仓库(中央仓库、其他远程公共仓库)。一些无法从外部仓库下载到的构件,也能从本地上传到私服供其他人使用。

  • Maven 私服其实并不是 Maven 的核心概念,它仅仅是一种衍生出来的特殊的仓库,但这并不代表它不重要,相反由于私服具有降低中央仓库负荷、节省外网带宽、以及提高项目稳定性等优点,使得私服在实际开发过程中得到了相当普遍地使用。建立了 Maven 私服后,当局域网内的用户需要某个构件时,会先请求本地仓库,若本地仓库不存在所需构件,则请求 Maven 私服,将所需构件下载到本地仓库,若私服中不存在所需构件,再去请求外部的远程仓库,将所需构件下载并缓存到 Maven 私服,若外部远程仓库不存在所需构件,则 Maven 直接报错。

Maven仓库管理器Nexus

什么是Nexus

Nexus 是 Sonatype 公司发布的一款仓库(Repository)管理软件,常用来搭建 Maven 私服,所以也有人将 Nexus 称为“Maven仓库管理器”。 Sonatype Nexus 是当前最流行,使用最广泛的 Maven 仓库管理器。Nexus 分为开源版和专业版,其中开源版足以满足大部分 Maven 用户的需求。

Nexus仓库的类型

Nexus默认内置了很多仓库,这些仓库可以划分为3种类型,每种类型的仓库用于存放特定的jar包,具体说明如下:

  1. hosted:宿主仓库,部署自己的jar包到这个类型的仓库中,包括ReleasesSnapshots两部分,Releases为公司内部发行版本仓库,Snapshots为公司内部测试版本仓库。
  2. proxy: 代理仓库,用于代理远程的公共仓库,如Maven中央仓库,用户连接私服,私服自动去中央仓库下载Jar包或者插件。
  3. group:仓库组,用来合并多个hosted/proxy仓库,通常用于配置自己的Maven连接仓库组。当我们下载jar包时,从仓库组中的每个仓库找一遍,找到后下载。

image.png

如何搭建私服

  1. 下载Nexus

官网地址:https://help.sonatype.com/repomanager3/download

在这里插入图片描述

  1. 安装Nexus

下载后解压到一个没有中文的路径下:

在这里插入图片描述

进入到D:\nexus-3.66.0-02\bin目录下:

在这里插入图片描述

双击nexus.exe没有反应,需要以管理员身份打开cmd窗口,输入命令: nexus /run 回车,需要等待一段时间,直到出现Started Sonatype Nexus OSS 3.66.0-02说明安装成功。

在这里插入图片描述

  1. 访问Nexus

访问地址:http://10.8.1.123:8081

端口号可以改变,在D:\softw\nexus-3.66.0-02-win64\nexus-3.66.0-02\etc目录下的nexus-default.properties文件中进行修改。

注意:如果浏览器访问一直转圈打不开页面,则在cmd窗口ctrl+c唤醒一下就可以打开了

Nexus私服的应用

登录

在这里插入图片描述

密码所在的文件:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

点Browse观察

在这里插入图片描述

设置仓库

  1. 创建仓库

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  1. 将创建好的仓库添加到public组中。

在这里插入图片描述

在这里插入图片描述

  1. 修改中央仓库地址

在这里插入图片描述

在这里插入图片描述

使用Nexus下载jar包

创建一个新的本地仓库,用来接收Nexus下载下来的jar包。因为要使用Maven工具访问私服,所以settings.xml文件要修改。

  1. 本地仓库地址修改为新地址
<localRepository>D:\softw\apache-maven-3.6.3\resp_nexus</localRepository>

在这里插入图片描述

  1. 设置<mirror>标签为Nexus的maven-public库地址(仓库组)
<mirror>
  <id>jzj-nexus</id>
  <mirrorOf>central</mirrorOf>
  <name>mynexus</name>
  <url>http://10.8.1.123:8081/repository/maven-public/</url>
</mirror>

其中<url>标签的地址复制maven-public库的地址。这个地址取代了中央仓库地址。我们所有的jar包下载都是从这个地址里找。它包含宿主库和代理库的所有地址。
在这里插入图片描述

  1. 设置每次访问Nexus的用户名和密码,修改settings.xml文件中的<server>标签
<server>
  <id>jzj-nexus</id>
  <username>admin</username>
  <password>admin</password>
</server>
  1. 打开Idea2023,确定是新的本地仓库地址

在这里插入图片描述

选择一个项目执行clean操作

在这里插入图片描述

观察本地仓库:

在这里插入图片描述

观察私服Nexus上的maven-public组

在这里插入图片描述

使用Idea部署jar包到Nexus私服

私服Nexus是部署在局域网的,是全公司共享的仓库地址,每个团队都可以将已完成的功能或测试版本发布到私服供别人来使用。

  1. 打开要部署的项目的pom.xml文件,设置上传路径
<distributionManagement>
    <repository>
        <id>jzj-nexus</id>
        <url>http://10.8.1.123:8081/repository/jzj-release/</url>
    </repository>
    <snapshotRepository>
        <id>jzj-nexus</id>
        <url>http://10.8.1.123:8081/repository/jzj-snapshot/</url>
    </snapshotRepository>
</distributionManagement>

其中<url>路径来自于私服jzj-snapshot、jzj-release

在这里插入图片描述

在这里插入图片描述

  1. 运行deploy部署命令

在这里插入图片描述

  1. 观察私服对应仓库变化

release项目部署

在这里插入图片描述

snapshot项目部署

在这里插入图片描述


原文地址:https://blog.csdn.net/weixin_46370595/article/details/140614015

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!