查看原文
其他

造个轮子,我学到了什么

flyhero 码上实战 2022-09-08

听说的最多的是不是“不要重复的造轮子”?不要被这句话蒙骗了,这句话应该还没说完整,在什么情况下不要造轮子?
实际项目中由于工期和质量原因,肯定不希望你造轮子,你造轮子花费时间且质量不如现有的轮子。

但是!不造轮子怎么去装X!不造轮子怎么去了解其中原理!不造轮子怎么成长!


那在造参数校验器轮子的过程中我学到了什么呢?

  • 注解的定义与使用

  • 反射的应用

  • Spring AOP的使用

  • 异常的抛出与处理






造之前的规划

雄心勃勃的规划,开干!我一定比hibernate validator做的好!

我要支持:

  • 属性验证

  • 方法参数验证

  • 方法验证

  • 方法内主动验证



注解

你初见注解时,是不是有种疑惑?为什么在某个类或方法属性上添加一个注解,它就能拥有某种功能呢?

那么我将为你慢慢解开这个迷惑。

注解就相当于一个标签,它本身并没有任何功能性,只是打个标签说明一下这是什么。那它怎么实现的某些功能呢?这就要说说反射了,只有注解和反射双剑合璧,才能发挥它的功效。我们先说注解,后说反射。


如何定义一个注解

格式

自定义注解的格式为:

public @interface 注解名{注解体}

  • @interface用来声明一个注解,并自动继承java.lang.annotation.Annotation接口。

  • 注解体中的类似方法定义的,我们称为注解中的元素。

  1. @Target(ElementType.FIELD)

  2. @Retention(RetentionPolicy.RUNTIME)

  3. @Documented

  4. public @interface NotNull{


  5. String value() default "";

  6. }


注解元素

格式权限修饰符 数据类型 元素名() default 默认值

权限修饰符:只能public和default(默认)

返回值类型:8种基本数据类型和String,Class,enum,Annotaion及他们的数组

默认值限制:编译器对元素的默认值有些挑剔,元素的值不能为null并且不能有不确定的默认值(即要么有默认值,要么使用时提供值),所以一般我们在定义注解时便加上默认值。


元注解

定义注解一定要使用到Java给我们提供的四种元注解,用于注解其他注解的。

  1. @Target

  2. @Retention

  3. @Documented

  4. @Inherited

我们重点关注@Target和@Retention.


@Target

说明定义的注解所作用的范围(可以用于修饰什么)。

取值(ElementType)有:

意义
CONSTRUCTOR构造器声明
FIELD属性声明(包括enum实例)
LOCAL_VARIABLE局部变量声明
METHOD方法声明
PACKAGE包声明
PARAMETER参数声明
TYPE用于描述类、接口(包括注解类型) 或enum声明

@Retention

表示需要在什么级别保存该注释信息,用于描述注解的生命周期(被描述的注解在什么范围内有效)

取值(RetentionPoicy)有:

意义
SOURCE在源文件中有效(即源文件保留)
CLASS在class文件中有效(即class保留)
RUNTIME在运行时有效(即运行时保留)


自定义的注解

参数校验定义的常用注解:

注解意义
NotNull参数不能为空
On数值的范围
OnMax最大值不能超过
OnMin最小值不能低于
Email邮箱格式



反射

反射中牵涉的类有:

Class,Method,Parameter,Annotation,Field

获取方式
ClassClass.forName(“”); 
clazz.getClass();
Type.class;
Methodclazz.getMethods();
Parametermethod.getParameters();
constructor.getParameters();
Annotationclazz.getAnnotations();
method.getAnnotations();
field.getAnnotations()
Fieldclazz.getFields();

用好反射的关键在于了解反射的API,之后我会单独一篇讲下我们常用的反射API。



Spring AOP的使用

我将借助Spring AOP来实现找到这些注解的功能。我这里只讲讲浅显一点的,因为很多人对于Spring AOP的使用还不了解,所以我只讲声明式编程,就是注解实现的。

使用比较简单,只需三步走:

    1、定义切面类

    2、指定切入点

    3、定义通知类型


  1. @Component //声明这是一个组件

  2. @Aspect //声明这是一个切面

  3. public class ServiceAspect {


  4. //定义切入点,没有方法体

  5. @Pointcut("@annotation(定义的注解)")

  6. public void pointcut(){ }


  7. /*

  8. * 前置通知,使用pointcut()上注册的切入点

  9. *

  10. * @param joinPoint 接受JoinPoint切入点对象,可以没有该参数

  11. */

  12. @Before("pointcut()")

  13. public void before(JoinPoint joinPoint){

  14. }


  15. //后置通知

  16. @After("pointcut()")

  17. public void after(JoinPoint joinPoint){

  18. }


  19. //环绕通知

  20. @Around("pointcut()")

  21. public void around(JoinPoint joinPoint){

  22. MethodSignature signature = (MethodSignature) joinPoint.getSignature();

  23. Method method = signature.getMethod();

  24. //反射就此打开序幕

  25. }


  26. //后置返回通知

  27. @AfterReturning("pointcut()")

  28. public void afterReturn(JoinPoint joinPoint){


  29. }


  30. //抛出异常后通知

  31. @AfterThrowing(pointcut="pointcut()", throwing="ex")

  32. public void afterThrow(JoinPoint joinPoint, Exception ex){

  33. }


  34. }

AOP底层原理是使用动态代理,动态代理有JDK动态代理和cglib动态代理,这里暂不细说了。



异常

自定义异常类

  1. public class FastValidatorException extends RuntimeException {

  2. public FastValidatorException(String message) {

  3. super(message);

  4. }

  5. }

设计时有两种失败模式:快速失败和安全失败

当参数校验,不符合要求时,快速失败将直接抛出此异常,安全失败将收集所有失败返回。

如:

  1. private void emptyResult(String fieldName) {

  2. if (isFailFast) {

  3. throw new FastValidatorException(fieldName + "不能为空");

  4. } else {

  5. formatResult(fieldName + "不能为空");

  6. }

  7. }


  8. private void formatResult(String msg) {

  9. if (!msg.isEmpty()) {

  10. result.getErrors().add(msg);

  11. }

  12. }


处理异常

如果使用快速失败模式,那么使用者将要对异常做全局统一处理。

将参数异常类信息,封装成比较友好的信息给前端。

  1. @RestControllerAdvice

  2. public class ErrorHandler {


  3. @ExceptionHandler(FastValidatorException.class)

  4. public JSONResult handle(FastValidatorException ex) {

  5. return new JSONResult(ex.getMessage(), ex.getStatus());

  6. }

  7. }


造之后的感想

还是hibernate validator做的好!  我服!

  .

  .

  .

but

造轮子能迫使我去了解更多的知识点,能迫使我去了解轮子的原理,也能加深我对知识的理解,顺便还能吹吹!

做的过程中你会思考如何优化它,一遍遍的推倒重来,会想到用怎么来解耦?用什么提高扩展性,灵活性?

造轮子的意义在于能让你不断的思考和学习。无论造的好坏,行动就好。


发现这个面试视频不错,分享给大家,公众号回复: 面试视频



海内存知己,天涯若比邻,喜欢就关注吧


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存