springboot利用切面記錄在線人數

衆所周知springboot兩大特性,ioc和aop即控制反轉和麪向切面編程。
aop的用處主要是橫向截取公有功能。比如管理事務,處理異常等
目前在做一個論壇系統,需要統計在線人數。
有兩個方案:
1、就是利用Session會話機制,通過監聽器判斷是否有新的會話生成,這種的好處是即使參數中沒有用戶信息也可以正常收集信息,缺點是如果同一個用戶打開多個會話,那麼會重複記錄。
2、就是利用一個Set集合。每次有用戶觸發接口,記錄用戶ID,最後Set.size()即是在線人數
springboot集成aop的註解實現主要有兩種
一種是正常的切點注入
首先pom文件添加依賴

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
然後是切面類
package com.hqjl.communityserv.aspect;

import com.hqjl.communityserv.request.RequestParamInfo;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.HashSet;
import java.util.Set;

/**
 * @author chunying
 * @Date: 2019/11/6 0006
 */
@Aspect
@Component
public class UserLine {

    private final Logger LOG = LoggerFactory.getLogger(UserLine.class);
    private static Set<String> onLineUsers = new HashSet<>();

    @Pointcut("execution(* com.hqjl.communityserv.controller..*(..))")
    public void addOnlineUser(){}

    @Around(addOnlineUser())
    public Object interceptor(ProceedingJoinPoint point) {
        Object result = null;
        Object[] args = point.getArgs();
        if (args != null && args.length > 0) {
            RequestParamInfo arg = (RequestParamInfo)args[0];
            String userID = arg.getUserID();
            if (!onLineUsers.contains(userID)) {
                onLineUsers.add(userID);
            }
        }
        try {
            result = point.proceed();
        }catch(Throwable e) {
            LOG.error(e.getMessage(), e);
        }
        return result;
    }

    public static Integer getOnlineCount() {
        return onLineUsers.size();
    }

}
Aspect註解:表明這是一個切面類
Component註解:不用多說吧 注入到spring中
Pointcut註解:切點配置  我這裏配置的表示 controller包下的所有類會被這個切面影響。具體寫法可查切面表達式
Around註解:和我們用xml配置文件一樣 表示advice。還有Before After等
參數ProceedingJoinPoint  這個是必須的  這個類繼承了JoinPoint
其中proceed()方法表示執行方法。這個裏面有許多方法
大概列一下
//攔截的實體類
Object target = point.getTarget();
//攔截的方法名稱
String methodName = point.getSignature().getName();
//攔截的方法參數
Object[] args = point.getArgs();
//攔截的放參數類型
Class[] parameterTypes = ((MethodSignature)point.getSignature()).getMethod().getParameterTypes();
Method m = null;
try {
//通過反射獲得攔截的method
m = target.getClass().getMethod(methodName, parameterTypes);
//如果是橋則要獲得實際攔截的method
if(m.isBridge()){
for(int i = 0; i < args.length; i++){
//獲得泛型類型
	Class genClazz = GenericsUtils.getSuperClassGenricType(target.getClass());
	//根據實際參數類型替換parameterType中的類型
	if(args[i].getClass().isAssignableFrom(genClazz)){
		parameterTypes[i] = genClazz;
	}
}
		//獲得parameterType參數類型的方法
		m = target.getClass().getMethod(methodName, parameterTypes);
	}
} catch (SecurityException e) {
	e.printStackTrace();
} catch (NoSuchMethodException e) {
	e.printStackTrace();
	}
}

這是第一種方法
如果覺得不夠靈活 我們可以用第二種
基於註解的配置
首先創建註解

package com.hqjl.communityserv.annotation;

import java.lang.annotation.*;

/**
 * @author chunying
 * @Date: 2019/11/6 0006
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface UserOnline {
}

然後配置切面類

package com.hqjl.communityserv.aspect;

import com.hqjl.communityserv.request.RequestParamInfo;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.HashSet;
import java.util.Set;

/**
 * @author chunying
 * @Date: 2019/11/6 0006
 */
@Aspect
@Component
public class UserLine {

    private final Logger LOG = LoggerFactory.getLogger(UserLine.class);
    private static Set<String> onLineUsers = new HashSet<>();

    @Around(value = "@annotation(com.hqjl.communityserv.annotation.UserOnline)")
    public Object interceptor(ProceedingJoinPoint point) {
        Object result = null;
        Object[] args = point.getArgs();
        if (args != null && args.length > 0) {
            RequestParamInfo arg = (RequestParamInfo)args[0];
            String userID = arg.getUserID();
            if (!onLineUsers.contains(userID)) {
                onLineUsers.add(userID);
            }
        }
        try {
            result = point.proceed();
        }catch(Throwable e) {
            LOG.error(e.getMessage(), e);
        }
        return result;
    }

    public static Integer getOnlineCount() {
        return onLineUsers.size();
    }

}

把這個註解配置到方法上 就會被切面注入了。

另外如果報了這個錯
Caused by: java.lang.IllegalArgumentException: error at ::0 can’t find referenced pointcut
有兩種可能 一種jdk版本與aop不兼容
第二種 請檢查你得pointcut 和 around 拼寫有沒有錯誤

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章