首页 > 编程语言 >JavaWeb开发基础 (5) :后端框架进阶(事务管理、AOP、Spring原理、Maven高级)

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

时间:2024-06-12 10:29:53浏览次数:25  
标签:事务管理 JavaWeb Spring Bean class bean 注解 方法 public

JavaWeb开发基础:后端框架进阶——事务管理、AOP、Spring原理、Maven高级

文章目录


1 事务管理

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

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

1.1 Spring事务管理

根据事务的定义,调整基础(4)案例中解散部门的方法为删除部门同时删除该部门下的所有员工,并在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。详见基础(4)5.2.2

/* com.hyplus.tlias.aop.LogAspect.java */
@Slf4j
@Component
@Aspect		// 切面类
public class LogAspect {
	// 直接注入一个Servlet请求Bean来获取JWT令牌(相关概念及工具类见基础(4)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 配置优先级

基础(4)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>中配置)

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


Web开发总结

在这里插入图片描述

在这里插入图片描述

标签:事务管理,JavaWeb,Spring,Bean,class,bean,注解,方法,public
From: https://blog.csdn.net/Akira37/article/details/139451410

相关文章

  • springboot打包将配置文件和jar分开
    <?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0......
  • 【Spring Boot 整合 Angular】
    今天我们尝试SpringBoot整合Angular,并决定建立一个非常简单的SpringBoot微服务,使用Angular作为前端渲编程语言进行前端页面渲染.基础环境技术版本Java1.8+SpringBoot1.5.x创建项目初始化项目bashmvnarchetype:generate-DgroupId=com.edurt.sli.sliss-Dartifact......
  • spring-1-IOC、创建bean的方式、创建bean的过程
    1.背景IOC(InversionofControl,控制反转)控制反转是一种设计原则,它将对象的创建和管理责任从应用代码中移交给容器。在Spring中,IOC容器负责管理应用中的所有对象,包括它们的生命周期和相互之间的依赖关系。IOC的主要目的是为了减少代码之间的耦合,使代码更加模块化和可测试。这......
  • SpringBoot内置数据源
    回顾:在我们之前学习在配置文件当中配置对应的数据源的时候,我们设置的数据源其实都是Druid的数据源,并且其配置有两种方式,当然这两种方式都需要我们导入对应的有关德鲁伊的依赖才行一种是直接在开始设置为druid数据源类型的一种是在对应的正常的数据库配置下,设置......
  • 【Azure Spring Apps】Spring App部署上云遇见 502 Bad Gateway nginx
    问题描述在部署AzureSpringApp应用后,访问应用,遇见了502BadGatewayNginx。问题解答502BadGateway, 并且由Nginx返回。而自己的应用中,并没有定义Nginx相关内容,所以需要查看问题是否出现在AzureSpringApp服务的设置上。根据SpringApp的通信模型图判断,502的请求是由N......
  • 基于SpringBoot的刷题小程序的设计与实现+附源码+数据库
    摘要:随着互联网技术的快速发展,在线教育平台逐渐成为学生学习和复习的重要工具。为了提高用户在学习过程中的效率和体验,本文提出并实现了一个基于SpringBoot的刷题小程序。该小程序旨在通过高效的题库管理、智能化的刷题功能以及友好的用户界面,帮助用户更好地进行知识点的巩......
  • Java项目:208Springboot + vue实现的校园服务平台(含论文+开题报告)
    作者主页:夜未央5788 简介:Java领域优质创作者、Java项目、学习资料、技术互助文末获取源码项目介绍基于Springboot+vue实现的汽车服务管理系统本系统包含管理员、接单员、普通用户三个角色。管理员角色:管理员管理、基础数据管理、接单详情管理、接单员管理、公告信......
  • Spring学习笔记--1.IoC入门
    Spring学习笔记一、IoC入门1.什么是IoCIoC即控制反转,一个类不再主动控制创建自己所依赖的类,而是交给外部容器去控制创建自己所依赖的类。例如,有一个汽车厂,原本想要制作一辆汽车,需要自己制作发动机、轮胎、方向盘等零部件,汽车就是这个类,发动机和轮胎就是它的依赖项,这些依......
  • 基于springboot+vue.js+uniapp小程序的社区团购系统附带文章源码部署视频讲解等
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaits系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • SpringBoot3.0.x适配mybatis版本
    SpringBoot适配mybatis版本最低为3.0.3<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.3</version><......