AOP是什麼
AOP(Aspect Oriented Programming),也就是面向切面編程,是透過預編譯方式和運行期間動態代理實現程式功能的傳統已維護的一種技術。
AOP的作用和優勢
作用:在程式運行期間,在不修改原始程式碼的情況下對某些方法進行功能增強
優勢:減少重複程式碼,提高開發效率,並且便於維護
常見的動態代理技術
jdk代理:基於介面的動態代理技術
- List item- Target(目標物件):代理的目標物件
- Proxy(代理程式):一個類別被AOP織入增強後,就產生一個結果代理類別
- Joinpoint(連接點):連接點是指那些被攔截到的點。在Spring中,這些點指的是方法,因為Spring只支援方法類型的連接點(可以被增強的方法叫做連接點)
- PointCut(切入點):切入點是指我們要對哪些Joinpoint進行攔截的定義
- Advice(通知/增強):通知是指攔截到Joinpoint之後所要做的事情就是通知 ##Aspect(切面):是切入點和通知的組合
- #Weaving(織入):把增強應用到目標物件來建立新的代理物件的過程。 Spring採用動態代理織入,而AspectJ採用編譯器織入和類別裝載器織入
- #實作
我在這裡採用基於註解形式的的AOP開發。
開發步驟
加入依賴
<!--引入AOP依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!--AOP--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.4</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.12</version> </dependency>
建立目標介面和目標類別(內部有切點)
建立切面類別(內部有增強方法)
將目標類別和切面類別的物件建立權交給Spirng
在切面類別中使用註解配置織入關係
在設定檔中開啟元件掃描和AOP自動代理
因為我的專案是SpringBoot Web項目,在這裡開啟註解就好了。
下面的程式碼為使用到的原子計數類別。
import java.util.concurrent.atomic.AtomicInteger; public class AtomicCounter { private static final AtomicCounter atomicCounter = new AtomicCounter(); /** * 单例,不允许外界主动实例化 */ private AtomicCounter() { } public static AtomicCounter getInstance() { return atomicCounter; } private static AtomicInteger counter = new AtomicInteger(); public int getValue() { return counter.get(); } public int increase() { return counter.incrementAndGet(); } public int decrease() { return counter.decrementAndGet(); } // 清零 public void toZero(){ counter.set(0); } }
下面的程式碼為實作的全域介面監控。
我在專案中簡單使用了Redis作緩存,所有你可以看到有Redis相關的操作。
使用 @Before 用於設定前置通知。指定增強的方法在切入點方法之前執行。
使用@ @AfterReturning 用於設定後置通知。指定增強的方法在切入點方法之後執行。
使用@ @AfterThrowing 用於設定例外狀況拋出通知。指定增強的方法在出現異常時執行。
@Component @Aspect public class GlobalActuator { private static final Logger log = LoggerFactory.getLogger(GlobalActuator.class); @Resource private StringRedisTemplate stringRedisTemplate; ThreadLocal<Long> startTime = new ThreadLocal<>(); ConcurrentHashMap<Object, Object> countMap = new ConcurrentHashMap<Object, Object>(); /** * 匹配控制层层通知 这里监控controller下的所有接口 */ @Pointcut("execution(* com.sf.controller.*Controller.*(..))") private void controllerPt() { } /** * 在接口原有的方法执行前,将会首先执行此处的代码 */ @Before("com.sf.actuator.GlobalActuator.controllerPt()") public void doBefore(JoinPoint joinPoint) throws Throwable { startTime.set(System.currentTimeMillis()); //获取传入目标方法的参数 Object[] args = joinPoint.getArgs(); } /** * 只有正常返回才会执行此方法 * 如果程序执行失败,则不执行此方法 */ @AfterReturning(returning = "returnVal", pointcut = "com.sf.actuator.GlobalActuator.controllerPt()") public void doAfterReturning(JoinPoint joinPoint, Object returnVal) throws Throwable { Signature signature = joinPoint.getSignature(); String declaringName = signature.getDeclaringTypeName(); String methodName = signature.getName(); String mapKey = declaringName + methodName; // 执行成功则计数加一 int increase = AtomicCounter.getInstance().increase(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); synchronized (this) { //在项目启动时,需要在Redis中读取原有的接口请求次数 if (countMap.size() == 0) { JSONObject jsonObject = RedisUtils.objFromRedis(StringConst.INTERFACE_ACTUATOR); if (jsonObject != null) { Set<String> strings = jsonObject.keySet(); for (String string : strings) { Object o = jsonObject.get(string); countMap.putIfAbsent(string, o); } } } } // 如果此次访问的接口不在countMap,放入countMap countMap.putIfAbsent(mapKey, 0); countMap.compute(mapKey, (key, value) -> (Integer) value + 1); synchronized (this) { // 内存计数达到30 更新redis if (increase == 30) { RedisUtils.objToRedis(StringConst.INTERFACE_ACTUATOR, countMap, Constants.AVA_REDIS_TIMEOUT); //删除过期时间 stringRedisTemplate.persist(StringConst.INTERFACE_ACTUATOR); //计数器置为0 AtomicCounter.getInstance().toZero(); } } //log.info("方法执行次数:" + mapKey + "------>" + countMap.get(mapKey)); //log.info("URI:[{}], 耗费时间:[{}] ms", request.getRequestURI(), System.currentTimeMillis() - startTime.get()); } /** * 当接口报错时执行此方法 */ @AfterThrowing(pointcut = "com.sf.actuator.GlobalActuator.controllerPt()") public void doAfterThrowing(JoinPoint joinPoint) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); log.info("接口访问失败,URI:[{}], 耗费时间:[{}] ms", request.getRequestURI(), System.currentTimeMillis() - startTime.get()); } }
這裡再給予Controller層程式碼。
@GetMapping("/interface/{intCount}") @ApiOperation(value = "查找接口成功访问次数(默认倒序)") public Result<List<InterfaceDto>> findInterfaceCount( @ApiParam(name = "intCount", value = "需要的接口数") @PathVariable Integer intCount ) { HashMap<String, Integer> hashMap = new HashMap<>(); JSONObject jsonObject = RedisUtils.objFromRedis(StringConst.INTERFACE_ACTUATOR); if (jsonObject != null) { Set<String> strings = jsonObject.keySet(); for (String string : strings) { Integer o = (Integer) jsonObject.get(string); hashMap.putIfAbsent(string, o); } } //根据value倒序 Map<String, Integer> sortedMap = hashMap.entrySet().stream().sorted(Collections.reverseOrder(Map.Entry.comparingByValue())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); //返回列表 List<InterfaceDto> resultList = new ArrayList<>(); //排序后中的map中所有的key Object[] objects = sortedMap.keySet().toArray(); for (int i = 0; i < intCount; i++) { InterfaceDto interfaceDto = new InterfaceDto(); interfaceDto.setName((String) objects[i]); interfaceDto.setCount(sortedMap.get((String) objects[i])); resultList.add(interfaceDto); } return Result.success(resultList); }
專案運行一段時間後,在Redis中可以看到介面的請求次數。
Web最終效果圖如下:
#
以上是SpringBoot如何使用AOP實現統計全域介面存取次數的詳細內容。更多資訊請關注PHP中文網其他相關文章!

ByteCodeachievesPlatFormIndenceByByByByByByExecutedBoviratualMachine(VM),允許CodetorunonanyplatformwithTheApprepreprepvm.Forexample,Javabytecodecodecodecodecanrunonanydevicewithajvm

Java不能做到100%的平台獨立性,但其平台獨立性通過JVM和字節碼實現,確保代碼在不同平台上運行。具體實現包括:1.編譯成字節碼;2.JVM的解釋執行;3.標準庫的一致性。然而,JVM實現差異、操作系統和硬件差異以及第三方庫的兼容性可能影響其平台獨立性。

Java通過“一次編寫,到處運行”實現平台獨立性,提升代碼可維護性:1.代碼重用性高,減少重複開發;2.維護成本低,只需一處修改;3.團隊協作效率高,方便知識共享。

在新平台上創建JVM面臨的主要挑戰包括硬件兼容性、操作系統兼容性和性能優化。 1.硬件兼容性:需要確保JVM能正確使用新平台的處理器指令集,如RISC-V。 2.操作系統兼容性:JVM需正確調用新平台的系統API,如Linux。 3.性能優化:需進行性能測試和調優,調整垃圾回收策略以適應新平台的內存特性。

javafxeffectife addressemanddressEndressencissencies uningusement insuplatform-agnosticsCenegraphandCsSsStyling.1)itabstractsplactsplatsplatsplatsplatsplatformsthroughascenegraph,確保consistentertrenderingrenderingrenderingacrosswindows,macoswindwind,Macos,MacOs.2)

JVM的工作原理是將Java代碼轉換為機器碼並管理資源。 1)類加載:加載.class文件到內存。 2)運行時數據區:管理內存區域。 3)執行引擎:解釋或編譯執行字節碼。 4)本地方法接口:通過JNI與操作系統交互。

JVM使Java實現跨平台運行。 1)JVM加載、驗證和執行字節碼。 2)JVM的工作包括類加載、字節碼驗證、解釋執行和內存管理。 3)JVM支持高級功能如動態類加載和反射。

Java應用可通過以下步驟在不同操作系統上運行:1)使用File或Paths類處理文件路徑;2)通過System.getenv()設置和獲取環境變量;3)利用Maven或Gradle管理依賴並測試。 Java的跨平台能力依賴於JVM的抽象層,但仍需手動處理某些操作系統特定的功能。


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

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

熱門文章

熱工具

WebStorm Mac版
好用的JavaScript開發工具

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

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具

Dreamweaver CS6
視覺化網頁開發工具

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。