Preface

In our practical development, we often use reflection, I present an example of the logging function done by reflection in practical development.

Traditional logging

Some basic knowledge about reflection can be found in Reflection Primer.

I believe that logging is not new to you, in the actual development of some more sensitive data tables we need to record every operation of it.

First, let’s look at the traditional way of writing.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    @Test
    public void insertSelective() throws Exception {

        Content content = new Content() ;
        content.setContent("Hello JavaIsland");
        content.setCreatedate("2022-04-06");
        contentService.insertSelective(content) ;

        ContentLog log = new ContentLog();
        log.setContentid(content.getContentid());
        log.setContent("Hello JavaIsland");
        log.setCreatedate("2022-04-06");
        contentLogService.insertSelective(log);
    }

It is very simple to save the same data to the log table after saving the data table.

But this has several problems as follows.

  • If the data table has more fields, for example hundreds of them. Then the setter() method of the log table will have to be written hundreds of times, and only if they are all correct.
  • If a field is added to the data table one day, the field will have to be added in every place where the log is written, raising the cost of maintenance.

Reflection is needed to solve the above situation.

Building logs using reflection

Let’s take a look at the changes to the code that result from using reflection.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
    @Test
    public void insertSelective2() throws Exception {
        Content content = new Content();
        content.setContent("Hello JavaIsland");
        content.setContentname("JavaIsland");
        content.setCreatedate("2022-04-06");

        contentService.insertSelective(content);

        ContentLog log = new ContentLog();
        CommonUtil.setLogValueModelToModel(content, log);
        contentLogService.insertSelective(log);
    }

The same save log, no matter how many fields, only three lines of code can be solved, and even if the fields change after writing the log this code still does not need to change.

In fact, the main method here is CommonUtil.setLogValueModelToModel(content, log);

Let’s see how this is done;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
     * Generate log entity tool
     *
     * @param objectFrom
     * @param objectTo
     */
    public static void setLogValueModelToModel(Object objectFrom, Object objectTo) {
        Class<? extends Object> clazzFrom = objectFrom.getClass();
        Class<? extends Object> clazzTo = objectTo.getClass();

        for (Method toSetMethod : clazzTo.getMethods()) {
            String mName = toSetMethod.getName();
            if (mName.startsWith("set")) {
                //field name
                String field = mName.substring(3);

                //Get the from value
                Object value;
                try {
                    if ("LogId".equals(field)) {
                        continue;
                    }
                    Method fromGetMethod = clazzFrom.getMethod("get" + field);
                    value = fromGetMethod.invoke(objectFrom);

                    // Set the value
                    toSetMethod.invoke(objectTo, value);
                } catch (NoSuchMethodException e) {
                    throw new RuntimeException(e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

Before we can use it again we first need to build the main data table and then new a log table object.

In the setLogValueModelToModel() method.

  • Get the class types of the data table and log table objects respectively.
  • Get the set of all methods of the log object.
  • Iterate through the collection and get the name of the method.
  • Take only the method that starts with set in it, which is the set method. Because we need to assign a value to each field of the log object in the loop.
  • After that, intercept the method name to get the specific field name.
  • Using the previously intercepted field name, the getMethod() method returns the getter method for that field in the data table.
  • It is equivalent to executing String content = content.getContent();
  • Execute this method to get the specific value of the field.
  • Use the setter method of the current loop to assign a value to each field of the log object.
  • It is equivalent to executing log.setContent("asdsf");

where the field name is LogId and the current loop is skipped because LogId is the primary key of the log table and does not need to be assigned a value.

When the loop ends, the log object is also constructed. After that, you just need to save it to the database.

Summary

Reflection is actually very resource-intensive and should be used with caution, and it is necessary to cache method, field, constructor and other objects.