1. An overview of the

AOP (aspect oriented programming) concept is now very widely used, the following is excerpted from baidu Encyclopedia explanation, relatively easy to understand

In the software industry, AOP for the abbreviation of Aspect Oriented Programming, meaning: section-oriented Programming, through pre-compilation and runtime dynamic proxy to achieve unified maintenance of program functions of a technology. AOP is a continuation of OOP, a hot topic in software development, and an important content in Spring framework. It is a derivative paradigm of functional programming. Using AOP, each part of the business logic can be isolated, thus reducing the degree of coupling between each part of the business logic, improving the reusability of the program, and improving the efficiency of development.

AOP is a programming idea, but it can be implemented in many ways, such as Spring, AspectJ, JavaAssist, ASM, etc. Since I’m an Android developer, I’ll use some examples from Android.

  • JakeWharton’s Hugo is a typical application that uses the custom Gradle plugin + AspectJ to print the parameters, return results, and execution times of a specified annotated method to Logcat for easy debugging
  • Due to the recent learning of Java bytecode and ASM knowledge, I also followed the lead and wrote a Koala, which realized the same function as Hugo, printing the parameters, return results and execution time of the method of specific annotations to Logcat, which is convenient for development and debugging. However, I use a custom Gradle plugin + ASM

So what is ASM? Here is an introduction to ASM 3.0, an introduction to AOP.

ASM is a Java bytecode manipulation framework. It can be used to dynamically generate classes or enhance the functionality of existing classes. ASM can either generate binary class files directly or dynamically change the behavior of classes before they are loaded into the Java virtual machine. Java classes are stored in rigorously formatted.class files that have enough metadata to parse all the elements of the class: class names, methods, attributes, and Java bytecodes (instructions). After reading information from class files, ASM can change class behavior, analyze class information, and even generate new classes based on user requirements.

To put it simply, using javac to compile.java files into.class files, the contents of the.class files are different, but they all have the same format. ASM uses the visitor pattern, Scan the contents of the.class file from beginning to end in a format that is unique to the.class file. During the scan, you can do something with the.class file

Java bytecode & Virtual machine

2.1 Java bytecode

Java bytecode is a file that can be compiled into a.class file using javac. The.class file contains the bytecode content of the.java file. For example, the following Demo. Java code is very simple:

package com.lijiankun24.classpractice;

public class Demo {

    private int m;

    public int inc(a) {
        return m + 1; }}Copy the code

Generate the corresponding Demo.class file through javac compilation, open demo. class with plain text file, the contents of the binary stream in 8-bit byte base units, ostensibly composed of hexadecimal symbols, This long string of hexadecimal symbols complies with the Java Virtual machine specification

cafe babe 0000 0034 0013 0a00 0400 0f09
0003 0010 0700 1107 0012 0100 016d 0100
0149 0100 063c 696e 6974 3e01 0003 2829
5601 0004 436f 6465 0100 0f4c 696e 654e
756d 6265 7254 6162 6c65 0100 0369 6e63
0100 0328 2949 0100 0a53 6f75 7263 6546
696c 6501 0009 4465 6d6f 2e6a 6176 610c
0007 0008 0c00 0500 0601 0004 4465 6d6f
0100 106a 6176 612f 6c61 6e67 2f4f 626a
6563 7400 2100 0300 0400 0000 0100 0200
0500 0600 0000 0200 0100 0700 0800 0100
0900 0000 1d00 0100 0100 0000 052a b700
01b1 0000 0001 000a 0000 0006 0001 0000
0001 0001 000b 000c 0001 0009 0000 001f
0002 0001 0000 0007 2ab4 0002 0460 ac00
0000 0100 0a00 0000 0600 0100 0000 0600
0100 0d00 0000 0200 0e
Copy the code

If you run the javap -verbose demo. class command to view the content in demo. class, see the following figure

As you can see from the figure above, the.class file contains constant pools, field tables, method tables, and property tables. How do you parse out the contents of a constant pool, method table from a binary stream based on 8-bit bytes? In this article there is a detailed introduction to the bytecode structure of.class files. This article takes a simple example, hand – by – hand analysis of the hexadecimal conformance representation of.class files

2.2 Java VM Class Loading Mechanism

The structure of.class files is described in the previous section, but.class files are static and must eventually be loaded by the VIRTUAL machine before they can be executed.

Generally speaking, a.class file contains a Java class, and.class files are closely related to Java classes. When it comes to loading time for.class files, we have to mention the life cycle of Java classes. As we all know, the life cycle of Java classes includes seven steps: loading, verification, preparation, parsing, initialization, use, and unloading. The Java Virtual Machine specification does not specify the loading time of Java classes, but it specifies the initialization time of Java classes, and the loading must be before the initialization. This indirectly dictates when.class files should be loaded.

There are five cases in which a class must be initialized. These five cases are called active references to Java classes. All references to Java classes other than active references are called passive references.

As mentioned above, the life cycle of a Java class is divided into load, validate, prepare, parse, initialize, use, and unload. The most important step is the first five steps of load, validate, prepare, parse, and initialize. What happens in these five steps?

Take a simple example, as shown below. In the following Constant class, there is a static code block and a static variable. When is value assigned? When is a static code block executed? The answer is in the initialization phase of the class.

public class Constant {

    static {
        System.out.println("Constant init!");
    }

    public static String value = "lijiankun24!";
}
Copy the code

In a Java class, if you have a static code block or static variable, the compiler automatically generates a class constructor for that class (note, not the instance constructor). The class constructor executes the static code block and initializes the static variable. The class constructor is executed during the initialization phase of the class

When it comes to loading Java classes, we have to talk about the ClassLoader in Java, and the parent delegate model and its benefits must also be clear.

The above is just a cursory overview. For more information about the five active references, class lifecycle, class constructor, class loader, and parent delegate model, see this article understanding class loading mechanisms in the JVM for more details

2.3 Java VM bytecode execution engine

A very important area of the Java memory model is the Java Virtual machine stack. Each method in Java pushes a stack frame into the Java virtual machine stack as it executes, and when the method completes, the stack frame is pushed off the stack. The most important concepts in stack frame are the local variable table and operand stack. When executing the bytecode of a Java method, Java bytecode instructions are actually called to manipulate the local variable table and operand stack, and finally the execution result is returned. This article is recommended if you want to learn about Java bytecode instructions.

In addition to the method execution process, you also need to learn about method calls in Java. A method call is the process of confirming a direct reference to a method through a symbolic reference to a method in a.class file. This process may occur at load time or at run time. Some methods have direct references to the method at load time, such as static methods, private methods, and instance constructor methods. In addition to parsing, static dispatch of a method also determines a direct reference to the method at load time, and such methods are often overloaded. Some methods acknowledge direct references to methods at runtime, such as overwritten methods, which need to be specific to the actual type of the object when called, so specific Java bytecode Invokevirtual is required to determine the appropriate method.

The Java virtual machine is executed based on the interpretation of the stack, which is the Java virtual machine stack. The interpretation execution is relative to the compilation execution, which means that the code is interpreted and executed after the bytecode instruction set is generated by the compilation. This does not have to understand too deep, understand these definitions

Stack frames, method calls, parsing, static dispatch, dynamic dispatch, and stack-based interpretation execution of the Java virtual machine (JVM) are described above. See the VIRTUAL Machine bytecode Execution Engine for more information.

Visitor pattern & ASM

3.1 Visitor Mode

The ASM library is a Java bytecode level code analysis and modification tool. How does ASM relate to the visitor pattern? The visitor pattern is mainly used to modify and operate some data structure is relatively stable data, through the previous learning, we know that the structure of the.class file is fixed, mainly has constant pool, field table, method table, property table and other contents, by using the visitor pattern in the.class file in the scanning of the contents of each table, You can modify these. Before learning about ASM, you can use this article to learn about the Visitor pattern the Visitor pattern and ASM.

3.2 Introduction and use of ASM libraries

ASM can either produce binary.class files directly or dynamically modify class behavior before classes are loaded into the JVM. This article introduces the structure of the ASM library and several important Core apis, including ClassVisitor, ClassReader, ClassWriter, MethodVisitor, AdviceAdapter, etc. And through two simple examples, how to modify the bytecode of a method in a Java class and modify the bytecode of an attribute.

In the beginning, you may not know the execution of bytecode, which makes it difficult to use ASM. ASMifier is a help tool provided by ASM. You can write object code first and compile it into a.class file using javac. Then use ASMifier to analyze the.class file to get the ASM code corresponding to the code to be inserted.

For details on how to use the Core Api and ASMifier of the ASM library, see the ASM library introduction and use in this article.

Four Koala.

Finally, after learning the theoretical knowledge, TO practice, I wrote a small project, using the custom Gradle plugin + ASM to implement the same function as JakeWharton’s Hugo library, called Koala. Print the incoming parameters, return results, and execution times of a particular annotated method to Logcat for easy development and debugging.

4.1 Adding Koala Gradle Plugin dependencies

Add the following code to your project’s build.gradle:

    buildscript {
        repositories {
            maven {
                url "https://plugins.gradle.org/m2/"
            }
        }
        dependencies {
            classpath "gradle.plugin.com.lijiankun24:buildSrc:1.1.1"}}Copy the code

Add the following code to build.gradle in the module you want to use:

    apply plugin: "com.lijiankun24.koala-plugin"
Copy the code

4.2 Adding a Koala dependency

Gradle:

    compile 'com. Lijiankun24: koala: 1.1.2'
Copy the code

Maven:

<dependency> <groupId>com.lijiankun24</groupId> <artifactId> Koala </artifactId> <version>1.1.2</version> <type>pom</type>  </dependency>Copy the code

4.3 the use of

It’s very simple to use. Add @koalalog annotations to Java methods, as shown below:

    @KoalaLog
    public String getName(String first, String last) {
        SystemClock.sleep(15); // Don't ever really do this!
        return first + "" + last;
    }
Copy the code

When the above method is called, the output in Logcat looks like this:

09-04 20:51:38. 008, 12076-12076 / com lijiankun24. Practicedemo I / 0 koalalog: ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -- - -- - ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -- -- -- -- -- - 09-04 20:51:38. 008 12076-12076 / com. Lijiankun24. Practicedemo I / 1 koalalog: │ The class's name: Com. Lijiankun24. Practicedemo. MainActivity 09-04 20:51:38. 008, 12076-12076 / com. Lijiankun24. Practicedemo I / 2 koalalog: │ The method 's name: GetName (Java. Lang. String, Java. Lang. String) 09-04 20:51:38. 008, 12076-12076 / com lijiankun24. Practicedemo I / 3 koalalog: │ The arguments: [li, jiankun's joyous mood darkened] 09-04 20:51:38. 008, 12076-12076 / com. Lijiankun24. Practicedemo I / 4 koalalog: │ The result: Li jiankun's joyous mood darkened 09-04 20:51:38. 008, 12076-12076 / com lijiankun24. Practicedemo I / 5 koalalog: │ The cost time: 15 ms 09-04 20:51:38. 008. 12076-12076 / com lijiankun24. Practicedemo I / 6 koalalog: └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -- - -- - ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ -- -- -- -- -- -Copy the code

4.4 Confusion Rules

 -keep class com.lijiankun24.koala.** { *; }
Copy the code

Welcome star and Fork Koala as well as likes and favorites

  • Email: [email protected]
  • Home: lijiankun24.com
  • Jane: www.jianshu.com/u/1abe21b7f…
  • Weibo: weibo.com/lijiankun24