IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 【SpringBoot实战系列】从AOP+自定义注解到redission分布式锁-接口防重提交场景设计实战 -> 正文阅读

[Java知识库]【SpringBoot实战系列】从AOP+自定义注解到redission分布式锁-接口防重提交场景设计实战

大家好,我是工藤学编程 🦉一个正在努力学习的小博主,期待你的关注
作业侠系列最新文章😉Java实现聊天程序
SpringBoot实战系列🐷【【SpringBoot实战系列】AOP+自定义注解-接口防重提交多场景设计实战
环境搭建大集合环境搭建大集合(持续更新)

在本栏中,我们之前已经完成了:
【SpringBoot实战系列】之发送短信验证码
【SpringBoot实战系列】之从Async组件应用实战到ThreadPoolTaskExecutor?定义线程池
【SpringBoot实战系列】之图形验证码开发并池化Redis6存储
【SpringBoot实战系列】阿里云OSS接入上传图片实战
【SpringBoot实战系列】Sharding-Jdbc实现分库分表到分布式ID生成器Snowflake自定义wrokId实战
【SpringBoot实战系列】RabbitMQ实现消息发送并实现邮箱发送异常监控报警实战

本片速览:
1.AOP简介及好处
2.Spring??的AOP常?概念
3.java核心知识-?定义注解
4.防重提交自定义注解实战
5.分布式锁
6.切面开发
7.测试结果

AOP简介及好处

Aspect Oriented Program ?向切?编程, 在不改变原有逻辑上增加额外的功能AOP思想把功能分两个部分,分离系统中的各种关注点
好处

  • 减少代码侵?,解耦
  • 可以统?处理横切逻辑
  • ?便添加和删除横切逻辑

Spring??的AOP常?概念

  • 横切关注点
    对哪些?法进?拦截,拦截后怎么处理,这些就叫横切关注点
    ?如 权限认证、?志、事物
  • 通知 Advice
    在特定的切?点上执?的增强处理
    做啥?
    ?如你需要记录?志,控制事务 ,提前编写好通?的模块,需要的地?直接调?
    ?如重复提交判断逻辑
    类型
  1. @Before前置通知
    在执??标?法之前运?
  2. @After后置通知
    在?标?法运?结束之后
  3. @AfterReturning返回通知
    在?标?法正常返回值后运?
  4. @AfterThrowing异常通知
    在?标?法出现异常后运?
  5. @Around环绕通知
    在?标?法完成前、后做增强处理 ,环绕通知是最重要的通知类型 ,像事务,?志等都是环绕通知,注意编程中核?是?个ProceedingJoinPoint,需要?动执? joinPoint.procced()
  • 连接点 JointPoint
    要?通知的地?,业务流程在运?过程中需要插?切?的
    具体位置,?般是?法的调?前后,全部?法都可以是连接点只是概念,没啥特殊
  • 切?点 Pointcut
    不能全部?法都是连接点,通过特定的规则来筛选连接点,就是Pointcut,选中那?个你想要的?法在程序中主要体现为书写切?点表达式(通过通配、正则
    表达式)过滤出特定的?组 JointPoint连接点过滤出相应的 Advice 将要发?的joinpoint地?
  • 切? Aspect
    通常是?个类,??定义 切?点+通知 , 定义在什么地?;
    什么时间点、做什么事情
    通知 advice指明了时间和做的事情(前置、后置等)切?点 pointcut 指定在什么地??这个事情web接?设计中,web层->?关层->服务层->数据层,每?层之间也是?个切?,对象和对象,?法和?法之间都是?个个切?
  • ?标 target
    ?标类,真正的业务逻辑,可以在?标类不知情的条件下,增加新的功能到?标类的链路上
  • 织? Weaving
    把切?(某个类)应?到?标函数的过程称为织?

java核心知识-?定义注解

  • Annotation(注解) 从JDK 1.5开始, Java增加了对元数据(MetaData)的?持,也就是 Annotation(注解)。
    注解其实就是代码?的特殊标记,它?于替代配置?件常?的很多 @Override、@Deprecated等
  • 什么是元注解
    注解的注解,?如当我们需要?定义注解时会需要?些元注解(meta-annotation),如@Target和@Retention
  • java内置4种元注解
    @Target 表示该注解?于什么地?
  1. ElementType.CONSTRUCTOR ?在构造器
  2. ElementType.FIELD ?于描述域-属性上
  3. ElementType.METHOD ?在?法上
  4. ElementType.TYPE ?在类或接?上
  5. ElementType.PACKAGE ?于描述包
  • @Retention 表示在什么级别保存该注解信息
  1. RetentionPolicy.SOURCE 保留到源码上
  2. RetentionPolicy.CLASS 保留到字节码上
  3. RetentionPolicy.RUNTIME 保留到虚拟机运?时(最多,可通过反射获取)
  • @Documented 将此注解包含在 javadoc 中
  • @Inherited 是否允许?类继承?类中的注解
  • @interface
    ?来声明?个注解,可以通过default来声明参数的默认值?定义注解时,?动继承了java.lang.annotation.Annotation接?通过反射可以获取?定义注解

防重提交自定义注解实战

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

    enum Type {PARAM,TOKEN}

    Type limitType() default Type.PARAM;

    long lockTime() default 5;



}

分布式锁
redission依赖

<dependency>
 <groupId>org.redisson</groupId>
 <artifactId>redisson</artifactId>
 <version>3.10.1</version>
</dependency>

配置类




@Configuration
public class RedissionConfiguration {
    @Value("${spring.redis.host}")
    private String redisHost;
    @Value("${spring.redis.port}")
    private String redisPort;
    @Value("${spring.redis.password}")
    private String redisPwd;

    /**
     * 配置分布式锁的redisson
     *
     * @return
     */
    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        //单机?式

        config.useSingleServer().setPassword(redisPwd).setAddress("redis://" + redisHost + ":" + redisPort);
        //集群


        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }
    /**
     * 集群模式
     * 备注:可以?"rediss://"来启?SSL连接
     */
 /*@Bean
 public RedissonClient redissonClusterClient() {
 Config config = new Config();

config.useClusterServers().setScanInterval(2000) //
集群状态扫描间隔时间,单位是毫秒

.addNodeAddress("redis://127.0.0.1:7000")

.addNodeAddress("redis://127.0.0.1:7002");
 RedissonClient redisson =
Redisson.create(config);
 return redisson;
 }*/
}

切面开发:

@Aspect
@Component
@Slf4j
public class RepeatSubmitAspect {


    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 定义 @Pointcut注解表达式,
     * ?式?:@annotation:当执?的?法上拥有指定的注解时
     ?效(我们采?这)
     * ?式?:execution:?般?于指定?法的执?
     *
     * @param repeatSubmit
     */
    @Pointcut("@annotation(repeatSubmit)")
    public void pointCutNoRepeatSubmit(RepeatSubmit repeatSubmit) {

    }

    /**
     * 环绕通知, 围绕着?法执?
     * @Around 可以?来在调??个具体?法前和调?后来完成?些具体的任务。
     *
     * ?式?:单? @Around("execution(*net.xdclass.controller.*.*(..))")可以
     * ?式?:?@Pointcut和@Around联合注解也可以(我们采?这个)
     *
     *
     * 两种?式
     * ?式?:加锁 固定时间内不能?复提交
     * <p>
     * ?式?:先请求获取token,这边再删除token,删除成功则是第?次提交
     *
     * @param joinPoint
     * @param noRepeatSubmit
     * @return
     * @throws Throwable
     */
    @Around("pointCutNoRepeatSubmit(repeatSubmit)")
    public Object around(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        Long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();

        boolean res = false;
        String type = repeatSubmit.limitType().name();

        if (type.equalsIgnoreCase(RepeatSubmit.Type.PARAM.name())) {
            long lockTime = repeatSubmit.lockTime();
            String ippAddr = CommonUtil.getIpAddr(request);
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            Method method = methodSignature.getMethod();
            String className = method.getDeclaringClass().getName();
            String key ="order-server-repeat-submit:"+CommonUtil.MD5(String.format("%s-%s-%s-%s", ippAddr, className, method, accountNo)) ;

            //res=redisTemplate.opsForValue().setIfAbsent(key,"1",lockTime, TimeUnit.SECONDS);
            RLock lock = redissonClient.getLock(key);
            res = lock.tryLock(0, lockTime, TimeUnit.SECONDS);
        } else {
            String requestToken = request.getHeader("request-token");
            if (StringUtils.isBlank(requestToken)) {
                throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_EQUAL_FAIL);
            }
            String key = String.format(RedisKey.SUBMIT_ORDER_TOKEN_KEY, accountNo, requestToken);
            res = redisTemplate.delete(key);
        }
        if (!res) {
            log.error("订单重复提交");
            return null;
        }
        log.info("环绕通知前:{}", CommonUtil.getCurrentTimestamp());
        Object obj = joinPoint.proceed();
        log.info("环绕通知后:{}", CommonUtil.getCurrentTimestamp());
        return obj;
    }


}
将自定义的注解加在对应想要防重提交的方法上即可
 @PostMapping("page")
    @RepeatSubmit
    public JsonData page(@RequestBody OrderPageRequest orderPageRequest){
        Map<String,Object>pageResult = productOrderService.page(orderPageRequest);

        return JsonData.buildSuccess(pageResult);

    }

访问对应接口
在这里插入图片描述
本篇完!

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-08-06 10:29:56  更:2022-08-06 10:31:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 12:48:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码