1
头图

Author: Brother Xiaofu
Blog: https://bugstack.cn

Precipitate, share, grow, and let yourself and others gain something! 😄

I. Introduction

one-sided!

On January 3rd, Tolstoy said: "How great a writer is, he is only writing one side of himself". Not to mention me, not to mention us!

Although we don't write articles, we write requirements, code, and comments. When we encounter issues that need to be discussed, they often become points of contention. This is good, that is bad, what are you using!

When you narrow the road, you will receive fewer new ideas, new ideas, new horizons, and very important income. Only by horizontal comparison, reference, and filling in gaps, can you have more ideas in your mind, whether it is in writing code, in financial management, or in life.

The purpose of demand

Have you ever been using IntelliJ IDEA for development, you need to get the SQL statement to execute and copy it out for verification, the statement is always like this: SELECT * FROM USER WHERE id = ? AND name = ? and you need to ? number with the input parameter value?

Of course, this requirement is not really big, and you can even use other methods to solve it. Then this chapter will provide you with a new idea, which may be handled in a way that you have almost never experienced.

Then in the case of this chapter, we use the IDEA Plugin-based development capability to inject the bytecode instrumentation probe, the Javaagent-based capability, into the code. Then, through the enhanced bytecode, com.mysql.jdbc.PreparedStatement -> executeInternal is obtained, so as to obtain the SQL statement that can be directly tested.

3. Case development

1. Engineering structure

guide-idea-plugin-probe
├── .gradle
├── probe-agent
│   ├── src
│   │   └── main
│   │       └── java
│   │           └── cn.bugstack.guide.idea.plugin
│   │               ├── MonitorMethod.java
│   │               └── PreAgent.java
│   └── build.gradle
└── probe-plugin
│   └── src
│   │   └── main
│   │       ├── java
│   │       │    └── cn.bugstack.guide.idea.plugin
│   │       │        └── utils
│   │       │        │    └── PluginUtil.java
│   │       │        └── PerRun.java
│   │       └── resources
│   │           └── META-INF
│   │                └── plugin.xml
│    └── build.gradle
├── build.gradle    
└── gradle.properties

source code to get : #public number: bugstack wormhole stack reply: idea to download all IDEA plug-in development source code

In this IDEA plug-in project, the project structure is divided into 2 blocks:

  • probe-agent: probe module, used for compiling and packaging to provide bytecode enhancement services for probe-plugin module
  • probe-plugin: plug-in module, which java.programPatcher , and obtains and prints the SQL statements that perform database operations.

2. Bytecode enhancement to get SQL

The bytecode enhancement method here adopts the Byte-Buddy bytecode framework, which is simpler to use. In the process of using it, it is a bit like using AOP's interception method to obtain the information you need.

In addition, when gradle is packaged and built, you need to add the shadowJar module and package Premain-Class This part of the code can view

2.1 Probe Entry

cn.bugstack.guide.idea.plugin.PreAgent

//JVM 首先尝试在代理类上调用以下方法
public static void premain(String agentArgs, Instrumentation inst) {
    AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> {
        return builder
                .method(ElementMatchers.named("executeInternal")) // 拦截任意方法
                .intercept(MethodDelegation.to(MonitorMethod.class)); // 委托
    };
    new AgentBuilder
            .Default()
            .type(ElementMatchers.nameStartsWith("com.mysql.jdbc.PreparedStatement"))
            .transform(transformer)
            .installOn(inst);
}
  • Through the Byte-buddy configuration, the matching classes and methods can be intercepted, because under this class and method, the complete execution of SQL statements can be obtained.

2.2 Intercepting SQL

cn.bugstack.guide.idea.plugin.MonitorMethod

@RuntimeType
public static Object intercept(@This Object obj, @Origin Method method, @SuperCall Callable<?> callable, @AllArguments Object... args) throws Exception {
    try {
        return callable.call();
    } finally {
        String originalSql = (String) BeanUtil.getFieldValue(obj, "originalSql");
        String replaceSql = ReflectUtil.invoke(obj, "asSql");
        System.out.println("数据库名称:Mysql");
        System.out.println("线程ID:" + Thread.currentThread().getId());
        System.out.println("时间:" + new Date());
        System.out.println("原始SQL:\r\n" + originalSql);
        System.out.println("替换SQL:\r\n" + replaceSql);
    }
}
  • @This Object obj parameters is a configurable operation. For example, 061e62639ea8a3 is to obtain the execution object of the current class, and @Origin Method method is to obtain the execution method.
  • In the finally block, we can get the attribute information of the current class through reflection, and get the executed SQL through reflection, and print it out.

2.3 Compile and package

Before testing and developing IDEA Plugin, we need to perform a packaging operation, which is to package the bytecode-enhanced code into a Jar package. in build.gradle -> shadowJar

  • After packaging and compiling, you can see the Jar under build -> libs: probe-agent-1.0-SNAPSHOT-all.jar This Jar is used for bytecode enhancement processing.

2.4 Test Verification

Here, before using the written bytecode enhancement component for the plug-in, a test verification can be done to avoid the need to start the plug-in every time to do the test.

unit test

public class ApiTest {

    public static void main(String[] args) throws Exception {

        String URL = "jdbc:mysql://127.0.0.1:3306/itstack?characterEncoding=utf-8";
        String USER = "root";
        String PASSWORD = "123456";
        Class.forName("com.mysql.jdbc.Driver");

        Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
        String sql="SELECT * FROM USER WHERE id = ? AND name = ?";
        PreparedStatement statement = conn.prepareStatement(sql);
        statement.setLong(1,1L);
        statement.setString(2,"谢飞机");
        ResultSet rs = statement.executeQuery();

        while (rs.next()) {
            System.out.println(rs.getString("name") + " " + rs.getString("address"));
        }

    }

}
  • VM options: -javaagent:your_path\libs\probe-agent-1.0-SNAPSHOT-all.jar
  • Note that when the test is running, you need to configure VM options for ApiTest to print intercepted SQL information

test result

原始SQL:
SELECT * FROM USER WHERE id = ? AND name = ?
替换SQL:
SELECT * FROM USER WHERE id = 1 AND name = '谢飞机'
谢飞机 北京.大兴区.通明湖公园
  • Ok, so we can intercept the SQL statements that can be copied and executed, and then we will do the processing of IDEA Plugin.

3. Introduce the probe Jar through plug-in development

Next, we need to copy the developed bytecode enhancement Jar package to the libs in the IDEA Plugin plug-in development module (you can create it yourself), and then load implementation fileTree(dir: 'libs', includes: ['*jar']) in the plugin.xml configuration so that you can find this jar package in the program and configured into the program.

3.1 Copy jar to libs

3.2 build.gradle configuration loading

dependencies {
    implementation fileTree(dir: 'libs', includes: ['*jar'])
}
  • Through the implementation fileTree of loading the file tree introduced by 061e62639eab88, the Jar we configured is loaded into the program operation.

3.3 Introducing javaagent into the program

cn.bugstack.guide.idea.plugin.PerRun

public class PerRun extends JavaProgramPatcher {

    @Override
    public void patchJavaParameters(Executor executor, RunProfile configuration, JavaParameters javaParameters) {

        RunConfiguration runConfiguration = (RunConfiguration) configuration;
        ParametersList vmParametersList = javaParameters.getVMParametersList();
        vmParametersList.addParametersString("-javaagent:" + agentCoreJarPath);
        vmParametersList.addNotEmptyProperty("guide-idea-plugin-probe.projectId", runConfiguration.getProject().getLocationHash());

    }

}
  • By inheriting the JavaProgramPatcher class, implementing the patchJavaParameters -javaagent package that we need to load through the configuration attribute.
  • In this way, when the plug-in has been installed through IDEA and the code is run, the function of intercepting and printing SQL will be executed.

3.4 plugin.xml add configuration

<extensions defaultExtensionNs="com.intellij">
    <!-- Add your extensions here -->
    <java.programPatcher implementation="cn.bugstack.guide.idea.plugin.PerRun"/>
</extensions>
  • After that, you also need to configure the developed loading class to java.programPatcher so that it can be loaded when the program is running.

4. Test verification

  • Prepare a project with database operations If it is other, you need to extend
  • After starting the plugin, open your project, run the unit tests, and view the print area

start plugin

  • If you are new to download the code, you can run it in probe-plugin -> Tasks -> intellij -> runIde.

unit test

@Test
public void test_update(){
    User user = new User();
    user.setId(1L);
    user.setName("谢飞机");
    user.setAge(18);
    user.setAddress("北京.大兴区.亦庄经济开发区");
    userDao.update(user);
}

test result

22:30:55.593 [main] DEBUG cn.bugstack.test.demo.infrastructure.dao.UserDao.update[143] - ==>  Preparing: UPDATE user SET name=?,age=?,address=? WHERE id=? 
22:30:55.625 [main] DEBUG cn.bugstack.test.demo.infrastructure.dao.UserDao.update[143] - ==> Parameters: 谢飞机(String), 18(Integer), 北京.大兴区.亦庄经济开发区(String), 1(Long)
数据库名称:Mysql
线程ID:1
原始SQL:
UPDATE user SET name=?,age=?,address=?
        WHERE id=?
替换SQL:
UPDATE user SET name='谢飞机',age=18,address='北京.大兴区.亦庄经济开发区'
        WHERE id=1
  • From the test results, we can see that we can get the SQL statements that are directly tested and verified, so we don't need to modify and test after copying the SQL with question marks.

V. Summary

  • First of all, in this chapter, we initially try to use the multi-module method to create projects, which can better maintain the code modules required by various projects. You can also try to use gradle to create a multi-module project
  • This article is just an introduction to the use of bytecode instrumentation enhancement. This technology can also be applied to more scenarios, and various tools to improve R&D efficiency have been developed.
  • Learn how the additional Jar package is loaded into the project, and how to let javaagent import the probe component developed by itself through configuration.

6. Series Recommendations


小傅哥
4.8k 声望28.4k 粉丝

CodeGuide | 程序员编码指南 - 原创文章、案例源码、资料书籍、简历模版等下载。