案例:参数填写默认值【注解+JDK动态代理】

曾经终败给现在 2023-10-10 14:15 80阅读 0赞

学如逆水行舟,不进则退~
今天记录一个小案例:当一个实例调用一个有参方法的时候,判断该参数是否为NULL,如果是NULL则给一个默认值。

1、定义一个校验是否为NULL并且给定默认值的参数

  1. package com.dongzi;
  2. import java.lang.annotation.*;
  3. @Documented
  4. @Retention(RetentionPolicy.RUNTIME)
  5. @Target(ElementType.PARAMETER)
  6. public @interface CheckParam {
  7. /**
  8. * 是否必填参数
  9. */
  10. boolean required() default false;
  11. /**
  12. * 默认值
  13. */
  14. String defaultValue() default "";
  15. }

2、定义一个为实现一个代理的统一行为人接口

  1. package com.dongzi;
  2. /**
  3. * 定义一个被代理的统一行为人
  4. */
  5. public interface People {
  6. /**
  7. * 所有人都具备讲话的能力
  8. */
  9. void say(String saySomething);
  10. }

3、定义一个实现该行为方式的学生类

  1. package com.dongzi;
  2. /**
  3. * 被代理的对象
  4. */
  5. public class Student implements People {
  6. @Override
  7. public void say(@CheckParam(required = true, defaultValue = "再见,小哥哥~~") String saySomething) {
  8. System.out.println("To Talk:" + saySomething);
  9. }
  10. }

4、定义一个动态代理对象

  1. package com.dongzi;
  2. import java.lang.annotation.Annotation;
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.Method;
  5. /**
  6. * 统一行为人的被代理类实现
  7. */
  8. public class PeopleInvocationHandlerImpl implements InvocationHandler {
  9. /**
  10. * 代理对象
  11. */
  12. private final Object proxyObj;
  13. public PeopleInvocationHandlerImpl(Object proxyObj) {
  14. this.proxyObj = proxyObj;
  15. }
  16. /**
  17. * @param proxy 代理类实例
  18. * @param method 代理方法
  19. * @param args 参数
  20. */
  21. @Override
  22. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  23. Class<?> proxyObjClass = this.proxyObj.getClass();
  24. Method[] methods = proxyObjClass.getMethods();
  25. Method myMethod = null;
  26. for (Method m : methods) {
  27. if (m.getName().equals(method.getName())) {
  28. myMethod = m;
  29. break;
  30. }
  31. }
  32. if (myMethod != null) {
  33. /*
  34. Annotation[i][j]
  35. i: 参数列表中第i个参数
  36. j: 第i个参数中的第j个注解
  37. */
  38. Annotation[][] annotations = myMethod.getParameterAnnotations();
  39. if (annotations.length > 0) {
  40. for (int i = 0; i < annotations.length; i++) {
  41. for (int j = 0; j < annotations[i].length; j++) {
  42. Annotation anno = annotations[i][j];
  43. if (anno instanceof CheckParam) {
  44. // 参数检查的注解
  45. if (((CheckParam) anno).required()) {
  46. String defaultValue = ((CheckParam) anno).defaultValue();
  47. // 对第i个参数并且符合参数检查的注解进行赋默认值
  48. // TODO:这里需要注意一下问题,方便测试使用的String类型参数,如果多个参数的话,类型也要为String,其他类型没有考虑,否则会因为参数类型不一致而无法正常执行invoke方法
  49. args[i] = (null == args[i] || "".equals(args[i])) ? defaultValue : args[i];
  50. }
  51. // TODO: 符合其他参数上的注解 if()
  52. }
  53. }
  54. }
  55. }
  56. }
  57. // 执行被代理类的方法
  58. // args参数类型必须全部匹配否则异常无法执行invoke反射到对应方法执行
  59. return method.invoke(this.proxyObj, args);
  60. }
  61. }

5、主程序

  1. package com.dongzi;
  2. import java.lang.reflect.Proxy;
  3. /**
  4. * 主程序
  5. */
  6. public class Main {
  7. public static void main(String[] args) {
  8. // 确定被代理的对象
  9. Student student = new Student();
  10. PeopleInvocationHandlerImpl handler = new PeopleInvocationHandlerImpl(student);
  11. /*
  12. 创建一个代理的实例对象:
  13. 创建代理对象必须要符合实现了同一个标准的接口 且 被代理对象只能把类型强制转换为接口
  14. */
  15. People studentProxyObj = (People) Proxy.newProxyInstance(student.getClass().getClassLoader(), student.getClass().getInterfaces(), handler);
  16. // 被代理之后,执行方法
  17. studentProxyObj.say("你好,小哥哥~~"); // 输出:To Talk:你好,小哥哥~~
  18. studentProxyObj.say(null); // 输出结果:To Talk:再见,小哥哥~~
  19. }
  20. }

6、执行结果

两次调用say()方法的结果:第一次调用传入参数,参数正常打印;第二次传入NULL,正常来说会打印NULL值,而不是输出一段描述,但是因为加了注解并给定默认值,所以会打印出defaultValue的值,而并不是NULL。
代理对象执行输出结果

附1:项目结构
项目结构

附2:一些关于JDK动态代理和注解

  • Java Reflect - 利用反射获取方法参数上的注解
  • Java JDK 动态代理(AOP)使用及实现原理分析

发表评论

表情:
评论列表 (有 0 条评论,80人围观)

还没有评论,来说两句吧...

相关阅读

    相关 代理-jdk动态代理

    1、基于接口的实现,要jdk动态代理的类必须要实现一个接口; 2、中介类:实现了InvocationHandler,并重写这个接口的 方法(public Object inv