#アスペクト (アスペクト): ポイントカットと通知の組み合わせです。
#ウィービング (ウィービング): ターゲット オブジェクトに拡張機能を適用して、新しいプロキシ オブジェクトを作成するプロセス。 Spring は動的プロキシ ウィービングを使用しますが、AspectJ はコンパイラ ウィービングとクラス ローダー ウィービングを使用します。
実装
依存関係の追加
<!--引入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()); } }
コントローラ層のコードをここに示します。
@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 中国語 Web サイトの他の関連記事を参照してください。