Introduction
Logging is not code for handling business logic, but rather for monitoring. That’s why logging appearing in the middle of business logic is very uncomfortable to see.
Spring provides AOP, but I wanted to try implementing it myself without looking at Spring’s implementation. So, this time, I want to think about how to implement a great logging AOP.
Situation
Let’s write a sayHello
method in the Person
class and call it from main
. Here, let’s assume the person object is provided by a framework.
Person
|
|
Since we will create several styles of frameworks, let’s define a framework interface.
Framework
|
|
Next, let’s write the client code for execution. It receives a specific framework as a parameter, passes arguments to the framework to get a person object, and then executes the hello method.
Runner
|
|
Methods That Come to Mind Immediately
Hmm… I learned about reflection before, but it’s not magic. I thought there would be a function to detect method calls at runtime, but it wasn’t in the reflection documentation.
So, recalling what I learned from Hibernate, I thought of passing instances using the proxy pattern. If you apply the proxy pattern, you can implement actions before and after method calls, and the client can use the proxy object without knowing it’s a proxy.
So, let’s think of some methods based on the keyword ‘proxy pattern.’
[Method 1] Classic Proxy Pattern (Using Interface)
Define an interface for the Person class to implement.
Then, both the Person class and ProxyPerson class implement this interface. Finally, when the client requests a person class, you return the ProxyPerson class.
Problem
The developer is forced to implement the interface. Even if only the Person class is needed, you are forced to create an interface just for logging.
It felt wrong, so I didn’t even implement it in code.
[Method 2] Proxy Pattern Using Inheritance
Implement a ProxyPerson class that extends the Person class.
According to the Liskov Substitution Principle, passing a subclass works fine, so we use inheritance. The code is as follows:
PersonProxy
|
|
Then, before calling the sayHello method, you log and then execute the method.
In the framework, when a Person object is requested, you should return this PersonProxy object.
PersonFramework
|
|
run
|
|
Output
|
|
Problem
You have to write the ProxyPerson class… Who writes it? In the end, the developer using the framework has to write it. The same problem as the first method.
So, we need a way to manipulate things without writing the class…
[Method 2] Anonymous Class
Then, let’s not write a Proxy class! Instead, use an anonymous class!
In the anonymous class, you can access the Person class’s methods via super. So, log first, then call the method.
AnonymousFramework
|
|
run
|
|
Output
|
|
It prints well~ It works fine without the user having to write a proxy class file.
Summary of Problems with These Methods
But… if you think about it, there are several problems.
Can you apply logging to arbitrary classes?
As you can see from the method names, the framework knows about the Person class.
When developing the framework, you shouldn’t even know the Person class exists, and you should be able to apply logging to various classes in the same way.
How can we generalize this?
I found a hint: dynamic proxy.
https://docs.oracle.com/javase/8/docs/technotes/guides/reflection/proxy.html
I’ll cover this in the next post.
What if you want to do something other than logging before a method call?
Right now, before super.sayHello, we log with system.out.println, but what if you need to do something else? You might think of the following problems:
- The responsibility for changes increases.
- How do you add code for additional tasks…?
I think… to handle these two, it’s best to implement it using the observer pattern.
Before executing super.sayHello, dispatch an event like the following to an event store data structure:
|
|
The event store data structure stores subscribers by class and event type. When the dispatch method is executed, it passes the above argument to the subscriber and calls a specific method.
|
|
Wouldn’t it work like this?
(Here, subscriber means logging objects, etc.)
In Conclusion
This time, I implemented some very simple methods and looked at their problems. Next time, I’ll implement a framework that can log not just for the Person class, but for any class. I’ll develop it step by step~~