This is an automated email from the ASF dual-hosted git repository.

albumenj pushed a commit to branch 3.3
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.3 by this push:
     new 0b6f2e4a8d Add Dubbo Annotation Processor (Add deprecated method warn 
when called by user, #10850) (#11513)
0b6f2e4a8d is described below

commit 0b6f2e4a8d045e137ce2b440e7fdd79941c57ec5
Author: Andy Cheung <[email protected]>
AuthorDate: Fri Mar 24 09:48:08 2023 +0800

    Add Dubbo Annotation Processor (Add deprecated method warn when called by 
user, #10850) (#11513)
---
 .licenserc.yaml                                    |   1 +
 NOTICE                                             |   2 +-
 dubbo-annotation-processor/pom.xml                 | 165 ++++++++
 .../annotation/AnnotationProcessingHandler.java    |  44 +++
 .../annotation/AnnotationProcessorContext.java     | 139 +++++++
 .../annotation/DispatchingAnnotationProcessor.java | 162 ++++++++
 .../annotation/handler/DeprecatedHandler.java      | 110 ++++++
 .../org/apache/dubbo/annotation/permit/Permit.java | 429 +++++++++++++++++++++
 .../dubbo/annotation/permit/dummy/Child.java       |  33 ++
 .../dubbo/annotation/permit/dummy/GrandChild.java  |  43 +++
 .../dubbo/annotation/permit/dummy/Parent.java      |  36 ++
 .../annotation/permit/dummy/package-info.java      |  32 ++
 .../dubbo/annotation/permit/package-info.java      |  32 ++
 .../org/apache/dubbo/annotation/util/ASTUtils.java | 111 ++++++
 .../apache/dubbo/annotation/util/FileUtils.java    | 155 ++++++++
 .../services/javax.annotation.processing.Processor |   1 +
 .../src/main/resources/handlers.cfg                |  17 +
 .../dubbo/annotation/RealInvocationTest.java       |  64 +++
 .../apache/dubbo/annotation/TestingCommons.java    |  71 ++++
 .../common/DeprecatedMethodInvocationCounter.java  |  36 ++
 .../apache/dubbo/eci/extractor/JavassistUtils.java | 151 ++++++++
 .../org/apache/dubbo/eci/util/ReflectUtils.java    |  51 +++
 .../org/testing/dm/TestConstructorMethod.java      |  28 ++
 .../dm/TestConstructorMethodParentClass.java       |  27 ++
 .../testing/dm/TestConstructorMethodSubClass.java  |  30 ++
 .../org/testing/dm/TestDeprecatedMethod.java       |  26 ++
 .../testing/dm/TestInterfaceDeprecatedMethod.java  |  24 ++
 dubbo-build-tools/pom.xml                          |   8 +-
 .../common/DeprecatedMethodInvocationCounter.java  |  83 ++++
 ...DeprecatedMethodInvocationCounterConstants.java |  35 ++
 .../common/constants/LoggerCodeConstants.java      |   4 +
 .../DeprecatedMethodInvocationCounterTest.java     |  59 +++
 dubbo-config/dubbo-config-api/pom.xml              |   1 -
 dubbo-metadata/dubbo-metadata-processor/pom.xml    |  13 +
 .../java/org/apache/dubbo/dependency/FileTest.java |   2 +
 pom.xml                                            |  10 +-
 36 files changed, 2228 insertions(+), 7 deletions(-)

diff --git a/.licenserc.yaml b/.licenserc.yaml
index 5b8980c95e..de74c43f6c 100644
--- a/.licenserc.yaml
+++ b/.licenserc.yaml
@@ -98,6 +98,7 @@ header:
     - 
'dubbo-native/src/main/java/org/apache/dubbo/aot/generate/BasicJsonWriter.java'
     - 
'dubbo-native/src/main/java/org/apache/dubbo/aot/generate/ExecutableMode.java'
     - 
'dubbo-native/src/main/java/org/apache/dubbo/aot/generate/MemberCategory.java'
+    - 
'dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/**'
 
   comment: on-failure
 
diff --git a/NOTICE b/NOTICE
index a060218b41..d409c01b52 100644
--- a/NOTICE
+++ b/NOTICE
@@ -4,7 +4,7 @@ Copyright 2018-2023 The Apache Software Foundation
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
 
-This product contains code form the Netty Project:
+This product contains code from the Netty Project:
 
 The Netty Project
 =================
diff --git a/dubbo-annotation-processor/pom.xml 
b/dubbo-annotation-processor/pom.xml
new file mode 100644
index 0000000000..8a89f1bf74
--- /dev/null
+++ b/dubbo-annotation-processor/pom.xml
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xmlns="http://maven.apache.org/POM/4.0.0";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.apache.dubbo</groupId>
+    <artifactId>dubbo-annotation-processor</artifactId>
+    <version>1.0.0</version>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+
+        <!--  System dependencies may cause problems in checkstyle. -->
+        <checkstyle.skip>true</checkstyle.skip>
+        <checkstyle_unix.skip>true</checkstyle_unix.skip>
+        <rat.skip>true</rat.skip>
+        <jacoco.skip>true</jacoco.skip>
+
+        <maven.deploy.skip>true</maven.deploy.skip>
+
+        <junit_jupiter_version>5.9.0</junit_jupiter_version>
+        <javassist_version>3.29.2-GA</javassist_version>
+    </properties>
+
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <!-- JUnit Jupiter Engine to depend on the JUnit5 engine and JUnit 5 
API -->
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <version>${junit_jupiter_version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>${junit_jupiter_version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-params</artifactId>
+            <version>${junit_jupiter_version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.javassist</groupId>
+            <artifactId>javassist</artifactId>
+            <version>${javassist_version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.10.1</version>
+                <configuration>
+                    <proc>none</proc>
+                    <fork>true</fork>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>default-profile-in-jdk-8</id>
+            <activation>
+                <file>
+                    <exists>${java.home}/../lib/tools.jar</exists>
+                </file>
+                <jdk>(, 9)</jdk>
+            </activation>
+            <properties>
+                <toolsjar>${java.home}/../lib/tools.jar</toolsjar>
+            </properties>
+
+            <dependencies>
+                <dependency>
+                    <groupId>javac</groupId>
+                    <artifactId>javac</artifactId>
+                    <version>1.0</version>
+                    <scope>system</scope>
+                    <systemPath>${toolsjar}</systemPath>
+                </dependency>
+            </dependencies>
+        </profile>
+
+        <profile>
+            <id>legacy-apple-jdk-profile</id>
+            <activation>
+                <activeByDefault>false</activeByDefault>
+                <file>
+                    <exists>${java.home}/../Classes/classes.jar</exists>
+                </file>
+                <jdk>(, 9)</jdk>
+            </activation>
+
+            <properties>
+                <toolsjar>${java.home}/../Classes/classes.jar</toolsjar>
+            </properties>
+
+            <dependencies>
+                <dependency>
+                    <groupId>javac</groupId>
+                    <artifactId>javac</artifactId>
+                    <version>1.0</version>
+                    <scope>system</scope>
+                    <systemPath>${toolsjar}</systemPath>
+                </dependency>
+            </dependencies>
+        </profile>
+
+        <profile>
+            <id>add-export-greater-than-9</id>
+            <activation>
+                <activeByDefault>false</activeByDefault>
+                <jdk>[9, )</jdk>
+            </activation>
+
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-compiler-plugin</artifactId>
+                        <version>3.10.1</version>
+                        <configuration>
+                            <fork>true</fork>
+                            <proc>none</proc>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git 
a/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/AnnotationProcessingHandler.java
 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/AnnotationProcessingHandler.java
new file mode 100644
index 0000000000..4830e32704
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/AnnotationProcessingHandler.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.annotation;
+
+import javax.lang.model.element.Element;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+/**
+ * Represents an annotation processing handler, which is invoked by 
DispatchingAnnotationProcessor.
+ */
+public interface AnnotationProcessingHandler {
+
+    /**
+     * Set the annotations that this handler will handle.
+     *
+     * @return annotations to handle
+     */
+    Set<Class<? extends Annotation>> getAnnotationsToHandle();
+
+    /**
+     * Formal processing method.
+     *
+     * @param elements the elements that annotated by the annotations of 
getAnnotationsToHandle()
+     * @param annotationProcessorContext the annotation processor context 
object (Javac object encapsulation)
+     */
+    void process(Set<Element> elements,
+                 AnnotationProcessorContext annotationProcessorContext);
+}
diff --git 
a/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/AnnotationProcessorContext.java
 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/AnnotationProcessorContext.java
new file mode 100644
index 0000000000..958385aba6
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/AnnotationProcessorContext.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.annotation;
+
+import com.sun.source.util.Trees;
+import com.sun.tools.javac.api.JavacTrees;
+import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.tree.TreeMaker;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.Names;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import java.lang.reflect.Method;
+import java.util.Objects;
+
+/**
+ * The Context Object of Annotation Processor, which stores objects related to 
javac.
+ */
+public class AnnotationProcessorContext {
+    private JavacProcessingEnvironment javacProcessingEnvironment;
+    private JavacTrees javacTrees;
+    private TreeMaker treeMaker;
+    private Names names;
+    private Context javacContext;
+    private Trees trees;
+
+    private AnnotationProcessorContext() { }
+
+    private static <T> T jbUnwrap(Class<? extends T> iface, T wrapper) {
+        T unwrapped = null;
+        try {
+            final Class<?> apiWrappers = 
wrapper.getClass().getClassLoader().loadClass("org.jetbrains.jps.javac.APIWrappers");
+            final Method unwrapMethod = 
apiWrappers.getDeclaredMethod("unwrap", Class.class, Object.class);
+            unwrapped = iface.cast(unwrapMethod.invoke(null, iface, wrapper));
+        } catch (Throwable ignored) {
+        }
+
+        return unwrapped != null ? unwrapped : wrapper;
+    }
+
+    public static AnnotationProcessorContext 
fromProcessingEnvironment(ProcessingEnvironment processingEnv) {
+        AnnotationProcessorContext apContext = new 
AnnotationProcessorContext();
+
+        Object procEnvToUnwrap = processingEnv.getClass() == 
JavacProcessingEnvironment.class ?
+            processingEnv : jbUnwrap(JavacProcessingEnvironment.class, 
processingEnv);
+
+        JavacProcessingEnvironment jcProcessingEnvironment = 
(JavacProcessingEnvironment) procEnvToUnwrap;
+
+        Context context = jcProcessingEnvironment.getContext();
+
+        apContext.javacProcessingEnvironment = jcProcessingEnvironment;
+
+        apContext.javacContext = context;
+        apContext.javacTrees = JavacTrees.instance(jcProcessingEnvironment);
+        apContext.treeMaker = TreeMaker.instance(context);
+        apContext.names = Names.instance(context);
+
+        apContext.trees = Trees.instance(jcProcessingEnvironment);
+
+        return apContext;
+    }
+
+    // Auto-generated methods.
+
+    public JavacTrees getJavacTrees() {
+        return javacTrees;
+    }
+
+    public TreeMaker getTreeMaker() {
+        return treeMaker;
+    }
+
+    public Names getNames() {
+        return names;
+    }
+
+    public Context getJavacContext() {
+        return javacContext;
+    }
+
+    public Trees getTrees() {
+        return trees;
+    }
+
+    public JavacProcessingEnvironment getJavacProcessingEnvironment() {
+        return javacProcessingEnvironment;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        AnnotationProcessorContext context = (AnnotationProcessorContext) o;
+
+        if (!Objects.equals(javacTrees, context.javacTrees)) return false;
+        if (!Objects.equals(treeMaker, context.treeMaker)) return false;
+        if (!Objects.equals(names, context.names)) return false;
+        if (!Objects.equals(javacContext, context.javacContext))
+            return false;
+        return Objects.equals(trees, context.trees);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = javacTrees != null ? javacTrees.hashCode() : 0;
+        result = 31 * result + (treeMaker != null ? treeMaker.hashCode() : 0);
+        result = 31 * result + (names != null ? names.hashCode() : 0);
+        result = 31 * result + (javacContext != null ? javacContext.hashCode() 
: 0);
+        result = 31 * result + (trees != null ? trees.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "AnnotationProcessorContext{" +
+            "javacTrees=" + javacTrees +
+            ", treeMaker=" + treeMaker +
+            ", names=" + names +
+            ", javacContext=" + javacContext +
+            ", trees=" + trees +
+            '}';
+    }
+}
diff --git 
a/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/DispatchingAnnotationProcessor.java
 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/DispatchingAnnotationProcessor.java
new file mode 100644
index 0000000000..8c11d72eff
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/DispatchingAnnotationProcessor.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.annotation;
+
+import org.apache.dubbo.annotation.permit.Permit;
+import org.apache.dubbo.annotation.util.FileUtils;
+
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.processing.JavacRoundEnvironment;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Dispatching annotation processor, which uses a file to locate handlers and 
invoke them.
+ */
+@SupportedAnnotationTypes("*")
+public class DispatchingAnnotationProcessor extends AbstractProcessor {
+
+    private static Set<AnnotationProcessingHandler> handlers;
+
+    private static Set<AnnotationProcessingHandler> loadHandlers() {
+        List<String> classNames = 
FileUtils.loadConfigurationFileInResources("handlers.cfg");
+        Set<AnnotationProcessingHandler> tempHandlers = new HashSet<>();
+
+        for (String clsName : classNames) {
+            try {
+                Class<? extends AnnotationProcessingHandler> cls = (Class<? 
extends AnnotationProcessingHandler>) Class.forName(clsName);
+                AnnotationProcessingHandler r = 
cls.getConstructor().newInstance();
+
+                tempHandlers.add(r);
+
+            } catch (InstantiationException | NoSuchMethodException | 
InvocationTargetException |
+                     IllegalAccessException | ClassNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        return Collections.unmodifiableSet(tempHandlers);
+    }
+
+    private AnnotationProcessorContext apContext;
+
+    @Override
+    public synchronized void init(ProcessingEnvironment processingEnv) {
+        Permit.addOpens();
+        super.init(processingEnv);
+
+        if (processingEnv.getClass().getName().startsWith("org.eclipse.jdt.")) 
{
+            // Don't run on ECJ, since this processor is javac based.
+            processingEnv
+                .getMessager()
+                .printMessage(Diagnostic.Kind.WARNING, "The Annotation 
processor doesn't support ECJ.");
+
+            return;
+        }
+
+        if (handlers == null) {
+            handlers = loadHandlers();
+        }
+        
+        apContext = 
AnnotationProcessorContext.fromProcessingEnvironment(processingEnv);
+    }
+
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, 
RoundEnvironment roundEnv) {
+
+        if (processingEnv.getClass().getName().startsWith("org.eclipse.jdt.")) 
{
+            // Don't run on ECJ, since this processor is javac based.
+            return false;
+        }
+
+        for (AnnotationProcessingHandler i : handlers) {
+            Set<Element> elements = new HashSet<>(16);
+
+            for (Class<? extends Annotation> annotationClass : 
i.getAnnotationsToHandle()) {
+                elements.addAll(getElementsAnnotatedWith(annotationClass, 
roundEnv));
+            }
+
+            i.process(elements, apContext);
+        }
+
+        return false;
+    }
+
+    /**
+     * A hack to make comment only .java file pass the compilation by skipping 
package symbol scan.
+     *
+     * @param annotationClass the annotation class to find
+     * @param roundEnvironment the javac's round environment
+     * @return the elements annotated with specified annotation
+     */
+    private Set<? extends Element> getElementsAnnotatedWith(Class<? extends 
Annotation> annotationClass, RoundEnvironment roundEnvironment) {
+
+        TypeElement a = 
processingEnv.getElementUtils().getTypeElement(annotationClass.getCanonicalName());
+
+        // Detect the Javac's version.
+        if (Integer.parseInt(SourceVersion.latest().name().split("_")[1]) > 8) 
{
+            return roundEnvironment.getElementsAnnotatedWith(a);
+        }
+
+        Set<Element> result = Collections.emptySet();
+
+        try {
+            Class annotationSetScannerClass = 
Class.forName("com.sun.tools.javac.processing.JavacRoundEnvironment$AnnotationSetScanner");
+            Constructor scannerConstructor = 
Permit.getConstructor(annotationSetScannerClass, JavacRoundEnvironment.class, 
Set.class);
+            Object scanner = scannerConstructor.newInstance(roundEnvironment, 
result);
+
+            Set<? extends Element> rootElements1 = roundEnvironment
+                .getRootElements()
+                .stream()
+                .filter(x -> x.getClass() != Symbol.PackageSymbol.class)
+                .collect(Collectors.toSet());
+
+            for (Element element : rootElements1) {
+                Method scanningMethod = 
Permit.getMethod(annotationSetScannerClass, "scan", Element.class, 
TypeElement.class);
+                result = (Set<Element>) scanningMethod.invoke(scanner, 
element, a);
+            }
+
+        } catch (InstantiationException | NoSuchMethodException | 
ClassNotFoundException | InvocationTargetException |
+                 IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+
+        return result;
+    }
+
+    @Override
+    public SourceVersion getSupportedSourceVersion() {
+        return SourceVersion.latestSupported();
+    }
+}
diff --git 
a/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/handler/DeprecatedHandler.java
 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/handler/DeprecatedHandler.java
new file mode 100644
index 0000000000..c87cb8f6c9
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/handler/DeprecatedHandler.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.annotation.handler;
+
+import org.apache.dubbo.annotation.AnnotationProcessingHandler;
+import org.apache.dubbo.annotation.AnnotationProcessorContext;
+import org.apache.dubbo.annotation.util.ASTUtils;
+
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.TreeTranslator;
+
+import javax.lang.model.element.Element;
+import java.lang.annotation.Annotation;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Handles @Deprecated annotation and adds logger warn call to the methods 
that are annotated with it.
+ */
+public class DeprecatedHandler implements AnnotationProcessingHandler {
+
+    @Override
+    public Set<Class<? extends Annotation>> getAnnotationsToHandle() {
+        return new HashSet<>(
+            Collections.singletonList(Deprecated.class)
+        );
+    }
+
+    @Override
+    public void process(Set<Element> elements, AnnotationProcessorContext 
apContext) {
+        for (Element element : elements) {
+            // Only interested in methods.
+            if (!(element instanceof Symbol.MethodSymbol)) {
+                continue;
+            }
+
+            Symbol.ClassSymbol classSymbol = (Symbol.ClassSymbol) 
element.getEnclosingElement();
+
+            ASTUtils.addImportStatement(apContext, classSymbol, 
"org.apache.dubbo.common", "DeprecatedMethodInvocationCounter");
+
+            JCTree methodTree = apContext.getJavacTrees().getTree(element);
+
+            methodTree.accept(new TreeTranslator() {
+                @Override
+                public void visitMethodDef(JCTree.JCMethodDecl jcMethodDecl) {
+
+                    JCTree.JCBlock block = jcMethodDecl.body;
+
+                    if (block == null) {
+                        // No method body. (i.e. interface method declaration.)
+                        return;
+                    }
+
+                    ASTUtils.insertStatementToHeadOfMethod(block, 
jcMethodDecl, generateCounterStatement(apContext, classSymbol, jcMethodDecl));
+                }
+            });
+        }
+    }
+
+    /**
+     * Generate an expression statement like this:
+     * 
<code>DeprecatedMethodInvocationCounter.onDeprecatedMethodCalled("....");
+     *
+     * @param originalMethodDecl the method declaration that will add logger 
statement
+     * @param apContext annotation processor context
+     * @param classSymbol the enclosing class that will be the logger's name
+     * @return generated expression statement
+     */
+    private JCTree.JCExpressionStatement 
generateCounterStatement(AnnotationProcessorContext apContext,
+                                                                  
Symbol.ClassSymbol classSymbol,
+                                                                  
JCTree.JCMethodDecl originalMethodDecl) {
+
+        JCTree.JCExpression fullStatement = apContext.getTreeMaker().Apply(
+            com.sun.tools.javac.util.List.nil(),
+
+            apContext.getTreeMaker().Select(
+                
apContext.getTreeMaker().Ident(apContext.getNames().fromString("DeprecatedMethodInvocationCounter")),
+                apContext.getNames().fromString("onDeprecatedMethodCalled")
+            ),
+
+            com.sun.tools.javac.util.List.of(
+                
apContext.getTreeMaker().Literal(getMethodDefinition(classSymbol, 
originalMethodDecl))
+            )
+        );
+
+        return apContext.getTreeMaker().Exec(fullStatement);
+    }
+
+    private String getMethodDefinition(Symbol.ClassSymbol classSymbol, 
JCTree.JCMethodDecl originalMethodDecl) {
+        return classSymbol.getQualifiedName() + "."
+            + originalMethodDecl.name.toString() + "(" + 
originalMethodDecl.params.toString() + ")";
+    }
+}
diff --git 
a/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/Permit.java
 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/Permit.java
new file mode 100644
index 0000000000..3d5c941764
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/Permit.java
@@ -0,0 +1,429 @@
+/*
+ * Authored by Project Lombok and licensed by MIT License, which is attached 
below:
+ *
+ * Copyright (C) 2018-2021 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.apache.dubbo.annotation.permit;
+
+import org.apache.dubbo.annotation.permit.dummy.Parent;
+
+import sun.misc.Unsafe;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+// sunapi suppresses javac's warning about using Unsafe; 'all' suppresses 
eclipse's warning about the unspecified 'sunapi' key. Leave them both.
+// Yes, javac's definition of the word 'all' is quite contrary to what the 
dictionary says it means. 'all' does NOT include 'sunapi' according to javac.
+@SuppressWarnings({"sunapi", "all"})
+public class Permit {
+    private Permit() {
+    }
+
+
+    private static final long ACCESSIBLE_OVERRIDE_FIELD_OFFSET;
+    private static final IllegalAccessException INIT_ERROR;
+    private static final sun.misc.Unsafe UNSAFE = (sun.misc.Unsafe) 
reflectiveStaticFieldAccess(sun.misc.Unsafe.class, "theUnsafe");
+
+    static {
+        Field f;
+        long g;
+        Throwable ex;
+
+        try {
+            g = getOverrideFieldOffset();
+            ex = null;
+        } catch (Throwable t) {
+            f = null;
+            g = -1L;
+            ex = t;
+        }
+
+        ACCESSIBLE_OVERRIDE_FIELD_OFFSET = g;
+        if (ex == null) INIT_ERROR = null;
+        else if (ex instanceof IllegalAccessException) INIT_ERROR = 
(IllegalAccessException) ex;
+        else {
+            INIT_ERROR = new IllegalAccessException("Cannot initialize 
Unsafe-based permit");
+            INIT_ERROR.initCause(ex);
+        }
+    }
+
+    public static <T extends AccessibleObject> T setAccessible(T accessor) {
+        if (INIT_ERROR == null) {
+            UNSAFE.putBoolean(accessor, ACCESSIBLE_OVERRIDE_FIELD_OFFSET, 
true);
+        } else {
+            accessor.setAccessible(true);
+        }
+
+        return accessor;
+    }
+
+    private static long getOverrideFieldOffset() throws Throwable {
+        Field f = null;
+        Throwable saved = null;
+        try {
+            f = AccessibleObject.class.getDeclaredField("override");
+        } catch (Throwable t) {
+            saved = t;
+        }
+
+        if (f != null) {
+            return UNSAFE.objectFieldOffset(f);
+        }
+        // The below seems very risky, but for all AccessibleObjects in java 
today it does work, and starting with JDK12, making the field accessible is no 
longer possible.
+        try {
+            return 
UNSAFE.objectFieldOffset(Fake.class.getDeclaredField("override"));
+        } catch (Throwable t) {
+            throw saved;
+        }
+    }
+
+    static class Fake {
+        boolean override;
+        Object accessCheckCache;
+    }
+
+    public static Method getMethod(Class<?> c, String mName, Class<?>... 
parameterTypes) throws NoSuchMethodException {
+        Method m = null;
+        Class<?> oc = c;
+        while (c != null) {
+            try {
+                m = c.getDeclaredMethod(mName, parameterTypes);
+                break;
+            } catch (NoSuchMethodException e) {
+            }
+            c = c.getSuperclass();
+        }
+
+        if (m == null) throw new NoSuchMethodException(oc.getName() + " :: " + 
mName + "(args)");
+        return setAccessible(m);
+    }
+
+    public static Method permissiveGetMethod(Class<?> c, String mName, 
Class<?>... parameterTypes) {
+        try {
+            return getMethod(c, mName, parameterTypes);
+        } catch (Exception ignore) {
+            return null;
+        }
+    }
+
+    public static Field getField(Class<?> c, String fName) throws 
NoSuchFieldException {
+        Field f = null;
+        Class<?> oc = c;
+        while (c != null) {
+            try {
+                f = c.getDeclaredField(fName);
+                break;
+            } catch (NoSuchFieldException e) {
+            }
+            c = c.getSuperclass();
+        }
+
+        if (f == null) throw new NoSuchFieldException(oc.getName() + " :: " + 
fName);
+
+        return setAccessible(f);
+    }
+
+    public static Field permissiveGetField(Class<?> c, String fName) {
+        try {
+            return getField(c, fName);
+        } catch (Exception ignore) {
+            return null;
+        }
+    }
+
+    public static <T> T permissiveReadField(Class<T> type, Field f, Object 
instance) {
+        try {
+            return type.cast(f.get(instance));
+        } catch (Exception ignore) {
+            return null;
+        }
+    }
+
+    public static <T> Constructor<T> getConstructor(Class<T> c, Class<?>... 
parameterTypes) throws NoSuchMethodException {
+        return setAccessible(c.getDeclaredConstructor(parameterTypes));
+    }
+
+    private static Object reflectiveStaticFieldAccess(Class<?> c, String 
fName) {
+        try {
+            Field f = c.getDeclaredField(fName);
+            f.setAccessible(true);
+            return f.get(null);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public static boolean isDebugReflection() {
+        return !"false".equals(System.getProperty("lombok.debug.reflection", 
"false"));
+    }
+
+    public static void handleReflectionDebug(Throwable t, Throwable initError) 
{
+        if (!isDebugReflection()) return;
+
+        System.err.println("** LOMBOK REFLECTION exception: " + t.getClass() + 
": " + (t.getMessage() == null ? "(no message)" : t.getMessage()));
+        t.printStackTrace(System.err);
+        if (initError != null) {
+            System.err.println("*** ADDITIONALLY, exception occurred setting 
up reflection: ");
+            initError.printStackTrace(System.err);
+        }
+    }
+
+    public static Object invoke(Method m, Object receiver, Object... args) 
throws IllegalAccessException, InvocationTargetException {
+        return invoke(null, m, receiver, args);
+    }
+
+    public static Object invoke(Throwable initError, Method m, Object 
receiver, Object... args) throws IllegalAccessException, 
InvocationTargetException {
+        try {
+            return m.invoke(receiver, args);
+        } catch (IllegalAccessException e) {
+            handleReflectionDebug(e, initError);
+            throw e;
+        } catch (RuntimeException e) {
+            handleReflectionDebug(e, initError);
+            throw e;
+        } catch (Error e) {
+            handleReflectionDebug(e, initError);
+            throw e;
+        }
+    }
+
+    public static Object invokeSneaky(Method m, Object receiver, Object... 
args) {
+        return invokeSneaky(null, m, receiver, args);
+    }
+
+    public static Object invokeSneaky(Throwable initError, Method m, Object 
receiver, Object... args) {
+        try {
+            return m.invoke(receiver, args);
+        } catch (NoClassDefFoundError e) {
+            handleReflectionDebug(e, initError);
+            //ignore, we don't have access to the correct ECJ classes, so 
lombok can't possibly
+            //do anything useful here.
+            return null;
+        } catch (NullPointerException e) {
+            handleReflectionDebug(e, initError);
+            //ignore, we don't have access to the correct ECJ classes, so 
lombok can't possibly
+            //do anything useful here.
+            return null;
+        } catch (IllegalAccessException e) {
+            handleReflectionDebug(e, initError);
+            throw sneakyThrow(e);
+        } catch (InvocationTargetException e) {
+            throw sneakyThrow(e.getCause());
+        } catch (RuntimeException e) {
+            handleReflectionDebug(e, initError);
+            throw e;
+        } catch (Error e) {
+            handleReflectionDebug(e, initError);
+            throw e;
+        }
+    }
+
+    public static <T> T newInstance(Constructor<T> c, Object... args) throws 
IllegalAccessException, InvocationTargetException, InstantiationException {
+        return newInstance(null, c, args);
+    }
+
+    public static <T> T newInstance(Throwable initError, Constructor<T> c, 
Object... args) throws IllegalAccessException, InvocationTargetException, 
InstantiationException {
+        try {
+            return c.newInstance(args);
+        } catch (IllegalAccessException e) {
+            handleReflectionDebug(e, initError);
+            throw e;
+        } catch (InstantiationException e) {
+            handleReflectionDebug(e, initError);
+            throw e;
+        } catch (RuntimeException e) {
+            handleReflectionDebug(e, initError);
+            throw e;
+        } catch (Error e) {
+            handleReflectionDebug(e, initError);
+            throw e;
+        }
+    }
+
+    public static <T> T newInstanceSneaky(Constructor<T> c, Object... args) {
+        return newInstanceSneaky(null, c, args);
+    }
+
+    public static <T> T newInstanceSneaky(Throwable initError, Constructor<T> 
c, Object... args) {
+        try {
+            return c.newInstance(args);
+        } catch (NoClassDefFoundError e) {
+            handleReflectionDebug(e, initError);
+            //ignore, we don't have access to the correct ECJ classes, so 
lombok can't possibly
+            //do anything useful here.
+            return null;
+        } catch (NullPointerException e) {
+            handleReflectionDebug(e, initError);
+            //ignore, we don't have access to the correct ECJ classes, so 
lombok can't possibly
+            //do anything useful here.
+            return null;
+        } catch (IllegalAccessException e) {
+            handleReflectionDebug(e, initError);
+            throw sneakyThrow(e);
+        } catch (InstantiationException e) {
+            handleReflectionDebug(e, initError);
+            throw sneakyThrow(e);
+        } catch (InvocationTargetException e) {
+            throw sneakyThrow(e.getCause());
+        } catch (RuntimeException e) {
+            handleReflectionDebug(e, initError);
+            throw e;
+        } catch (Error e) {
+            handleReflectionDebug(e, initError);
+            throw e;
+        }
+    }
+
+    public static Object get(Field f, Object receiver) throws 
IllegalAccessException {
+        try {
+            return f.get(receiver);
+        } catch (IllegalAccessException e) {
+            handleReflectionDebug(e, null);
+            throw e;
+        } catch (RuntimeException e) {
+            handleReflectionDebug(e, null);
+            throw e;
+        } catch (Error e) {
+            handleReflectionDebug(e, null);
+            throw e;
+        }
+    }
+
+    public static void set(Field f, Object receiver, Object newValue) throws 
IllegalAccessException {
+        try {
+            f.set(receiver, newValue);
+        } catch (IllegalAccessException e) {
+            handleReflectionDebug(e, null);
+            throw e;
+        } catch (RuntimeException e) {
+            handleReflectionDebug(e, null);
+            throw e;
+        } catch (Error e) {
+            handleReflectionDebug(e, null);
+            throw e;
+        }
+    }
+
+    public static void reportReflectionProblem(Throwable initError, String 
msg) {
+        if (!isDebugReflection()) return;
+        System.err.println("** LOMBOK REFLECTION issue: " + msg);
+        if (initError != null) {
+            System.err.println("*** ADDITIONALLY, exception occurred setting 
up reflection: ");
+            initError.printStackTrace(System.err);
+        }
+    }
+
+    public static RuntimeException sneakyThrow(Throwable t) {
+        if (t == null) throw new NullPointerException("t");
+        return Permit.<RuntimeException>sneakyThrow0(t);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T {
+        throw (T) t;
+    }
+
+    private static Object getJdkCompilerModule() {
+               /* call public api: 
ModuleLayer.boot().findModule("jdk.compiler").get();
+                  but use reflection because we don't want this code to crash 
on jdk1.7 and below.
+                  In that case, none of this stuff was needed in the first 
place, so we just exit via
+                  the catch block and do nothing.
+                */
+
+        try {
+            Class<?> cModuleLayer = Class.forName("java.lang.ModuleLayer");
+            Method mBoot = cModuleLayer.getDeclaredMethod("boot");
+            Object bootLayer = mBoot.invoke(null);
+            Class<?> cOptional = Class.forName("java.util.Optional");
+            Method mFindModule = cModuleLayer.getDeclaredMethod("findModule", 
String.class);
+            Object oCompilerO = mFindModule.invoke(bootLayer, "jdk.compiler");
+            return cOptional.getDeclaredMethod("get").invoke(oCompilerO);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    private static Object getOwnModule() {
+        try {
+            Method m = Permit.getMethod(Class.class, "getModule");
+            return m.invoke(Permit.class);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    private static long getFirstFieldOffset(Unsafe unsafe) {
+        try {
+            return 
unsafe.objectFieldOffset(Parent.class.getDeclaredField("first"));
+        } catch (NoSuchFieldException e) {
+            // can't happen.
+            throw new RuntimeException(e);
+        } catch (SecurityException e) {
+            // can't happen
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void addOpens() {
+        Class<?> cModule;
+        try {
+            cModule = Class.forName("java.lang.Module");
+        } catch (ClassNotFoundException e) {
+            return; //jdk8-; this is not needed.
+        }
+
+        Unsafe unsafe = UNSAFE;
+
+        Object jdkCompilerModule = getJdkCompilerModule();
+        Object ownModule = getOwnModule();
+        String[] allPkgs = {
+            "com.sun.tools.javac.code",
+            "com.sun.tools.javac.comp",
+            "com.sun.tools.javac.file",
+            "com.sun.tools.javac.main",
+            "com.sun.tools.javac.model",
+            "com.sun.tools.javac.parser",
+            "com.sun.tools.javac.processing",
+            "com.sun.tools.javac.tree",
+            "com.sun.tools.javac.util",
+            "com.sun.tools.javac.jvm",
+            "com.sun.tools.javac.api"
+        };
+
+        try {
+            Method m = cModule.getDeclaredMethod("implAddOpens", String.class, 
cModule);
+            long firstFieldOffset = getFirstFieldOffset(unsafe);
+            unsafe.putBooleanVolatile(m, firstFieldOffset, true);
+            for (String p : allPkgs) m.invoke(jdkCompilerModule, p, ownModule);
+        } catch (Exception ignore) {}
+
+        try {
+            Method m = cModule.getDeclaredMethod("implAddExports", 
String.class, cModule);
+            long firstFieldOffset = getFirstFieldOffset(unsafe);
+            unsafe.putBooleanVolatile(m, firstFieldOffset, true);
+            for (String p : allPkgs) m.invoke(jdkCompilerModule, p, ownModule);
+        } catch (Exception ignore) {}
+    }
+}
diff --git 
a/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/dummy/Child.java
 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/dummy/Child.java
new file mode 100644
index 0000000000..46c647bedc
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/dummy/Child.java
@@ -0,0 +1,33 @@
+/*
+ * Authored by Project Lombok and licensed by MIT License, which is attached 
below:
+ *
+ * Copyright (C) 2018-2021 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.apache.dubbo.annotation.permit.dummy;
+
+@SuppressWarnings("all")
+public abstract class Child extends Parent {
+    private transient volatile boolean foo;
+    private transient volatile Object[] bar;
+    private transient volatile Object baz;
+
+}
diff --git 
a/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/dummy/GrandChild.java
 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/dummy/GrandChild.java
new file mode 100644
index 0000000000..c2bf737629
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/dummy/GrandChild.java
@@ -0,0 +1,43 @@
+/*
+ * Authored by Project Lombok and licensed by MIT License, which is attached 
below:
+ *
+ * Copyright (C) 2018-2021 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.apache.dubbo.annotation.permit.dummy;
+
+@SuppressWarnings("all")
+public final class GrandChild extends Child {
+    private Class<?> a;
+    private int b;
+    private String c;
+    private Class<?> d;
+    private Class<?>[] e;
+    private Class<?>[] f;
+    private int g;
+    private transient String h;
+    private transient Object i;
+    private byte[] j;
+    private byte[] k;
+    private byte[] l;
+    private volatile Object m;
+    private Object n;
+}
diff --git 
a/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/dummy/Parent.java
 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/dummy/Parent.java
new file mode 100644
index 0000000000..53cb8c8ce5
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/dummy/Parent.java
@@ -0,0 +1,36 @@
+/*
+ * Authored by Project Lombok and licensed by MIT License, which is attached 
below:
+ *
+ * Copyright (C) 2018-2021 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.apache.dubbo.annotation.permit.dummy;
+
+import java.io.OutputStream;
+
+@SuppressWarnings("all")
+public class Parent {
+    boolean first;
+    static final Object staticObj = OutputStream.class;
+    volatile Object second;
+    private static volatile boolean staticSecond;
+    private static volatile boolean staticThird;
+}
diff --git 
a/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/dummy/package-info.java
 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/dummy/package-info.java
new file mode 100644
index 0000000000..dcbf5a58a0
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/dummy/package-info.java
@@ -0,0 +1,32 @@
+/*
+ * Authored by Project Lombok and licensed by MIT License, which is attached 
below:
+ *
+ * Copyright (C) 2018-2021 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * This package recreates the type hierarchy of {@code 
java.lang.reflect.AccessibleObject} and friends (such as {@code 
java.lang.reflect.Method});
+ * its purpose is to allow us to ask {@code sun.misc.internal.Unsafe} about 
the exact offset of the {@code override} field of {@code AccessibleObject};
+ * asking about that field directly doesn't work after jdk14, presumably 
because the fields of AO are expressly hidden somehow.
+ * <p>
+ * NB: It's usually 12, on the vast majority of OS, VM, and architecture 
combos.
+ */
+package org.apache.dubbo.annotation.permit.dummy;
diff --git 
a/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/package-info.java
 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/package-info.java
new file mode 100644
index 0000000000..95b72451c6
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/package-info.java
@@ -0,0 +1,32 @@
+/*
+ * Authored by Project Lombok and licensed by MIT License, which is attached 
below:
+ *
+ * Copyright (C) 2018-2021 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * This is a reduced copy of the nqzero Permit-reflect project.
+ * https://github.com/nqzero/permit-reflect
+ * <p>
+ * Many thanks to nqzero. The permit-reflect project is, like lombok itself, 
licensed under the MIT license.
+ * See https://github.com/nqzero/permit-reflect/blob/master/License for 
license info.
+ */
+package org.apache.dubbo.annotation.permit;
diff --git 
a/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/util/ASTUtils.java
 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/util/ASTUtils.java
new file mode 100644
index 0000000000..bfd463785f
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/util/ASTUtils.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.annotation.util;
+
+import org.apache.dubbo.annotation.AnnotationProcessorContext;
+
+import com.sun.source.util.TreePath;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.util.ListBuffer;
+
+import java.util.List;
+
+/**
+ * Some utils about AST manipulating.
+ */
+public final class ASTUtils {
+
+    private ASTUtils() {
+        throw new UnsupportedOperationException("No instance of 'ASTUtils' for 
you! ");
+    }
+
+    public static void addImportStatement(AnnotationProcessorContext apContext,
+                                    Symbol.ClassSymbol classSymbol,
+                                    String packageName,
+                                    String className) {
+
+        JCTree.JCImport jcImport = apContext.getTreeMaker().Import(
+            apContext.getTreeMaker().Select(
+                
apContext.getTreeMaker().Ident(apContext.getNames().fromString(packageName)),
+                apContext.getNames().fromString(className)
+            ), false);
+
+        TreePath treePath = apContext.getTrees().getPath(classSymbol);
+        TreePath parentPath = treePath.getParentPath();
+        JCTree.JCCompilationUnit compilationUnit = (JCTree.JCCompilationUnit) 
parentPath.getCompilationUnit();
+
+        List<JCTree.JCImport> imports = compilationUnit.getImports();
+        if (imports.stream().noneMatch(x -> 
x.qualid.toString().contains(packageName + "." + className))) {
+
+            compilationUnit.accept(new JCTree.Visitor() {
+                @Override
+                public void visitTopLevel(JCTree.JCCompilationUnit that) {
+
+                    List<JCTree> defs = compilationUnit.defs;
+
+                    ListBuffer<JCTree> newDefs = new ListBuffer<>();
+
+                    newDefs.add(defs.get(0));
+                    newDefs.add(jcImport);
+                    newDefs.addAll(defs.subList(1, defs.size()));
+
+                    compilationUnit.defs = newDefs.toList();
+                }
+            });
+        }
+    }
+
+    /**
+     * Insert statement to head of the method.
+     *
+     * @param block the method body
+     * @param originalMethodDecl the method declaration that will add logger 
statement
+     * @param fullExpressionStatement the statement to insert.
+     */
+    public static void insertStatementToHeadOfMethod(JCTree.JCBlock block,
+                                                     JCTree.JCMethodDecl 
originalMethodDecl,
+                                                     JCTree.JCStatement 
fullExpressionStatement) {
+
+        boolean isConstructor = 
originalMethodDecl.name.toString().equals("<init>");
+        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
+
+        // In constructor, super(...) or this(...) should be the first 
statement.
+
+        if (isConstructor && !block.stats.isEmpty()) {
+
+            boolean startsWithSuper = 
block.stats.get(0).toString().startsWith("super(");
+            boolean startsWithThis = 
block.stats.get(0).toString().startsWith("this(");
+
+            if (startsWithSuper || startsWithThis) {
+                statements.add(block.stats.get(0));
+                statements.add(fullExpressionStatement);
+                statements.addAll(block.stats.subList(1, block.stats.size()));
+            } else {
+                statements.add(fullExpressionStatement);
+                statements.addAll(block.stats);
+            }
+
+        } else {
+            statements.add(fullExpressionStatement);
+            statements.addAll(block.stats);
+        }
+
+        block.stats = statements.toList();
+    }
+}
diff --git 
a/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/util/FileUtils.java
 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/util/FileUtils.java
new file mode 100644
index 0000000000..66a897913a
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/util/FileUtils.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.annotation.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Scanner;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Utilities of iterating file.
+ */
+public final class FileUtils {
+
+    private static final Pattern WINDOWS_PATH_PATTERN = 
Pattern.compile("file:/\\w:/.*");
+
+
+    private FileUtils() {
+        throw new UnsupportedOperationException("No instance of FileUtils for 
you! ");
+    }
+
+    public static List<Path> getAllClassFilePaths(String rootPath) {
+        List<Path> targetFolders;
+
+        try (Stream<Path> filesStream = Files.walk(Paths.get(rootPath))) {
+            targetFolders = filesStream.filter(x -> !x.toFile().isFile())
+                .filter(x -> x.toString().contains("classes") && 
!x.toString().contains("test-classes"))
+                .filter(x -> 
x.toString().contains("\\org\\apache\\dubbo".replace('\\', File.separatorChar)))
+                .collect(Collectors.toList());
+
+            return targetFolders;
+
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static byte[] openFileAsByteArray(String filePath) {
+        try (FileChannel fileChannel = FileChannel.open(Paths.get(filePath))) {
+
+            ByteBuffer byteBuffer = ByteBuffer.allocate((int) 
fileChannel.size());
+            fileChannel.read(byteBuffer);
+
+            return byteBuffer.array();
+
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static String openFileAsString(String filePath) {
+        return new String(openFileAsByteArray(filePath));
+    }
+
+    public static String getSourceFilePathFromClassFilePath(String 
classFilePath) {
+
+        String classesPathString = "\\target\\classes\\".replace("\\", 
File.separator);
+        String sourcesPathString = "\\src\\main\\java\\".replace("\\", 
File.separator);
+
+        String sourceFilePathByReplace = 
classFilePath.replace(classesPathString, sourcesPathString)
+            .replace(".class", ".java");
+
+        // Inner classes.
+        if (sourceFilePathByReplace.lastIndexOf('$') != -1) {
+            int dollarCharIndex = sourceFilePathByReplace.lastIndexOf('$');
+            String outerClassPath = sourceFilePathByReplace.substring(0, 
dollarCharIndex);
+
+            return outerClassPath + ".java";
+        }
+
+        return sourceFilePathByReplace;
+    }
+
+    public static List<String> loadConfigurationFileInResources(String path) {
+
+        ReadableByteChannel resourceReadableByteChannel = Channels.newChannel(
+                
Objects.requireNonNull(FileUtils.class.getClassLoader().getResourceAsStream(path)));
+
+        List<String> lines = new ArrayList<>();
+
+        try (Scanner scanner = new Scanner(resourceReadableByteChannel)) {
+
+            while (scanner.hasNextLine()) {
+                String line = scanner.nextLine().trim();
+
+                if (!line.startsWith("#") && !line.isEmpty()) {
+                    lines.add(line);
+                }
+            }
+
+            return lines;
+        }
+    }
+
+    /**
+     * <p>Get absolute path of resource.
+     *
+     * <p>Retained for testing. It won't work in JAR.
+     *
+     * @param path relative path of resources folder.
+     * @return absolute path of resource
+     */
+    public static String getResourceFilePath(String path) {
+        String resourceFilePath = 
FileUtils.class.getClassLoader().getResource(path).toString();
+
+        if (WINDOWS_PATH_PATTERN.matcher(resourceFilePath).matches()) {
+            resourceFilePath = resourceFilePath.replace("file:/", "");
+        } else {
+            resourceFilePath = resourceFilePath.replace("file:", "");
+        }
+
+        return resourceFilePath;
+    }
+
+    public static List<Path> getAllFilesInDirectory(Path targetFolder) {
+
+        try (Stream<Path> classFilesStream = Files.walk(targetFolder)) {
+
+            return classFilesStream
+                    .filter(x -> x.toFile().isFile())
+                    .collect(Collectors.toList());
+
+        } catch (IOException e) {
+            return Collections.emptyList();
+        }
+    }
+}
diff --git 
a/dubbo-annotation-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
 
b/dubbo-annotation-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 0000000000..fe51e15be0
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+org.apache.dubbo.annotation.DispatchingAnnotationProcessor
diff --git a/dubbo-annotation-processor/src/main/resources/handlers.cfg 
b/dubbo-annotation-processor/src/main/resources/handlers.cfg
new file mode 100644
index 0000000000..bcdca71445
--- /dev/null
+++ b/dubbo-annotation-processor/src/main/resources/handlers.cfg
@@ -0,0 +1,17 @@
+#
+#    Licensed to the Apache Software Foundation (ASF) under one or more
+#    contributor license agreements.  See the NOTICE file distributed with
+#    this work for additional information regarding copyright ownership.
+#    The ASF licenses this file to You under the Apache License, Version 2.0
+#    (the "License"); you may not use this file except in compliance with
+#    the License.  You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+
+org.apache.dubbo.annotation.handler.DeprecatedHandler
diff --git 
a/dubbo-annotation-processor/src/test/java/org/apache/dubbo/annotation/RealInvocationTest.java
 
b/dubbo-annotation-processor/src/test/java/org/apache/dubbo/annotation/RealInvocationTest.java
new file mode 100644
index 0000000000..9544990ffe
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/test/java/org/apache/dubbo/annotation/RealInvocationTest.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.annotation;
+
+import org.apache.dubbo.annotation.util.FileUtils;
+import org.apache.dubbo.eci.extractor.JavassistUtils;
+
+import javassist.bytecode.ClassFile;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Real invocation test of DispatchingAnnotationProcessor (and 
DeprecatedHandler).
+ */
+class RealInvocationTest {
+
+    /**
+     * [File name, Should the counter exist in the class file? ]
+     */
+    private static final Map<String, Boolean> FILES = new HashMap<>(5, 1);
+
+    static {
+        FILES.put("TestConstructorMethod.java", true);
+        FILES.put("TestDeprecatedMethod.java", true);
+        FILES.put("TestInterfaceDeprecatedMethod.java", false);
+        FILES.put("TestConstructorMethodParentClass.java", false);
+        FILES.put("TestConstructorMethodSubClass.java", true);
+    }
+
+    @Test
+    void test() {
+
+        for (Map.Entry<String, Boolean> i : FILES.entrySet()) {
+            String filePath = FileUtils.getResourceFilePath("org/testing/dm/" 
+ i.getKey());
+
+            Assertions.assertTrue(TestingCommons.compileTheSource(filePath), 
"Compile failed! ");
+
+            String classFilePath = filePath.replace(".java", ".class");
+            ClassFile classFile = JavassistUtils.openClassFile(classFilePath);
+            List<String> stringItems = 
JavassistUtils.getConstPoolStringItems(classFile.getConstPool());
+
+            Assertions.assertEquals(i.getValue(), 
stringItems.contains("org/apache/dubbo/common/DeprecatedMethodInvocationCounter"));
+        }
+    }
+}
diff --git 
a/dubbo-annotation-processor/src/test/java/org/apache/dubbo/annotation/TestingCommons.java
 
b/dubbo-annotation-processor/src/test/java/org/apache/dubbo/annotation/TestingCommons.java
new file mode 100644
index 0000000000..cca11f56e6
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/test/java/org/apache/dubbo/annotation/TestingCommons.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.annotation;
+
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.Locale;
+
+import static java.util.Arrays.asList;
+
+/**
+ * Common code of annotation processor testing.
+ */
+public final class TestingCommons {
+    private TestingCommons() {
+        throw new UnsupportedOperationException("No instance of TestingCommons 
for you! ");
+    }
+
+    private static class ObjectHolders {
+        static final JavaCompiler javaCompiler = 
ToolProvider.getSystemJavaCompiler();
+
+        static final StandardJavaFileManager javaFileManager = 
javaCompiler.getStandardFileManager(
+            null,
+            Locale.ROOT,
+            StandardCharsets.UTF_8
+        );
+    }
+
+    public static boolean compileTheSource(String filePath) {
+
+
+        JavaCompiler.CompilationTask compilationTask = 
ObjectHolders.javaCompiler.getTask(
+            null,
+            ObjectHolders.javaFileManager,
+            null,
+            asList("-parameters", "-Xlint:unchecked", "-nowarn", 
"-Xlint:deprecation"),
+            null,
+            getSourceFileJavaFileObject(filePath)
+        );
+
+        compilationTask.setProcessors(
+            Collections.singletonList(new DispatchingAnnotationProcessor())
+        );
+
+        return compilationTask.call();
+    }
+
+    private static Iterable<? extends JavaFileObject> 
getSourceFileJavaFileObject(String filePath) {
+
+        return ObjectHolders.javaFileManager.getJavaFileObjects(filePath);
+    }
+}
diff --git 
a/dubbo-annotation-processor/src/test/java/org/apache/dubbo/common/DeprecatedMethodInvocationCounter.java
 
b/dubbo-annotation-processor/src/test/java/org/apache/dubbo/common/DeprecatedMethodInvocationCounter.java
new file mode 100644
index 0000000000..cc34299c81
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/test/java/org/apache/dubbo/common/DeprecatedMethodInvocationCounter.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.common;
+
+/**
+ * Reduced mock of Deprecated method invocation counter.
+ */
+public final class DeprecatedMethodInvocationCounter {
+    private DeprecatedMethodInvocationCounter() {
+        throw new UnsupportedOperationException("No instance of 
DeprecatedMethodInvocationCounter for you! ");
+    }
+
+    /**
+     * Invoked by (modified) deprecated method.
+     *
+     * @param methodDefinition filled by annotation processor. (like 
'org.apache.dubbo.common.URL.getServiceName()')
+     */
+    public static void onDeprecatedMethodCalled(String methodDefinition) {
+        // Intended to be empty.
+    }
+}
diff --git 
a/dubbo-annotation-processor/src/test/java/org/apache/dubbo/eci/extractor/JavassistUtils.java
 
b/dubbo-annotation-processor/src/test/java/org/apache/dubbo/eci/extractor/JavassistUtils.java
new file mode 100644
index 0000000000..3a2475591a
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/test/java/org/apache/dubbo/eci/extractor/JavassistUtils.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.eci.extractor;
+
+import org.apache.dubbo.annotation.util.FileUtils;
+
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.ConstPool;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utilities of Javassist.
+ */
+public class JavassistUtils {
+    private static final Map<Class, Field> stringFieldCache = new HashMap<>(2, 
1);
+
+    private static Method getItemMethodCache = null;
+
+    private JavassistUtils() {
+        throw new UnsupportedOperationException("No instance of JavassistUtils 
for you! ");
+    }
+
+    public static ClassFile openClassFile(String classFilePath) {
+        try {
+            byte[] clsB = FileUtils.openFileAsByteArray(classFilePath);
+            return new ClassFile(new DataInputStream(new 
ByteArrayInputStream(clsB)));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    static List<Object> getConstPoolItems(ConstPool cp) {
+        List<Object> objects = new ArrayList<>(cp.getSize());
+
+        for (int i = 0; i < cp.getSize(); i++) {
+            objects.add(getConstPoolItem(cp, i));
+        }
+
+        return objects;
+    }
+
+    /**
+     * Calls ConstPool.getItem() method reflectively.
+     *
+     * @param cp The ConstPool object.
+     * @param index The index of items.
+     * @return The XXXInfo Object. Since it's invisible, return Object instead.
+     */
+    static Object getConstPoolItem(ConstPool cp, int index) {
+
+        if (getItemMethodCache == null) {
+            Class<ConstPool> cpc = ConstPool.class;
+            Method getItemMethod;
+            try {
+                getItemMethod = cpc.getDeclaredMethod("getItem", int.class);
+                getItemMethod.setAccessible(true);
+
+                getItemMethodCache = getItemMethod;
+
+            } catch (NoSuchMethodException e) {
+                throw new RuntimeException("Javassist internal method 
changed.", e);
+            }
+        }
+
+        try {
+            return getItemMethodCache.invoke(cp, index);
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            throw new RuntimeException("Javassist internal method changed.", 
e);
+        }
+    }
+
+    public static List<String> getConstPoolStringItems(ConstPool cp) {
+        List<Object> objects = getConstPoolItems(cp);
+        List<String> stringItems = new ArrayList<>(cp.getSize());
+
+        for (Object item : objects) {
+
+            Field stringField;
+
+            if (item != null) {
+                stringField = getStringFieldInConstPoolItems(item);
+
+                if (stringField == null) {
+                    continue;
+                }
+
+                Object fieldData;
+
+                try {
+                    fieldData = stringField.get(item);
+                } catch (IllegalAccessException e) {
+                    throw new RuntimeException("Javassist internal field 
changed.", e);
+                }
+
+                if (fieldData.getClass() == String.class) {
+                    stringItems.add((String) fieldData);
+                }
+            }
+        }
+
+        return stringItems;
+    }
+
+    /**
+     * Obtain the 'string' field in Utf8Info and StringInfo.
+     *
+     * @param item The instance of Utf8Info and StringInfo.
+     * @return 'string' field's value
+     */
+    static Field getStringFieldInConstPoolItems(Object item) {
+        if (stringFieldCache.containsKey(item.getClass())) {
+            return stringFieldCache.get(item.getClass());
+        } else {
+            try {
+                Field stringField = item.getClass().getDeclaredField("string");
+                stringField.setAccessible(true);
+                stringFieldCache.put(item.getClass(), stringField);
+
+                return stringField;
+            } catch (NoSuchFieldException ignored) {
+            }
+        }
+
+        return null;
+    }
+}
diff --git 
a/dubbo-annotation-processor/src/test/java/org/apache/dubbo/eci/util/ReflectUtils.java
 
b/dubbo-annotation-processor/src/test/java/org/apache/dubbo/eci/util/ReflectUtils.java
new file mode 100644
index 0000000000..681bdc0441
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/test/java/org/apache/dubbo/eci/util/ReflectUtils.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.eci.util;
+
+import java.lang.reflect.Field;
+
+/**
+ * Tools of reflective operations.
+ */
+public final class ReflectUtils {
+    private ReflectUtils() {
+        throw new UnsupportedOperationException("No instance of ReflectUtils 
for you! ");
+    }
+
+    /**
+     * Searches (a private) field in super classes recursively.
+     *
+     * @param cls the actual type
+     * @param name the field name
+     * @return the corresponding Field object, or null if the field does not 
exist.
+     */
+    public static Field getDeclaredFieldRecursively(Class cls, String name) {
+        try {
+            Field indexField = cls.getDeclaredField(name);
+            indexField.setAccessible(true);
+
+            return indexField;
+        } catch (NoSuchFieldException e) {
+            if (cls == Object.class) {
+                return null;
+            }
+
+            return getDeclaredFieldRecursively(cls.getSuperclass(), name);
+        }
+    }
+}
diff --git 
a/dubbo-annotation-processor/src/test/resources/org/testing/dm/TestConstructorMethod.java
 
b/dubbo-annotation-processor/src/test/resources/org/testing/dm/TestConstructorMethod.java
new file mode 100644
index 0000000000..c705ca8d13
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/test/resources/org/testing/dm/TestConstructorMethod.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.testing.dm;
+
+public class TestConstructorMethod {
+    @Deprecated
+    TestConstructorMethod() {
+        this("1");
+    }
+
+    TestConstructorMethod(String s) {
+    }
+}
diff --git 
a/dubbo-annotation-processor/src/test/resources/org/testing/dm/TestConstructorMethodParentClass.java
 
b/dubbo-annotation-processor/src/test/resources/org/testing/dm/TestConstructorMethodParentClass.java
new file mode 100644
index 0000000000..8e14e67c51
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/test/resources/org/testing/dm/TestConstructorMethodParentClass.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.testing.dm;
+
+public class TestConstructorMethodParentClass {
+
+    public TestConstructorMethodParentClass() {
+    }
+
+    public TestConstructorMethodParentClass(String s) {
+    }
+}
diff --git 
a/dubbo-annotation-processor/src/test/resources/org/testing/dm/TestConstructorMethodSubClass.java
 
b/dubbo-annotation-processor/src/test/resources/org/testing/dm/TestConstructorMethodSubClass.java
new file mode 100644
index 0000000000..3b71626c9b
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/test/resources/org/testing/dm/TestConstructorMethodSubClass.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.testing.dm;
+
+public class TestConstructorMethodSubClass extends 
TestConstructorMethodParentClass {
+
+    @Deprecated
+    public TestConstructorMethodSubClass(int i) {
+        super();
+    }
+
+    public TestConstructorMethodSubClass(String s) {
+        super(s);
+    }
+}
diff --git 
a/dubbo-annotation-processor/src/test/resources/org/testing/dm/TestDeprecatedMethod.java
 
b/dubbo-annotation-processor/src/test/resources/org/testing/dm/TestDeprecatedMethod.java
new file mode 100644
index 0000000000..6360c3e137
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/test/resources/org/testing/dm/TestDeprecatedMethod.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.testing.dm;
+
+public class TestDeprecatedMethod {
+
+    @Deprecated
+    public void m1() {
+        System.out.println("M1! ");
+    }
+}
diff --git 
a/dubbo-annotation-processor/src/test/resources/org/testing/dm/TestInterfaceDeprecatedMethod.java
 
b/dubbo-annotation-processor/src/test/resources/org/testing/dm/TestInterfaceDeprecatedMethod.java
new file mode 100644
index 0000000000..a2ce7af598
--- /dev/null
+++ 
b/dubbo-annotation-processor/src/test/resources/org/testing/dm/TestInterfaceDeprecatedMethod.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.testing.dm;
+
+public interface TestInterfaceDeprecatedMethod {
+
+    @Deprecated
+    public void m1();
+}
diff --git a/dubbo-build-tools/pom.xml b/dubbo-build-tools/pom.xml
index 28bae806b1..5c3b83e2dd 100644
--- a/dubbo-build-tools/pom.xml
+++ b/dubbo-build-tools/pom.xml
@@ -13,8 +13,8 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
-<project xmlns="http://maven.apache.org/POM/4.0.0";
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xmlns="http://maven.apache.org/POM/4.0.0";
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
     <modelVersion>4.0.0</modelVersion>
 
@@ -24,7 +24,7 @@
     <packaging>jar</packaging>
 
     <properties>
-      <maven.deploy.skip>true</maven.deploy.skip>
+        <maven.deploy.skip>true</maven.deploy.skip>
     </properties>
 
-</project>
\ No newline at end of file
+</project>
diff --git 
a/dubbo-common/src/main/java/org/apache/dubbo/common/DeprecatedMethodInvocationCounter.java
 
b/dubbo-common/src/main/java/org/apache/dubbo/common/DeprecatedMethodInvocationCounter.java
new file mode 100644
index 0000000000..b4245c5bfa
--- /dev/null
+++ 
b/dubbo-common/src/main/java/org/apache/dubbo/common/DeprecatedMethodInvocationCounter.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.common;
+
+import 
org.apache.dubbo.common.constants.DeprecatedMethodInvocationCounterConstants;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.LongAdder;
+
+/**
+ * Deprecated method invocation counter, which is used by annotation processor.
+ * <p>
+ * If an IDE says it is unused, just ignore it.
+ */
+public final class DeprecatedMethodInvocationCounter {
+    private DeprecatedMethodInvocationCounter() {
+        throw new UnsupportedOperationException("No instance of 
DeprecatedMethodInvocationCounter for you! ");
+    }
+
+    private static final ConcurrentHashMap<String, LongAdder> COUNTERS = new 
ConcurrentHashMap<>();
+
+    private static final ErrorTypeAwareLogger LOGGER = 
LoggerFactory.getErrorTypeAwareLogger(DeprecatedMethodInvocationCounter.class);
+
+    /**
+     * Invoked by (modified) deprecated method.
+     *
+     * @param methodDefinition filled by annotation processor. (like 
'org.apache.dubbo.common.URL.getServiceName()')
+     */
+    public static void onDeprecatedMethodCalled(String methodDefinition) {
+        if (!hasThisMethodInvoked(methodDefinition)) {
+            LOGGER.warn(
+                DeprecatedMethodInvocationCounterConstants.ERROR_CODE,
+                DeprecatedMethodInvocationCounterConstants.POSSIBLE_CAUSE,
+                DeprecatedMethodInvocationCounterConstants.EXTENDED_MESSAGE,
+                
DeprecatedMethodInvocationCounterConstants.LOGGER_MESSAGE_PREFIX + 
methodDefinition
+            );
+        }
+
+        increaseInvocationCount(methodDefinition);
+    }
+
+    private static void increaseInvocationCount(String methodDefinition) {
+        COUNTERS.computeIfAbsent(methodDefinition, k -> new LongAdder());
+        LongAdder adder = COUNTERS.get(methodDefinition);
+
+        adder.increment();
+    }
+
+    public static boolean hasThisMethodInvoked(String methodDefinition) {
+        return COUNTERS.containsKey(methodDefinition);
+    }
+
+    public static Map<String, Integer> getInvocationRecord() {
+        // Perform a deep-copy to avoid concurrent issues.
+        HashMap<String, Integer> copyOfCounters = new HashMap<>();
+
+        for (Map.Entry<String, LongAdder> entry : COUNTERS.entrySet()) {
+            copyOfCounters.put(entry.getKey(), entry.getValue().intValue());
+        }
+
+        return Collections.unmodifiableMap(copyOfCounters);
+    }
+}
diff --git 
a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/DeprecatedMethodInvocationCounterConstants.java
 
b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/DeprecatedMethodInvocationCounterConstants.java
new file mode 100644
index 0000000000..f747568491
--- /dev/null
+++ 
b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/DeprecatedMethodInvocationCounterConstants.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.common.constants;
+
+/**
+ * Constants of Deprecated Method Invocation Counter.
+ */
+public final class DeprecatedMethodInvocationCounterConstants {
+    private DeprecatedMethodInvocationCounterConstants() {
+        throw new UnsupportedOperationException("No instance of 
DeprecatedMethodInvocationCounterConstants for you! ");
+    }
+
+    public static final String ERROR_CODE = 
LoggerCodeConstants.COMMON_DEPRECATED_METHOD_INVOKED;
+
+    public static final String POSSIBLE_CAUSE = "invocation of deprecated 
method";
+
+    public static final String EXTENDED_MESSAGE = "";
+
+    public static final String LOGGER_MESSAGE_PREFIX = "Deprecated method 
invoked. The method is ";
+}
diff --git 
a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java
 
b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java
index 0682c5233d..c39fa1c698 100644
--- 
a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java
+++ 
b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/LoggerCodeConstants.java
@@ -92,6 +92,10 @@ public interface LoggerCodeConstants {
 
     String VULNERABILITY_WARNING = "0-28";
 
+    /**
+     * Used in annotation processor to indicate a deprecated method is invoked.
+     */
+    String COMMON_DEPRECATED_METHOD_INVOKED = "0-99";
 
     // Registry module
 
diff --git 
a/dubbo-common/src/test/java/org/apache/dubbo/common/DeprecatedMethodInvocationCounterTest.java
 
b/dubbo-common/src/test/java/org/apache/dubbo/common/DeprecatedMethodInvocationCounterTest.java
new file mode 100644
index 0000000000..b1ad0943a7
--- /dev/null
+++ 
b/dubbo-common/src/test/java/org/apache/dubbo/common/DeprecatedMethodInvocationCounterTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.common;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Map;
+
+/**
+ * Tests of DeprecatedMethodInvocationCounter.
+ */
+class DeprecatedMethodInvocationCounterTest {
+
+    private static final String METHOD_DEFINITION = 
"org.apache.dubbo.common.URL.getServiceName()";
+
+    @Test
+    void testRealInvocation() {
+        
Assertions.assertFalse(DeprecatedMethodInvocationCounter.hasThisMethodInvoked(METHOD_DEFINITION));
+
+        // Invoke a deprecated method.
+        URL url = 
URL.valueOf("dubbo://admin:[email protected]:20880/context/path#index?version=1.0.0&id=org.apache.dubbo.config.RegistryConfig#0");
+
+        // Not a typo, intentionally invoke twice.
+        invokeDeprecatedMethod(url);
+        invokeDeprecatedMethod(url);
+
+        
Assertions.assertTrue(DeprecatedMethodInvocationCounter.hasThisMethodInvoked(METHOD_DEFINITION));
+
+        Map<String, Integer> record = 
DeprecatedMethodInvocationCounter.getInvocationRecord();
+        Assertions.assertEquals(2, record.get(METHOD_DEFINITION));
+    }
+
+    private void invokeDeprecatedMethod(URL url) {
+        try {
+            Method m = URL.class.getMethod("getServiceName");
+            m.invoke(url);
+        } catch (InvocationTargetException | IllegalAccessException | 
NoSuchMethodException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/dubbo-config/dubbo-config-api/pom.xml 
b/dubbo-config/dubbo-config-api/pom.xml
index 42ee10a4e0..4687ce2285 100644
--- a/dubbo-config/dubbo-config-api/pom.xml
+++ b/dubbo-config/dubbo-config-api/pom.xml
@@ -226,6 +226,5 @@
             <version>1.17.6</version>
             <scope>test</scope>
         </dependency>
-
     </dependencies>
 </project>
diff --git a/dubbo-metadata/dubbo-metadata-processor/pom.xml 
b/dubbo-metadata/dubbo-metadata-processor/pom.xml
index 0cb2c9139d..009ce910e4 100644
--- a/dubbo-metadata/dubbo-metadata-processor/pom.xml
+++ b/dubbo-metadata/dubbo-metadata-processor/pom.xml
@@ -161,4 +161,17 @@
             <scope>test</scope>
         </dependency>
     </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <fork>true</fork>
+                    <proc>none</proc>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
 </project>
diff --git 
a/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java
 
b/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java
index 4f912e3001..7f4b3250b4 100644
--- 
a/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java
+++ 
b/dubbo-test/dubbo-test-modules/src/test/java/org/apache/dubbo/dependency/FileTest.java
@@ -48,6 +48,7 @@ class FileTest {
         ignoredModules.add(Pattern.compile("dubbo-parent"));
         ignoredModules.add(Pattern.compile("dubbo-core-spi"));
         ignoredModules.add(Pattern.compile("dubbo-demo.*"));
+        ignoredModules.add(Pattern.compile("dubbo-annotation-processor"));
 
         ignoredModulesInDubboAll.add(Pattern.compile("dubbo"));
         ignoredModulesInDubboAll.add(Pattern.compile("dubbo-bom"));
@@ -57,6 +58,7 @@ class FileTest {
         
ignoredModulesInDubboAll.add(Pattern.compile("dubbo-metadata-processor"));
         ignoredModulesInDubboAll.add(Pattern.compile("dubbo-native.*"));
         ignoredModulesInDubboAll.add(Pattern.compile("dubbo-spring-boot.*"));
+        
ignoredModulesInDubboAll.add(Pattern.compile("dubbo-annotation-processor.*"));
         ignoredModulesInDubboAll.add(Pattern.compile("dubbo-maven-plugin"));
     }
 
diff --git a/pom.xml b/pom.xml
index 0af455fd55..a792bd4a95 100644
--- a/pom.xml
+++ b/pom.xml
@@ -125,6 +125,7 @@
         <maven_flatten_version>1.3.0</maven_flatten_version>
         <maven_enforce_version>3.2.1</maven_enforce_version>
         <maven_antrun_version>3.1.0</maven_antrun_version>
+        
<dubbo_annotation_processor_version>1.0.0</dubbo_annotation_processor_version>
         <arguments />
         <checkstyle.skip>true</checkstyle.skip>
         <checkstyle_unix.skip>true</checkstyle_unix.skip>
@@ -134,6 +135,7 @@
     </properties>
 
     <modules>
+        <module>dubbo-annotation-processor</module>
         <module>dubbo-common</module>
         <module>dubbo-container</module>
         <module>dubbo-compiler</module>
@@ -177,6 +179,13 @@
     </dependencyManagement>
 
     <dependencies>
+
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-annotation-processor</artifactId>
+            <version>${dubbo_annotation_processor_version}</version>
+        </dependency>
+
         <!--JUnit Jupiter Engine to depend on the JUnit5 engine and JUnit 5 
API -->
         <dependency>
             <groupId>org.junit.jupiter</groupId>
@@ -710,7 +719,6 @@
                 <version>${maven_compiler_version}</version>
                 <configuration>
                     <compilerArgs>
-                        <compilerArg>-proc:none</compilerArg>
                         <compilerArg>-parameters</compilerArg>
                     </compilerArgs>
                     <fork>true</fork>

Reply via email to