zHan_Web后端笔记总结

161 阅读8分钟

[springboot]

关于IOC容器(控制反转)

注册bean

(注册bean,放进IOC容器中,交给spring管理方便解耦)
@Bean : 默认类名为首字母小写,可用 Value 属性指定bean的名字
@Component : 声明bean的基础注解
@Configuration : 声明配置类

由此@Component衍生的注解:
@Controller : 标注在控制器类(Controller)上 -> 注:在springboot中,控制类上只能用这个
@Service : 标注在业务类(Service)上
@Repository : 标注在数据访问类(Dao/Mapper)上 -> 注:[少用,后续用@Mapper代替]

关于bean对象的管理

获取bean

Spring容器中提供了一些方法,可以主动从IOC容器中获取到bean对象,下面介绍3种常用方式:

  1. 根据name获取bean

    Object getBean(String name)
    
  2. 根据类型获取bean

    <T> T getBean(Class<T> requiredType)
    
  3. 根据name获取bean(带类型转换)

    <T> T getBean(String name, Class<T> requiredType)
    

bean的作用域

作用域说明
* singleton容器内同名称的bean只有一个实例(单例)(默认)
* prototype每次使用该bean时会创建新的实例(非单例)
request每个请求范围内会创建新的实例(web环境中,了解)
session每个会话范围内会创建新的实例(web环境中,了解)
application每个应用范围内会创建新的实例(web环境中,了解)

可以借助Spring中的@Scope注解来进行配置作用域

@Scope : 配置作用域(与bean配套使用)

第三方bean

  • 如果要管理的bean对象来自于第三方(不是自定义的),是无法用@Component 及衍生注解声明bean的,就需要用到**@Bean**注解。
解决方案
方案1 : 在启动类上添加@Bean标识的方法 (不建议使用)

方案2 : 在配置类中定义@Bean标识的方法
	注意: 
		- 通过@Bean注解的name或value属性可以声明bean的名称,如果不指定,默认bean的名称就是方法名。
		- 如果第三方bean需要依赖其它bean对象,直接在bean定义方法中设置形参即可,容器会根据类型自动装配。

例如:

@Configuration //配置类  (在配置类当中对第三方bean进行集中的配置管理)
public class CommonConfig {

    //声明第三方bean
    @Bean //将当前方法的返回值对象交给IOC容器管理, 成为IOC容器bean
    public SAXReader reader(DeptService deptService){
        System.out.println(deptService);
        return new SAXReader();
    }
}

关于DI(依赖注入)

使用bean

@Autowired : 进行依赖注入(使用Bean)[常用]

注意:
如果存在多个相同类型的bean,解决方案如下:
	@Primary : 在类上标注
	@Autowired + @Qualifiler: 在有Autowired的地方标注
	@Resource : 在依赖注入的地方标注(无Autowired),属性为name

* @Autowired 和 @Resource的区别:
	@Autowired : 是spring框架提供的注解,默认是按照类型注入
	@Resource : 是JDK提供的注解,默认是按照名称注入

关于自动配置

  • 在类上添加@Component注解来声明bean对象时,还需要保证@Component注解能被Spring的组件扫描到。

  • SpringBoot项目中的@SpringBootApplication注解,具有包扫描的作用,但是它只会扫描启动类所在的当前包以及子包

解决方案

方案一:
	@ComponentScan : 扫描组件
	例如 : @ComponentScan({"com.zHan.Dao","com.zHan"})
	
方案二: 
	@Import : 导入(使用@Import导入的类会被Spring加载到IOC容器中)
		导入形式主要有以下几种:
            - 导入普通类
            - 导入配置类
            - 导入ImportSelector接口实现类
            - 使用第三方依赖提供的 @EnableXxxxx注解

配置文件参数的注入

@Value : @Value("${配置文件中的key}")
@ConfigurationProperties : @ConfigurationProperties(perfix = "配置参数项的前缀")

区别:
    相同点 : 都是用来注入外部配置的属性的。
    不同点 :
    	@Value注解 : 只能一个一个的进行外部属性的注入。
    	ConfigurationProperties : 可以批量的将外部的属性配置注入到bean对象的属性中。

Spring提供的简化方式套路:
    1. 需要创建一个实现类,且实体类中的属性名和配置文件当中key的名字必须要一致。
    	另外,实体类当中的属性还需要提供 getter / setter方法
    2. 需要将实体类交给Spring的IOC容器管理,成为IOC容器当中的bean对象
    3. 在实体类上添加`@ConfigurationProperties`注解,并通过perfix属性来指定配置参数项的前缀

关于AOP(面向切面编程)

实现步骤

  1. 导入依赖:在pom.xml中导入AOP的依赖

  2. 编写AOP程序:针对于特定方法根据业务需要进行编程

pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

涉及注解

@Aspect : 表示当前类为切面类

@Pointcut : 指定切入点表达式
	例如 : @Pointcut("@annotation(com.zHan.anno.Log)")

通知类型: 
	- * @Around (重点): 环绕通知,此注解标注的通知方法在目标方法前、后都被执行
    - @Before : 前置通知,此注解标注的通知方法在目标方法前被执行
    - @After : 后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
    - @AfterReturning (了解): 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行
    - @AfterThrowing (了解): 异常后通知,此注解标注的通知方法发生异常后执行
    
    属性:
    	* execution : 主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配
    		格式 : execution([访问修饰符]  返回值  [包名.类名.]方法名(方法参数) [throws 异常])
    			(注意: 带 [] 的表示可以省略的部分)
            # 使用通配符描述切入点: 
                - * : 单个独立的任意符号
                    - 可以通配任意返回值、包名、类名、方法名、任意类型的一个参数
                    - 可以通配包、类、方法名的一部分
                - .. : 多个连续的任意符号
                    - 可以通配任意层级的包,或任意类型、任意个数的参数
            / 例如:
                @Around("execution(* com.*.service.*.*(..))")
                
		* @annotation :
			实现步骤 : 1. 编写自定义注解  2.在业务类要做为连接点的方法上添加自定义注解
			
			/ 例如 :
				@Target(ElementType.METHOD)	//编译时执行
                @Retention(RetentionPolicy.RUNTIME)	//添加在方法上
                public @interface MyLog {
                }

核心概念(5个)

1. 连接点:JoinPoint

可以被AOP控制的方法(暗含方法执行时的相关信息)

​ 连接点指的是可以被aop控制的方法。例如:入门程序当中所有的业务方法都是可以被aop控制的方法。

​ 在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名、方法名、方法参数等。

image-20230112160708474.png

  • 对于@Around通知,获取连接点信息只能使用ProceedingJoinPoint类型

  • 对于其他四种通知,获取连接点信息只能使用JoinPoint,它是ProceedingJoinPoint的父类型

@Slf4j
@Component
@Aspect
public class MyAspect {

    @Pointcut("@annotation(com.itheima.anno.MyLog)")
    private void pt(){}
   
    //前置通知
    @Before("pt()")
    public void before(JoinPoint joinPoint){
        log.info(joinPoint.getSignature().getName() + " MyAspect -> before ...");
    }
    
    //后置通知
    @Before("pt()")
    public void after(JoinPoint joinPoint){
        log.info(joinPoint.getSignature().getName() + " MyAspect -> after ...");
    }

    //环绕通知
    @Around("pt()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //获取目标类名
        String name = joinPoint.getTarget().getClass().getName();
        log.info("目标类名:{}",name);

        //目标方法名
        String methodName = joinPoint.getSignature().getName();
        log.info("目标方法名:{}",methodName);

        //获取方法执行时需要的参数
        Object[] args = joinPoint.getArgs();
        log.info("目标方法参数:{}", Arrays.toString(args));

        //执行原始方法
        Object object = joinPoint.proceed();

        return object;
    }
}

2. 通知:Advice

指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)

​ 在入门程序中是需要统计各个业务方法的执行耗时的,此时我们就需要在这些业务方法运行开始之前,先记录这个方法运行的开始时间,在每一个业务方法运行结束的时候,再来记录这个方法运行的结束时间。

​ 但是在AOP面向切面编程当中,我们只需要将这部分重复的代码逻辑抽取出来单独定义。抽取出来的这一部分重复的逻辑,也就是共性的功能。 ​

image-20230112160852883.png

3. 切入点:PointCut

匹配连接点的条件,通知仅会在切入点方法执行时被应用

​ 在通知当中,我们所定义的共性功能到底要应用在哪些方法上?此时就涉及到了切入点pointcut概念。切入点指的是匹配连接点的条件。通知仅会在切入点方法运行时才会被应用。

​ 在aop的开发当中,我们通常会通过一个切入点表达式来描述切入点(后面会有详解)。

image-20230112161131937.png

​ 假如:切入点表达式改为DeptServiceImpl.list(),此时就代表仅仅只有list这一个方法是切入点。只有list()方法在运行的时候才会应用通知。

4. 切面:Aspect

描述通知与切入点的对应关系(通知+切入点)

​ 当通知和切入点结合在一起,就形成了一个切面。通过切面就能够描述当前aop程序需要针对于哪个原始方法,在什么时候执行什么样的操作。 ​ ​ 切面所在的类,我们一般称为切面类(被@Aspect注解标识的类)

image-20230112161335186.png

5. 目标对象:Target

通知所应用的对象 ​ 目标对象指的就是通知所应用的对象,我们就称之为目标对象。 ​

image-20230112161657667.png

关于请求参数

@RequestParam

@RequestParam : 用于简单参数、数组集合参数
方法形参名称与请求名称不匹配,通过该注解完成映射

属性:
defaultValue : 设置默认值

例如 : http://localhost:8080?name=zHan&age=21
		@RequestParam(name="name")
(required)属性默认为true,代表请求参数必须传递

注意:如果封装list集合,必须要加
例如 : http://localhost:8080?hobby=java&hobby=python

@DataTimeFormat

@DataTimeFormat
参数 : pattern
例如 : @DataTimeFormat(pattern='yyyy-MM-dd HH:mm:ss')

@RequestBody

@DataTimeFormat : 用于接收json参数时使用
注意:json数据键名和形参属性名保持一致,定义pojo类型形参即可

@PathVariable

@PathVariable : 用于接收路径参数
例如 : http://localhost:8080/id/name

注意:
要与在@RequestMapping中的参数相对应、
顺序要一一对应
例如 : @RequestMapping("/{id}/{name}")

关于响应参数

@ResponseBody

@ResponseBody : 作用在Controller类/方法上
作用 : 将方法返回值直接响应,若返回值类型是 实体对象/集合,转json格式响应

注意:
可用@RestController代替 : 使用rest风格

过滤器Filter(*属于javaweb)

使用步骤:

- 第1步 : 定义过滤器 :1.定义一个类,实现 Filter 接口,并重写其所有方法。
- 第2步 : 配置过滤器:Filter类上加 @WebFilter 注解,配置拦截资源的路径。引导类上加 

Filter接口的方法:

- init方法 : 过滤器的初始化方法。在web服务器启动的时候会自动的创建Filter过滤器对象,在创建过滤器对象的时候会自动调用init初始化方法,这个方法只会被调用一次。

- doFilter方法 (*): 这个方法是在每一次拦截到请求之后都会被调用,所以这个方法是会被调用多次的,每拦截到一次请求就会调用一次doFilter()方法。

- destroy方法 : 是销毁的方法。当我们关闭服务器的时候,它会自动的调用销毁方法destroy,而这个销毁方法也只会被调用一次。

涉及的注解:

@ServletComponentScan 开启Servlet组件支持。
	@WebFilter : 在Filter类上添加
		- 属性 : urlPatterns ---> 通过这个属性指定过滤器要拦截哪些请求
	@ServletComponentScan : 在启动类上面加上一个注解,通过这个注解来开启SpringBoot项目对于Servlet组件的支持

拦截器Interceptor

使用步骤:

- 第1步 : 自定义拦截器。// 实现HandlerInterceptor接口,并重写其所有方法
- 第2步 : 注册配置拦截器。// 实现WebMvcConfigurer接口,并重写addInterceptors方法

(第2步)例如:
@Configuration  
public class WebConfig implements WebMvcConfigurer {

    //自定义的拦截器对象
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
       //注册自定义拦截器对象
        registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");//设置拦截器拦截的请求路径( /** 表示拦截所有请求)
    }
}

HandlerInterceptor接口的方法:

- preHandle方法 (*): 目标资源方法执行前执行。 返回true:放行; 返回false:不放行

- postHandle方法 : 目标资源方法执行后执行

- afterCompletion方法 : 视图渲染完毕后执行,最后执行

涉及的注解:

@Component : 在自定义拦截器类上添加
@Configuration : 在配置类上添加

过滤器和拦截器的区别:

接口规范不同: 
     - 过滤器需要实现Filter接口
     - 拦截器需要实现HandlerInterceptor接口。
拦截范围不同: 
	- 过滤器Filter会拦截所有的资源
	- 拦截器Interceptor只会拦截Spring环境中的资源。

异常处理

使用步骤:

- 第1步 : 定义一个类,在类上加上一个注解@RestControllerAdvice,代表我们定义了一个全局异常处理器。
- 第2步 : 在全局异常处理器当中,定义一个方法来捕获异常,在这个方法上需要加上注解@ExceptionHandler。通过@ExceptionHandler注解当中的value属性来指定我们要捕获的是哪一类型的异常。

涉及的注解:

@RestControllerAdvice  // 添加在异常处理器类上,表示当前类为全局异常处理器
@ExceptionHandler  // 添加在方法上,指定可以捕获哪种类型的异常进行处理

例如:
@RestControllerAdvice
public class GlobalExceptionHandler {

    //处理异常
    @ExceptionHandler(Exception.class) //指定能够处理的异常类型
    public Result ex(Exception e){
        e.printStackTrace();//打印堆栈中的异常信息

        //捕获到异常之后,响应一个标准的Result
        return Result.error("对不起,操作失败,请联系管理员");
    }
}
  • 当有全局异常处理器和特定异常处理器时,只执行特定异常处理器的内容

事务

@Transactional

书写位置: 
    - 方法 : 当前方法交给spring进行事务管理
    -  : 当前类中所有的方法都交由spring进行事务管理
    - 接口 : 接口下所有的实现类当中所有的方法都交给spring 进行事务管理

- 注意: 
	在Spring的事务管理中,默认只有运行时异常 RuntimeException才会回滚。

* 属性: 
	- rollbackFor : 指定出现何种异常类型回滚事务
		例如 : @Transactional(rollbackFor=Exception.class)
	- propagation : 指定传播行为
		例如 : @Transactional(propagation = Propagation.REQUIRES_NEW)
常见的事务传播行为:
属性值含义
* REQUIRED【默认值】需要事务,有则加入,无则创建新事务
* REQUIRES_NEW需要新事务,无论有无,总是创建新事务
SUPPORTS支持事务,有则加入,无则在无事务状态中运行
NOT_SUPPORTED不支持事务,在无事务状态下运行,如果当前存在已有事务,则挂起当前事务
MANDATORY必须有事务,否则抛异常
NEVER必须没事务,否则抛异常

对于这些事务传播行为,我们只需要关注以下两个就可以了:

  1. REQUIRED(默认值)
  2. REQUIRES_NEW

事务相关yml配置:

#spring事务管理日志
logging:
  level:
    org.springframework.jdbc.support.JdbcTransactionManager: debug

CORS跨域问题

  1. 使用@CrossOriginzhu注解(可以添加在Controller类上 或 在支持跨域的方法上)

  2. 使用WebMvcConfigurer的addCorsMappings方法配置(创建配置类实现WebMvcConfigurer接口)

    @Configuration
    public class CorsConfig implements WebMvcConfigurer {
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
          // 设置允许跨域的路径
            registry.addMapping("/**")
                    // 设置允许跨域请求的域名
                    .allowedOriginPatterns("*")
                    // 是否允许cookie
                    .allowCredentials(true)
                    // 设置允许的请求方式
                    .allowedMethods("GET", "POST", "DELETE", "PUT")
                    // 设置允许的header属性
                    .allowedHeaders("*")
                    // 跨域允许时间
                    .maxAge(3600);
        }
    }
    

[lombok]

引入依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
@Data : 更综合代码包含(@Getter、@Setter、@ToString、@EqualsAndHashCode)
@NoArgsConstructor : 为实体类生成无参的构造方法
@AllArgsConstructor : 为实体类生成了static修饰的字段之外带有各参数的构造方法

[mybatis]

引入依赖

<!--mybatis起步依赖-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>

<!--mysql驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

常用注解

@Insert : 新增
@Delete : 删除
@Update : 修改
@Select : 查询

@Options : 获取返回的主键,参数为 keyProperty,useGeneratedKeys为true
例如 : @Options(keyProperty="id",useGeneratedKeys=true)

@Results + @Result : 手动结果映射


* 预编译sql(可防止sql注入) : #{} 
* 模糊查询中,推荐使用 : concat('%',#{},'%')
(不推荐使用,存在sql注入) : ${}
注意 : springboot1.x中,要是有@Param注解,与字段名一一对应,springboot2.x则不需要

注意:如果遇到别名和实体类属性名不一样,如果字段名与属性名符合驼峰命名规则,则配置文件须打开通过驼峰命名规则映射
例如:
mybatis.
	configuration.
		map-underscore-to-camel-case : true

XML映射文件

规范:

* XML映射文件的名称与Mapping接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)。
* XML映射文件的namespace属性为Mapper接口全限定名一致。
* XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致。

格式:
<mapper namespace="com.zHan.mapper.EmpMapper">
	<select id="list" resultType="com.itheima.pojo.Emp">
		<where>
			<if 条件>
			
			</if>
		</where>
	</select>
</mapper>

涉及的标签:

<INsert> : 新增
<Delete> : 删除
<Update> : 修改
<Select> : 查询

其他标签:
* <if>
    用于判断条件是否成立,如果条件为true,则拼接SQL。
    形式 : <if test="name != null">...</if>

* <where>
	where 元素只会在子元素有内容的情况下才插入where子句,而且会自动去除子句的开头的and或or
	
* <set>
	动态地在行首插入 SET 关键字,并会删掉额外的逗号。 (用在update语句中)
	
* <foreach>
	属性:
		collection : 集合名称
        0item : 集合遍历出来的元素/项
        separator : 每一次遍历使用的分隔符
        open : 遍历开始前拼接的片段
        close : 遍历结束后拼接的片段
	例如 : 
		<delete id="deleteBylds">
			<foreach collection="ids" item="id" separator="," open="(" close=")">
				#{id}
			</foreach>
		</delete>

* <sql>: 定义可重用的 SOL 片段
* <include>: 通过属性refid,指定包含的sql片段
(两者配套使用)
	例如:
    	<sql id="commonSelect">
            select id, username, password, name, gender, image, job, entrydatedept_id, create_time, update_time from emp
        </sql>
    
    	<include lefid="commonSelect"/>
    
注意:
    除include外,都是双标签!

分页查询

* 前端传递给后端的参数
	当前页码 : page
	每页展示记录数 : pageSize
    
* 后端给前端返回的数据 及 sql语句:
	获得列表数据 : List  --> select * from 表名 limit 起始索引,查询返回记录数;
	获得总记录数 : total  --> select count(*) from 表名;

    * 需要参数:
    	1. 起始索引=(页码 - 1)*每页展示记录数
    	2. 查询返回记录数 = 每页展示记录数

[maven]高级

继承

继承关系

  • 概念:继承描述的是两个工程间的关系,与java中的继承相似,子工程可以继承父工程中的配置信息,常见于依赖关系的继承。

  • 作用:简化依赖配置、统一管理依赖

  • 实现:

    <parent>
        <groupId>...</groupId>
        <artifactId>...</artifactId>
        <version>...</version>
        <relativePath>....</relativePath>
    </parent>
    

注意:

  • 在子工程中,配置了继承关系之后,坐标中的groupId是可以省略的,因为会自动继承父工程的 。
  • relativePath指定父工程的pom文件的相对位置(如果不指定,将从本地仓库/远程仓库查找该工程)。
    • ../ 代表的上一级目录

版本锁定(包含属性配置)

在maven中,可以在父工程的pom文件中通过 <dependencyManagement> 来统一管理依赖版本。

父工程:

<properties>
	<xxx.version>...</xxx.version>
</properties>

<!--统一管理依赖版本-->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>...</groupId>
            <artifactId>...</artifactId>
            <version>${xxx.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

子工程:

<dependencies>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
    </dependency>
</dependencies>

注意:

  • 在父工程中所配置的 <dependencyManagement> 只能统一管理依赖版本,并不会将这个依赖直接引入进来。 这点和 <dependencies> 是不同的。

  • 子工程要使用这个依赖,还是需要引入的,只是此时就无需指定 <version> 版本号了,父工程统一管理。变更依赖版本,只需在父工程中统一变更。

面试题:<dependencyManagement><dependencies> 的区别是什么?

  • <dependencies> 是直接依赖,在父工程配置了依赖,子工程会直接继承下来。
  • <dependencyManagement> 是统一管理依赖版本,不会直接依赖,还需要在子工程中引入所需依赖(无需指定版本)

聚合

  • 聚合:将多个模块组织成一个整体,同时进行项目的构建。
  • 聚合工程:一个不具有业务功能的“空”工程(有且仅有一个pom文件) 【PS:一般来说,继承关系中的父工程与聚合关系中的聚合工程是同一个】
  • 作用:快速构建项目(无需根据依赖关系手动构建,直接在聚合工程上构建即可)
  • 实现: 在父类中,添加如下配置,来指定当前聚合工程,需要聚合的模块:
<!--聚合其他模块-->
<modules>
    <module>...</module>
    <module>...</module>
    <module>...</module>
</modules>

在项目中所聚合的其他模块全部都会执行 package 指令,这就是通过聚合实现项目的一键构建(一键清理clean、一键编译compile、一键测试test、一键打包package、一键安装install等)。

继承和聚合的对比

  • 作用

    • 聚合用于快速构建项目

    • 继承用于简化依赖配置、统一管理依赖

  • 相同点:

    • 聚合与继承的pom.xml文件打包方式均为pom,通常将两种关系制作到同一个pom文件中

    • 聚合与继承均属于设计型模块,并无实际的模块内容

  • 不同点:

    • 聚合是在聚合工程中配置关系,聚合可以感知到参与聚合的模块有哪些

    • 继承是在子模块中配置关系,父模块无法感知哪些子模块继承了自己

Management>` 的区别是什么?**

  • <dependencies> 是直接依赖,在父工程配置了依赖,子工程会直接继承下来。
  • <dependencyManagement> 是统一管理依赖版本,不会直接依赖,还需要在子工程中引入所需依赖(无需指定版本)

聚合

  • 聚合:将多个模块组织成一个整体,同时进行项目的构建。
  • 聚合工程:一个不具有业务功能的“空”工程(有且仅有一个pom文件) 【PS:一般来说,继承关系中的父工程与聚合关系中的聚合工程是同一个】
  • 作用:快速构建项目(无需根据依赖关系手动构建,直接在聚合工程上构建即可)
  • 实现

在父类中,添加如下配置,来指定当前聚合工程,需要聚合的模块:

<!--聚合其他模块-->
<modules>
    <module>...</module>
    <module>...</module>
    <module>...</module>
</modules>

在项目中所聚合的其他模块全部都会执行 package 指令,这就是通过聚合实现项目的一键构建(一键清理clean、一键编译compile、一键测试test、一键打包package、一键安装install等)。

继承和聚合的对比

  • 作用

    • 聚合用于快速构建项目

    • 继承用于简化依赖配置、统一管理依赖

  • 相同点:

    • 聚合与继承的pom.xml文件打包方式均为pom,通常将两种关系制作到同一个pom文件中

    • 聚合与继承均属于设计型模块,并无实际的模块内容

  • 不同点:

    • 聚合是在聚合工程中配置关系,聚合可以感知到参与聚合的模块有哪些

    • 继承是在子模块中配置关系,父模块无法感知哪些子模块继承了自己