SSM框架基础 (4) :事务管理、AOP、Spring原理、Maven高级

JavaWeb开发 SSM框架基础 后端框架进阶——Spring框架高级操作(事务管理、AOP)、Spring原理、Maven高级

在这里插入图片描述

在这里插入图片描述


1 事务管理

事务(Transaction)是一组操作的集合,是一个不可分割的工作单位,这些操作要么同时成功,要么同时失败。

  • 开启事务(一组操作开始前):start transaction; / begin;
  • 提交事务(这组操作全部成功后):commit;
  • 回滚事务(中间任何一个操作出现异常):rollback;

1.1 Spring事务管理

根据事务的定义,调整基础(3)的案例中解散部门的方法为删除部门同时删除该部门下的所有员工,并在Service层的方法添加注解@Transactional,将其交给spring进行事务管理。
方法执行前,开启事务;成功执行完毕,提交事务;出现异常,回滚事务。

在Service层类、接口上使用该注解@Transactional时,表示其所属所有方法均进行事务管理。常加在Service层经常使用增删改操作的方法上。

/* DeptServiceImpl.java */
// 新增empMapper
@Autowired
private EmpMapper empMapper;

@Transactional		// 交给spring进行事务管理
@Override
public void delete(Integer id) {
    deptMapper.deleteById(id);  // 根据ID删除部门数据

    empMapper.deleteByDeptId(id);   // 根据ID删除该部门下的员工
}
/* EmpMapper.java */
// 根据部门ID删除该部门下的员工数据
@Delete("delete from emp where dept_id = #{deptId}")
void deleteByDeptId(Integer deptId);

在yml文件中配置spring事务日志开关:

# 开启事务管理日志
logging:
 level:
  org.springframework.jdbc.support.jdbcTransactionManager: debug

1.2 rollbackFor和propagation

  1. rollbackFor属性:默认情况下,只有出现RuntimeException才回滚异常。可以设置rollbackFor属性控制出现何种异常类型时回滚事务,要想出现任何异常时都回滚可设置@Transactional(rollbackFor = Exception.class)
  2. propagation属性:当一个事务方法被另一个事务方法调用时,该事务方法进行事务控制的方式称为事务传播行为。可以设置propagation属性来控制该方法运行时是否需要/支持事务。
    @Transactional
    public void a() {
    	userService.b();
    }
    
    @Transactional(propagation = Propagation.REQUIRED)	// 该方法运行时需要事务,有则加入,无则新建
    public void b() { ... }
    

常用的propagation属性值如下表所示:

propagation属性值含义
REQUIRED需要事务。有则加入,无则创建新事务【默认值】
REQUIRES_NEW需要新事务。无论有无,总是创建新事务
SUPPORTS支持事务。有则加入,无则在无事务状态中运行
NOT_SUPPORTED不支持事务。在无事务状态下运行,如果当前存在已有事务,则挂起当前事务
MANDATORY必须有事务,否则抛异常
NEVER必须无事务,否则抛异常

综上,进一步修改解散部门方法:要求解散部门时,无论是成功还是失败,都要记录操作日志。即新增记录日志到数据库表中。需要创建对应的接口及其实现类,具体如下所示:

/* DeptServiceImpl.java */
// 新增deptLogService
@Autowired
private DeptLogService deptLogService;

@Transactional(rollbackFor = Exception.class)	// 设为出现所有异常都进行回滚
@Override
public void delete(Integer id) {
	try {
	    deptMapper.deleteById(id);
	
	    empMapper.deleteByDeptId(id);
	} finally {		// 将记录日志部分的代码放在finally中
		DeptLog deptLog = new DeptLog();	// 部门日志表POJO,表参数有create_time、description
		deptLog.setCreateTime(LocalDateTime.now());
		deptLog.setDescription("执行了解散部门的操作,此次解散的是" + id + "号部门");
		deptLogService.insert(deptLog);
	}
}
/* DeptLogService.java */
public interface DeptLogService {
	void insert(DeptLog deptLog);
}
/* DeptLogServiceImpl.java */
public class DeptLogServiceImpl implements DeptLogService {
	@Autowired
	private DeptLogMapper deptLogMapper;

	@Transactional(propagation = Propagation.REQUIRES_NEW)	// 设置为需要新事务,使得不被同时结束
	@Override
	public void insert(DeptLog deptLog) {
		deptLogMapper.insert(deptLog);
	}
}
  • REQUIRED:大部分情况下都是用该传播行为即可。
  • REQUIRES_NEW:当不希望事务之间相互影响时,可以使用该传播行为。比如:下订单前需要记录日志,不论订单保存成功与否,都需要保证日志记录能够记录成功。

2 AOP

AOP(Aspect Oriented Programming,面向切面编程、面向方面编程)即为面向特定方法编程。

2.1 AOP快速入门

动态代理是面向切面编程最主流的实现。而SpringAOP是Spring框架的高级技术,旨在管理bean对象的过程中,主要通过底层的动态代理机制,对特定的方法进行编程。

案例:统计各个业务层方法执行耗时——获取方法运行开始时间;运行原始方法;获取方法运行结束时间并计算执行耗时

  1. 导入依赖:在pom.xml中导入AOP的依赖
<!-- pom.xml -->
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>3.2.1</version>
</dependency>
  1. 编写AOP程序:针对于特定方法根据业务需要进行编程。在类上添加注解@Aspect,在模板方法上添加通知注解,此处用@Around(详见2.3),并设置切入点表达式指定特定方法(详见2.5)
/* TimeAspect.java */
@Slf4j
@Component
@Aspect     //AOP类
public class TimeAspect {
	// 将切入点表达式进行抽取,提高复用性
	@Pointcut("execution(* com.hyplus.tlias.service.*.*())")	// 执行目录下所有接口/类的方法时都会调用!
	private void pt() {}

	// 统计方法运行耗时
    @Around("pt()")		// 引用抽取至pt方法的切入点表达式
    public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
        // 1. 记录开始时间
        long begin = System.currentTimeMillis();

        // 2. 调用原始方法
        Object result = joinPoint.proceed();

        // 3. 获取方法运行结束时间并计算执行耗时
        long end = System.currentTimeMillis();
        log.info(joinPoint.getSignature() + "方法执行耗时:" + (end - begin) + "ms");	// getSignature:获取方法签名
        
        return result;
    }
}

2.2 核心概念

  • 连接点(Join Point):可以被AOP控制的原始方法/目标方法(暗含方法执行时的相关信息,详见2.6)
  • 通知(Advice):指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)
  • 切入点(Point Cut):匹配连接点的条件,通知仅会在切入点方法执行时被应用(即实际被AOP控制的方法)
  • 切面(Aspect):描述通知与切入点的对应关系(通知+切入点)
  • 目标对象(Target):通知所应用的对象

在这里插入图片描述

2.3 通知类型

5种常用的通知注解:

注解类型说明
@Before前置通知此注解标注的通知方法在目标方法前被执行
@Around环绕通知此注解标注的通知方法在目标方法前、后都被执行【最常用】
① 需有方法参数来传入目标方法,类型为ProceedingJoinPoint,并要求其调用方法proceed()来运行(更多信息调用详见2.6)
② 返回值必须指定为Object,来接收原始方法的返回值
@After后置通知此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行,又称最终通知
@AfterReturning返回后通知此注解标注的通知方法在目标方法后被执行,有异常不会执行
@AfterThrowing异常后通知此注解标注的通知方法发生异常后执行

@PointCut:将公共的切入点表达式抽取出来,需要用到时引用该切入点表达式即可("方法名()")。为private时仅能在当前类中被引用,为public时可在外部类中被引用。

2.4 通知顺序

当有多个切面的切入点都匹配到了目标方法,目标方法运行时,多个通知方法都会被执行。

  1. 不同切面类中,默认按照切面类的类名字母排序
    • 目标方法的通知方法:字母排名靠前的执行
    • 目标方法的通知方法:字母排名靠前的执行
  2. 在切面类上添加注解@Order(数字)来控制顺序
    • 目标方法的通知方法:数字小的执行
    • 目标方法的通知方法:数字小的执行

2.5 切入点表达式

切入点表达式:描述切入点方法的一种表达式,主要作用为决定项目中的哪些方法需要加入通知。

@Pointcut("切入点表达式")

2.5.1 execution

execution(...):根据方法签名(方法的返回值、包名、类名、方法名、方法参数等信息)来匹配

execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)

execution(public void com.hyplus.tilas.mapper.EmpMapper.*(java.lang.Integer))
  • ?的表示可省略的部分:
    • 访问修饰符(比如public、protected等。建议省略)
    • 包名.类名(省略后匹配范围为整个项目,范围过大,因此不建议省略)
    • throws 异常(注意是方法上声明抛出的异常,不是实际抛出的异常。通常不指定)
  • 两种通配符
    1. *:一级通配符单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分
      // 例:通配如下所示包下所有以Service结尾的类中所有以delete开头的单参数方法
      execution(* com.*.service.*Service.delete*(*))
      
    2. ..:多个连续的任意符号,可以通配任意层级的包,或任意类型任意个数的参数。
      // 例1:通配com.hyplus目录下所有DeptService类的任意参数方法
      execution(* com.hyplus..DeptService.*(..))
      // 例2:通配任意方法(慎用)
      execution(* *(..))
      
  • 对于多个execution,可正常使用&&||!来连接
    @Pointcut("execution(* com..service.DeptService.list()) || " +
    		  "execution(* com..service.DeptService.delete(*))")
    
  • 书写建议
    1. 所有业务方法名在命名时尽量规范,方便切入点表达式快速匹配。如:查询类方法都是find开头,更新类方法都是update开头。
    2. 描述切入点方法通常基于接口描述,而不是直接描述实现类,增强拓展性
    3. 在满足业务需要的前提下,尽量缩小切入点的匹配范围。如:包名匹配尽量不使用..,而是使用*匹配单个包。

2.5.2 @annotation

@annotation:用于匹配标识有特定注解的方法

@annotation(注解全类名)
// 例:匹配所有含有MyLog注解的方法
@Pointcut("@annotation(com.hyplus.aop.MyLog)")

若想某方法被匹配,只需给它加上相应的被检测注解即可。

2.6 连接点

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

  • 对于@Around通知,获取连接点信息只能用类ProceedingJoinPoint
/* MyAspectExamples.java */
@Around("pt()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
	log.info("MyAspectExamples around before ...");
	
	// 1. 获取目标对象的类名
	String className = joinPoint.getTarget().getClass().getName();
	log.info("目标对象的类名: {}", className);

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

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

	// 4. 放行:目标方法执行,并获取其返回值
	Object result = joinPoint.proceed();
	log.info("目标方法运行时的返回值: {}", result);

	log.info("MyAspectExamples around after ...");
	return result;
}
  • 对于其他4种通知,获取连接点信息只能用类JoinPoint,其为ProceedingJoinPoint的父类型。
    • 调用方法同上,但某些通知由于其作用时机,无法获得返回值。

案例:记录接口操作日志至数据库表中

将前述案例中增、删、改相关接口的操作日志记录至数据库表中。

日志信息包含:操作人、操作时间、执行方法的全类名、执行方法名、方法运行时参数、返回值、方法执行时长。

思路分析:需要对所有业务类中的增、删、改方法添加统一功能,使用AOP技术最为方便——@Around环绕通知;由于增、删、改方法名没有规律,可以自定义@Log注解完成目标方法匹配。

  1. 准备
    • 在案例工程中引入AOP的起步依赖(详见2.1)
    • 导入资料中准备好的数据库表结构,并引入对应的实体类
/* com.hyplus.tlias.pojo.OperateLog.java 表结构对应的POJO */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OperateLog {
	private Integer id;		// ID
	private Integer operateUser;		// 操作人ID
	private LocalDateTime operateTime;	// 操作时间
	private String className; 		// 操作类名
	private String methodName; 	// 操作方法名
	private String methodParams;	// 操作方法参数
	private String returnValue;		// 操作方法返回值
	private Long costTime;		//操作耗时
}
/* com.hyplus.OperateLogMapper.java */
@Mapper
public interface OperateLogMapper {
	// 插入日志数据
	@Insert("insert into operate_log (operate_user, operate_time, class_name, method_name, method_params, return_value, cost_time) " +
			"values (#{operateUser}, #{operateTime}, #{className}, #{methodName}, #{methodParams}, #{returnValue}, #{costTime})")
	public void insert(OperateLog log);
}
  1. 编码
    • 自定义注解@Log
    • 定义切面类,完成记录操作日志的逻辑
/* com.hyplus.tlias.anno.Log.java */
@Retention(RetentionPolicy.RUNTIME)		// 设为运行时生效
@Target(ElementType.METHOD)				// 设为只能添加在方法上
public @interface Log {}

获取当前登录用户的方法:获取request对象,从请求头中获取JWT令牌,解析令牌获取当前用户的ID。详见基础(3)5.2.2

/* com.hyplus.tlias.aop.LogAspect.java */
@Slf4j
@Component
@Aspect		// 切面类
public class LogAspect {
	// 直接注入一个Servlet请求Bean来获取JWT令牌(相关概念及工具类见基础(3)5.2.2)
	@Autowired
	private HttpServletRequest request;

	@Autowired
	private OperateLogMapper operateLogMapper;
	
	@Around("@annotation(com.hyplus.tlias.anno.Log)")
	public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {
		// 获取操作人ID(当前登录员工ID):从令牌获取
		// 获取请求头中的JWT令牌,解析令牌
		String jwt = request.getHeader("token");
		Claims claims = JwtUtils.parseJWT(jwt);
		Integer operateUser = (Integer) claims.get("id");

		// 获取操作时间
		LocalDateTime operateTime = LocalDateTime.now();

		// 获取操作类名
		String className = joinPoint.getTarget().getClass().getName();

		// 获取操作方法名
		String methodName = joinPoint.getSignature().getName();

		// 获取操作方法参数
		Object[] args = joinPoint.getArgs();
		String methodParams = Arrays.toString(args);

		// 调用原始方法运行
		long begin = System.currentTimeMillis();
		Object result = joinPoint.proceed();
		long end = System.currentTimeMillis();
		
		// 获取方法返回值
		String returnValue = JSONObject.toJSONString(result);

		// 获取操作耗时
		long costTime = end - begin;
	
		// 记录操作日志
		OperateLog operateLog = new OperateLog(null, operateUser, operateTime, className, 
											   methodName, methodParams, returnValue, costTime);
		operateLogMapper.insert(operateLog);
		log.info("AOP已记录操作日志: {}", operateLog);
		
		return result;
	}
}

再给所有需要的方法添加自定义注解@Log即可。


3 Spring原理篇

3.1 配置优先级

基础(3)4.2所述,SpringBoot中支持三种格式的配置文件,优先级:properties > yml > yaml

在项目开发时,推荐统一使用一种格式的配置(yml是主流)。

除了配置文件外,SpringBoot还支持Java系统属性-Dserver.port=9000命令行参数--server.port=10010的方式进行属性配置。

Idea中提供了可视化界面来配置:

在这里插入图片描述

打包后配置属性:

  1. 执行maven打包指令package生成jar包
  2. 执行java指令运行jar包,与此同时可执行上述属性配置
java -Dserver.port=9000 -jar tlias-web-management-0.8.1-SNAPSHOT.jar --server.port=10019

3.2 Bean管理

3.2.1 获取Bean

默认情况下,Spring项目启动时会把bean都创建好放在IOC容器中。

会受到作用域及延迟初始化影响,本小节主要针对于默认的单例非延迟加载的bean而言。

若想要主动获取这些bean,可通过如下方法:

  1. 根据名称获取bean:Object getBean(String name)
  2. 根据类型获取bean:<T> T getBean(Class<T> requiredType)
  3. 根据名称和类型获取bean:<T> T getBean(String name, Class<T> requiredType)(自带类型转换)
/* 单元测试类 示例 */
// 需注入一个IOC容器对象
@Autowired
private ApplicationContext applicationContext;

@Test
public void testGetBean() {
	// 根据名称获取bean(名称默认为类名首字母小写)
	DeptController bean1 = (DeptController) applicationContext.getBean("deptController");	// 返回Object,需强转
	System.out.println(bean1);

	// 根据类型获取bean
	DeptController bean2 = applicationContext.getBean(DeptController.class);
	System.out.println(bean2);

	// 根据名称与类型获取bean
	DeptController bean3 = applicationContext.getBean("deptController", DeptController.class);
	System.out.println(bean3);
}

3.2.2 Bean作用域

Spring支持五种作用域,后三种在web环境才生效:

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

可以通过@Scope注解来配置作用域

@Scope("prototype")		// 设置该bean为非单例
@RestController
@RequestMapping("/depts")
public class DeptController { ... }

默认singleton的bean,会在容器启动时被创建。添加注解@Lazy可延迟初始化(延迟到其使用时才初始化)。
prototype的bean,每一次使用该bean的时候都会创建一个新的实例。
实际开发当中,绝大部分的Bean是单例的,也就是说绝大部分Bean不需要配置scope属性。

3.2.3 第三方Bean

如果要管理的Bean对象来自于第三方(不是自定义的),是无法用@Component及衍生注解声明Bean的。需要用到@Bean注解。

  1. 在启动类中定义方法,将方法返回值交给IOC容器管理,成为IOC容器的Bean对象(不建议)
@SpringBootApplication
public class SpringbootWebConfig2Application {
	// 声明第三方Bean
	@Bean(name = "method1")	// 将方法返回值交给IOC容器管理,成为IOC容器的Bean对象
	public SAXReader saxReader() {
		return new SAXReader();
	}
}
  1. 通过@Configuration注解声明一个配置类,对这些bean进行集中分类配置,其他同上
@Configuration
public class SpringbootWebConfig2Application {
	@Bean(value = "method2")
	public SAXReader saxReader() {
		return new SAXReader();
	}
}

通过@Bean注解的namevalue属性可以声明bean的名称,默认为方法名。

若第三方Bean需要依赖其它Bean对象,直接在Bean定义方法中设置形参即可,容器会根据类型自动装配。

3.3 SpringBoot原理1:起步依赖

Spring Boot两大原理:起步依赖、自动配置。

原始的Spring框架进行Web程序开发,需要引入大量依赖
在这里插入图片描述

Spring Boot的起步依赖原理就是maven的依赖传递

在这里插入图片描述

3.4 SpringBoot原理2:自动配置

SpringBoot的自动配置就是当spring容器启动后,一些配置类、bean对象就自动存入到了IOC容器中,不需要手动声明,从而简化了开发,省去了繁琐的配置操作。

3.4.1 扫描方案

  1. @ComponentScan组件扫描(使用繁琐,性能低)
@ComponentScan({"com.example", "com.hyplus"})	// 设定组件扫描的包
@SpringBootApplication
public class SpringbootWebConfig2Application { ... }
  1. @Import导入,使用该注解导入的类会被Spring加载到IOC容器中。导入形式有:
    • 直接导入普通类
    • 直接导入配置类
    • 导入ImporterSelector接口实现类
/* ImporterSelector接口实现类 */
public class MyImportSelector implements ImportSelector {
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		return new String[]{"com.example.HeaderConfig"};
	}
}
@Import({TokenParser.class, HeaderConfig.class, MyImportSelector.class})	// 导入
@SpringBootApplication
public class SpringbootWebConfig2Application { ... }
  1. 第三方包提供Enable开头的注解(形如@EnableXxxx),封装@Import注解(更方便、优雅,被Spring Boot采用)
/* 第三方包中提供的 @EnableXxxx 格式的注解 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportSelector.class)		// 封装导入注解@Import
public @interface EnableHeaderConfig {
}
@EnableHeaderConfig		// 添加该注解相当于直接使用上述Import
@SpringBootApplication
public class SpringbootWebConfig2Application { ... }

3.4.2 底层原理

@SpringBootApplication注解标识在SpringBoot工程引导类上,是SpringBoot中最最最重要的注解。由三个部分组成:

  1. @SpringBootConfiguration: 该注解与@Configuration注解作用相同,用来声明当前也是一个配置类。
  2. @ComponentScan:组件扫描,默认扫描当前引导类所在包及其子包。
  3. @EnableAutoConfiguration: SpringBoot实现自动化配置的核心注解。

在这里插入图片描述

在这里插入图片描述

如上所示,并非全部装配为IOC容器的Bean,而是添加了@Conditional及其子注释来进行条件装配
@Conditional:在方法、类上使用,按照一定的条件进行判断,在满足给定条件后才会注册对应的bean对象到Spring IOC容器中。
其子注解如下图所示

在这里插入图片描述

常用的3个为:

  • @ConditionalOnClass:判断环境中是否有对应字节码文件,才注册bean到IOC容器。需使用属性name指定类的全类名,或属性value直接指定字节码文件对象。
    @Bean
    @ConditionalOnClass(name = "io.jsonwebtoken.jwts")	//当前环境存在指定的这个类时,才声明该bean
    public HeaderParser headerParser() { ... }
    
  • @ConditionalOnMissingBean:判断环境中没有对应的bean,才注册bean到IOC容器。可指定类型(value属性)或名称(name属性)。
    @Bean
    @ConditionalOnMissingBean	// 当不存在当前类型的bean时,才声明该bean
    public HeaderParser headerParser() { ... }
    
  • @ConditionalOnProperty:判断配置文件中有对应属性name属性)和对应值havingValue属性),才注册bean到IOC容器。
    @Bean
    @ConditionalOnProperty(name = "name", havingValue = "hyplus")	// 配置文件中存在对应的属性和值,才注册bean到IOC容器。
    public HeaderParser headerParser() { ... }
    

3.4.3 自定义starter

在实际开发中,经常会定义一些公共组件,提供给各个项目团队使用。而在SpringBoot的项目中,一般会将这些公共组件封装为SpringBoot的starter

在这里插入图片描述

案例需求:自定义aliyun-Oss-spring-boot-starter,完成阿里云OSS操作工具类AliyunOSSUtils的自动配置。
目标:引入起步依赖引入之后,要想使用阿里云OSS,注入AliyunOSSUtils直接使用即可。
步骤:

  1. 创建aliyun-oss-spring-boot-starter模块
  2. 创建aliyun-oss-spring-boot-autoconfigure模块,在starter中引入该模块
  3. 在aliyun-oss-spring-boot-autoconfigure模块中的定义自动配置功能,并定义自动配置文件META-INF/spring/xxxx.imports

在这里插入图片描述


4 Maven高级

4.1 分模块设计与开发

分模块设计即将项目按照功能拆分成若干个子模块
作用:方便项目的管理维护、扩展,也方便模块间的相互调用,资源共享。

注:分模块开发需要先针对模块功能进行设计,再进行编码。不会先将工程开发完毕,然后进行拆分。

在这里插入图片描述在这里插入图片描述

4.2 继承

4.2.1 继承关系

继承描述的是两个工程间的关系,子工程可以继承父工程中的配置信息,常见于依赖关系的继承。与Java类继承相似,Maven工程继承只能单继承,但支持多重继承。
作用:简化依赖配置、统一管理依赖
实现:<parent> ... </parent>

在这里插入图片描述

  1. 创建maven模块tlias-parent,该工程为父工程,设置打包方式为pom
  • jar:普通模块打包,springboot项目基本都是jar包(内嵌tomcat运行)【默认】
  • war:普通web程序打包,需要部署在外部的tomcat服务器中运行
  • pom:父工程或聚合工程,该模块不写代码,仅进行依赖管理

在这里插入图片描述

  1. 子工程的pom.xml文件中,配置继承关系
    • 配置了继承关系之后坐标中的groupId可省略,因为会自动继承父工程的。
    • <relativePath>指定父工程的pom文件的相对位置。若不指定,将从本地仓库/远程仓库查找该工程。

在这里插入图片描述

  1. 在父工程中配置各个工程共有的依赖,如lombok、各种starter等(子工程会自动继承父工程的依赖)
    • 若父子工程都配置了同一个依赖的不同版本,以子工程的为准。

4.2.2 版本锁定

在maven中,可以在父工程的pom文件中通过<dependencyManagement>来统一管理依赖版本。子工程引入依赖时,无需指定<version>版本号,父工程统一管理。变更依赖版本,只需在父工程中统一变更。

<dependencies><dependencyManagement>的区别:

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

在这里插入图片描述

自定义属性/引用属性

在这里插入图片描述

4.3 聚合

聚合指将多个模块组织成一个整体,同时进行项目的构建。聚合工程为一个不具有业务功能的“空”工程(有且仅有一个pom文件)。
作用:快速构建项目(无需根据依赖关系手动构建,直接在聚合工程上构建即可)

在这里插入图片描述

maven中可以通过<modules>设置当前聚合工程所包含的子模块名称。
聚合工程中所包含的模块,在构建时,会自动根据模块间的依赖关系设置构建顺序,与聚合工程中模块的配置书写位置无关。

在这里插入图片描述

继承与聚合

  • 作用
    • 聚合用于快速构建项目
    • 继承用于简化依赖配置、统一管理依赖
  • 相同点
    • 聚合与继承的pom.xml文件打包方式均为pom,可以将两种关系制作到同一个pom文件中
    • 聚合与继承均属于设计型模块,并无实际的模块内容
  • 不同点
    • 聚合是在聚合工程中配置关系,聚合可以感知到参与聚合的模块有哪些
    • 继承是在子模块中配置关系,父模块无法感知哪些子模块继承了自己

4.4 私服

私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,用来代理位于外部的中央仓库,用于解决团队内部的资源共享与资源同步问题。

依赖查找顺序:本地仓库 → 私服 → 中央仓库

私服在企业项目开发中,一个项目/公司,只需要1台即可(无需我们自己搭建,会使用即可)

在这里插入图片描述

资源上传与下载如下图所示

资源版本

  • RELEASE发行版本):功能趋于稳定、当前更新停止,可以用于发行的版本,存储在私服中的RELEASE仓库中
  • SNAPSHOT快照版本):功能不稳定、尚处于开发中的版本,存储在私服的SNAPSHOT仓库中

在这里插入图片描述

流程

  1. 设置私服的访问用户名/密码(maven配置文件settings.xml中的<servers>中配置)

在这里插入图片描述

  1. 在IDEA的maven工程的pom文件中配置上传(发布)地址

在这里插入图片描述

  1. 设置私服依赖下载的仓库组地址(maven配置文件settings.xml中的<mirrors><profiles>中配置)

在这里插入图片描述在这里插入图片描述

  • 54
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Akira37

💰unneeded

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值