Blog: bugstack.cn

Precipitation, share, grow, let yourself and others can gain something! 😄

One, foreword

The one-sided!

“What a great writer,” Tolstoy said on January 3, “writes only one side of himself.” How much more I, how much more we!

While we don’t write articles, we write requirements, we write code, we write comments, and when we come across a problem point that needs to be discussed, it often becomes a point of contention. This is good, that is bad, you use what what what what!

When you narrow your path, you reduce your access to new ideas, new ideas, new horizons and, very importantly, your income. Only by horizontal comparison and reference, can you have more ideas in your mind, whether in writing code, or in financial management, or in life.

Second, demand and purpose

Do you have IntelliJ IDEA in the development process, need to get the execution of SQL statement, copy out for verification, always such a statement: SELECT * FROM USER WHERE id =? AND name = ? Need to do it yourself again? What if the number is replaced by the input parameter?

Of course, this need is not that big, and you can even solve it in other ways. Then this chapter will provide you with a new way of thinking that you probably haven’t approached before.

So in the case of this chapter, we use the development ability based on IDEA Plugin to inject bytecode petting probe based on Javaagent into the code. Through the enhanced bytecode, access to com. The mysql. JDBC. PreparedStatement – > executeInternal execution object, which can directly test the SQL statement.

Case development

1. Engineering structure

Guide - idea - the plugin - the probe ├ ─ ─ the gradle ├ ─ ─ the probe - agent │ ├ ─ ─ the SRC │ │ └ ─ ─ the main │ │ └ ─ ─ Java │ │ └ ─ ─ Cn. Bugstack. Guide. Idea. The plugin │ │ ├ ─ ─ MonitorMethod. Java │ │ └ ─ ─ PreAgent. Java │ └ ─ ─ build. Gradle └ ─ ─ the probe - the plugin │ └ ─ ─ SRC │ │ └ ─ ─ the main │ │ ├ ─ ─ Java │ │ │ └ ─ ─ cn. Bugstack. Guide. Idea. The plugin │ │ │ └ ─ ─ utils │ │ │ │ └ ─ ─ PluginUtil. Java │ │ │ └ ─ ─ PerRun. Java │ │ └ ─ ─ resources │ │ └ ─ ─ meta-inf │ │ └ ─ ─ the plugin. The XML │ └ ─ ─ build. Gradle ├ ─ ─ build. Gradle └ ─ ─ gradle.propertiesCopy the code

Bugstack wormhole stack can be downloaded from the whole IDEA plug-in source code

In this IDEA plug-in project, the engineering structure is divided into two parts:

  • Probe-agent: Probe module used to compile and package bytecode enhancement services for use by the probe-plugin module
  • Probe-plugin: Plug-in module, passedjava.programPatcherLoad the bytecode enhancement package to get and print THE SQL statements that perform database operations.

2. Bytecode enhancement to obtain SQL

The byte-Buddy bytecode enhancement here is much simpler to use, somewhat like AOP interception, to get the information you need.

In addition, when gradle packages a build, you need to add the shadowJar module to include premain-class. You can check it out in this section of code

2.1 Probe Inlet

cn.bugstack.guide.idea.plugin.PreAgent

// The JVM first tries to call the following methods on the proxy class
public static void premain(String agentArgs, Instrumentation inst) {
    AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, javaModule) -> {
        return builder
                .method(ElementMatchers.named("executeInternal")) // Intercept any method
                .intercept(MethodDelegation.to(MonitorMethod.class)); / / to entrust
    };
    new AgentBuilder
            .Default()
            .type(ElementMatchers.nameStartsWith("com.mysql.jdbc.PreparedStatement"))
            .transform(transformer)
            .installOn(inst);
}
Copy the code
  • The byte-buddy configuration intercepts matching classes and methods, because the entire SQL statement can be retrieved from these classes and methods.

2.2 intercept SQL

cn.bugstack.guide.idea.plugin.MonitorMethod

@RuntimeType
public static Object intercept(@This Object obj, @Origin Method method, @SuperCallCallable<? > 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("Database name: Mysql");
        System.out.println("Thread ID:" + Thread.currentThread().getId());
        System.out.println("Time:" + new Date());
        System.out.println("SQL: \r\n" + originalSql);
        System.out.println("SQL: \r\n"+ replaceSql); }}Copy the code
  • Intercepting method inputs are configurable operations such as@This Object objTo get the execution object of the current class,@Origin Method methodIs to get the execution method.
  • In the finally block, we can get the property information of the current class by reflection, and the SQL executed by reflection, and print it out.

2.3 Compilation and Packaging

Before testing and developing the IDEA Plugin, we need to do a packaging operation, which is to package the bytecode enhanced code into a Jar package. In the build. Gradle – > shadowJar

  • After the package is compiled, you can see the Jar under build -> libs:The probe - agent - 1.0 - the SNAPSHOT - all. JarThis Jar is used to do bytecode enhancement.

2.4 Test and Verification

The bytecode enhancement component can be tested before being used by the plug-in to avoid having to start the plug-in each time.

Unit testing

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."Thank you airplane.");
        ResultSet rs = statement.executeQuery();

        while (rs.next()) {
            System.out.println(rs.getString("name") + "" + rs.getString("address")); }}}Copy the code
  • The VM options:- javaAgent: your path \libs\ probe-agent-1.0-snapshot-all.jar
  • Note that when the test runs, you need to configure VM options for ApiTest to print the intercepting SQL information

The test results

SQL: SELECT * FROM USER WHERE id =? AND name = ? Replace SQL: SELECT * FROM USER WHERE id =1 AND name = 'Thank you aeroplane'Xie Yiyi Beijing. Daxing District. Tongming Lake ParkCopy the code
  • Ok, now that we can intercept SQL statements that can be copied to execute, let’s do the IDEA Plugin processing.

3. Introduce the probe Jar through plug-in development

Next, we need to develop the bytecode enhancement Jar package, copy it to the IDEA Plugin Plugin development module liBS (can create their own), and then load implementation fileTree in plugin.xml configuration (dir: ‘libs’, includes: [‘*jar’]).

3.1 Copying jars to LIBS

3.2 Build. gradle configuration loaded

dependencies {
    implementation fileTree(dir: 'libs', includes: ['*jar'])
}
Copy the code
  • throughimplementation fileTreeIntroduce the method of loading file tree, and load our configured Jar into the program run.

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()); }}Copy the code
  • Through inheritanceJavaProgramPatcherClasses that implementpatchJavaParametersMethod to configure our own needs to be loaded via the Configuration property-javaagentThe package.
  • In this way, the interception and printing of SQL will be implemented when the code is run after the plug-in is installed through IDEA.

3.4 Plugin.xml Adds the configuration

<extensions defaultExtensionNs="com.intellij">
    <! -- Add your extensions here -->
    <java.programPatcher implementation="cn.bugstack.guide.idea.plugin.PerRun"/>
</extensions>
Copy the code
  • Then you need to configure the developed loading classes tojava.programPatcherThis way the program can be loaded when it runs.

4. Test and verification

  • Prepare a project with database operations that require JDBC,If it’s something else, you need to expand it yourself
  • After starting the plug-in, open your project, run the unit tests, and view the print area

Start the plugin

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

Unit testing

@Test
public void test_update(a){
    User user = new User();
    user.setId(1L);
    user.setName("Thank you airplane.");
    user.setAge(18);
    user.setAddress("Daxing District, Beijing. "" Yizhuang Economic Development Zone" ");
    userDao.update(user);
}
Copy the code

The test results

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:18(Integer), Beijing, China. Daxing District. Yizhuang Economic Development Zone (String),1(Long) Mysql thread ID:1SQL: UPDATE user SET name=? ,age=? ,address=? WHERE id=? SQL: UPDATE user SET name='Thank you aeroplane',age=18,address=Daxing District, Beijing. Yizhuang Economic Development Zone '
        WHERE id=1
Copy the code
  • As you can see from the test results, we can get the SQL statement directly to test validation, instead of copying the SQL with question marks and having to modify the test.

Five, the summary

  • First of all, we try to use the multi-module method to create a project in this chapter, which can better maintain all kinds of code modules required by a project.You can also try using Gradle to create multi-module projects
  • This article is just an introduction to the use of bytecode staking enhancement, but the technology can be used in many more scenarios to develop a variety of tools to improve development efficiency.
  • Know how additional Jar packages are loaded into the project and how to configure themjavaagentIntroduce our own developed probe components.

Six, series recommendation

  • ASM, Javassist, Byte-Buddy, Javaagent bytecode programming content extension learning
  • Scheme design: Based on IDEA plug-in development and bytecode staking technology, realize automatic analysis of r&d delivery quality
  • IDEA plug-in development completed, how to release?
  • Application development – level non – invasive full – link monitoring design
  • Lottery Lottery System – A four-tier architecture practice based on domain-driven design