一、添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
二、添加AOP操作类
2.1 指定方法切面,做日志输出
package com.vcredit.fts.common.aop.exception; import com.alibaba.fastjson.JSON; import com.vcredit.fts.common.util.IPUtil; import lombok.Setter; import lombok.extern.log4j.Log4j2; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; @Log4j2 @Aspect @Component public class LogAspect { private ThreadLocal<ApiLogAspect.ApiLog> apiLogThreadLocal = new ThreadLocal<>(); /** * 声明切点 */ @Pointcut("execution(* com..web.*Controller.*(..))") public void pointCut() { } @Around("pointCut()") public final Object doPrintLog(ProceedingJoinPoint joinPoint) throws Throwable { return check(joinPoint); } protected Object check(ProceedingJoinPoint joinPoint) throws Throwable { Object result = null; try { apiLogThreadLocal.set(ApiLogAspect.ApiLog.newInstance()); doBefore(joinPoint); result = joinPoint.proceed(joinPoint.getArgs()); doAfterReturning(result); } catch (Throwable e) { doException(e); throw e; } finally { apiLogThreadLocal.remove(); } return result; } private void doException(Throwable e) { // 处理完请求,返回内容 ApiLogAspect.ApiLog apiLog = apiLogThreadLocal.get(); apiLog.setResponseBody("exception:" + e.getMessage()); apiLog.setEndTime(System.currentTimeMillis()); log.info(apiLog.toString()); } private void doBefore(ProceedingJoinPoint joinPoint) { // 接收到请求,记录请求内容 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); // 记录下请求内容 ApiLogAspect.ApiLog apiLog = apiLogThreadLocal.get(); apiLog.setApiClassName(joinPoint.getSignature().getDeclaringTypeName()); apiLog.setApiMethodName(joinPoint.getSignature().getName()); apiLog.setRequestBody(JSON.toJSONString(joinPoint.getArgs())); apiLog.setRequestMethod(request.getMethod()); apiLog.setRequestIp(IPUtil.getIpAddr(request)); apiLog.setRequestUrl(request.getRequestURL().toString()); } private void doAfterReturning(Object result) { // 处理完请求,返回内容 ApiLogAspect.ApiLog apiLog = apiLogThreadLocal.get(); apiLog.setResponseBody(JSON.toJSONString(result)); apiLog.setEndTime(System.currentTimeMillis()); log.info(apiLog.toString()); } @Setter public static class ApiLog { private Long startTime; private Long endTime; private Long consumeTime; private String requestUrl; private String requestMethod; private String requestIp; private String apiClassName; private String apiMethodName; private String requestBody; private String responseBody; public static LogAspect.ApiLog newInstance() { return new LogAspect.ApiLog(); } private ApiLog() { startTime = System.currentTimeMillis(); } public void setEndTime(Long endTime) { this.endTime = endTime; this.consumeTime = this.endTime - this.startTime; } @Override public String toString() { StringBuffer buffer = new StringBuffer(); buffer.append("耗时:").append(consumeTime).append("ms").append(" , "); buffer.append("URL:").append(requestUrl).append(" , "); buffer.append("IP:").append(requestIp).append(" , "); buffer.append("方式:").append(requestMethod).append(" , "); buffer.append("请求方法:").append(apiClassName).append(".").append(apiMethodName).append(" , "); buffer.append("\r\n"); buffer.append("输入:").append(requestBody); buffer.append("\r\n"); buffer.append("输出:").append(responseBody); return buffer.toString(); } } }
2.2 指定注解切面,做日志输出
A:创建标记注解
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface OperationLog { /** * 操作方法 * * @return */ String value() default ""; }
B:创建切面处理类(包括mongo类)
package com.vcredit.fts.common.aop.exception; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.google.common.collect.Maps; import com.vcredit.fts.common.aop.exception.annotation.OperationLog; import com.vcredit.fts.common.service.mongo.MongoService; import com.vcredit.fts.common.util.IPUtil; import io.netty.util.internal.StringUtil; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.time.LocalDateTime; import java.util.Map; @Slf4j @Aspect @Component public class OperationLogAspect { private ThreadLocal<WebOperationLogPO> sysOperatorLogThreadLocal = new ThreadLocal<>(); @Resource private MongoService mongoService; @Pointcut("@annotation(com.vcredit.fts.common.aop.exception.annotation.OperationLog)") public void pointcut() { } @Around("pointcut()") public final Object doSaveUserLog(ProceedingJoinPoint joinPoint) throws Throwable { return check(joinPoint); } /** * 功能描述: 切面检查<br> 〈功能详细描述〉 * * @param joinPoint 切面 * @return Object * @throws Throwable */ protected Object check(ProceedingJoinPoint joinPoint) throws Throwable { setSysOperatorLogBefore(joinPoint); Object result = joinPoint.proceed(); setSysOperatorLogAfter(joinPoint); saveUserOperatorLog(); return result; } /** * 设置操作日志属性(之前) * * @param joinPoint 代理对象 */ private void setSysOperatorLogBefore(ProceedingJoinPoint joinPoint) { WebOperationLogPO sysOperatorLog = new WebOperationLogPO(); // 操作类型 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); OperationLog logAnnotation = method.getAnnotation(OperationLog.class); if (logAnnotation != null) { sysOperatorLog.setOperation(logAnnotation.value()); } // 用户信息 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String userName = request.getHeader(USER_NAME_KEY); if (StringUtil.isNullOrEmpty(userName)) { userName = "未知"; } sysOperatorLog.setMethod(request.getMethod()); sysOperatorLog.setCreaterUser(userName); sysOperatorLog.setCreateTime(LocalDateTime.now()); sysOperatorLog.setIp(IPUtil.getIpAddr(request)); sysOperatorLogThreadLocal.set(sysOperatorLog); } /** * 获取用户名key */ private static final String USER_NAME_KEY = "userName"; /** * 设置操作日志属性(之后) * * @param joinPoint 代理对象 */ private void setSysOperatorLogAfter(ProceedingJoinPoint joinPoint) { WebOperationLogPO sysOperatorLog = sysOperatorLogThreadLocal.get(); // 请求参数 // Map paramMap = generateValueMap(joinPoint); // sysOperatorLog.setContext(JSON.toJSONString(paramMap)); sysOperatorLog.setContext(JSON.toJSONString(joinPoint.getArgs())); } /** * 保存操作日志 */ private void saveUserOperatorLog() { try { // 用户操作异常时,不保存操作日志 WebOperationLogPO sysOperatorLog = sysOperatorLogThreadLocal.get(); if (sysOperatorLog == null) { return; } // 保存日志 mongoService.save(sysOperatorLog, "Demo.OperationLog"); } catch (Exception e) { log.warn("用户操作日志保存失败:{} , log : {}", e.getMessage(), JSON.toJSONString(sysOperatorLogThreadLocal.get())); } finally { // 清除threadLocal sysOperatorLogThreadLocal.remove(); } } /** * 生成参数信息 * * @param joinPoint * @return */ protected final Map<String, Object> generateValueMap(ProceedingJoinPoint joinPoint) { Map<String, Object> valueMap = Maps.newHashMap(); // 获取当前注解方法 Object[] args = joinPoint.getArgs(); MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); for (int i = 0; i < method.getParameters().length; i++) { Parameter parameter = method.getParameters()[i]; if (parameter.getAnnotation(RequestBody.class) != null) { JSONObject jsonObject; if (args[i] instanceof String) { jsonObject = JSON.parseObject(args[i].toString()); } else { jsonObject = JSON.parseObject(JSON.toJSONString(args[i])); } generateValueMap(valueMap, jsonObject); } else { valueMap.put(parameter.getName(), args[i]); } } return valueMap; } /** * 生成参数信息 * * @param valueMap * @param jsonObject */ protected final void generateValueMap(Map<String, Object> valueMap, JSONObject jsonObject) { for (Map.Entry<String,Object> entry : jsonObject.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); valueMap.put(key, value); if (value instanceof JSONObject) { generateValueMap(valueMap, jsonObject.getJSONObject(key)); } } } @Data @Document(collection = "Demo.OperationLog") public static class WebOperationLogPO{ /** * 创建人 */ private String createrUser; /** * 内容 */ private String context; /** * ip地址 */ private String ip; /** * 目标方法 */ private String method; /** * 操作 */ @Indexed private String operation; /** * 创建时间 */ @Indexed private LocalDateTime createTime; } }
C: 在需要添加操作记录的方法上添加注解
/**
* 清除指定缓存
*/
@GetMapping(value = "/clear-redis/{redisKey}")
@OperationLog("测试-clearRedis")
public CommonResult clearRedis(@PathVariable String redisKey) {
if (StringUtil.isNullOrEmpty(redisKey)) {
return CommonResult.init(SystemStatus.PARAM_VALID_ERROR);
}
redisKey = RedisKeyFlag.BASE + redisKey;
// 任务结束时,移除redis
if (redisService.exists(redisKey)) {
redisService.del(redisKey);
} else if (redisKey.endsWith("*")) {
var keys = redisService.keys(redisKey);
if (!ListUtil.isNullOrEmpty(keys)) {
redisService.del(keys);
}
}
return CommonResult.successBase();
}
D:查看结果
2.3 全局异常切面,做统一输出及报警
A:创建系统公共返回枚举
/** * 系统通用状态枚举 * * @author huangzhongqing * @date 2020-12-04 */ public enum SystemStatus { // --------------- 成功 --------------- SYS_SUCCESS(0,"成功!"), // --------------- 系统异常 --------------- SYS_ERROR(1001,"系统异常!"), PARAM_VALID_ERROR(1002,"参数校验失败!"), CONGIG_ERROR(1003,"配置异常!"), EMPTY_ERROR(1003,"结果为空!"), PARAM_VALIDATE_ERROR(1003,"用户交互验证失败!"), IDEMPOTENT_CHECK_ERROR(1004,"幂等校验不通过!"), PROCESS_IS_RUNNING(1005,"当前流程正在处理中"), ENCRYPT_ERROR(1006,"数据加密异常"), DECRYPT_ERROR(1007,"数据解密异常"), // --------------- 外部异常 --------------- OUT_REQUEST_ERROR(2001,"调用外部接口异常"), ECM_DOWNLOAD_ERROR(2002,"调用ecm下载服务异常"), ECM_UPLOAD_ERROR(2003,"调用ecm上传服务异常"), SFTP_LOGIN_ERROR(2004,"sftp连接异常"), SFTP_CREAT_PATH_ERROR(2005,"sftp创建路径异常"), SFTP_SERVICE_ERROR(2006,"sftp服务器异常"), SFTP_UPLOAD_ERROR(2007,"sftp上传文件异常"), // --------------- 其他异常 --------------- EMAIL_SEND_ERROR(3001,"发送邮件异常"), CONTRACT_QUERY_DOING(3002,"合同文件列表为空,可能原因bid不存在或者合同生成中"), CONTRACT_QUERY_SERVICE_ERROR(3002,"查询文件异常"), FILE_QUERY_SERVICE_ERROR(3002,"查询合同文件列表异常"), FILE_QUERY_SQL_ERROR(3002,"查询合同文件异常"), FILE_TYPE_LIST_ERROR(3002,"文件类型列表为空"), DOC_SAVE_SQL_ERROR(3002,"保存文件信息异常"), QUERY_COUNT_CONFIG_ERROR(3002,"获取查询数量限制配置异常"), QUERY_COUNT_OVER_LIMIT_ERROR(3002,"查询数量查过配置限制查询数量"), UPDATE_QUERY_ERROR(3002,"更新流程信息失败,未查询到订单数据"), UPDATE_PROCESS_ERROR(3002,"更新流程信息异常"), SYNC_DATA_PROCESS_ERROR(3002,"流程类型不存在"), QUERY_SERVICE_ERROR(3002,"未查询到数据"), FILE_QUERY_CANCEL_ERROR(9000,"订单已解约"), NOT_ENUM_TYPE(9001,"aop参数类型错误,非枚举类型!"), ; private int code; private String message; SystemStatus(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
B:创建公共返回类
import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; /** * 系统通用返回参数 * * @author huangzhongqing * @date 2020-12-04 */ @Data @Accessors(chain = true) @NoArgsConstructor public class CommonResult<T> extends BaseModel { /** * 状态码 */ private int Code; /** * 消息 */ private String Message; /** * 结果 */ private T Result; private CommonResult(SystemStatus baseEnum) { this.Code = baseEnum.getCode(); this.Message = baseEnum.getMessage(); } private CommonResult(T result) { this.Code = SystemStatus.SYS_SUCCESS.getCode(); this.Message = SystemStatus.SYS_SUCCESS.getMessage(); this.Result = result; } private CommonResult( Integer code, String message) { this.Code = code; this.Message = message; } /** * 基本正确的返回 * @return Result */ public static CommonResult successBase() { return new CommonResult( SystemStatus.SYS_SUCCESS); } /** * 正确的返回帶返回体 * @return Result */ public static <T> CommonResult<T> successBase(T body) { return new CommonResult(body); } /** * 基本异常的返回 * @return Result */ public static CommonResult errorBase() { return new CommonResult(SystemStatus.SYS_ERROR); } /** * 自定义返回 * @return Result */ public static CommonResult init(SystemStatus code) { return new CommonResult(code); } /** * 指定异常枚举,返回错误信息 * @param baseEnum 异常枚举 * @return Result */ public static CommonResult error(SystemStatus baseEnum) { return new CommonResult(baseEnum); } public static CommonResult error(Integer code, String alertMessage) { return new CommonResult(code,alertMessage); } }
C:创建自定义异常类
import com.vcredit.fts.common.model.enums.SystemStatus; import com.vcredit.fts.common.model.consts.ConstParam; import com.vcredit.fts.common.util.StringUtil; import lombok.Data; import java.util.Objects; /** * 基本异常 * * @author huangzhongqing * @date 2020-12-04 */ @Data public class SysException extends RuntimeException { /** * serialVersionUID */ private static final long serialVersionUID = ConstParam.ONE_L; /** * 异常详情 */ private Exception Exp; /** * 系统状态 */ private SystemStatus Status; /** * 是否通知 */ private Boolean IsNotice; // --------------- 构造函数 begain --------------- /** * 基本异常 */ public SysException() { this.Status = SystemStatus.SYS_ERROR; } /** * 基本异常 * * @param message */ public SysException(String message) { this.Status = SystemStatus.SYS_ERROR; this.Status.setMessage(message); } /** * 基本异常 */ public SysException(SystemStatus status) { this.Status = status; } /** * 基本异常 * * @param status * @param message */ public SysException(SystemStatus status, String message) { this.Status = status; this.Status.setMessage(message); } /** * 基本异常 * * @param ex */ public SysException(Exception ex) { this.Status = SystemStatus.SYS_ERROR; this.Status.setMessage(ex.getMessage()); this.Exp = ex; } /** * 基本异常 * * @param status * @param ex */ public SysException(SystemStatus status, Exception ex) { this.Status = status; this.Exp = ex; } /** * 基本异常 * * @param isNotice */ public SysException(Boolean isNotice) { this.Status = SystemStatus.SYS_ERROR; this.IsNotice = isNotice; } /** * 基本异常 * * @param message * @param isNotice */ public SysException(String message, Boolean isNotice) { this.Status = SystemStatus.SYS_ERROR; this.Status.setMessage(message); this.IsNotice = isNotice; } /** * 基本异常 * * @param status * @param isNotice */ public SysException(SystemStatus status, Boolean isNotice) { this.Status = status; this.IsNotice = isNotice; } /** * 基本异常 * * @param status * @param message * @param isNotice */ public SysException(SystemStatus status, String message, Boolean isNotice) { this.Status = status; this.Status.setMessage(message); this.IsNotice = isNotice; } /** * 基本异常 * * @param ex * @param isNotice */ public SysException(Exception ex, Boolean isNotice) { this.Status = SystemStatus.SYS_ERROR; this.Status.setMessage(ex.getMessage()); this.Exp = ex; this.IsNotice = isNotice; } /** * 基本异常 * * @param status * @param ex * @param isNotice */ public SysException(SystemStatus status, Exception ex, Boolean isNotice) { this.Status = status; this.Exp = ex; this.IsNotice = isNotice; } // --------------- 构造函数 end --------------- /** * 获取消息 * * @return 结果 */ @Override public String getMessage() { if(!Objects.isNull(this.Status) && !StringUtil.isNullOrEmpty(this.Status.getMessage())){ return this.Status.getMessage(); } else{ return "未知"; } } }
D:创建切面处理类
import com.alibaba.fastjson.JSON; import com.vcredit.fts.common.exception.SysException; import com.vcredit.fts.common.model.consts.ConstParam; import com.vcredit.fts.common.model.dto.CommonResult; import com.vcredit.fts.common.model.enums.SystemStatus; import com.vcredit.fts.common.util.EmailUtil; import com.vcredit.fts.common.util.StringUtil; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.http.converter.HttpMessageConversionException; import org.springframework.util.StringUtils; import org.springframework.validation.BindingResult; import org.springframework.validation.ObjectError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.annotation.Resource; @RestControllerAdvice @Log4j2 public class ExceptionAspect { @Resource private EmailUtil email; @Autowired protected MessageSource messageSource; /** * 全局异常处理 * * @param e 异常对象 * @return CommonResult */ @ExceptionHandler(Throwable.class) public CommonResult handleException(Throwable e) { log.error("Exception :", e); String subject = e.getMessage(); if(StringUtil.isNullOrEmpty(subject)) { subject="系统异常"; } email.sendMail(e.toString(), "【异常】" + e.getMessage()); return CommonResult.errorBase().setMessage(e.getMessage()); } /** * HTTP消息转换异常 * * @param e 异常对象 * @return Result */ @ExceptionHandler(HttpMessageConversionException.class) public CommonResult handleHttpMessageConversionException(HttpMessageConversionException e) { log.error("HTTP消息转换异常 : {}", e.getMessage()); return CommonResult.error(SystemStatus.PARAM_VALIDATE_ERROR); } /** * 参数验证异常处理 * * @param ex 异常对象 * @return Result */ @ExceptionHandler(MethodArgumentNotValidException.class) public CommonResult handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) { log.debug("参数验证异常处理 :{}" + ex.getMessage()); return CommonResult.errorBase().setCode(SystemStatus.PARAM_VALID_ERROR.getCode()).setMessage(errorMessage(ex)); } /** * 内置异常处理 * * @param e 异常对象 * @return CommonResult */ @ExceptionHandler(SysException.class) public CommonResult handleSysException(SysException e) { log.error("sysException :", e); if(e.getIsNotice() != null && e.getIsNotice().compareTo(true) == ConstParam.ZERO){ email.sendMail(JSON.toJSONString(e),"【异常】XX系统"); } return CommonResult.init(e.getStatus()); } private String errorMessage(MethodArgumentNotValidException e) { BindingResult bindingResult = e.getBindingResult(); StringBuilder stringBuilder = new StringBuilder(); // 只返回一个错误 if (bindingResult.hasErrors()) { ObjectError firstError = bindingResult.getAllErrors().get(0); String messages = firstError.getArguments()[0].toString().split("default message")[1]; stringBuilder.append(StringUtils.isEmpty(firstError.getDefaultMessage()) ? "param is invalid" : String.format("参数%s : %s", messages, firstError.getDefaultMessage())); } return stringBuilder.toString(); } }
三、详细解析
生命周期相关注解主要包括@Around、@Before、@After、@AfterReturning、@AfterThrowing
执行顺序为
@Around作用
1.可以在目标方法之前进行增强动作,也可以执行完目标方法后执行
2.可以决定目标方法何时,以何种方式执行,甚至可以不执行
3.可以改变目标方法的参数值,也可以改变执行目标方法之后的返回值;当需要改变目标方法的返回值时只能使用@Around
注意: 使用@Around的方法的第一个形参必须是ProceedJoinPoint类型,只有调用该参数的proceed()方法才会执行目标函数。
ProceedJoinPoint是JoinPoint的子类,多了proceed()方法,并可以抛出异常。
@Pointcut("execution(* com..web.*Controller.*(..))")
@Pointcut("@annotation(com.vcredit.fts.common.aop.exception.annotation.OperationLog)")
execution表达式基本语法格式为:
execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
说明1:
1、除了返回类型模式,方法名模式和参数模式外,其它项都是可选的。
2、多个execution表达式通过 || 进行连接
说明2:
1、【非必填】修饰符模式。public 表示public 级别方法。 可以不写,不写就是所有的方法(public,private,protected等级别的方法)。
2、【必填】返回类型模式。表示方法返回值的类型, * 表示全部。
3、【必填】方法名模式。表示通配符; .. 表示包以及包下面的子包。
4、【必填】参数模式。括号表示参数,两个点表示任何参数类型。 *(..) 表示全部方法。
5、【非必填】异常模式。
四、补充
springboot添加启动日志
@Log4j2 public class MainShowInfoUtil { private MainShowInfoUtil() { } private static final String NOT_SET = "[Not Set]"; public static void showApplicationSummary(Environment env) { String protocol = StringUtils.hasText(env.getProperty("server.ssl.key-store")) ? "https" : "http"; String appId = env.getProperty("spring.application.appId"); if (!StringUtils.hasText(appId)) { appId = NOT_SET; } String nodeId = env.getProperty("nodeId"); if (!StringUtils.hasText(nodeId)) { nodeId = NOT_SET; } try { log.info("\n---------------------------程序启动完成-------------------------------\n\t" + "Application '{}' is running! \n\t" + "Access URLs:\n\t" + " - Local: {}://localhost:{}/swagger-ui.html\n\t" + " - External: {}://{}:{}/swagger-ui.html\n\t" + " - Prometheus: {}://localhost:{}/actuator/prometheus\n\t" + "Runtime Parameters: \n\t" + " - appID: {}\n\t" + " - nodeId: {}\n\t" + "Active Profile(s): {}\n" + "-------------------------------------------------------------------", env.getProperty("spring.application.name"), protocol, env.getProperty("local.server.port"), protocol,InetAddress.getLocalHost().getHostAddress(), env.getProperty("local.server.port"), protocol, env.getProperty("local.server.port"), appId, nodeId, env.getActiveProfiles()); } catch (UnknownHostException e) { log.catching(e); } } }
@EnableApolloConfig @SpringBootApplication(scanBasePackages = "com.hzq.fts") @EnableEcmService @Slf4j public class FtsProcessApplication { public static void main(String[] args) { ConfigurableEnvironment environment = SpringApplication.run(FtsProcessApplication.class,args).getEnvironment(); MainShowInfoUtil.showApplicationSummary(environment); } }