Reflection implements the dynamic proxy mode of AOP (implementation principle of Spring AOP)
I haven't used spring for a long time. I suddenly picked up the book. I found myself unfamiliar with AOP.
In fact, AOP means Aspect-Oriented Programming.
OO focuses on our solution to the problem (encapsulated as a method), while AOP focuses on the commonalities of many solutions to the problem and supplements the OO idea!
Let's explain it with an example that people often give:
For example, there are many business methods in an application to be developed. However, we need to perform comprehensive monitoring or partial monitoring on the implementation of this method. maybe we will add a log record before some methods,
Let's write an example to see our simplest solution.
First, write the ihello. Java code of the interface as follows:
1 package sinosoft. DJ. AOP. staticaop;
2
3 Public interface ihello {
4 /***//**
5 * assume this is a business method
6 * @ Param name
7 */
8 void sayhello (string name );
9}
10
There is a method used to input the name "hello" added in; Let's write a class to implement the ihello interface.
Package sinosoft. DJ. AOP. staticaop;
Public class Hello implements ihello {
Public void sayhello (string name ){
System. Out. println ("hello" + name );
}
}
Now we need to add a log-recorded business for this business method. What can we do without changing the original code? Maybe, you will write a class to implement the ihello interface and rely on the class hello. The Code is as follows:
1 package sinosoft. DJ. AOP. staticaop;
2
3 Public class helloproxy implements ihello {
4 private ihello hello;
5
6 Public helloproxy (ihello Hello ){
7 This. Hello = Hello;
8}
9
10 public void sayhello (string name ){
11 logger. Logging (level. debuge, "sayhello method start .");
12 hello. sayhello (name );
13 logger. Logging (level. info, "sayhello method end! ");
14
15}
16
17}
18
The. Logger class and level enumeration Code are as follows:
Logger. Java
1 package sinosoft. DJ. AOP. staticaop;
2
3 Import java. util. date;
4
5 public class logger {
6 /***//**
7 * log records by level
8 * @ Param level
9 * @ Param Context
10 */
11 public static void logging (Level level, string context ){
12 if (level. Equals (level. info )){
13 system. Out. println (new date (). tolocalestring () + "" + context );
14}
15 if (level. Equals (level. debuge )){
16 system. Err. println (new date () + "" + context );
17}
18}
19
20}
21level. Java
1 package sinosoft. DJ. AOP. staticaop;
2
3 Public Enum level {
4 info, debuge;
5}
6. Let's write a test class. The Code is as follows:
Test. Java
1 package sinosoft. DJ. AOP. staticaop;
2
3 Public class test {
4 public static void main (string [] ARGs ){
5 ihello Hello = new helloproxy (New Hello ());
6 hello. sayhello ("doublej ");
7}
8}
9 run the above code and we can get the following results:
Tue Mar 04 20:57:12 CST 2008 sayhello method start.
Hello doublej
20:57:12 sayhello method end!
From the code above, we can see that the hello object is created by the so-called proxy state helloproxy. in this way, if we want to remove the logging function in the future. we only need to change the code for getting the hello object to the following:
1 package sinosoft. DJ. AOP. staticaop;
2
3 Public class test {
4 public static void main (string [] ARGs ){
5 ihello Hello = new Hello ();
6 hello. sayhello ("doublej ");
7}
8}
9
The above code is the simplest implementation of AOP!
But we will find a problem. If we have many classes like hello, are we going to write many classes like helloproxy. yes, yes. it is also a very troublesome task. after jdk1.3. JDK provides an API Java with us. lang. reflect. invocationhandler class. this class allows us to do something dynamic for some methods when JVM calls a class method. let's change the above Code to see the effect.
Similarly, we write an ihello interface and a hello implementation class. In the interface, we define two methods. The Code is as follows:
Ihello. Java
1 package sinosoft. DJ. AOP. proxyaop;
2
3 Public interface ihello {
4 /***//**
5 * business processing method
6 * @ Param name
7 */
8 void sayhello (string name );
9 /***//**
10 * business processing method B
11 * @ Param name
12 */
13 void saygoogbye (string name );
14}
15
Hello. Java
1 package sinosoft. DJ. AOP. proxyaop;
2
3 Public class Hello implements ihello {
4
5 Public void sayhello (string name ){
6 system. Out. println ("hello" + name );
7}
8 Public void saygoogbye (string name ){
9 system. Out. println (name + "goodbye! ");
10}
11}
12
Let's write a proxy class in the same way. Let this class implement the java. Lang. Reflect. invocationhandler interface. The Code is as follows:
1 package sinosoft. DJ. AOP. proxyaop;
2
3 Import java. Lang. Reflect. invocationhandler;
4 Import java. Lang. Reflect. method;
5 import java. Lang. Reflect. proxy;
6
7 public class dynaproxyhello implements invocationhandler {
8
9 /***//**
10 * objects to be processed (that is, objects with business logic before and after the method, such as hello in the example)
11 */
12 private object delegate;
13
14 /***//**
15 * objects processed by the dynamic generation method (fixed syntax)
16 *
17 * @ Param delegate
18 * @ Param proxy
19 * @ return
20 */
21 public object BIND (Object delegate ){
22 This. Delegate = delegate;
23 return proxy. newproxyinstance (
24 This. Delegate. getclass (). getclassloader (), this. Delegate
25. getclass (). getinterfaces (), this );
26}
27 /***//**
28 * Each method in the object to be processed is sent to JVM for calling. That is, the method of the object to be processed can only be called through this method.
29 * This method is dynamic, not manually called
30 */
31 public object invoke (Object proxy, method, object [] ARGs)
32 throws throwable {
33 object result = NULL;
34 try {
35 // record the log before executing the original method
36 logger. Logging (level. debuge, method. getname () + "method end .");
37
38 // JVM executes the original method (reflection mechanism) through this statement)
39 result = method. Invoke (this. Delegate, argS );
40 // logs are recorded after the original method is executed
41 logger. Logging (level. info, method. getname () + "method start! ");
42} catch (exception e ){
43 E. printstacktrace ();
44}
45 // return the method return value to the caller
46 Return result;
47}
48
49}
50
The logger class and level enumeration in the above class are the same as those in the example above. The Code will not be pasted here.
Let's write a test class to test it. The Code is as follows:
Test. Java
1 package sinosoft. DJ. AOP. proxyaop;
2
3 Public class test {
4 public static void main (string [] ARGs ){
5 ihello Hello = (ihello) New dynaproxyhello (). BIND (New Hello ());
6 hello. saygoogbye ("Double J ");
7 hello. sayhello ("Double J ");
8
9}
10}
11
The output result is as follows:
Tue Mar 04 21:24:03 CST 2008 saygoogbye method end.
Double J goodbye!
21:24:03 saygoogbye method start!
Tue Mar 04 21:24:03 CST 2008 sayhello method end.
Hello double J
21:24:03 sayhello method start!
Due to the thread relationship, the beginning of the second method appears before the end of the first method. This is not what we are concerned about!
We can see from the above example. as long as you adopt interface-oriented programming, you can add the operations that record logs before executing any object method. he (dynapoxyhello) automatically goes to the proxy to execute every method in the proxy object (Hello), a java. lang. reflect. the invocationhandler interface decouples our proxy object from the proxy object. however, we found another problem. The dynapoxyhello object can only be followed by the operations recorded before and after the method. can we decouple the dynapoxyhello object from the log operation object (logger?
The result is positive. Let's analyze our needs.
We need to add the log operation code (or other operation code) before or after the method of the proxy object ),
Then, we can abstract an interface. There are only two methods in this interface. One is the method that is executed before the proxy object executes the method. We call it start, the second method is the method executed after the method is executed by the proxy object. the interface is defined as follows:
1 package sinosoft. DJ. AOP. proxyaop;
2
3 Import java. Lang. Reflect. method;
4
5 public interface ioperation {
6 /***//**
7 * operations before method execution
8 * @ Param Method
9 */
10 void start (method );
11 /***//**
12 * operations after method execution
13 * @ Param Method
14 */
15 void end (method );
16}
17
We will write a class that implements the above interface. We will act as its real operator. Below is a class of the LOG Operator:
Loggeroperation. Java
Package sinosoft. DJ. AOP. proxyaop;
Import java. Lang. Reflect. method;
Public class loggeroperation implements ioperation {
Public void end (method ){
Logger. Logging (level. debuge, method. getname () + "method end .");
}
Public void start (method ){
Logger. Logging (level. info, method. getname () + "method start! ");
}
}
Then we need to change the code in the proxy object dynaproxyhello as follows:
1 package sinosoft. DJ. AOP. proxyaop;
2
3 Import java. Lang. Reflect. invocationhandler;
4 Import java. Lang. Reflect. method;
5 import java. Lang. Reflect. proxy;
6
7 public class dynaproxyhello implements invocationhandler {
8 /***//**
9 * operator
10 */
11 private object proxy;
12 /***//**
13 * objects to be processed (that is, objects with business logic before and after the method, such as hello in the example)
14 */
15 private object delegate;
16
17 /***//**
18 * objects processed by the dynamic generation method (fixed syntax)
19 *
20 * @ Param delegate
21 * @ Param proxy
22 * @ return
23 */
24 public object BIND (Object delegate, object proxy ){
25
26 This. Proxy = proxy;
27 This. Delegate = delegate;
28 return proxy. newproxyinstance (
29 This. Delegate. getclass (). getclassloader (), this. Delegate
30. getclass (). getinterfaces (), this );
31}
32 /***//**
33 * Each method in the object to be processed is sent to JVM for calling. That is, the method of the object to be processed can only be called through this method.
34 * This method is dynamic, not manually called
35 */
36 public object invoke (Object proxy, method, object [] ARGs)
37 throws throwable {
38 object result = NULL;
39 try {
40 // reflection to get the operator's instance
41 class clazz = This. Proxy. getclass ();
42 // obtain the operator's start method through reflection
43 method start = clazz. getdeclaredmethod ("START ",
44 new class [] {method. Class });
45 // execute the start method in reflection
46 start. Invoke (this. Proxy, new object [] {method });
47 // execute the original method for processing the object
48 result = method. Invoke (this. Delegate, argS );
49 // obtain the end method of the operator through reflection
50 method end = clazz. getdeclaredmethod ("end ",
51 new class [] {method. Class });
52 // execute the end method through reflection
53 end. Invoke (this. Proxy, new object [] {method });
54
55} catch (exception e ){
56 E. printstacktrace ();
57}
58 return result;
59}
60
61}
62
Then let's change the code in test. Java and test it:
Package sinosoft. DJ. AOP. proxyaop;
Public class test {
Public static void main (string [] ARGs ){
Ihello Hello = (ihello) New dynaproxyhello (). BIND (New Hello (), new loggeroperation ());
Hello. saygoogbye ("Double J ");
Hello. sayhello ("Double J ");
}
}
The results are the same.
If you want to add a log record before each method, instead of adding a log record after the method, you can change the loggeroperation class to the following:
1 package sinosoft. DJ. AOP. proxyaop;
2
3 Import java. Lang. Reflect. method;
4
5 public class loggeroperation implements ioperation {
6
7 public void end (method ){
8 // logger. Logging (level. debuge, method. getname () + "method end .");
9}
10
11 Public void start (method ){
12 logger. Logging (level. info, method. getname () + "method start! ");
13}
14
15}
16
Run it. You will find that no logs are recorded after each method. In this way, we will remove the proxy and operator!
Next, let's leave a question for everyone. If we don't want to record all the methods, how should we solve the problem .?
My idea is to add an IF () in the public object invoke (Object proxy, method, object [] ARGs) method of the proxy object (), determine the name of the passed method, and the judgment conditions exist in XML. in this way, we can parse the configuration file. if you are interested, you can configure the operator and the agent in the configuration file, then you can write a simple springaop framework.