博客3 Aop使用、缓存优化 别的优化、总结

java学习总结(十二):AOP操作日志、AOP事务、登录日志 上面记录操作日志的操作存在问题是所有的Controller方法都记录了操作日志,这样就会记录太多没有价值的日志,基于自定义注解方式只记录我们想记录的Controller方法的操作日志。/*** 模块*/OperLogAspect类里面切面表达式修改为指定指定注解方式/*** 日志切点*///@MyLog@MyLog(module="用户模块")/*** 前置通知*/if (admin!= null) {//---------添加代码--------------- 阅读详情

1、AOP使用

(1)定义一个注解:

//Type 代表可以放在类上面 Method 代表可以放在方法上
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {
    String module() default "";
    String operator() default "";
}

然后再Controller加注解就行,加了注解就表示是切点了,因为下面切面是这样定义的
在这里插入图片描述
然后我们还得加个切面:

import com.alibaba.fastjson.JSON;
import com.hjy.utils.HttpContextUtils;
import com.hjy.utils.IpUtils;
import lombok.extern.slf4j.Slf4j;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

@Component
@Aspect   //切面 定义了通知和切点的关系
@Slf4j
public class LogAspect {

    //@annotation(com.hjy.common.aop.LogAnnotation)是切点表达式,表示加了LogAnnotation注解都是切点
    //pt()就是切点表达式分装好了
    @Pointcut("@annotation(com.hjy.common.aop.LogAnnotation)")
    public void pt(){}

    //环绕通知,在执行切点(就是加了注解的才是切点)就执行环绕通知
    @Around("pt()")
    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
        long beginTime = System.currentTimeMillis();
        //执行方法
        Object result = joinPoint.proceed();
        //执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        //保存日志
        recordLog(joinPoint, time);
        return result;
    }

    private void recordLog(ProceedingJoinPoint joinPoint, long time) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
        log.info("=====================log start================================");
        log.info("module:{}",logAnnotation.module());
        log.info("operation:{}",logAnnotation.operator());

        //请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        log.info("request method:{}",className + "." + methodName + "()");

//        //请求的参数
        Object[] args = joinPoint.getArgs();
        String params = JSON.toJSONString(args[0]);
        log.info("params:{}",params);

        //获取request 设置IP地址
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        log.info("ip:{}", IpUtils.getIpAddr(request));


        log.info("excute time : {} ms",time);
        log.info("=====================log end================================");
    }
}

再把ip等工具类加上:

public class HttpContextUtils {

    public static HttpServletRequest getHttpServletRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }

}
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;

/**
 * 获取Ip
 *
 */
@Slf4j
public class IpUtils {

    /**
     * 获取IP地址
     * <p>
     * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
     * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ip = null, unknown = "unknown", seperator = ",";
        int maxLength = 15;
        try {
            ip = request.getHeader("x-forwarded-for");
            if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (StringUtils.isEmpty(ip) || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
            }
            if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
        } catch (Exception e) {
            log.error("IpUtils ERROR ", e);
        }

        // 使用代理,则获取第一个IP地址
        if (StringUtils.isEmpty(ip) && ip.length() > maxLength) {
            int idx = ip.indexOf(seperator);
            if (idx > 0) {
                ip = ip.substring(0, idx);
            }
        }

        return ip;
    }

    /**
     * 获取ip地址
     *
     * @return
     */
    public static String getIpAddr() {
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        return getIpAddr(request);
    }
}

访问那个接口完事:
在这里插入图片描述

可详细可参考:AOP

2.缓存优化

这里也是用了aop来缓存接口:

先定义注解:


@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Cache {

    long expire() default 1 * 60 * 1000;
    //缓存标识 key
    String name() default "";

}

切面:



import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hjy.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AliasFor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.time.Duration;
//aop 定义一个切面,切面定义了切点和通知的关系
@Aspect
@Component
@Slf4j
public class CacheAspect {

    private static final ObjectMapper objectMapper = new ObjectMapper();


    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Pointcut("@annotation(com.包路径.Cache)")
    public void pt(){}

    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp){
        try {
            Signature signature = pjp.getSignature();
            //类名
            String className = pjp.getTarget().getClass().getSimpleName();
            //调用的方法名
            String methodName = signature.getName();


            Class[] parameterTypes = new Class[pjp.getArgs().length];
            Object[] args = pjp.getArgs();
            //参数
            String params = "";
            for(int i=0; i<args.length; i++) {
                if(args[i] != null) {
                    params += JSON.toJSONString(args[i]);
                    parameterTypes[i] = args[i].getClass();
                }else {
                    parameterTypes[i] = null;
                }
            }
            if (StringUtils.isNotEmpty(params)) {
                //加密 以防出现key过长以及字符转义获取不到的情况
                params = DigestUtils.md5Hex(params);
            }
            Method method = pjp.getSignature().getDeclaringType().getMethod(methodName, parameterTypes);
            //获取Cache注解
            Cache annotation = method.getAnnotation(Cache.class);
            //缓存过期时间
            long expire = annotation.expire();
            //缓存名称
            String name = annotation.name();
            //先从redis获取
            String redisKey = name + "::" + className+"::"+methodName+"::"+params;
            String redisValue = redisTemplate.opsForValue().get(redisKey);
            if (StringUtils.isNotEmpty(redisValue)){
                log.info("走了缓存~~~,{},{}",className,methodName);
                Result result = JSON.parseObject(redisValue, Result.class);
                return result;
            }
            Object proceed = pjp.proceed();
            redisTemplate.opsForValue().set(redisKey,JSON.toJSONString(proceed), Duration.ofMillis(expire));
            log.info("存入缓存~~~ {},{}",className,methodName);
            return proceed;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return Result.fail(-999,"系统错误");
    }

}

ctroller:
在这里插入图片描述
测试:
在这里插入图片描述

3、别的优化

在这里插入图片描述

4、总结

在这里插入图片描述

springboot注解方式实现aop及常规方式 springboot实现aop的两种方式,注解加常规 阅读详情

相关推荐

SpringBoot AOP (三)

SpringBoot AOP (三)

专搞技术的小兔子 306

【Spring学习】AOP实现日志记录

AOP,面向切面编程。通过预编译方式和运行时动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。

Lemonss123的博客 1万+

SpringBoot中通过自定义缓存注解(AOP切面拦截)实现数据库数据缓存到Redis

场景 若依前后端分离版本地搭建开发环境并运行项目的教程: https://blog.youkuaiyun.com/BADAO_LIUMANG_QIZHI/article/details/108465662 基于上面搭建起来前后端分离的Vue+SpringBoot的项目。 其中数据库使用的是Mysql,缓存层用的是Redis。 数据库中某个表存储的信息,在业务代码比如定时任务中,需要频繁的查询。 所以将表中的数据存储到redis中一份。 其原理是,在调用查询方法时,判断redis中是否已经有,如果有则优先从

BADAO_LIUMANG_QIZHI的博客 1685

使用@LogAnnotation注释保存操作记录

1、controller中添加注释 `` @PostMapping(value = “/getXXX/XXX”) @ApiOperation(httpMethod = “POST”) @LogAnnotation(logType=“XXX”,isSaveRequestData=false,isSaveResponseData=false) //添加注释,以及将参数填写其中 public List...

weixin_35442221的博客 8970

Spring框架中AOP应用的入门案例

面向切面编程(AOP)是软件开发中的一种编程范式,它通过分离横切关注点(cross-cutting concerns)来增加模块化。在Spring框架中,AOP被广泛应用于事务管理、安全检查、日志记录和缓存等方面。它允许开发者定义方法执行前后以及抛出异常时需要执行的代码(通知),而不需要修改原有业务逻辑。Spring AOP利用动态代理机制来实现AOP,支持方法级别的拦截。接下来,本文将深入探讨AOP的核心概念,包括切面、通知、连接点、织入等,并比较Spring AOP与其他AOP实现的不同之处。

weixin_42351520的博客 948

安卓手机游戏《英雄迷宫冒险》存档修改

警告 修改前务必备份存档文件夹。该游戏有自动删除存档的功能,有问题的存档会自动删除。 修改对应的游戏版本为1.5。 存档保存位置 内部存储\Android\data\com.xmmf.yxmgmx.taptap\files 该文件夹中文件如下: data.wsf:数据文件。 wsfSave0:第一个存档。 wsfSave1:第二个存档。 修改金钱的例子 先进游戏确认当前金钱数量。当前金钱是2498,然后退出游戏。 根据金钱数量计算出存档中的内容。2498的十六进制是9C2,存档中的内容在存档中搜索该数

番茄鸡蛋的专栏 9362

java代码优化方案2(Aop处理Token令牌,页面缓存)

目录前言一、Aop处理Token令牌1、如果不需要验证身份信息2、如果需要验证身份信息二、页面缓存,页面伪静态化(应对秒杀)1、页面缓存2、页面伪静态化(应对秒杀)后言 前言 我们本次要解决的问题有两个大点 一、每次用户请求都需要判断其是否身份验证过 解决方案:使用Aop来为每一个需要进行身份验证后才能调用的方法写一个注解 思路:每次调用方法前都先判断这个方法是否携带注解,如果没携带则需要验证。 ...

旺旺的博客 826

手把手教你复现IEEE 2025高光谱图像盲超分算法DBSR(附开源代码与避坑指南)

本文详细介绍了如何从零复现IEEE 2025高光谱图像盲超分算法DBSR,包括环境配置、数据预处理、核心模块解析、训练策略及常见问题解决方案。特别针对开源代码中的关键步骤和潜在问题提供了实用指南,帮助研究者和开发者高效实现Deep blind super-resolution技术,适用于遥感、医疗等领域的图像增强应用。

weixin_42524708的博客 413

Springboot通过aop实现声明式缓存

【代码】Springboot通过aop实现声明式缓存。

weixin_60223449的博客 587

RK3588内核崩溃日志捕获:pstore与ramoops的实战配置指南

本文详细介绍了在RK3588嵌入式设备上配置pstore与ramoops以捕获内核崩溃日志的实战指南。通过内核配置调整、设备树设置及高级调试技巧,帮助开发者有效定位系统崩溃原因,特别适合户外智能设备等无法长期连接调试器的场景。文章还提供了生产环境优化建议,确保日志捕获的可靠性与安全性。

weixin_29189363的博客 407

Mac上IDEA的PlantUML插件报错‘找不到Graphviz’?手把手教你用Homebrew搞定(附阿里云镜像避坑)

本文详细解决了Mac上IDEA的PlantUML插件报错‘找不到Graphviz’的问题,通过Homebrew安装Graphviz并配置阿里云镜像源,特别针对Apple Silicon芯片用户提供了优化方案。文章还涵盖了IDEA中PlantUML插件的正确配置和常见问题排查,帮助开发者高效使用PlantUML进行UML图表设计。

weixin_42619742的博客 468

SSM+自定义注解+AOP完成定制Redis缓存结构

上一篇博客说到虽然使用Spring注解来指定缓存非常方便,但是想要进行非常详细、系统的设计缓存数据结构,仅仅使用自带注解还是完全不够的。 https://blog.youkuaiyun.com/weixin_43041241/article/details/89338076 本博客就是之前的博客进行的拓展:使用自定义注解+AOP完成定制Redis缓存结构。 ·-------------------------...

他的博客 423

【RS】哨兵系列数据下载(哨兵2号Sentinel-2波段介绍、下载)

以哨兵二号为示例,分享如何下载欧空局的哨兵系列数据

Remote Sensing迷途小书童 1万+

Springboot+Vue博客项目总结

文章目录Springboot+Vue博客项目总结1.工程搭建1.1 新建maven工程1.2 application.properties配置1.3 配置分页插件1.4 配置解决跨域1.5 添加启动类2.统一异常处理3.登录功能实现3.1 接口说明3.2 JWT3.3 Controller3.4 Service3.5 登录参数,redis配置5.获取用户信息5.1 接口说明5.2 Controller5.3 Service6.登录拦截器6.1 拦截器实现6.2 使拦截器生效7.ThreadLocal保存用户

小皮皮LL的博客 6611

银河麒麟V10系统下VLC播放器安装全攻略(附3.0.9.2版本离线包下载)

本文详细介绍了在银河麒麟V10操作系统无网络环境下,从零开始编译安装VLC 3.0.9.2播放器的完整攻略。通过构建本地YUM仓库解决依赖问题,提供离线资源包下载,并逐步指导完成编译环境配置、软件编译与安装,帮助用户在国产麒麟系统上成功部署强大的多媒体播放能力。

a2b3c4d5e的博客 1213

SAP SD模块定价过程后台配置简介

只有手工输入定价时(例如,在定价中的定价浏览屏幕中),或者只有从外部处理转移这些定价时(例如,成本核算),才将这些定价包括在确定(价格确定、输出确定,批次确定)之中。定价过程的定义和分配是SD模块中最核心的配置,包含了条件表、条件类型、存取顺序、发票类型、客户定价过程等,确定了销售订单已什么价格销售、税率、附加折扣等多个条件行程有一个定价过程。12、小计:记录该条件类型在VBAP表中是否存储,以及其后续用途,如记入信贷,占用信贷额度,记入成本等,如果需要金额暂用信贷额度的话,在小计这里选择A即可。

我小时候很黑的博客 3783

Spring AOP中Cache操作实现

缓存场景分析 在业务方法中我们可能调用数据层方法获取数据库中数据,假如访问数据的频率比较高,为了提高的查询效率,降低数据库的访问压力,可以在业务层对数据进行缓存。 Spring 中业务缓存应用实现 首先我们要创建一个新的项目 这里我们暂时不添加任何依赖,直接next 这样子我们的项目就创建成功了。 然后将我们的项目转成maven项目 建一个包 在这个包下建一个接口 在这个接口类里面定义一个查询的方法,然后将查询的结果定义到一个集合里 package com.

weixin_49792497的博客 619

【脑电信号】脑电信号EEG形态学滤波处理【含Matlab源码 4023期】

脑电信号EEG形态学滤波处理 完整的代码,包运行;运行操作视频见优快云资源!适合小白!

订阅付费专栏Matlab(奶茶价版),可赠送奶茶价版付费专栏指定代码1份; 823

个人博客--小小笔记4(优化精度损失解决方案、自定义接口以AOP切入方式来实现对方法的更改(使用redis缓存数据))

优化精度损失解决方案、自定义接口以AOP切入方式来实现对方法的更改(使用redis缓存数据

qq_48021871的博客 416

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

博客项目优化(码神之路)

三、优化 1. 统一缓存处理(优化) 内存的访问速度远远大于磁盘的访问速度(1000倍起) com.lum.blog.common.cache包下新建cache注解 package com.lum.blog.common.cache; import java.lang.annotation.*; /** * @author lum * @date 2021/9/5 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME

想带你去遥远的屿 809

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

基于博客系统的统一缓存处理

内存的访问速度 远远大于 磁盘的访问速度(10^3~10^6倍),所以我们要减少对磁盘的访问。 在各个系统里,最好不要在接口上优化,而是要做一个统一的缓存处理。 新建自定义注解chach package com.kaisa.blog.common.cache; ​ import java.lang.annotation.*; ​ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public

想去很远地方 840

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

统一缓存处理

**22*///过期时间//缓存标识key}SpringCache是Spring提供的一整套的缓存解决方案。虽然它本身并没有提供缓存的实现,但是它提供了一整套的接口和代码规范、配置、注解等,这样它就可以整合各种缓存方案了,比如Redis、Ehcache,我们也就不用关心操作缓存的细节。Cache接口它包含了缓存的各种操作方式,同时还提供了各种xxxSpringCache使用效果每次调用某方法,而此方法又是带有缓存功能时,Spring框架就会检查指定参数。......

huing的博客 1349

高手如何处理缓存:SpringBoot集成Redis实现缓存处理(AOP技术)!

作者:smileNicky   第一章 需求分析 计划在Team的开源项目里加入Redis实现缓存处理,因为业务功能已经实现了一部分,通过写Redis工具类,然后引用,改动量较大,而且不可以实现解耦合,所以想到了Spring框架的AOP(面向切面编程)。 开源项目:https://github.com/u014427391/jeeplatform 推荐教程:https://edu.csd...

优快云学习 721

java导出xml,使用AOP切面实现字典项转义,使用redis缓存字典项提高效率。

java导出xml,使用AOP切面实现字典项转义,使用redis缓存字典项提高效率。

kanwodedajiba的博客 189

AOP

1,j2ee应用系统与j2ee容器               j2ee应用系统只有部署在j2ee容器中才能运行,j2ee容器分离了一般应用系统的一些通用功能,例如:事务机制、安全机制以及线程池等性能优化机制。由专业人员开发,节省了程序开发的周期也提高了系统的稳定好快速度得到保证。形成了专门的j2ee容器服务器产品,如jboss,websphere,weblogic等。2,(separation

jiangyaowei的专栏 1109

优化Redis缓存效率

项目修炼之路(5)高并发下优化Redis缓存效率 ...

zxl2016的博客 5955
上一篇: 博客重点内容2 登录拦截器 ThreadLocal使用、线程池生产使用
下一篇: 黑马设计模式概念、类关系、七大原则
618GLOAL_COOK2237
博客等级 码龄6年 14粉丝 240原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值