一、說明
近期,心中萌發了做一個個人網站的想法,來一場說走就走的程式設計之旅。說做就做,在專案框架搭建(SpringMVC mybatis mysql)好了之後,開始考慮專案中日誌的設計。經過考慮並結合網路上的資料,決定採用註解的方式來記錄訪問日誌。當然,目前的日誌設計還不夠完美,後期會在開發的過程中逐漸完善。
二、實作
2.1 關於AOP及相關註解
相對於AOP,有很多人偏向使用攔截器來管理日誌,這點要看個人的想法了。那麼如何實現AOP攔截controller呢?由於預設的情況下,controller是交給jdk去代理的,因此,要想AOP能夠攔截到controller,必須將其指定給cglib代理。
下面介紹一下,使用AOP攔截controller用到的註解(標紅字段代表將會使用),當然,我們也可以使用配置文件的方式去定義,但是個人更喜歡將模組集中在一起,找檔案真的很累~
@Target:註解的作用目標,也就是註解會對哪些物件產生作用。包括:
ElementType.TYPE 、枚舉、註解
ElementType.FIELD 、列舉的常數
# ## ElementType.CONSTRUCTOR ElementType. LOCAL_VARIABLE 局部變數
註解 ElementType.PACKAGE # @Retention:註解的保留位置,用於描述註解的生命週期,通俗的講,@ Retention註解負責定義該註解在什麼範圍內或條件下才會去產生作用。 RetentionPolicy.SOURCE 節碼檔案不包含 RetentionPolicy.CLASS 名詞化 存在但在運作時無法取得 #RetentionPolicy.RUNTIME 可以透過反射取得到# @Document:說明此註解將包含在javadoc中
以上註解,上加為@Inherited、@Repeatabled#d # 註解,稱為java中的元註解。什麼是元註解? 元註解的功能是負責註解其他註解。 Java5.0定義了4個標準的meta-annotation類型,它們被用來提供對其它 annotation類型作說明。 關於註解及元註解的解釋,請點擊這裡 @Aspect:當@Aspect宣告與類別上時,表示這個類別將會作為一個切面,即切面類別。此時容器就可以讀到這個類,但前提是開啟了cglib代理。 @Component:將類別宣告為bean並注入到容器中,spring在啟動時會掃描並載入。與在設定檔中定義bean的效果相同。 @Pointcut:方法層級的註解,使用此註解後,可被其他方法引用。 與@Aspect、@Pointcut一起使用的還有5種通知型註解,也稱為增強註解: @After @AfterReturning 後置【try】通知,放在方法頭上,並使用returning引用方法傳回值 2.2 設定cglib代理<aop:aspectj-autoproxy proxy-target-class="true" />
2.3 自訂註解,用於描述日誌資訊
一般我們在建立自訂註解時,使用@interface會使此類預設繼承annotation。程式碼如下:package com.t1heluosh1.system.log.annotion; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 日志controller层注解 * * @author xuyong * */ //作用于参数和方法上 @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SysControllerLog { int logType() default 100; //日志类型,默认为100-系统 int module() default 100; //操作模块,默认为100-登录 String description() default ""; //操作描述 }自訂註解建立後,我們需要將註解作為bean注入到容器中,在spring-mvc.xml檔案中加入如下程式碼:
<context:component-scan base-package="com.t1heluosh1.system.log" > <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>#2.4定義切面類,實作記錄日誌的功能 這裡沒有什麼可以多說的,直接看程式碼:
package com.t1heluosh1.system.log.aspect; import java.lang.reflect.Method; import java.util.Date; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import com.t1heluosh1.pullulate.biz.sysLog.service.SysLogService; import com.t1heluosh1.pullulate.biz.sysLog.vo.SysLogVo; import com.t1heluosh1.pullulate.biz.user.model.User; import com.t1heluosh1.system.constant.SysParams; import com.t1heluosh1.system.log.annotion.SysControllerLog; import com.t1heluosh1.util.IPUtil; /** * 日志切点类即实现类 * * @author xuyong * */ @Aspect @Component public class SysLogAspect { //本地异常日志记录对象 private static final Logger logger = Logger.getLogger(SysLogAspect.class); @Resource private SysLogService logService; //切入点定义:controller @Pointcut("@annotation(com.t1heluosh1.system.log.annotion.SysControllerLog)") public void controllerAspect() { System.out.println("---------------------controllerAspect for log start----------------------------"); } /** * controller切入点方法实现 * * @param joinPoint */ @Before("controllerAspect()") public void doBefore(JoinPoint joinPoint) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder .getRequestAttributes()).getRequest(); //获取登录用户的信息 User user = (User)request.getSession().getAttribute(SysParams.CURRENT_USER); //获取请求IP地址 String ip = IPUtil.getRemoteHost(request); try { String methodDesc = getControllerMethodDescription(joinPoint); System.out.println("request method : " + joinPoint.getTarget().getClass().getName()+"."+joinPoint.getSignature().getName()+"()"); System.out.println("method description : " + methodDesc); System.out.println("request username : " + (user==null?"no login info found":user.getUserName())); System.out.println("request ip address : "+ ip); System.out.println("insert log infos into db start ..."); //获取相关日志参数 Object[] orgs = joinPoint.getArgs(); SysLogVo sysLogVo = null; if (orgs != null && orgs.length > 0) { for (Object obj:orgs) { if (obj instanceof SysLogVo) sysLogVo = (SysLogVo)obj; } } if (sysLogVo == null) { sysLogVo = new SysLogVo(); } //执行日志入库操作 //获取注解的信息 MethodSignature ms = (MethodSignature)joinPoint.getSignature(); Method method = ms.getMethod(); SysControllerLog log = method.getAnnotation(SysControllerLog.class); sysLogVo.setLogType(log.logType()); sysLogVo.setModule(log.module()); sysLogVo.setIpAddr(ip); sysLogVo.setUrl(request.getRequestURI()); sysLogVo.setMethodName(joinPoint.getTarget().getClass().getName()+"."+joinPoint.getSignature().getName()+"()"); sysLogVo.setMethodDesc(methodDesc); //TODO:remark可根据业务来进行改变,暂时为方法描述 sysLogVo.setRemark(log.description()); Date date = new Date(); sysLogVo.setAddTime(date); sysLogVo.setAddUser(user==null?SysParams.ADMIN_ID:String.valueOf(user.getId())); sysLogVo.setUpdateTime(date); sysLogVo.setUpdateUser(user==null?SysParams.ADMIN_ID:String.valueOf(user.getId())); logService.save(sysLogVo); System.out.println("insert log infos into db successful."); } catch (Exception e) { logger.error("--------------controllerAspect for log fail-----------------------"); logger.error("exception info : ", e); } } /** * 获取方法的描述 * * @param joinPoint * @return * @throws Exception */ @SuppressWarnings("rawtypes") private String getControllerMethodDescription(JoinPoint joinPoint) throws Exception { //获取目标类名 String targetName = joinPoint.getTarget().getClass().getName(); //获取方法名 String methodName = joinPoint.getSignature().getName(); //获取相关参数 Object[] arguments = joinPoint.getArgs(); //生成类对象 Class targetClass = Class.forName(targetName); //获取该类中的方法 Method[] methods = targetClass.getMethods(); String description = ""; for(Method method : methods) { if(!method.getName().equals(methodName)) { continue; } Class[] clazzs = method.getParameterTypes(); if(clazzs.length != arguments.length) { continue; } description = method.getAnnotation(SysControllerLog.class).description(); } return description; } }
2.5 使用demo
具體使用的方法如下:/** * 跳转到登陆页面 * * @param request * @return * @throws Exception */ @RequestMapping(value="login") @SysControllerLog(description="跳转到登录页面",logType=100,module=100) public ModelAndView gotoLogin(HttpServletRequest request) { ModelAndView modelAndView = new ModelAndView("/show/login"); return modelAndView; }當使用者重新整理頁面時,控制台會列印相關的存取資訊並將這些資訊入庫。當然,日誌的使用需要根據項目來決定,每個方法前都加入註解,一是影響系統的效能,且使得存取日誌的效果大打折扣;二是這種方式記錄日誌,對於程式碼還是有一定的侵入性的。最後來看看資料庫記錄的資訊:
以上是java開發實戰之springMVC使用AOP實作存取日誌的管理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

javaispopularforcross-platformdesktopapplicationsduetoits“ writeonce,runany where”哲學。 1)itusesbytiesebyTecodeThatrunsonAnyJvm-備用Platform.2)librarieslikeslikeslikeswingingandjavafxhelpcreatenative-lookingenative-lookinguisis.3)

在Java中編寫平台特定代碼的原因包括訪問特定操作系統功能、與特定硬件交互和優化性能。 1)使用JNA或JNI訪問Windows註冊表;2)通過JNI與Linux特定硬件驅動程序交互;3)通過JNI使用Metal優化macOS上的遊戲性能。儘管如此,編寫平台特定代碼會影響代碼的可移植性、增加複雜性、可能帶來性能開銷和安全風險。

Java將通過雲原生應用、多平台部署和跨語言互操作進一步提昇平台獨立性。 1)雲原生應用將使用GraalVM和Quarkus提升啟動速度。 2)Java將擴展到嵌入式設備、移動設備和量子計算機。 3)通過GraalVM,Java將與Python、JavaScript等語言無縫集成,增強跨語言互操作性。

Java的強類型系統通過類型安全、統一的類型轉換和多態性確保了平台獨立性。 1)類型安全在編譯時進行類型檢查,避免運行時錯誤;2)統一的類型轉換規則在所有平台上一致;3)多態性和接口機制使代碼在不同平台上行為一致。

JNI會破壞Java的平台獨立性。 1)JNI需要特定平台的本地庫,2)本地代碼需在目標平台編譯和鏈接,3)不同版本的操作系統或JVM可能需要不同的本地庫版本,4)本地代碼可能引入安全漏洞或導致程序崩潰。

新興技術對Java的平台獨立性既有威脅也有增強。 1)雲計算和容器化技術如Docker增強了Java的平台獨立性,但需要優化以適應不同雲環境。 2)WebAssembly通過GraalVM編譯Java代碼,擴展了其平台獨立性,但需與其他語言競爭性能。

不同JVM實現都能提供平台獨立性,但表現略有不同。 1.OracleHotSpot和OpenJDKJVM在平台獨立性上表現相似,但OpenJDK可能需額外配置。 2.IBMJ9JVM在特定操作系統上表現優化。 3.GraalVM支持多語言,需額外配置。 4.AzulZingJVM需特定平台調整。

平台獨立性通過在多種操作系統上運行同一套代碼,降低開發成本和縮短開發時間。具體表現為:1.減少開發時間,只需維護一套代碼;2.降低維護成本,統一測試流程;3.快速迭代和團隊協作,簡化部署過程。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

Dreamweaver Mac版
視覺化網頁開發工具

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

Dreamweaver CS6
視覺化網頁開發工具