SpringBoot实践——外观模式,自定义注解实现

401 阅读3分钟

这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战

一、基本介绍

定义

外观模式是一种结构型设计模式,隐藏了系统的复杂性, 能为程序库、 框架或其他复杂类提供一个简单的接口。

作用

主要解决的是降低调用方的使用接口的复杂逻辑组合。这样调用方与实际的接口提供方提供方提供了一个中间层,用于包装逻辑提供API接口。

应用场景

  • 如果需要将子系统组织为多层结构, 可以使用外观。

  • 可以用在中间件层,对服务中的通用性复杂逻辑进行中间件层包装,让使用方可以只关心业务开发。

优缺点

  • 可以让自己的代码独立于复杂子系统。

  • 外观可能成为与程序中所有类都耦合的上帝对象

画外(自己的话QAQ)

这么说,其实我们平时开发的时候也是有注意到这个的,比如一个下订单功能,里面又细分有生成订单订单、删除购物车、增加积分等等操作,但是我们对外暴露的,只会是一个 提交订单服务,调用该服务的人,只需要关心如何调用就行,而不需要关心下单的一系列内部逻辑。

实例

下面举的例子是 外观模式应用在中间层的实例

需求:比如系统有一些接口:查看店铺信息接口、查看商品信息接口、查看个人信息接口等等,我们需要做个统计,即调用了这些接口的次数统计,如果一个个接口去处理,有略显繁琐,并且随着需求的增加,CV工作量还会加大,代码重复量多也不雅观(不够炫!QAQ)

处理:所以考虑自定义个注解,在有需要的接口加上注解,然后统计。


二、实践

(1)新建个测试Controller,只有简单的测试接口

@RestController
@RequestMapping("/facade")
public class FacadeController {

    @GetMapping("/")
    public String text(){
        return "外观模式实践:自定义注解";
    }

    @GetMapping("/getShopInfo")
    public String getShopInfo(String shop_id){
        return "查看店铺信息,店铺ID:"+shop_id;
    }

    @GetMapping("/getProductInfo")
    public String getProductInfo(String product_id){
        return "查看商品信息,商品ID:"+product_id;
    }

    @GetMapping("/getPersonInfo")
    public String getPersonInfo(String person_id){
        return "查看个人信息,个人ID"+person_id;
    }


}

(2)自定义注解 CountInterface

/**
 * @author 勤任风
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CountInterface {
    String key() default "";
}

(3)切面实现 注解的逻辑

/**
 * @author 勤任风
 * @date 2021-08-07 14:08
 */
@Aspect
@Component
@Slf4j
public class CountInterfacePoint {

    @Autowired
    RedisTemplate redisTemplate;

    @Pointcut("@annotation(com.example.demo.design.facade.annotation.CountInterface)")
    public void aopPoint() {
    }
    @Around("aopPoint()")
    public Object doCount(ProceedingJoinPoint jp) throws Throwable {
        log.info("进来注解了!");
        //获取内容
        Method method = getMethod(jp);
        CountInterface countInterface = method.getAnnotation(CountInterface.class);
        //获取注解字段key的值
        String key = countInterface.key();
        //获取方法参数的值
        Object[] params = jp.getArgs();
        // 获取第一个参数的值,并做为redis的key补充,
        String temp="";
        if(params!=null){
            temp=params[0].toString();
        }
        // 例如:用key+参数值作为redis的键,如:shop01、shop02、person01,然后值自增+1
        RedisAtomicLong entityIdCounter = new RedisAtomicLong(key+temp, redisTemplate.getConnectionFactory());
        entityIdCounter.incrementAndGet();
        //继续执行 注解之后的逻辑
        return jp.proceed();

    }

    private Method getMethod(JoinPoint jp) throws NoSuchMethodException {
        Signature sig = jp.getSignature();
        MethodSignature methodSignature = (MethodSignature) sig;
        return getClass(jp).getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
    }

    private Class<? extends Object> getClass(JoinPoint jp) throws NoSuchMethodException {
        return jp.getTarget().getClass();
    }
}

(4)在需要统计的接口,加上@CountInterface注解,内容key自己定,结果如下:

image.png

(5)测试,调用获取ID为12的店铺信息接口,连续调用n次

image.png

(6)可以看到Redis里面已经有了自增的数据 image.png