原创作者:田超凡(程序员田宝宝)
版权所有,引用请注明原作者,严禁复制转载
整合配置文件
1.在springboot整合配置文件,分成两大类:
application.properties
application.yml
或者是
Bootstrap.properties
Bootstrap.yml
相对于来说yml文件格式写法更加精简,减少配置文件的冗余性。
2.加载顺序:
bootstrap.yml 先加载 application.yml后加载
bootstrap.yml 用于应用程序上下文的引导阶段。
bootstrap.yml 由父Spring ApplicationContext加载。
- 区别:
bootstrap.yml 和 application.yml 都可以用来配置参数。
bootstrap.yml 用来程序引导时执行,应用于更加早期配置信息读取。可以理解成系统级别的一些参数配置,这些参数一般是不会变动的。一旦bootStrap.yml 被加载,则内容不会被覆盖。
application.yml 可以用来定义应用级别, 应用程序特有配置信息,可以用来配置后续各个模块中需使用的公共参数等。
分布式配置中心:
Properties在线转换yml格式网址:https://www.toyaml.com/index.html
7.1使用@value注解:
|---------------------------------------------------|
| @Value("${tcf.name}") privateString name;
|
7.2@ConfigurationProperties
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| \<!--导入配置文件处理器,配置文件进行绑定就会有提示--\><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
importorg.springframework.boot.context.properties.ConfigurationProperties; importorg.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "tcf") public classTcfUserEntity { privateString addres; privateString age; privateString name; publicString getAddres() { returnaddres; } publicString getAge() { returnage; } publicString getName() { returnname; } public voidsetAddres(String addres) { this.addres= addres; } public voidsetAge(String age) { this.age= age; } public voidsetName(String name) { this.name= name; } @Override publicString toString() { return"TcfUserEntity{"+ "addres='"+ addres+ '\\''+ ", age='"+ age+ '\\''+ ", name='"+ name+ '\\''+ '}'; } }
tcf: addres: www.tcf.com age: 22 name: tcf
@Autowired privateTcfUserEntity tcfUserEntity;
@RequestMapping("/getNameAndAgeAddres") publicString getNameAndAgeAddres() { returntcfUserEntity.toString(); }
|
7.3配置文件占位符
在SpringBoot的配置文件中,我们可以使用SpringBoot提供的的一些随机数
{random.value}、{random.int}、${random.long}
{random.int(10)}、{random.int[1024,65536]}
-${app.name:默认值} 来制定找不到属性时的默认值
7.4多环境配置
|---------------------------------------|
| spring: profiles: active: pre
|
|-----------------------------------------------------------------------------|
| application-dev.yml:开发环境 application-test.yml:测试环境 application-prd.yml:生产环境 |
7.5、核心配置
server:
port: 8081
servlet:
context-path: /tcf
Springboot 默认的情况下整合tomcat容器
- 日志管理
8.1.使用logback记录日志
Springboot 已经默认帮你整合好了logback
日志输出文件在当前项目路径log文件夹下
Maven依赖
|---------------------------------------------------------------------------------------------------------|
| <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
|
Logback配置
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| <configuration> \<!--本文主要输出日志为控制台日志,系统日志,sql日志,异常日志--\> \<!-- %m输出的信息,%p日志级别,%t线程名,%d日期,%c类的全名,,,, --\> \<!--控制台--\><appendername="console"class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d %p (%file:%line\)- %m%n</pattern> <charset>UTF-8</charset> </encoder> </appender> \<!--系统info级别日志--\> \<!--\<File\>日志目录,没有会自动创建--\> \<!--\<rollingPolicy\>日志策略,每天建立一个日志文件,或者当天日志文件超过64MB时--\> \<!--encoder日志编码及输出格式--\><appendername="fileLog"class="ch.qos.logback.core.rolling.RollingFileAppender"> <File>log/file/fileLog.log</File> <rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>log/file/fileLog.log.%d.%i</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicyclass="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> \<!-- or whenever the file size reaches 64 MB --\><maxFileSize>64 MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> <encoder> <pattern> %d %p (%file:%line\)- %m%n </pattern> <charset>UTF-8</charset> \<!--此处设置字符集--\></encoder> </appender> \<!--sql日志--\><appendername="sqlFile"class="ch.qos.logback.core.rolling.RollingFileAppender"> <File>log/sql/sqlFile.log</File> <rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>log/sql/sqlFile.log.%d.%i</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicyclass="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> \<!-- or whenever the file size reaches 64 MB --\><maxFileSize>64 MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> \<!--对记录事件进行格式化。负责两件事,一是把日志信息转换成字节数组,二是把字节数组写入到输出流。--\><encoder> \<!--用来设置日志的输入格式--\><pattern> %d %p (%file:%line\)- %m%n </pattern> <charset>UTF-8</charset> \<!--此处设置字符集--\></encoder> </appender> \<!--异常日志--\><appendername="errorFile"class="ch.qos.logback.core.rolling.RollingFileAppender"> <File>log/error/errorFile.log</File> <rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>log/error/errorFile.%d.log.%i</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicyclass="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> \<!-- or whenever the file size reaches 64 MB --\><maxFileSize>64 MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> \<!--对记录事件进行格式化。负责两件事,一是把日志信息转换成字节数组,二是把字节数组写入到输出流。--\><encoder> \<!--用来设置日志的输入格式--\><pattern> %d %p (%file:%line\)- %m%n </pattern> <charset>UTF-8</charset> \<!--此处设置字符集--\></encoder> \<!--日志都在这里 过滤出error使用try {}catch (Exception e){}的话异常无法写入日志,可以在catch里用logger.error()方法手动写入日志--\><filterclass="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> \<!--日志输出级别--\> \<!--All\\DEBUG\\INFO\\WARN\\ERROR\\FATAL\\OFF--\> \<!--打印info级别日志,分别在控制台,fileLog,errorFile输出 异常日志在上面由过滤器过滤出ERROR日志打印--\><rootlevel="INFO"> <appender-refref="fileLog"/> <appender-refref="console"/> <appender-refref="errorFile"/> </root> \<!--打印sql至sqlFile文件日志--\><loggername="com.dolphin.mapper"level="DEBUG"additivity="false"> <appender-refref="console"/> <appender-refref="sqlFile"/> </logger> </configuration>
|
application
|----------------------------------------------------------------------|
| ###指定读取logback配置文件logging: config: classpath:log/logback.xml
|
测试案例
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| @RestController @Slf4j public classMyIndexService {
@RequestMapping("/getName") publicString getName(String name, intage) { *log*.info("name:{},age:{}", name, age); returnname; }
}
|
日志级别
ALL 最低等级的,用于打开所有日志记录。
TRACE designates finer-grained informational events than the DEBUG.Since:1.2.12,很低的日志级别,一般不会使用。
DEBUG 指出细粒度信息事件对调试应用程序是非常有帮助的,主要用于开发过程中打印一些运行信息。
INFO 消息在粗粒度级别上突出强调应用程序的运行过程。打印一些你感兴趣的或者重要的信息,这个可以用于生产环境中输出程序运行的一些重要信息,但是不能滥用,避免打印过多的日志。
WARN 表明会出现潜在错误的情形,有些信息不是错误信息,但是也要给程序员的一些提示
ERROR 指出虽然发生错误事件,但仍然不影响系统的继续运行。打印错误和异常信息,如果不想输出太多的日志,可以使用这个级别。
FATAL 指出每个严重的错误事件将会导致应用程序的退出。这个级别比较高了。重大错误,这种级别你可以直接停止程序了。
OFF 最高等级的,用于关闭所有日志记录。
日志目录
8.2.使用log4j记录日志
日志级别
机制:如果一条日志信息的级别大于等于配置文件的级别,就记录。
trace:追踪,就是程序推进一下,可以写个trace输出
debug:调试,一般作为最低级别,trace基本不用。
info:输出重要的信息,使用较多
warn:警告,有些信息不是错误信息,但也要给程序员一些提示。
error:错误信息。用的也很多。
fatal:致命错误。
输出源
CONSOLE(输出到控制台)
FILE(输出到文件)
格式
SimpleLayout:以简单的形式显示
HTMLLayout:以HTML表格显示
PatternLayout:自定义形式显示
8.2.1新建log4j配置文件
文件名称log4j.properties
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| #log4j.rootLogger=CONSOLE,info,error,DEBUGlog4j.rootLogger=DEBUG,error,CONSOLE,infolog4j.appender.CONSOLE=org.apache.log4j.ConsoleAppenderlog4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayoutlog4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd-HH-mm} \[%t\] \[%c\] \[%p\] - %m%nlog4j.logger.info=infolog4j.appender.info=org.apache.log4j.DailyRollingFileAppenderlog4j.appender.info.layout=org.apache.log4j.PatternLayoutlog4j.appender.info.layout.ConversionPattern=%d{yyyy-MM-dd-HH-mm} \[%t\] \[%c\] \[%p\] - %m%nlog4j.appender.info.datePattern='.'yyyy-MM-ddlog4j.appender.info.Threshold= infolog4j.appender.info.append=truelog4j.appender.info.File=E:/code/log/info.loglog4j.logger.error=errorlog4j.appender.error=org.apache.log4j.DailyRollingFileAppenderlog4j.appender.error.layout=org.apache.log4j.PatternLayoutlog4j.appender.error.layout.ConversionPattern=%d{yyyy-MM-dd-HH-mm} \[%t\] \[%c\] \[%p\] - %m%nlog4j.appender.error.datePattern='.'yyyy-MM-ddlog4j.appender.error.Threshold= errorlog4j.appender.error.append=truelog4j.appender.error.File=E:/code/log/error.loglog4j.logger.DEBUG=DEBUGlog4j.appender.DEBUG=org.apache.log4j.DailyRollingFileAppenderlog4j.appender.DEBUG.layout=org.apache.log4j.PatternLayoutlog4j.appender.DEBUG.layout.ConversionPattern=%d{yyyy-MM-dd-HH-mm} \[%t\] \[%c\] \[%p\] - %m%nlog4j.appender.DEBUG.datePattern='.'yyyy-MM-ddlog4j.appender.DEBUG.Threshold= DEBUGlog4j.appender.DEBUG.append=truelog4j.appender.DEBUG.File=E:/code/log/dubug.log
log4j 代码 private static final Logger logger = LoggerFactory.getLogger(IndexController.class); |
8.2.2.Maven依赖
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> \<!-- spring boot start --\><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> \<!--排除自带的logback依赖--\><exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> \<!-- springboot-log4j --\><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j</artifactId> <version>1.3.8.RELEASE</version> </dependency>
|
8.2.3.application
|--------------------------------------------------------------------------------|
| ###指定log4j.properties配置文件路径logging: config: classpath:log4j.properties
|
8.3.使用AOP统一处理Web请求日志
基于AOP实现 或者elk
在我们的方法的前后实现拦截 减少打印日志代码的冗余性的问题
8.3.1 POM文件新增依赖
|-----------------------------------------------------------------------------------------------------------------------------------------|
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-++aop++</artifactId> </dependency> |
6.3.2 AOP切面相关配置
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| @Aspect @Component public class WebLogAspect { private static final Logger logger = LoggerFactory.getLogger(WebLogAspect.class ); @Pointcut("execution(public * com.tcf.controller.*.*(..))") public void webLog() { } @Before("webLog()") public void doBefore(JoinPoint joinPoint) throws Throwable { // 接收到请求,记录请求内容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 记录下请求内容 logger .info("URL : " + request.getRequestURL().toString()); logger .info("HTTP_METHOD : " + request.getMethod()); logger .info("IP : " + request.getRemoteAddr()); Enumeration<String> enu = request.getParameterNames(); while (enu.hasMoreElements()) { String name = (String) enu.nextElement(); logger .info("name:{},value:{}", name, request.getParameter(name)); } } @AfterReturning(returning = "ret", pointcut = "webLog()") public void doAfterReturning(Object ret) throws Throwable { // 处理完请求,返回内容 logger .info("RESPONSE : " + ret); } } |
- 其他内容
9.1、使用@Scheduled创建定时任务
在Spring Boot的主类中加入@EnableScheduling注解,启用定时任务的配置
@Component
public class ScheduledTasks {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
@Scheduled(fixedRate = 5000)
++public++ ++void++ ++reportCurrentTime++ ++() {++
++System.out.println(++ ++"++ ++现在时间:++ ++"++ +++ dateFormat.format(++ ++new++ ++Date()));++
++}++
}
@Scheduled(cron = "0/2 * * * * *") 写法:
quartz/Cron/Crontab表达式在线生成工具-BeJSON.com
Xxl-job
9.2、使用@Async实现异步调用
启动加上@EnableAsync ,需要执行异步方法上加入 @Async
异步应用场景
@Async实际就是多线程封装的
异步线程执行方法有可能会非常消耗cpu的资源,所以大的项目建议使用
Mq异步实现。
整合线程池
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| importorg.springframework.context.annotation.Bean; importorg.springframework.context.annotation.Configuration; importorg.springframework.core.task.TaskExecutor; importorg.springframework.scheduling.annotation.EnableAsync; importorg.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; importjava.util.concurrent.ThreadPoolExecutor; @Configuration @EnableAsync public classThreadPoolConfig { /\*\* \*每秒需要多少个线程处理? \* tasks/(1/taskcost) \*/private intcorePoolSize= 3; /\*\* \*线程池维护线程的最大数量\* (max(tasks)- queueCapacity)/(1/taskcost) \*/private intmaxPoolSize= 3; /\*\* \*缓存队列\* (coreSizePool/taskcost)\*responsetime \*/private intqueueCapacity= 10; /\*\* \*允许的空闲时间\*默认为60 \*/private intkeepAlive= 100; @Bean publicTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = newThreadPoolTaskExecutor(); //设置核心线程数executor.setCorePoolSize(corePoolSize); //设置最大线程数executor.setMaxPoolSize(maxPoolSize); //设置队列容量executor.setQueueCapacity(queueCapacity); //设置允许的空闲时间(秒)//executor.setKeepAliveSeconds(keepAlive); //设置默认线程名称executor.setThreadNamePrefix("thread-"); //设置拒绝策略rejection-policy:当pool已经达到max size的时候,如何处理新任务// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行executor.setRejectedExecutionHandler(newThreadPoolExecutor.CallerRunsPolicy()); //等待所有任务结束后再关闭线程池executor.setWaitForTasksToCompleteOnShutdown(true); returnexecutor; } }
importlombok.extern.slf4j.Slf4j; importorg.springframework.scheduling.annotation.Async; importorg.springframework.stereotype.Component; @Slf4j @Component public classMemberServiceAsync { @Async("taskExecutor") publicString smsAsync() { *log*.info("\>02\<"); try{ *log*.info("\>正在发送短信..\<"); Thread.sleep(3000); } catch(Exception e) { } *log*.info("\>03\<"); return"短信发送完成!"; } }
|
异步注解配置类
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| importorg.springframework.context.annotation.Bean; importorg.springframework.context.annotation.Configuration; importorg.springframework.core.task.TaskExecutor; importorg.springframework.scheduling.annotation.EnableAsync; importorg.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; importjava.util.concurrent.ThreadPoolExecutor; @Configuration @EnableAsync public classThreadPoolConfig { /\*\* \*每秒需要多少个线程处理? \* tasks/(1/taskcost) \*/private intcorePoolSize= 3; /\*\* \*线程池维护线程的最大数量\* (max(tasks)- queueCapacity)/(1/taskcost) \*/private intmaxPoolSize= 3; /\*\* \*缓存队列\* (coreSizePool/taskcost)\*responsetime \*/private intqueueCapacity= 10; /\*\* \*允许的空闲时间\*默认为60 \*/private intkeepAlive= 100; @Bean publicTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = newThreadPoolTaskExecutor(); //设置核心线程数executor.setCorePoolSize(corePoolSize); //设置最大线程数executor.setMaxPoolSize(maxPoolSize); //设置队列容量executor.setQueueCapacity(queueCapacity); //设置允许的空闲时间(秒)//executor.setKeepAliveSeconds(keepAlive); //设置默认线程名称executor.setThreadNamePrefix("thread-"); //设置拒绝策略rejection-policy:当pool已经达到max size的时候,如何处理新任务// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行executor.setRejectedExecutionHandler(newThreadPoolExecutor.CallerRunsPolicy()); //等待所有任务结束后再关闭线程池executor.setWaitForTasksToCompleteOnShutdown(true); returnexecutor; } }
|
注意失效问题
注意:如果异步注解写当前自己类,有可能aop会失效,无法拦截注解,最终导致异步注解失效,需要经过代理类调用接口;
所以需要将异步的代码单独抽取成一个类调用接口。
9.3全局捕获异常
@ExceptionHandler 表示拦截异常
- @ControllerAdvice 是 controller 的一个辅助类,最常用的就是作为全局异常处理的切面类
- @ControllerAdvice 可以指定扫描范围
- @ControllerAdvice 约定了几种可行的返回值,如果是直接返回 model 类的话,需要使用 @ResponseBody 进行 json 转换
- 返回 String,表示跳到某个 view
- 返回 modelAndView
- 返回 model + @ResponseBody
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| @ControllerAdvice public classTcfExceptionHandler { /\*\* \*拦截运行异常出现的错误\~\~\~ \* \* **@return**\*/@ExceptionHandler(RuntimeException.class) @ResponseBody publicMap<Object, Object> exceptionHandler() { Map<Object, Object> map = newHashMap<>(); map.put("error", "500"); map.put("msg", "系统出现错误\~"); returnmap; } }
|
9.4发布打包
使用mvn package 打包
使用java --jar 包名
如果报错没有主清单,在pom文件中新增
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> <configuration> <mainClass>com.tcf.App</mainClass> <excludes> <exclude> <groupId>junit</groupId> <artifactId>junit</artifactId> </exclude> <exclude> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build>
|
本文部分素材转载自蚂蚁课堂