java項目實(shí)現(xiàn)統(tǒng)一打印入?yún)⒊鰠⒌热罩?/h1>
更新時間:2023年03月31日 09:52:58 作者:Yuhei001
這篇文章主要介紹了java項目實(shí)現(xiàn)統(tǒng)一打印入?yún)⒊鰠⒌热罩痉绞?,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
1.背景
SpringBoot項目中,之前都是在controller方法的第一行手動打印 log,return之前再打印返回值。有多個返回點(diǎn)時,就需要出現(xiàn)多少重復(fù)代碼,過多的非業(yè)務(wù)代碼顯得十分凌亂。
本文將采用AOP 配置自定義注解實(shí)現(xiàn) 入?yún)?、出參的日志打印(方法的入?yún)⒑头祷刂刀疾捎?fastjson 序列化)。
2.設(shè)計思路
將特定包下所有的controller生成代理類對象,并交由Spring容器管理,并重寫invoke方法進(jìn)行增強(qiáng)(入?yún)?、出參的打?.
3.核心代碼
3.1 自定義注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({InteractRecordBeanPostProcessor.class})
public @interface EnableInteractRecord {
? ? /**
? ? ?* app對應(yīng)controller包名
? ? ?*/
? ? String[] basePackages() default {};
? ? /**
? ? ?* 排除某些包
? ? ?*/
? ? String[] exclusions() default {};
}
3.2 實(shí)現(xiàn)BeanFactoryPostProcessor接口
作用:獲取EnableInteractRecord注解對象,用于獲取需要創(chuàng)建代理對象的包名,以及需要排除的包名
@Component
public class InteractRecordFactoryPostProcessor implements BeanFactoryPostProcessor {
? ? private static Logger logger = LoggerFactory.getLogger(InteractRecordFactoryPostProcessor.class);
? ? private EnableInteractRecord enableInteractRecord;
? ? @Override
? ? public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
? ? ? ? try {
? ? ? ? ? ? String[] names = beanFactory.getBeanNamesForAnnotation(EnableInteractRecord.class);
? ? ? ? ? ? for (String name : names) {
? ? ? ? ? ? ? ? enableInteractRecord = beanFactory.findAnnotationOnBean(name, EnableInteractRecord.class);
? ? ? ? ? ? ? ? logger.info("開啟交互記錄 ", enableInteractRecord);
? ? ? ? ? ? }
? ? ? ? } catch (Exception e) {
? ? ? ? ? ? logger.error("postProcessBeanFactory() Exception ", e);
? ? ? ? }
? ? }
? ? public EnableInteractRecord getEnableInteractRecord() {
? ? ? ? return enableInteractRecord;
? ? }
}
3.3 實(shí)現(xiàn)MethodInterceptor編寫打印日志邏輯
作用:進(jìn)行入?yún)?、出參打印,包含是否打印邏?/p>
@Component
public class ControllerMethodInterceptor implements MethodInterceptor {
? ? private static Logger logger = LoggerFactory.getLogger(ControllerMethodInterceptor.class);
? ? // 請求開始時間
? ? ThreadLocal<Long> startTime = new ThreadLocal<>();
? ? private String localIp = "";
? ? @PostConstruct
? ? public void init() {
? ? ? ? try {
? ? ? ? ? ? localIp = InetAddress.getLocalHost().getHostAddress();
? ? ? ? } catch (UnknownHostException e) {
? ? ? ? ? ? logger.error("本地IP初始化失敗 : ", e);
? ? ? ? }
? ? }
? ? @Override
? ? public Object invoke(MethodInvocation invocation) {
? ? ? ? pre(invocation);
? ? ? ? Object result;
? ? ? ? try {
? ? ? ? ? ? result = invocation.proceed();
? ? ? ? ? ? post(invocation, result);
? ? ? ? ? ? return result;
? ? ? ? } catch (Throwable ex) {
? ? ? ? ? ? logger.error("controller 執(zhí)行異常: ", ex);
? ? ? ? ? ? error(invocation, ex);
? ? ? ? }
? ? ? ? return null;
? ? }
? ? public void error(MethodInvocation invocation, Throwable ex) {
? ? ? ? String msgText = ex.getMessage();
? ? ? ? logger.info(startTime.get() + " 異常,請求結(jié)束");
? ? ? ? logger.info("RESPONSE : " + msgText);
? ? ? ? logger.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get()));
? ? }
? ? private void pre(MethodInvocation invocation) {
? ? ? ? long now = System.currentTimeMillis();
? ? ? ? startTime.set(now);
? ? ? ? logger.info(now + " 請求開始");
? ? ? ? ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
? ? ? ? HttpServletRequest request = attributes.getRequest();
? ? ? ? logger.info("URL : " + request.getRequestURL().toString());
? ? ? ? logger.info("HTTP_METHOD : " + request.getMethod());
? ? ? ? logger.info("REMOTE_IP : " + getRemoteIp(request));
? ? ? ? logger.info("LOCAL_IP : " + localIp);
? ? ? ? logger.info("METHOD : " + request.getMethod());
? ? ? ? logger.info("CLASS_METHOD : " + getTargetClassName(invocation) + "." + invocation.getMethod().getName());
? ? ? ? // 獲取請求頭header參數(shù)
? ? ? ? Map<String, String> map = new HashMap<String, String>();
? ? ? ? Enumeration<String> headerNames = request.getHeaderNames();
? ? ? ? while (headerNames.hasMoreElements()) {
? ? ? ? ? ? String key = (String) headerNames.nextElement();
? ? ? ? ? ? String value = request.getHeader(key);
? ? ? ? ? ? map.put(key, value);
? ? ? ? }
? ? ? ? logger.info("HEADERS : " + JSONObject.toJSONString(map));
? ? ? ? Date createTime = new Date(now);
? ? ? ? // 請求報文
? ? ? ? Object[] args = invocation.getArguments();// 參數(shù)
? ? ? ? String msgText = "";
? ? ? ? Annotation[][] annotationss = invocation.getMethod().getParameterAnnotations();
? ? ? ? for (int i = 0; i < args.length; i++) {
? ? ? ? ? ? Object arg = args[i];
? ? ? ? ? ? if (!(arg instanceof ServletRequest)
? ? ? ? ? ? ? ? ? ? && !(arg instanceof ServletResponse)
? ? ? ? ? ? ? ? ? ? && !(arg instanceof Model)) {
? ? ? ? ? ? ? ? RequestParam rp = null;
? ? ? ? ? ? ? ? Annotation[] annotations = annotationss[i];
? ? ? ? ? ? ? ? for (Annotation annotation : annotations) {
? ? ? ? ? ? ? ? ? ? if (annotation instanceof RequestParam) {
? ? ? ? ? ? ? ? ? ? ? ? rp = (RequestParam) annotation;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (msgText.equals("")) {
? ? ? ? ? ? ? ? ? ? msgText += (rp != null ? rp.value() + " = " : " ") + JSONObject.toJSONString(arg);
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? msgText += "," + (rp != null ? rp.value() + " = " : " ") + JSONObject.toJSONString(arg);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? logger.info("PARAMS : " + msgText);
? ? }
? ? private void post(MethodInvocation invocation, Object result) {
? ? ? ? logger.info(startTime.get() + " 請求結(jié)束");
? ? ? ? if (!(result instanceof ModelAndView)) {
? ? ? ? ? ? String msgText = JSONObject.toJSONString(result);
? ? ? ? ? ? logger.info("RESPONSE : " + msgText);
? ? ? ? }
? ? ? ? logger.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get()));
? ? }
? ? private String getRemoteIp(HttpServletRequest request) {
? ? ? ? String remoteIp = null;
? ? ? ? String remoteAddr = request.getRemoteAddr();
? ? ? ? String forwarded = request.getHeader("X-Forwarded-For");
? ? ? ? String realIp = request.getHeader("X-Real-IP");
? ? ? ? if (realIp == null) {
? ? ? ? ? ? if (forwarded == null) {
? ? ? ? ? ? ? ? remoteIp = remoteAddr;
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? remoteIp = remoteAddr + "/" + forwarded.split(",")[0];
? ? ? ? ? ? }
? ? ? ? } else {
? ? ? ? ? ? if (realIp.equals(forwarded)) {
? ? ? ? ? ? ? ? remoteIp = realIp;
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? if (forwarded != null) {
? ? ? ? ? ? ? ? ? ? forwarded = forwarded.split(",")[0];
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? remoteIp = realIp + "/" + forwarded;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return remoteIp;
? ? }
? ? private String getTargetClassName(MethodInvocation invocation) {
? ? ? ? String targetClassName = "";
? ? ? ? try {
? ? ? ? ? ? targetClassName = AopTargetUtils.getTarget(invocation.getThis()).getClass().getName();
? ? ? ? } catch (Exception e) {
? ? ? ? ? ? targetClassName = invocation.getThis().getClass().getName();
? ? ? ? }
? ? ? ? return targetClassName;
? ? }
}
AopTargetUtils:
public class AopTargetUtils { ?
??
? ? ??
? ? /**?
? ? ?* 獲取 目標(biāo)對象?
? ? ?* @param proxy 代理對象?
? ? ?* @return ?
? ? ?* @throws Exception?
? ? ?*/ ?
? ? public static Object getTarget(Object proxy) throws Exception { ?
? ? ? ? ??
? ? ? ? if(!AopUtils.isAopProxy(proxy)) {
? ? ? ? ? ? return proxy;//不是代理對象 ?
? ? ? ? } ?
? ? ? ? ??
? ? ? ? if(AopUtils.isJdkDynamicProxy(proxy)) {
? ? ? ? ? ? return getJdkDynamicProxyTargetObject(proxy); ?
? ? ? ? } else { //cglib ?
? ? ? ? ? ? return getCglibProxyTargetObject(proxy); ?
? ? ? ? } ?
? ? ? ? ??
? ? ? ? ??
? ? ? ? ??
? ? } ?
??
??
? ? private static Object getCglibProxyTargetObject(Object proxy) throws Exception { ?
? ? ? ? Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0"); ?
? ? ? ? h.setAccessible(true);
? ? ? ? Object dynamicAdvisedInterceptor = h.get(proxy); ?
? ? ? ? ??
? ? ? ? Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised"); ?
? ? ? ? advised.setAccessible(true); ?
? ? ? ? ??
? ? ? ? Object target = ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
? ? ? ? ??
? ? ? ? return getTarget(target);
? ? } ?
??
??
? ? private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception { ?
? ? ? ? Field h = proxy.getClass().getSuperclass().getDeclaredField("h"); ?
? ? ? ? h.setAccessible(true); ?
? ? ? ? AopProxy aopProxy = (AopProxy) h.get(proxy);
? ? ? ? ??
? ? ? ? Field advised = aopProxy.getClass().getDeclaredField("advised"); ?
? ? ? ? advised.setAccessible(true); ?
? ? ? ? ??
? ? ? ? Object target = ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget();
? ? ? ? ??
? ? ? ? return getTarget(target);?
? ? } ?
? ? ??
}
3.4 實(shí)現(xiàn)BeanPostProcessor接口
作用:篩選出需要生成代理的類,并生成代理類,返回給Spring容器管理。
public class InteractRecordBeanPostProcessor implements BeanPostProcessor {
? ? private static Logger logger = LoggerFactory.getLogger(InteractRecordBeanPostProcessor.class);
? ? @Autowired
? ? private InteractRecordFactoryPostProcessor interactRecordFactoryPostProcessor;
? ? @Autowired
? ? private ControllerMethodInterceptor controllerMethodInterceptor;
? ? private String BASE_PACKAGES[];//需要攔截的包
? ? private String EXCLUDING[];// 過濾的包
? ? //一層目錄匹配
? ? private static final String ONE_REGEX = "[a-zA-Z0-9_]+";
? ? //多層目錄匹配
? ? private static final String ALL_REGEX = ".*";
? ? private static final String END_ALL_REGEX = "*";
? ? @PostConstruct
? ? public void init() {
? ? ? ? EnableInteractRecord ir = interactRecordFactoryPostProcessor.getEnableInteractRecord();
? ? ? ? BASE_PACKAGES = ir.basePackages();
? ? ? ? EXCLUDING = ir.exclusions();
? ? }
? ? @Override
? ? public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
? ? ? ? try {
? ? ? ? ? ? if (interactRecordFactoryPostProcessor.getEnableInteractRecord() != null) {
? ? ? ? ? ? ? ? // 根據(jù)注解配置的包名記錄對應(yīng)的controller層
? ? ? ? ? ? ? ? if (BASE_PACKAGES != null && BASE_PACKAGES.length > 0) {
? ? ? ? ? ? ? ? ? ? Object proxyObj = doEnhanceForController(bean);
? ? ? ? ? ? ? ? ? ? if (proxyObj != null) {
? ? ? ? ? ? ? ? ? ? ? ? return proxyObj;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? } catch (Exception e) {
? ? ? ? ? ? logger.error("postProcessAfterInitialization() Exception ", e);
? ? ? ? }
? ? ? ? return bean;
? ? }
? ? @Override
? ? public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
? ? ? ? return bean;
? ? }
? ? private Object doEnhanceForController(Object bean) {
? ? ? ? String beanPackageName = getBeanPackageName(bean);
? ? ? ? if (StringUtils.isNotBlank(beanPackageName)) {
? ? ? ? ? ? for (String basePackage : BASE_PACKAGES) {
? ? ? ? ? ? ? ? if (matchingPackage(basePackage, beanPackageName)) {
? ? ? ? ? ? ? ? ? ? if (EXCLUDING != null && EXCLUDING.length > 0) {
? ? ? ? ? ? ? ? ? ? ? ? for (String excluding : EXCLUDING) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? if (matchingPackage(excluding, beanPackageName)) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return bean;
? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? Object target = null;
? ? ? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? ? ? target = AopTargetUtils.getTarget(bean);
? ? ? ? ? ? ? ? ? ? } catch (Exception e) {
? ? ? ? ? ? ? ? ? ? ? ? logger.error("AopTargetUtils.getTarget() exception", e);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? if (target != null) {
? ? ? ? ? ? ? ? ? ? ? ? boolean isController = target.getClass().isAnnotationPresent(Controller.class);
? ? ? ? ? ? ? ? ? ? ? ? boolean isRestController = target.getClass().isAnnotationPresent(RestController.class);
? ? ? ? ? ? ? ? ? ? ? ? if (isController || isRestController) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ProxyFactory proxy = new ProxyFactory();
? ? ? ? ? ? ? ? ? ? ? ? ? ? proxy.setTarget(bean);
? ? ? ? ? ? ? ? ? ? ? ? ? ? proxy.addAdvice(controllerMethodInterceptor);
? ? ? ? ? ? ? ? ? ? ? ? ? ? return proxy.getProxy();
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return null;
? ? }
? ? private static boolean matchingPackage(String basePackage, String currentPackage) {
? ? ? ? if (StringUtils.isEmpty(basePackage) || StringUtils.isEmpty(currentPackage)) {
? ? ? ? ? ? return false;
? ? ? ? }
? ? ? ? if (basePackage.indexOf("*") != -1) {
? ? ? ? ? ? String patterns[] = StringUtils.split(basePackage, ".");
? ? ? ? ? ? for (int i = 0; i < patterns.length; i++) {
? ? ? ? ? ? ? ? String patternNode = patterns[i];
? ? ? ? ? ? ? ? if (patternNode.equals("*")) {
? ? ? ? ? ? ? ? ? ? patterns[i] = ONE_REGEX;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (patternNode.equals("**")) {
? ? ? ? ? ? ? ? ? ? if (i == patterns.length - 1) {
? ? ? ? ? ? ? ? ? ? ? ? patterns[i] = END_ALL_REGEX;
? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? patterns[i] = ALL_REGEX;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? String basePackageRegex = StringUtils.join(patterns, "\\.");
? ? ? ? ? ? Pattern r = Pattern.compile(basePackageRegex);
? ? ? ? ? ? Matcher m = r.matcher(currentPackage);
? ? ? ? ? ? return m.find();
? ? ? ? } else {
? ? ? ? ? ? return basePackage.equals(currentPackage);
? ? ? ? }
? ? }
? ? private String getBeanPackageName(Object bean) {
? ? ? ? String beanPackageName = "";
? ? ? ? if (bean != null) {
? ? ? ? ? ? Class<?> beanClass = bean.getClass();
? ? ? ? ? ? if (beanClass != null) {
? ? ? ? ? ? ? ? Package beanPackage = beanClass.getPackage();
? ? ? ? ? ? ? ? if (beanPackage != null) {
? ? ? ? ? ? ? ? ? ? beanPackageName = beanPackage.getName();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return beanPackageName;
? ? }
}
3.5 啟動類配置注解
@EnableInteractRecord(basePackages = “com.test.test.controller”,exclusions = “com.test.demo.controller”)
以上即可實(shí)現(xiàn)入?yún)ⅰ⒊鰠⑷罩窘y(tǒng)一打印,并且可以將特定的controller集中管理,并不進(jìn)行日志的打印(及不進(jìn)生成代理類)。
4.出現(xiàn)的問題(及其解決辦法)
實(shí)際開發(fā)中,特定不需要打印日志的接口,無法統(tǒng)一到一個包下。大部分需要打印的接口,和不需要打印的接口,大概率會參雜在同一個controller中,根據(jù)以上設(shè)計思路,無法進(jìn)行區(qū)分。
解決辦法:
自定義排除入?yún)⒋蛴∽⒔?/p>
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExcludeReqLog {
}
自定義排除出參打印注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExcludeRespLog {
}
增加邏輯
// 1.在解析requestParam之前進(jìn)行判斷
?? ??? ?Method method = invocation.getMethod();
? ? ? ? Annotation[] declaredAnnotations = method.getDeclaredAnnotations();
? ? ? ? boolean flag = true;
? ? ? ? for (Annotation annotation : declaredAnnotations) {
? ? ? ? ? ? if (annotation instanceof ExcludeReqLog) {
? ? ? ? ? ? ? ? flag = false;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? if (!flag) {
? ? ? ? ? ? logger.info("該方法已排除,不打印入?yún)?);
? ? ? ? ? ? return;
? ? ? ? }
// 2.在解析requestResp之前進(jìn)行判斷
?? ??? ?Method method = invocation.getMethod();
? ? ? ? Annotation[] declaredAnnotations = method.getDeclaredAnnotations();
? ? ? ? boolean flag = true;
? ? ? ? for (Annotation annotation : declaredAnnotations) {
? ? ? ? ? ? if (annotation instanceof ExcludeRespLog) {
? ? ? ? ? ? ? ? flag = false;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? if (!flag) {
? ? ? ? ? ? logger.info("該方法已排除,不打印出參");
? ? ? ? ? ? return;
? ? ? ? }
使用方法
// 1.不打印入?yún)?
? ? @PostMapping("/uploadImg")
? ? @ExcludeReqLog
? ? public Result<List<Demo>> uploadIdeaImg(@RequestParam(value = "imgFile", required = false) MultipartFile[] imgFile) {
? ? ? ? return demoService.uploadIdeaImg(imgFile);
? ? }
//2.不打印出參
? ? @PostMapping("/uploadImg")
? ? @ExcludeRespLog?
? ? public Result<List<Demo>> uploadIdeaImg(@RequestParam(value = "imgFile", required = false) MultipartFile[] imgFile) {
? ? ? ? return demoService.uploadIdeaImg(imgFile);
? ? }
問題解決
5.總結(jié)
以上即可兼容包排除和注解排除兩種方式,進(jìn)行入?yún)?、出參統(tǒng)一打印的控制。除此之外,還可以根據(jù)需求,進(jìn)行其他增強(qiáng)。
這些僅為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
-
服務(wù)性能優(yōu)化之mybatis-plus開啟與關(guān)閉SQL日志打印方法
這篇文章主要介紹了在Mybatis-plus中開啟和關(guān)閉控制臺SQL日志打印,在`application.properties`文件中,可以通過配置來實(shí)現(xiàn)SQL日志的開啟和關(guān)閉,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下 2024-12-12
-
解決@Transaction注解導(dǎo)致動態(tài)切換更改數(shù)據(jù)庫失效問題
這篇文章主要介紹了解決@Transaction注解導(dǎo)致動態(tài)切換更改數(shù)據(jù)庫失效問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教 2021-09-09
-
SpringCloud之監(jiān)控數(shù)據(jù)聚合Turbine的實(shí)現(xiàn)
這篇文章主要介紹了SpringCloud之監(jiān)控數(shù)據(jù)聚合Turbine的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧 2019-08-08
-
Mybatis調(diào)用MySQL存儲過程的簡單實(shí)現(xiàn)
本篇文章主要介紹了Mybatis調(diào)用MySQL存儲過程的簡單實(shí)現(xiàn),具有一定的參考價值,感興趣的小伙伴們可以參考一下。
2017-04-04
-
Java過濾器與監(jiān)聽器間區(qū)別與聯(lián)系
監(jiān)聽器是一個接口內(nèi)容由我們實(shí)現(xiàn),會在特定時間被調(diào)用,監(jiān)聽器用于監(jiān)聽web應(yīng)用中三大域?qū)ο?request,session,application),信息的創(chuàng)建,銷毀,增加,修改,刪除等動作的發(fā)生,然后做出相應(yīng)的響應(yīng)處理 2023-01-01
-
JAVA系統(tǒng)中Spring?Boot應(yīng)用程序的配置文件application.yml使用詳解
這篇文章主要介紹了JAVA系統(tǒng)中Spring?Boot應(yīng)用程序的配置文件application.yml的相關(guān)資料,application.yml是Spring?Boot應(yīng)用程序的配置文件,定義了服務(wù)器、Spring、日志、安全及其他配置屬性,確保應(yīng)用程序正確啟動和運(yùn)行,需要的朋友可以參考下 2025-01-01
-
Java 前臺加后臺精品圖書管理系統(tǒng)的實(shí)現(xiàn)
相信每一個學(xué)生學(xué)編程的時候,應(yīng)該都會寫一個小項目——圖書管理系統(tǒng)。為什么這么說呢?我認(rèn)為一個學(xué)校的氛圍很大一部分可以從圖書館的氛圍看出來,而圖書管理系統(tǒng)這個不大不小的項目,接觸的多,也比較熟悉,不會有陌生感,能夠練手,又有些難度,所以我的小項目也來了 2021-11-11
最新評論
1.背景
SpringBoot項目中,之前都是在controller方法的第一行手動打印 log,return之前再打印返回值。有多個返回點(diǎn)時,就需要出現(xiàn)多少重復(fù)代碼,過多的非業(yè)務(wù)代碼顯得十分凌亂。
本文將采用AOP 配置自定義注解實(shí)現(xiàn) 入?yún)?、出參的日志打印(方法的入?yún)⒑头祷刂刀疾捎?fastjson 序列化)。
2.設(shè)計思路
將特定包下所有的controller生成代理類對象,并交由Spring容器管理,并重寫invoke方法進(jìn)行增強(qiáng)(入?yún)?、出參的打?.
3.核心代碼
3.1 自定義注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({InteractRecordBeanPostProcessor.class})
public @interface EnableInteractRecord {
? ? /**
? ? ?* app對應(yīng)controller包名
? ? ?*/
? ? String[] basePackages() default {};
? ? /**
? ? ?* 排除某些包
? ? ?*/
? ? String[] exclusions() default {};
}3.2 實(shí)現(xiàn)BeanFactoryPostProcessor接口
作用:獲取EnableInteractRecord注解對象,用于獲取需要創(chuàng)建代理對象的包名,以及需要排除的包名
@Component
public class InteractRecordFactoryPostProcessor implements BeanFactoryPostProcessor {
? ? private static Logger logger = LoggerFactory.getLogger(InteractRecordFactoryPostProcessor.class);
? ? private EnableInteractRecord enableInteractRecord;
? ? @Override
? ? public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
? ? ? ? try {
? ? ? ? ? ? String[] names = beanFactory.getBeanNamesForAnnotation(EnableInteractRecord.class);
? ? ? ? ? ? for (String name : names) {
? ? ? ? ? ? ? ? enableInteractRecord = beanFactory.findAnnotationOnBean(name, EnableInteractRecord.class);
? ? ? ? ? ? ? ? logger.info("開啟交互記錄 ", enableInteractRecord);
? ? ? ? ? ? }
? ? ? ? } catch (Exception e) {
? ? ? ? ? ? logger.error("postProcessBeanFactory() Exception ", e);
? ? ? ? }
? ? }
? ? public EnableInteractRecord getEnableInteractRecord() {
? ? ? ? return enableInteractRecord;
? ? }
}3.3 實(shí)現(xiàn)MethodInterceptor編寫打印日志邏輯
作用:進(jìn)行入?yún)?、出參打印,包含是否打印邏?/p>
@Component
public class ControllerMethodInterceptor implements MethodInterceptor {
? ? private static Logger logger = LoggerFactory.getLogger(ControllerMethodInterceptor.class);
? ? // 請求開始時間
? ? ThreadLocal<Long> startTime = new ThreadLocal<>();
? ? private String localIp = "";
? ? @PostConstruct
? ? public void init() {
? ? ? ? try {
? ? ? ? ? ? localIp = InetAddress.getLocalHost().getHostAddress();
? ? ? ? } catch (UnknownHostException e) {
? ? ? ? ? ? logger.error("本地IP初始化失敗 : ", e);
? ? ? ? }
? ? }
? ? @Override
? ? public Object invoke(MethodInvocation invocation) {
? ? ? ? pre(invocation);
? ? ? ? Object result;
? ? ? ? try {
? ? ? ? ? ? result = invocation.proceed();
? ? ? ? ? ? post(invocation, result);
? ? ? ? ? ? return result;
? ? ? ? } catch (Throwable ex) {
? ? ? ? ? ? logger.error("controller 執(zhí)行異常: ", ex);
? ? ? ? ? ? error(invocation, ex);
? ? ? ? }
? ? ? ? return null;
? ? }
? ? public void error(MethodInvocation invocation, Throwable ex) {
? ? ? ? String msgText = ex.getMessage();
? ? ? ? logger.info(startTime.get() + " 異常,請求結(jié)束");
? ? ? ? logger.info("RESPONSE : " + msgText);
? ? ? ? logger.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get()));
? ? }
? ? private void pre(MethodInvocation invocation) {
? ? ? ? long now = System.currentTimeMillis();
? ? ? ? startTime.set(now);
? ? ? ? logger.info(now + " 請求開始");
? ? ? ? ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
? ? ? ? HttpServletRequest request = attributes.getRequest();
? ? ? ? logger.info("URL : " + request.getRequestURL().toString());
? ? ? ? logger.info("HTTP_METHOD : " + request.getMethod());
? ? ? ? logger.info("REMOTE_IP : " + getRemoteIp(request));
? ? ? ? logger.info("LOCAL_IP : " + localIp);
? ? ? ? logger.info("METHOD : " + request.getMethod());
? ? ? ? logger.info("CLASS_METHOD : " + getTargetClassName(invocation) + "." + invocation.getMethod().getName());
? ? ? ? // 獲取請求頭header參數(shù)
? ? ? ? Map<String, String> map = new HashMap<String, String>();
? ? ? ? Enumeration<String> headerNames = request.getHeaderNames();
? ? ? ? while (headerNames.hasMoreElements()) {
? ? ? ? ? ? String key = (String) headerNames.nextElement();
? ? ? ? ? ? String value = request.getHeader(key);
? ? ? ? ? ? map.put(key, value);
? ? ? ? }
? ? ? ? logger.info("HEADERS : " + JSONObject.toJSONString(map));
? ? ? ? Date createTime = new Date(now);
? ? ? ? // 請求報文
? ? ? ? Object[] args = invocation.getArguments();// 參數(shù)
? ? ? ? String msgText = "";
? ? ? ? Annotation[][] annotationss = invocation.getMethod().getParameterAnnotations();
? ? ? ? for (int i = 0; i < args.length; i++) {
? ? ? ? ? ? Object arg = args[i];
? ? ? ? ? ? if (!(arg instanceof ServletRequest)
? ? ? ? ? ? ? ? ? ? && !(arg instanceof ServletResponse)
? ? ? ? ? ? ? ? ? ? && !(arg instanceof Model)) {
? ? ? ? ? ? ? ? RequestParam rp = null;
? ? ? ? ? ? ? ? Annotation[] annotations = annotationss[i];
? ? ? ? ? ? ? ? for (Annotation annotation : annotations) {
? ? ? ? ? ? ? ? ? ? if (annotation instanceof RequestParam) {
? ? ? ? ? ? ? ? ? ? ? ? rp = (RequestParam) annotation;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (msgText.equals("")) {
? ? ? ? ? ? ? ? ? ? msgText += (rp != null ? rp.value() + " = " : " ") + JSONObject.toJSONString(arg);
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? msgText += "," + (rp != null ? rp.value() + " = " : " ") + JSONObject.toJSONString(arg);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? logger.info("PARAMS : " + msgText);
? ? }
? ? private void post(MethodInvocation invocation, Object result) {
? ? ? ? logger.info(startTime.get() + " 請求結(jié)束");
? ? ? ? if (!(result instanceof ModelAndView)) {
? ? ? ? ? ? String msgText = JSONObject.toJSONString(result);
? ? ? ? ? ? logger.info("RESPONSE : " + msgText);
? ? ? ? }
? ? ? ? logger.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get()));
? ? }
? ? private String getRemoteIp(HttpServletRequest request) {
? ? ? ? String remoteIp = null;
? ? ? ? String remoteAddr = request.getRemoteAddr();
? ? ? ? String forwarded = request.getHeader("X-Forwarded-For");
? ? ? ? String realIp = request.getHeader("X-Real-IP");
? ? ? ? if (realIp == null) {
? ? ? ? ? ? if (forwarded == null) {
? ? ? ? ? ? ? ? remoteIp = remoteAddr;
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? remoteIp = remoteAddr + "/" + forwarded.split(",")[0];
? ? ? ? ? ? }
? ? ? ? } else {
? ? ? ? ? ? if (realIp.equals(forwarded)) {
? ? ? ? ? ? ? ? remoteIp = realIp;
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? if (forwarded != null) {
? ? ? ? ? ? ? ? ? ? forwarded = forwarded.split(",")[0];
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? remoteIp = realIp + "/" + forwarded;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return remoteIp;
? ? }
? ? private String getTargetClassName(MethodInvocation invocation) {
? ? ? ? String targetClassName = "";
? ? ? ? try {
? ? ? ? ? ? targetClassName = AopTargetUtils.getTarget(invocation.getThis()).getClass().getName();
? ? ? ? } catch (Exception e) {
? ? ? ? ? ? targetClassName = invocation.getThis().getClass().getName();
? ? ? ? }
? ? ? ? return targetClassName;
? ? }
}AopTargetUtils:
public class AopTargetUtils { ?
??
? ? ??
? ? /**?
? ? ?* 獲取 目標(biāo)對象?
? ? ?* @param proxy 代理對象?
? ? ?* @return ?
? ? ?* @throws Exception?
? ? ?*/ ?
? ? public static Object getTarget(Object proxy) throws Exception { ?
? ? ? ? ??
? ? ? ? if(!AopUtils.isAopProxy(proxy)) {
? ? ? ? ? ? return proxy;//不是代理對象 ?
? ? ? ? } ?
? ? ? ? ??
? ? ? ? if(AopUtils.isJdkDynamicProxy(proxy)) {
? ? ? ? ? ? return getJdkDynamicProxyTargetObject(proxy); ?
? ? ? ? } else { //cglib ?
? ? ? ? ? ? return getCglibProxyTargetObject(proxy); ?
? ? ? ? } ?
? ? ? ? ??
? ? ? ? ??
? ? ? ? ??
? ? } ?
??
??
? ? private static Object getCglibProxyTargetObject(Object proxy) throws Exception { ?
? ? ? ? Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0"); ?
? ? ? ? h.setAccessible(true);
? ? ? ? Object dynamicAdvisedInterceptor = h.get(proxy); ?
? ? ? ? ??
? ? ? ? Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised"); ?
? ? ? ? advised.setAccessible(true); ?
? ? ? ? ??
? ? ? ? Object target = ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
? ? ? ? ??
? ? ? ? return getTarget(target);
? ? } ?
??
??
? ? private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception { ?
? ? ? ? Field h = proxy.getClass().getSuperclass().getDeclaredField("h"); ?
? ? ? ? h.setAccessible(true); ?
? ? ? ? AopProxy aopProxy = (AopProxy) h.get(proxy);
? ? ? ? ??
? ? ? ? Field advised = aopProxy.getClass().getDeclaredField("advised"); ?
? ? ? ? advised.setAccessible(true); ?
? ? ? ? ??
? ? ? ? Object target = ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget();
? ? ? ? ??
? ? ? ? return getTarget(target);?
? ? } ?
? ? ??
}3.4 實(shí)現(xiàn)BeanPostProcessor接口
作用:篩選出需要生成代理的類,并生成代理類,返回給Spring容器管理。
public class InteractRecordBeanPostProcessor implements BeanPostProcessor {
? ? private static Logger logger = LoggerFactory.getLogger(InteractRecordBeanPostProcessor.class);
? ? @Autowired
? ? private InteractRecordFactoryPostProcessor interactRecordFactoryPostProcessor;
? ? @Autowired
? ? private ControllerMethodInterceptor controllerMethodInterceptor;
? ? private String BASE_PACKAGES[];//需要攔截的包
? ? private String EXCLUDING[];// 過濾的包
? ? //一層目錄匹配
? ? private static final String ONE_REGEX = "[a-zA-Z0-9_]+";
? ? //多層目錄匹配
? ? private static final String ALL_REGEX = ".*";
? ? private static final String END_ALL_REGEX = "*";
? ? @PostConstruct
? ? public void init() {
? ? ? ? EnableInteractRecord ir = interactRecordFactoryPostProcessor.getEnableInteractRecord();
? ? ? ? BASE_PACKAGES = ir.basePackages();
? ? ? ? EXCLUDING = ir.exclusions();
? ? }
? ? @Override
? ? public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
? ? ? ? try {
? ? ? ? ? ? if (interactRecordFactoryPostProcessor.getEnableInteractRecord() != null) {
? ? ? ? ? ? ? ? // 根據(jù)注解配置的包名記錄對應(yīng)的controller層
? ? ? ? ? ? ? ? if (BASE_PACKAGES != null && BASE_PACKAGES.length > 0) {
? ? ? ? ? ? ? ? ? ? Object proxyObj = doEnhanceForController(bean);
? ? ? ? ? ? ? ? ? ? if (proxyObj != null) {
? ? ? ? ? ? ? ? ? ? ? ? return proxyObj;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? } catch (Exception e) {
? ? ? ? ? ? logger.error("postProcessAfterInitialization() Exception ", e);
? ? ? ? }
? ? ? ? return bean;
? ? }
? ? @Override
? ? public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
? ? ? ? return bean;
? ? }
? ? private Object doEnhanceForController(Object bean) {
? ? ? ? String beanPackageName = getBeanPackageName(bean);
? ? ? ? if (StringUtils.isNotBlank(beanPackageName)) {
? ? ? ? ? ? for (String basePackage : BASE_PACKAGES) {
? ? ? ? ? ? ? ? if (matchingPackage(basePackage, beanPackageName)) {
? ? ? ? ? ? ? ? ? ? if (EXCLUDING != null && EXCLUDING.length > 0) {
? ? ? ? ? ? ? ? ? ? ? ? for (String excluding : EXCLUDING) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? if (matchingPackage(excluding, beanPackageName)) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return bean;
? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? Object target = null;
? ? ? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? ? ? target = AopTargetUtils.getTarget(bean);
? ? ? ? ? ? ? ? ? ? } catch (Exception e) {
? ? ? ? ? ? ? ? ? ? ? ? logger.error("AopTargetUtils.getTarget() exception", e);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? if (target != null) {
? ? ? ? ? ? ? ? ? ? ? ? boolean isController = target.getClass().isAnnotationPresent(Controller.class);
? ? ? ? ? ? ? ? ? ? ? ? boolean isRestController = target.getClass().isAnnotationPresent(RestController.class);
? ? ? ? ? ? ? ? ? ? ? ? if (isController || isRestController) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ProxyFactory proxy = new ProxyFactory();
? ? ? ? ? ? ? ? ? ? ? ? ? ? proxy.setTarget(bean);
? ? ? ? ? ? ? ? ? ? ? ? ? ? proxy.addAdvice(controllerMethodInterceptor);
? ? ? ? ? ? ? ? ? ? ? ? ? ? return proxy.getProxy();
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return null;
? ? }
? ? private static boolean matchingPackage(String basePackage, String currentPackage) {
? ? ? ? if (StringUtils.isEmpty(basePackage) || StringUtils.isEmpty(currentPackage)) {
? ? ? ? ? ? return false;
? ? ? ? }
? ? ? ? if (basePackage.indexOf("*") != -1) {
? ? ? ? ? ? String patterns[] = StringUtils.split(basePackage, ".");
? ? ? ? ? ? for (int i = 0; i < patterns.length; i++) {
? ? ? ? ? ? ? ? String patternNode = patterns[i];
? ? ? ? ? ? ? ? if (patternNode.equals("*")) {
? ? ? ? ? ? ? ? ? ? patterns[i] = ONE_REGEX;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (patternNode.equals("**")) {
? ? ? ? ? ? ? ? ? ? if (i == patterns.length - 1) {
? ? ? ? ? ? ? ? ? ? ? ? patterns[i] = END_ALL_REGEX;
? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? patterns[i] = ALL_REGEX;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? String basePackageRegex = StringUtils.join(patterns, "\\.");
? ? ? ? ? ? Pattern r = Pattern.compile(basePackageRegex);
? ? ? ? ? ? Matcher m = r.matcher(currentPackage);
? ? ? ? ? ? return m.find();
? ? ? ? } else {
? ? ? ? ? ? return basePackage.equals(currentPackage);
? ? ? ? }
? ? }
? ? private String getBeanPackageName(Object bean) {
? ? ? ? String beanPackageName = "";
? ? ? ? if (bean != null) {
? ? ? ? ? ? Class<?> beanClass = bean.getClass();
? ? ? ? ? ? if (beanClass != null) {
? ? ? ? ? ? ? ? Package beanPackage = beanClass.getPackage();
? ? ? ? ? ? ? ? if (beanPackage != null) {
? ? ? ? ? ? ? ? ? ? beanPackageName = beanPackage.getName();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return beanPackageName;
? ? }
}3.5 啟動類配置注解
@EnableInteractRecord(basePackages = “com.test.test.controller”,exclusions = “com.test.demo.controller”)
以上即可實(shí)現(xiàn)入?yún)ⅰ⒊鰠⑷罩窘y(tǒng)一打印,并且可以將特定的controller集中管理,并不進(jìn)行日志的打印(及不進(jìn)生成代理類)。
4.出現(xiàn)的問題(及其解決辦法)
實(shí)際開發(fā)中,特定不需要打印日志的接口,無法統(tǒng)一到一個包下。大部分需要打印的接口,和不需要打印的接口,大概率會參雜在同一個controller中,根據(jù)以上設(shè)計思路,無法進(jìn)行區(qū)分。
解決辦法:
自定義排除入?yún)⒋蛴∽⒔?/p>
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExcludeReqLog {
}自定義排除出參打印注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExcludeRespLog {
}增加邏輯
// 1.在解析requestParam之前進(jìn)行判斷
?? ??? ?Method method = invocation.getMethod();
? ? ? ? Annotation[] declaredAnnotations = method.getDeclaredAnnotations();
? ? ? ? boolean flag = true;
? ? ? ? for (Annotation annotation : declaredAnnotations) {
? ? ? ? ? ? if (annotation instanceof ExcludeReqLog) {
? ? ? ? ? ? ? ? flag = false;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? if (!flag) {
? ? ? ? ? ? logger.info("該方法已排除,不打印入?yún)?);
? ? ? ? ? ? return;
? ? ? ? }
// 2.在解析requestResp之前進(jìn)行判斷
?? ??? ?Method method = invocation.getMethod();
? ? ? ? Annotation[] declaredAnnotations = method.getDeclaredAnnotations();
? ? ? ? boolean flag = true;
? ? ? ? for (Annotation annotation : declaredAnnotations) {
? ? ? ? ? ? if (annotation instanceof ExcludeRespLog) {
? ? ? ? ? ? ? ? flag = false;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? if (!flag) {
? ? ? ? ? ? logger.info("該方法已排除,不打印出參");
? ? ? ? ? ? return;
? ? ? ? }使用方法
// 1.不打印入?yún)?
? ? @PostMapping("/uploadImg")
? ? @ExcludeReqLog
? ? public Result<List<Demo>> uploadIdeaImg(@RequestParam(value = "imgFile", required = false) MultipartFile[] imgFile) {
? ? ? ? return demoService.uploadIdeaImg(imgFile);
? ? }
//2.不打印出參
? ? @PostMapping("/uploadImg")
? ? @ExcludeRespLog?
? ? public Result<List<Demo>> uploadIdeaImg(@RequestParam(value = "imgFile", required = false) MultipartFile[] imgFile) {
? ? ? ? return demoService.uploadIdeaImg(imgFile);
? ? }問題解決
5.總結(jié)
以上即可兼容包排除和注解排除兩種方式,進(jìn)行入?yún)?、出參統(tǒng)一打印的控制。除此之外,還可以根據(jù)需求,進(jìn)行其他增強(qiáng)。
這些僅為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
服務(wù)性能優(yōu)化之mybatis-plus開啟與關(guān)閉SQL日志打印方法
這篇文章主要介紹了在Mybatis-plus中開啟和關(guān)閉控制臺SQL日志打印,在`application.properties`文件中,可以通過配置來實(shí)現(xiàn)SQL日志的開啟和關(guān)閉,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-12-12
解決@Transaction注解導(dǎo)致動態(tài)切換更改數(shù)據(jù)庫失效問題
這篇文章主要介紹了解決@Transaction注解導(dǎo)致動態(tài)切換更改數(shù)據(jù)庫失效問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
SpringCloud之監(jiān)控數(shù)據(jù)聚合Turbine的實(shí)現(xiàn)
這篇文章主要介紹了SpringCloud之監(jiān)控數(shù)據(jù)聚合Turbine的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
Mybatis調(diào)用MySQL存儲過程的簡單實(shí)現(xiàn)
本篇文章主要介紹了Mybatis調(diào)用MySQL存儲過程的簡單實(shí)現(xiàn),具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-04-04
Java過濾器與監(jiān)聽器間區(qū)別與聯(lián)系
監(jiān)聽器是一個接口內(nèi)容由我們實(shí)現(xiàn),會在特定時間被調(diào)用,監(jiān)聽器用于監(jiān)聽web應(yīng)用中三大域?qū)ο?request,session,application),信息的創(chuàng)建,銷毀,增加,修改,刪除等動作的發(fā)生,然后做出相應(yīng)的響應(yīng)處理2023-01-01
JAVA系統(tǒng)中Spring?Boot應(yīng)用程序的配置文件application.yml使用詳解
這篇文章主要介紹了JAVA系統(tǒng)中Spring?Boot應(yīng)用程序的配置文件application.yml的相關(guān)資料,application.yml是Spring?Boot應(yīng)用程序的配置文件,定義了服務(wù)器、Spring、日志、安全及其他配置屬性,確保應(yīng)用程序正確啟動和運(yùn)行,需要的朋友可以參考下2025-01-01
Java 前臺加后臺精品圖書管理系統(tǒng)的實(shí)現(xiàn)
相信每一個學(xué)生學(xué)編程的時候,應(yīng)該都會寫一個小項目——圖書管理系統(tǒng)。為什么這么說呢?我認(rèn)為一個學(xué)校的氛圍很大一部分可以從圖書館的氛圍看出來,而圖書管理系統(tǒng)這個不大不小的項目,接觸的多,也比較熟悉,不會有陌生感,能夠練手,又有些難度,所以我的小項目也來了2021-11-11

