[Java] Spring AOP basic knowledge-dynamic proxy

Source: Internet
Author: User

[Java] Spring AOP basic knowledge-dynamic proxy
Spring AOP uses dynamic proxy technology to weave enhanced code at runtime. To reveal the underlying Working Mechanism of Spring AOP, it is necessary to learn the Java knowledge involved. Spring AOP uses two proxy mechanisms: JDK-based Dynamic proxy and CGLib-based Dynamic proxy. Two proxy mechanisms are required, largely because JDK only provides interface proxies, rather than class proxies.

Instances with cross-cutting Logic

We use the specific code to implement the performance monitoring cross-cutting logic of the example described in the previous section, and use dynamic proxy technology to transform this. Start method performance monitoring when calling each target class method, and record the time spent on the method when the target class method is called.

Code List 6-2 ForumService: contains performance monitoring cross-cutting code
Java code

  1. Package com. baobaotao. proxy;
  2. Public class ForumServiceImpl implements ForumService {
  3. Public void removeTopic (int topicId ){
  4.  
  5. // ①-1 start to monitor the performance of this method
  6. PerformanceMonitor. begin (
  7. Com. baobaotao. proxy. ForumServiceImpl. removeTopic );
  8. System. out. println (simulate the deletion of Topic records: + topicId );
  9. Try {
  10. Thread. currentThread (). sleep (20 );
  11. } Catch (Exception e ){
  12. Throw new RuntimeException (e );
  13. }
  14.  
  15. // ①-2 end to monitor the performance of this method
  16. Optional cemonitor. end ();
  17. }
  18.  
  19. Public void removeForum (int forumId ){
  20. // ②-1 start to monitor the performance of this method
  21. PerformanceMonitor. begin (
  22. Com. baobaotao. proxy. ForumServiceImpl. removeForum );
  23. System. out. println (simulate the deletion of the Forum record: + forumId );
  24. Try {
  25. Thread. currentThread (). sleep (40 );
  26. } Catch (Exception e ){
  27. Throw new RuntimeException (e );
  28. }
  29.  
  30. // ②-2 end to monitor the performance of this method
  31. Optional cemonitor. end ();
  32. }
  33. }
    In code list 6-2, the code in bold indicates that the Code has the characteristics of the cross-cutting logic. Each Service class and each Service method body execute the same code logic before and after each Service method body: Initiate the PerformanceMonitor before the method is called, after the method is called, PerformanceMonitor is notified to end performance monitoring and record the performance monitoring results.

    PerformanceMonitor is an implementation class for performance monitoring. We provide a very simple implementation version. Its code is shown in Listing 6-3:

    Code List 6-3 Export cemonitor
    Java code
    1. Package com. baobaotao. proxy;
    2. Public class extends cemonitor {
    3. // ① Use ThreadLocal to save performance monitoring information related to the call thread
    4. Private static ThreadLocal PerformanceRecord =
    5. New ThreadLocal ();
    6.  
    7. // ② Start performance monitoring for a specific method
    8. Public static void begin (String method ){
    9. System. out. println (begin monitor ...);
    10. MethodPerformance mp = new MethodPerformance (method );
    11. PerformanceRecord. set (mp );
    12. }
    13. Public static void end (){
    14. System. out. println (end monitor ...);
    15. MethodPerformance mp = performanceRecord. get ();
    16.  
    17. // ③ Print the result of method performance monitoring.
    18. Mp. printPerformance ();
    19. }
    20. }
      ThreadLocal is a magic weapon to transform a non-thread security class into a thread security class. In section 9.2, we will introduce this basic Java knowledge in detail. PerformanceMonitor provides two methods: You can call the begin (String method) method to start monitoring a target class method. method is the fully qualified name of the target class method, while end () the method ends the monitoring of the target class method and provides the performance monitoring information. These two methods must be used together.

      Shows the MethodPerformance code used to record performance monitoring information:

      Code List 6-4 MethodPerformance
      Java code
      1. Package com. baobaotao. proxy;
      2. Public class MethodPerformance {
      3. Private long begin;
      4. Private long end;
      5. Private String serviceMethod;
      6. Public MethodPerformance (String serviceMethod ){
      7. This. serviceMethod = serviceMethod;
      8.  
      9. // ① Record the system time at which the target class method starts execution
      10. This. begin = System. currentTimeMillis ();
      11.  
      12. }
      13. Public void printPerformance (){
      14.  
      15. // ② Obtain the system time after the target class method is executed, and then calculate the execution time of the target class method.
      16. End = System. currentTimeMillis ();
      17. Long elapse = end-begin;
      18.  
      19. // ③ Report the execution time of the target method
      20. System. out. println (serviceMethod + cost + elapse + millisecond .);
      21. }
      22. }
        Use the following code to test the ForumServiceImpl business method with performance monitoring capabilities:

        Java code
        1. Package com. baobaotao. proxy;
        2.  
        3. Public class TestForumService {
        4. Public static void main (String [] args ){
        5. ForumService forumService = new ForumServiceImpl ();
        6. ForumService. removeForum (10 );
        7. ForumService. removeTopic (1012 );
        8. }
        9. }
          We get the following output information:
          Performance monitoring report using the begin monitor... ① removeForum (10) Method
          Simulate the deletion of a Forum record: 10
          End monitor...
          Com. baobaotao. proxy. ForumServiceImpl. removeForum takes 47 milliseconds.

          Performance monitoring report of the remoin monitor... ① removeTopic (1012) Method
          Simulate Topic deletion: 1012
          End monitor...
          Com. baobaotao. proxy. ForumServiceImpl. removeTopic takes 26 milliseconds.
          As shown in the example of code listing 6 2, when a method requires performance monitoring, you must adjust the method code to enable and end the performance monitoring code before and after the method body. The performance monitoring code of these non-business logic breaks the purity of the ForumServiceImpl business logic. We hope to completely remove the cross-cutting code that starts and ends performance monitoring from the business class through proxy. The dynamic JDK proxy technology or CGLib dynamic proxy technology are used to dynamically route the cross-section code to the corresponding position of the target method.

          JDK dynamic proxy

          After JDK 1.3, Java provides dynamic proxy technology that allows developers to create proxy instances for interfaces at runtime. When Sun launched dynamic proxy, it was hard to imagine how much it actually used. Now we finally found that dynamic proxy is an excellent underlying technology for Implementing AOP.

          JDK's dynamic Proxy mainly involves two classes in the java. lang. reflect package: Proxy and InvocationHandler. InvocationHandler is an interface that defines the cross-cutting logic by implementing this interface, and calls the code of the target class through the reflection mechanism, dynamically weaving the cross-cutting logic and the business logic together.

          The Proxy uses InvocationHandler to dynamically create an instance that complies with a certain interface and generate the Proxy object of the target class. In this case, it must be very abstract. We will immediately begin to use the Proxy and InvocationHandler magic rings to innovate the performance monitoring code in the previous section.

          First, we delete the cross-cutting code of performance monitoring from the business class ForumServiceImpl, so that ForumServiceImpl is only responsible for specific business logic, as shown in code list 6-5:

          Code List 6-5 ForumServiceImpl: Remove performance monitoring crosstab code
          Java code
          1. Package com. baobaotao. proxy;
          2.  
          3. Public class ForumServiceImpl implements ForumService {
          4.  
          5. Public void removeTopic (int topicId ){
          6. System. out. println (simulate the deletion of Topic records: + topicId );
          7. Try {
          8. Thread. currentThread (). sleep (20 );
          9. } Catch (Exception e ){
          10. Throw new RuntimeException (e );
          11. }
          12. }
          13. Public void removeForum (int forumId ){
          14. System. out. println (simulate the deletion of the Forum record: + forumId );
          15. Try {
          16. Thread. currentThread (). sleep (40 );
          17. } Catch (Exception e ){
          18. Throw new RuntimeException (e );
          19. }
          20. }
          21. }
            At ① and ② In code list 6-5, the original performance monitoring code is removed, and we only keep the real business logic.

            Of course, the cross-cutting performance monitoring Code removed from the business class cannot be floating in the air. It also needs to find a place to live. InvocationHandler is the anjiayuan of the cross-cutting code, we place the performance monitoring code in PerformanceHandler, as shown in Listing 6-6:

            Java code
            1. Code List 6-6 PerformanceHandler
            2. Package com. baobaotao. proxy;
            3. Import java. lang. reflect. InvocationHandler;
            4. Import java. lang. reflect. Method;
            5.  
            6. Public class cecehandler implements InvocationHandler {// ① implement InvocationHandler
            7. Private Object target;
            8. Public PerformanceHandler (Object target) {// ② service class with target
            9. This.tar get = target;
            10. }
            11. Public Object invoke (Object proxy, Method method, Object [] args) ③
            12. Throws Throwable {
            13. Extends cemonitor. begin (target. getClass (). getName () +. + method. getName (); ③-1
            14. Object obj = method. invoke (target, args); // ③-2 call the target method of the business class through the Reflection method
            15. Optional cemonitor. end (); ③-1
            16. Return obj;
            17. }
            18. }

              ③ The code shown in bold in the invoke () method is the cross-cutting code for performance monitoring. We found that the cross-cutting code only appears once, instead of the original Star. Method at ③-2. the invoke () Statement indirectly calls the method of the target object through the Java reflection mechanism, so that the InvocationHandler's invoke () method will cut the logic code (③-1) it is woven with the business logic code (③-2) of the business method, so we can regard InvocationHandler as a braid. Next, we will further describe this code.

              First, we implement the InvocationHandler interface, which defines an invoke (Object proxy, Method method, Object [] args) Method. proxy is the final proxy instance, which is generally not used; method is a specific method of the proxy target instance. Through this method, you can initiate a reflection call to the method of the target instance. args is an input parameter of a method of the proxy instance, used for method reflection calls.

              In addition, we pass in the target Object to be proxy through the target in the constructor, as shown in Area ②, In the InvocationHandler interface Method invoke (Object proxy, method Method, Object [] args) to pass the target instance to method. invoke () method to call the method of the target instance, as shown in ③.
              Next, we create a Proxy instance for the ForumService interface by Using Proxy and PerformanceHandler, as shown in code listing 6-7:

              Code List 6-7 TestForumService: Create a proxy instance
              Java code
              1. Package com. baobaotao. proxy;
              2. Import java. lang. reflect. Proxy;
              3. Public class TestForumService {
              4. Public static void main (String [] args ){
              5.  
              6. // ① Target business class to be proxy
              7. ForumService target = new ForumServiceImpl ();
              8.  
              9. // ② Combine the target business class and cross-cutting code
              10. PerformanceHandler handler = new PerformanceHandler (target );
              11.  
              12. // ③ Create a proxy instance based on the InvocationHandler instance that knitting the target business logic and performance monitoring cross-cutting Logic
              13. ForumService proxy = (ForumService) Proxy. newProxyInstance (
              14. Target. getClass (). getClassLoader (),
              15. Target. getClass (). getInterfaces (),
              16. Handler );
              17.  
              18. // ④ Call the proxy instance
              19. Proxy. removeForum (10 );
              20. Proxy. removeTopic (1012 );
              21. }
              22. }
                The code above completes Business Code and cross-section code weaving and generates a proxy instance. At ②, we asked cecehandler to weave the performance monitoring cross-cutting logic into the ForumService instance, and then at ③, through the Proxy's newProxyInstance () the static method creates a proxy instance that complies with the ForumService interface for handler that knitting the business logic and performance monitoring logic. The first input parameter of this method is the class loader, and the second input parameter is a set of interfaces required to create a proxy instance; the third parameter is the braid object that integrates the business logic and the cross-cutting logic.

                According to the setting method at ③, this proxy instance implements all the interfaces of the target service class, that is, the ForumService interface of Forum ServiceImpl. In this way, we can call the proxy instance in the same way as calling the ForumService interface instance, as shown in figure 4. Run the above Code and output the following information:
                Reference begin monitor...
                Simulate the deletion of a Forum record: 10
                End monitor...
                Com. baobaotao. proxy. ForumServiceImpl. removeForum takes 47 milliseconds.

                Begin monitor...
                Simulate Topic deletion: 1012
                End monitor...
                Com. baobaotao. proxy. ForumServiceImpl. removeTopic takes 26 milliseconds.
                We found that the running effect of the program is the same as that of the performance monitoring logic written directly in the business class, but here, the original scattered cross-cutting logic code has been extracted to PerformanceHandler. When performance monitoring is also required for other business methods (such as UserService and SystemService), we only need to create proxy objects for them in a similar way as in code list 6-7. Next, we use a time sequence diagram to describe the overall logic of calling a business method by creating a proxy object to further understand the nature of the proxy object, as shown in 6-3.





                The dotted line is used to highlight the ForumService Proxy instance created through the Proxy. The ForumService Proxy instance uses the javasmacehandler to integrate the cross-cutting logic and business logic. When the caller calls the removeForum () and removeTopic () Methods of the proxy object, the internal call sequence clearly tells us what actually happens.

                CGLib dynamic proxy

                There is a restriction on using JDK to create a Proxy, that is, it can only create Proxy instances for interfaces. This can be done from the Proxy interface newProxyInstance (ClassLoader loader, Class [] interfaces, InvocationHandler h) the method signature is clear: the second input parameter interfaces is the list of interfaces that need to be implemented by the proxy instance. Although the idea of interface-oriented programming has been admired by many masters (including Rod Johnson), many developers are also deeply confused in actual development: is it necessary to honestly create five classes (domain object class, Dao interface, Dao implementation class, Service interface and Service implementation class) for operations on a simple business table? Isn't it possible to build programs directly by implementing classes? It is difficult for us to give an accurate judgment on the merits and demerits of this problem, however, we do find that many projects that do not use interfaces have also achieved very good results (including SpringSide open-source projects that are familiar to everyone ).

                How can I dynamically create a proxy instance for a class that does not define a business method through an interface? JDK's proxy technology is obviously already poor. CGLib, as a replacement, fills this gap.

                CGLib uses the very underlying bytecode technology. It can create subclasses for a class and use the method Blocking Technology in the subclass to intercept the calls of all parent class methods, and easily weave the cross-cutting logic. Next, we use CGLib technology to compile a proxy builder that can create and weave performance monitoring cross-cutting logic proxy objects for any class, as shown in code list 6-8:

                Code List 6-8 CglibProxy
                Java code
                1. Package com. baobaotao. proxy;
                2. Import java. lang. reflect. Method;
                3. Import net. sf. cglib. proxy. Enhancer;
                4. Import net. sf. cglib. proxy. MethodInterceptor;
                5. Import net. sf. cglib. proxy. MethodProxy;
                6.  
                7. Public class CglibProxy implements MethodInterceptor {
                8. Private Enhancer enhancer = new Enhancer ();
                9. Public Object getProxy (Class clazz ){
                10. Enhancer. setSuperclass (clazz); // ① set the class for which a subclass is to be created
                11. Enhancer. setCallback (this );
                12. Return enhancer. create (); // ② use bytecode technology to dynamically create a subclass instance
                13.  
                14. }
                15.  
                16. // ③ Intercept calls of all methods of the parent class
                17. Public Object intercept (Object obj, Method method, Object [] args,
                18. MethodProxy proxy) throws Throwable {
                19. Export cemonitor. begin (obj. getClass (). getName () +. + method. getName (); // ③-1
                20. Object result = proxy. invokeSuper (obj, args); ③-2
                21. Using cemonitor. end (); // ③-1 call the method in the parent class through the proxy class
                22. Return result;
                23. }
                24. }
                  In the code above, you can use getProxy (Class clazz) to create a dynamic proxy object for a Class. This proxy object creates a proxy object by extending clazz. In this proxy object, we weave the cross-cutting logic of performance monitoring (③-1 ). Intercept (Object obj, Method method, Object [] args, MethodProxy proxy) is the Method of the Interceptor interface defined by CGLib, which intercepts calls of all target class methods, obj indicates the instance of the target class; method is the reflection object of the target class method; args is the dynamic input parameter of the method; and proxy is the proxy class instance.

                  Next, we use CglibProxy to create a proxy object for the ForumServiceImpl class and test the method of the proxy object, as shown in Listing 6-9:

                  Code List 6-9 TestForumService: test the proxy class created by Cglib
                  Java code
                  1. Package com. baobaotao. proxy;
                  2. Import java. lang. reflect. Proxy;
                  3. Public class TestForumService {
                  4. Public static void main (String [] args ){
                  5. CglibProxy proxy = new CglibProxy ();
                  6. ForumServiceImpl forumService = ①
                  7. (ForumServiceImpl) proxy. getProxy (ForumServiceImpl. class );
                  8. ForumService. removeForum (10 );
                  9. ForumService. removeTopic (1023 );
                  10. }
                  11. }

                    In ①, We dynamically create a proxy object for ForumServiceImpl through CglibProxy and call the service method of the proxy class. Run the above Code and enter the following information:
                    Reference begin monitor...
                    Simulate the deletion of a Forum record: 10
                    End monitor...
                    Com. baobaotao. proxy. ForumServiceImpl $ EnhancerByCGLIB $2a9199c0. removeForum takes 47 milliseconds.
                    Begin monitor...
                    Simulate Topic deletion: 1023
                    End monitor...
                    Com. baobaotao. proxy. ForumServiceImpl $ EnhancerByCGLIB $2a9199c0. removeTopic takes 16 milliseconds.
                    Observe the above output. In addition to the logic of performance monitoring, we also find that the name of the proxy class is com. baobaotao. proxy. forumServiceImpl $ EnhancerByCGLIB $2a9199c0. This special class is the dynamic subclass created by CGLib for ForumServiceImpl.

                    Proxy knowledge Summary

                    The bottom layer of Spring AOP is to use JDK dynamic proxy or CGLib dynamic proxy technology to weave the cross-cutting logic for the target Bean. Here, we will make a summary of the previous two sections dynamically creating proxy objects.

                    Although we use PerformanceHandler or CglibProxy to achieve dynamic weaving of the cross-cutting logic of performance monitoring, there are three obvious improvements to this implementation method:

                    1) all methods of the target class have added performance monitoring cross-cutting logic. Sometimes, this is not what we expect. We may just want to add a cross-cutting logic to some specific methods in the business class;
                    2) We specify the weaving point of the cross-cutting logic through hard coding, that is, the code is woven before the start and end of the target business method;
                    3) We manually write the creation process of the proxy instance. When creating a proxy for different classes, we need to write the corresponding creation Code separately, which cannot be universal.

                    The above three problems occupy an important position in AOP, because the main work of Spring AOP is to focus on the above three points: Spring AOP uses Pointcut (cut point) specifies which methods of the classes are woven into the cross-cutting logic, through Advice (enhanced) describes the specific weaving points of the cross-cutting logic and method (before, after, and at both ends of the method ). In addition, Spring assembles Pointcut and Advice through Advisor. With the information of Advisor, Spring can use the dynamic proxy technology of JDK or CGLib to create a proxy object for the target Bean in a unified way.

                    The proxy object created by the JDK dynamic proxy has poor performance in JDK 1.3. Although the performance of dynamic proxy objects has been greatly improved in the JDK of the higher version, some studies have shown that, the performance of the dynamic proxy object created by CGLib is still much higher than that of the proxy object created by JDK (about 10 times ). However, CGLib takes more time to create proxy objects than JDK dynamic proxies (about 8 times). Therefore, for singleton proxy objects or proxies with instance pools, because you do not need to frequently create proxy objects, CGLib dynamic proxy technology is more suitable, whereas JDK dynamic proxy technology is also suitable. It is worth mentioning that, because CGLib uses dynamic subclass creation to generate proxy objects, the final method in the target class cannot be used as a proxy.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.