AspectJ简单实现某些功能的PV统计

1.场景

    在项目中,需要对某些功能或者接口进行PV统计,那么这些需求一般在项目初期可能不会出现。如果项目基本开发完成,或者在迭代的过程中需要增加PV等统计功能,那么,最原始的做法是在原有代码的基础上增加统计代码,但这样一来,对原有的代码有入侵性,而且与主业务逻辑耦合,代码可读性以及维护性和扩展性会非常差。

2.思路

    如果项目中使用了Spring,可以通过Aop的方式,对原有需要进行统计的功能进行增强,不对原有程序进行修改,从而完成PV统计的实现,可以使用AspectJ来实现该需求。

3.实现

    1.在pom.xml文件中增加aspectJ的支持,当然还要有spring基础组件以及spring-aop的支持。

        

    2.拿我们的项目来说,需要统计的功能有,首页,商品列表,商品详情,机构列表,机构详情,新闻资讯等等。

    3.为了方便扩展,可将需要统计的功能做成一个Enum,当然比如存到库中之类也是可以的。

        

/**

 * 统计PV类型枚举

 */

public enum PvTypeEnum {


    DEFAULT("未知"),

    HOME("首页"),

    GOODS_LIST("商品列表"),

    GOODS_DETAIL("商品详情"),

    CAMPUS_LIST("机构列表"),

    CAMPUS_DETAIL("机构详情"),

    INFORMATION_LIST("资讯列表"),

    INFORMATION_DETAIL("资讯详情"),

    USERINFO("我的");


    private String value;


    public String getValue() {

        return value;

    }


    PvTypeEnum(String value) {

        this.value = value;

    }

}

    4.Aop可以设置切点(pointcut),实现功能的增加,比如在某个方法执行前,方法执行后,抛出异常时,或者环绕通知,相关内容不在做过多解释了,详细的大家可以了解AspectJ的相关用法,比较简单。AspectJ可以通过设置,拦截某一个类中的某些方法,或者某个包下的某些方法,表达式与Spring声明式事务的表达式基本一致。但是上述两种方式灵活度可能不太高,还有一种方法,可以拦截带有特定注解的方法,完成功能的增强。本次采用的就是通过拦截注解的方式,首先定义一个自定义注解。

    

/**

 * 统计PV用注解

 */

@Target({ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

public @interface PvCount {


    PvTypeEnum description() default PvTypeEnum.DEFAULT;


}

    5.在需要统计的功能上增加注解,本次将注解写在了Controller上,当然,写在Service方法上也是可以的,根据自己的需求来决定注解的位置。

    

    6.编写用于处理逻辑的Aop类。就是当拦截到带有注解的方法时,该执行哪些特殊逻辑。

    

/**

 * PV日志统计

 */

@Component

@Aspect

public class PvAopLog {


    @Autowired

    private TokenUtil tokenUtil;


    @Autowired

    private LogPvMapper logPvMapper;


    @Autowired

    private RedisTemplate<String,LogPv> redisTemplate;


    //定义切点
    @Pointcut(value = "@annotation(com.meilong.annotation.PvCount)")

    public void pointcut() {

    }


    /**

     * 拦截带有PvCount注解的方法

     * 在执行方法后 执行该切面,并将注解传递到切面中

     * @param joinPoint

     * @param pvCount

     */

    @After(value = "pointcut() && @annotation(pvCount)")

    public void after(JoinPoint joinPoint,PvCount pvCount){

        LogPv logPv = new LogPv();

        //方法参数

        Object[] args = joinPoint.getArgs();

        //当前请求

        HttpServletRequest request = HttpUtil.getCurrentRequest();

        //类型

        logPv.setPvType(pvCount.description().getValue());

        //请求ip地址

        logPv.setIpAddr(request.getRemoteAddr());

        //时间

        logPv.setPvTime(new Date());

        //用户id

        if(!StringUtils.isEmpty(request.getHeader("token"))){

            logPv.setUserId(tokenUtil.getToken(request.getHeader("token")).getId().intValue());

        }

        //向redis中保存数据

        redisTemplate.opsForList().leftPush(RedisKey.LOG_REDIS_KEY,logPv);

    }


}

    7.代码比较简单,这里面可能存在一些争议。在这个方法中我使用了Redis,因为如果每次直接将记录存库的话,比较耗费性能,所以可以选择先放入Redis,在使用定时任务或者其他方式一次性将数据落地,因为统计日志并不需要那么高的时效性或者一致性。当然这里也可以选择将记录写入文件,或者使用消息队列,让记录日志的操作与主业务逻辑解耦。方法比较多,大家可根据需求自行选择。

    8.接下来,需要增加spring对aspectj的配置支持。*******这里千万注意,如果需要拦截Controller中的某个方法,那么一定要将配置写到springmvc的配置文件中,而不是spring的配置文件,千万注意********。本次我拦截的就是Controller中的方法。

    

    9.到这里基本就结束了,后续日志记录该怎么保存的逻辑在这就不列出来了,上面也提到了一些方法,大家可以参考一下。


    
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值