日更挑战-java注解实现代码调试


越不懂的越爱装
大家都同等:IT世界没有难不难,只有是否了解过

挑战目录

为什么可以使用注解进行代码调试

  • 因为研究源码的方法里面有一个重要的方法是:为每个方法写上描述方法作用的log,然后运行时会打印这些方法的运行过程。那么流程就一目了然了。
  • 静态代理(Aspectd):我们可以在编译时改变编译的代码(需要特殊处理的编译器, Aspectd的编译器 ),去代理目标方法,在目标方法前后织入我们的代码段。
  • 动态代理(Spring AOP): 不会去修改字节码,而是在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法

注解的实现

  • 元注解:为注解的注解,用于定义注解

    分别为@Retention、 @Target、 @Document、 @Inherited和@Repeatable(JDK1.8加入)五种

    • @Retention 注解的作用时间:源码(编译期阶段),class(类加载阶段),运行时(JVM阶段)

      • @Retention(RetentionPolicy.SOURCE) 在源码,不在class,不在运行时
      • @Retention(RetentionPolicy.CLASS) 在源码,在class,不在运行时 (默认的)
      • @Retention(RetentionPolicy.RUNTIME) 在源码,在class,在运行时
    • @Target 注解的作用范围:接口、类、枚举、注解、枚举的常量、方法、方法参数、属性字段、构造函数、局部变量、泛型方法、泛型类、泛型接口 、任意类型除了 class

      • @Target(ElementType.TYPE) 作用接口、类、枚举、注解
      • @Target(ElementType.FIELD) 作用属性字段、枚举的常量
      • @Target(ElementType.METHOD) 作用方法
      • @Target(ElementType.PARAMETER) 作用方法参数
      • @Target(ElementType.CONSTRUCTOR) 作用构造函数
      • @Target(ElementType.LOCAL_VARIABLE)作用局部变量
      • @Target(ElementType.ANNOTATION_TYPE)作用于注解(@Retention注解中就使用该属性)
      • @Target(ElementType.PACKAGE) 作用于包
      • @Target(ElementType.TYPE_PARAMETER) 作用于类型泛型,即泛型方法、泛型类、泛型接口 (jdk1.8加入)
      • @Target(ElementType.TYPE_USE) 类型使用.可以用于标注任意类型除了 class (jdk1.8加入)
    • @Document 注解的Javadoc文档

    • @Inherited 注解作用与类的继承:如果java父类使用了该注解,当子类没有使用注解时。默认也使用该注解。

    • @Repeatable 可以使用多次:可以以不同的作用多次作用目标

  • 注解的属性:为注解的属性,用于定义注解里面的变量
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
public @interface TestAnnotation {
   String methodName() default ""; //属性一
   String desc() default ""; //属性二
}

获取注解属性:根据@Retention的描述,知道可能有三种方式

  • 源码(编译期阶段)
    • 使用注解处理器 Annotation Processing Tool 简称APT

      1. 实现注解处理器(继承 AbstractProcessor 抽象类)

        public class TestProcessor extends AbstractProcessor {
        
           @Override
           public SourceVersion getSupportedSourceVersion() {
               //处理器支持的版本
               return SourceVersion.RELEASE_8;
           }
        
           @Override
           public Set<String> getSupportedAnnotationTypes() {
               Set<String> 处理器支持处理的注解集合 = new HashSet<>();
               处理器支持处理的注解集合.add(Test.class.toString());
               return 处理器支持处理的注解集合;
           }
        
           /**
            * @param processingEnvironment
            */
           @Override
           public synchronized void init(ProcessingEnvironment processingEnvironment){
               super.init(processingEnvironment);
           }
        
           @Override
           public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
               Set<? extends Element> es = roundEnvironment.getElementsAnnotatedWith(Test.class);//获取被Test注解标记的元素集合
               es.forEach(e -> { //e 标识被标注的对象,相当于反射获取的那个
                   //获取注解标注在哪
                   e.getKind().isClass();
                   e.getKind().isField();
                   e.getKind().isInterface();
                   //获取被标注的地方的名字
                   e.getSimpleName();
                   //获取使用的注解对象
                   Test test = e.getAnnotation(Test.class);
                   //获取对象属性
                   test.name();
                   //使用反射api对e和test对象的操作
                   ...
               });
               return false;
           }
        }
        
      2. 声明注解处理器

        • 创建resources/META-INF/services/javax.annotation.processing.Process文件
          在文件中添加注解处理器的全类名(一行只能写一个)

        • 如果是作为第三方jar包提供给别人 : 需要在maven打包时增加如下配置,主要也是把javax. annotation.processing.Processor文件打包到对应目录:

        <build>
            <resources>
                <resource>
                    <directory>src/main/resources</directory>
                    <excludes>
                        <exclude>META-INF/**/*</exclude>
                    </excludes>
                </resource>
            </resources>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>2.6</version>
                    <executions>
                        <execution>
                            <id>process-META</id>
                            <phase>prepare-package</phase>
                            <goals>
                                <goal>copy-resources</goal>
                            </goals>
                            <configuration>
                                <outputDirectory>target/classes</outputDirectory>
                                <resources>
                                    <resource>
                                        <directory>${basedir}/src/main/resources/</directory>
                                        <includes>
                                            <include>**/*</include>
                                        </includes>
                                    </resource>
                                </resources>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>
        
  • class(类加载阶段)
  • 运行时(JVM阶段)
    • 使用反射

      反射提供的支持方法有:

      1. 是否存在注解对象:isAnnotationPresent(Class<? extends Annotation> annotationClass) )

      2. 获取指定注解对象:getAnnotation(Class<A> annotationClass)

      3. 获取所有注解对象数组:getAnnotations()

      4. 示例

        /**
        * 获取类注解属性
        */
        Class<C> c = C.class;
        boolean exist = c.isAnnotationPresent(A.class);
          if(exist){
             A a = c.getAnnotation(A.class);
             System.out.println(a.name());//注解A中定义了name属性
          }
          
        try {
          /**
          * 获取变量注解属性
          */
              Field name = c.getDeclaredField("name");
              boolean nameAExist = name.isAnnotationPresent(Name.class);
            if(nameAExist){
               Name nameA = name.getAnnotation(Name.class);
               System.out.println(nameA.value());
            }
            
            /**
          * 获取方法注解属性
          */
            Method setName = c.class.getDeclaredMethod("setName");
            if (setName!=null){
                A a = setName.getAnnotation(A.class);
                A[] as = a.value();
                for (A a : as) {
                   System.out.println(a.value());
                }
             }
            } catch (NoSuchFieldException e) {
              e.printStackTrace();
            }
        

使用静态代理(Aspectd)实现方法日志打印

  1. 定义注解

    @Retention(RetentionPolicy.SOURCE)
    @Target(ElementType.METHOD)
    public @interface TestAnnotation {
        String methodName() default "";
        String desc() default "";
    }
    
  2. 实现处理

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.reflect.MethodSignature;
    
    import java.lang.reflect.Method;
    
    @Aspect
    public class mp {
    
        @Around("@annotation(TestAnnotation)")
        public Object beforeReturnValue(ProceedingJoinPoint point) throws Throwable {
            Method methodSignart = ((MethodSignature) point.getSignature()).getMethod();
            TestAnnotation methodAnnotation = methodSignart.getAnnotation(TestAnnotation.class);
            if (methodAnnotation != null) {
                String methodName = methodAnnotation.methodName();
                String desc = methodAnnotation.desc();
                System.out.println("----------方法开始执行----------" + methodName);
                System.out.println("----------方法描述----------" + desc);
                Object proceed = point.proceed();
                System.out.println("----------方法执行结束----------" + methodName);
                return proceed;
            }
            return point.proceed();
        }
    
    }
    
  3. 编写测试类

    public class TestMain {
       public static void main(String[] args) {
         new Test().test("x");
    }
    }
    
    public class Test {
        @TestAnnotation
        String test(String name){
            System.out.println("exe test");
            return name;
        }
    }
    

使用动态代理(Spring AOP)实现方法日志打印

略,以后写Spring AOP实现原理的时候在写

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容