Talking about the Understanding and Implementation of AOP

Keywords: JDK Spring xml encoding

In a previous discussion with a bull, I found that my knowledge of AOP was not thorough enough, so in the recent period of study, I combed the AOP related knowledge again.

What is AOP

AOP refers to facet-oriented programming, which mainly extracts some common functions required by business logic from the business implementation, so when you need to use these functions, you only need to cut them in, not code each business once.This technology can also bring together scattered blocks of public code in your business.
This is good for code upgrade and maintenance, and if you want to modify the public part, you only need to modify it in the extracted part of the code, not in other places.

How to implement AOP function

There are two ways to implement AOP:
**JDK dynamic proxy:** AOP is implemented through proxy mode, which is a common proxy implementation. If you want to enhance some of the methods in the proxy class, you need to rewrite them all, even if you do the same thing.
The JDK dynamic proxy can solve this problem. Take printing logs as an example. If I want to print logs before and after the method is executed, I don't need to add a print log operation to every method that needs to be printed. As long as I know the method and parameters that need to print the logs, I will automatically execute the print log and the method operation.
Dynamic proxy steps:
1. Create a class that implements the interface InvocationHandler, which must implement the invoke method
2. Create proxied classes and interfaces
3. Static methods via Proxy
newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h) creates a proxy
4. Call a method through a proxy (automatically handled with invoke())
There is information about dynamic proxy, which is detailed in this blog
https://blog.csdn.net/jiankunking/article/details/52143504
Implementation of JDK Dynamic Agent

public interface IDemo {//Interface
      
      public void say();
      public void hello();    
}

public class Demo implements IDemo {
      @Override
      public void say() {
            // TODO Auto-generated method stub
            System.out.println("AOP JDK demo");
      }
      @Override
      public void hello() {
            // TODO Auto-generated method stub
            System.out.println("AOP JDK method hello");
      }
}
//Implement InvocationHandler 
// DynDemo is a proxy class
public class DynDemo implements InvocationHandler 
{ 
      // OBJ is the object to be proxied at this time
    private Object obj;
    //This is a benefit of dynamic proxy, encapsulated objects are of type Object and accept any type of object 
 
    public DynDemo() 
    { 
    } 
 
    // The parameter obj is the object to be proxied at this time
    public DynDemo(Object obj) 
    { 
        this.obj = obj; 
    } 
 
    //This method is not what we show to call 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
    { 
      System.out.println(method.getName());
      if(method.getName().equals("hello"))//You can also control which methods need to be cut in, but this is hard-coded and can be achieved by configuration
      {
             method.invoke(obj, args); 
             return null;
      }
      
        System.out.println("before calling " + method); 
 
        method.invoke(obj, args);  // The entry point to execute the proxy object
 
        System.out.println("after calling " + method); 
 
        return null; 
    } 
 
} 

//Client: Generate proxy instance and call say() method 
public class TestDemo { 
 
    public static void main(String[] args) throws Throwable{ 
        // TODO Auto-generated method stub 
 
        IDemo rs=new Demo();//This specifies the proxy class 
        InvocationHandler ds=new DynDemo(rs); 
        Class<?> cls=rs.getClass(); 
         
        //Here is a one-time build agent 
         
         IDemo subject=(IDemo) Proxy.newProxyInstance( 
                cls.getClassLoader(),cls.getInterfaces(), ds); 
        
         
        //Here you can prove by running the result that the subject is an instance of Proxy that implements the IDemo interface 
        System.out.println(subject instanceof Proxy); 
         
        //You can see here that the lass Class of the subject is $Proxy0, which inherits Proxy and implements the IDemo interface 
        System.out.println("subject Of Class Classes are:"+subject.getClass().toString()); 
         
        System.out.print("subject The attributes in are:"); 
         
        Field[] field=subject.getClass().getDeclaredFields(); 
        for(Field f:field){ 
            System.out.print(f.getName()+", "+f.getType()+" # ");
            f.setAccessible(true);
            System.out.println(f.get(subject));
        } 
         
        System.out.print("\n"+"subject The methods in are:"); 
         
        Method[] method=subject.getClass().getDeclaredMethods(); 
         
        for(Method m:method){ 
            System.out.print(m.getName()+", "); 
        } 
         
        System.out.println("\n"+"subject The parent of is:"+subject.getClass().getSuperclass()); 
         
        System.out.print("\n"+"subject The interfaces implemented are:"); 
         
        Class<?>[] interfaces=subject.getClass().getInterfaces(); 
         
        for(Class<?> i:interfaces){ 
            System.out.print(i.getName()+", "); 
        } 
 
        System.out.println("\n\n"+"The results are:");
        //subject is an instance of an auto-generated actual proxy class that automatically calls invoke() processing when executing a method in the class in the strap.
        //Parameters of invoke: proxy is the subject here, the method is called, such as say, hello; parameter is the parameter in the calling method
        subject.say(); 
        subject.hello();
    } 
} 

The JDK dynamic proxy solves the problem of duplicate code entry, but also requires both the proxy class and the proxy class to implement the same interface, depending on the interface to complete, and byte code enhancement technology is not needed

**Byte Code Enhancement: ** This implements AOP. By inheriting from the parent class, each inheritable method of the parent class is a connection point (a point to cut in). The subclass overrides the methods that the parent class needs to enhance, and additional operations can be added before and after calling the parent method

SpringAOP
In the code implementation above, the code that is cut in before and after the invoke method is still dead, and you have to write a different proxy class DynDemo (a class that implements the InvocationHandler interface) for different function cuts in.
In springAOP, these two parts are configured through configuration files, which greatly increases flexibility and makes modification and maintenance easier without the programmer's own implementation.

<!--configuration file beans.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:mvc="http://www.springframework.org/schema/mvc"
      xsi:schemaLocation="http://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsd">
     <!-- The class being proxied or the class that needs to be cut in -->
    <bean id="demoService" class="com.hong.springaop.ServiceDemo">
        <property name="name" value="hugeyurt" />
        <property name="url" value="hugeyurt.com" />
    </bean>
   
    <!-- Cut-in code before and after configuration method, i.e. added operation -->
   <bean id="beforeDemo" class="com.hong.springaop.DemoBeforeAdvice" />
    <bean id="afterDemo"  class="com.hong.springaop.AfterAdviceDemo"/>
    <bean id="beforeDemo2" class="com.hong.springaop.BeforeDemoTwo"/>
   
    <!-- Configure the proxy factory, responsible for creating proxy classes, and generate the class to be entered if no interface proxy is used (here is ServiceDemo)Subclasses -->
    <bean id="ServiceProxy"
                 class="org.springframework.aop.framework.ProxyFactoryBean">
         <!-- target Is an instance object of the class being cut into -->
        <property name="target" ref="demoService" />
       
        <!-- Here you configure the cut-in operation, and if you do not configure it, the method will not be blocked.Execute in configuration order before or after all methods-->
        <property name="interceptorNames">
            <list>
                <value>beforeDemo2</value>
                 <value>beforeDemo</value>
                <value>afterDemo</value>
               
            </list>
        </property>
    </bean>
</beans>
public class ServiceDemo {//Proxied Classes
    private String name;
    private String url;
    public void setName(String name) {
        this.name = name;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public  void printName() {
        System.out.println("name : " + this.name);
    }
    public void printURL() {
        System.out.println("company : " + this.url);
    }
    public void printThrowException() {
        throw new IllegalArgumentException();
    }
}

public class BeforeDemoTwo implements MethodBeforeAdvice {//Actions before methods, implementing the MethodBeforeAdvice interface
      @Override
      public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
            // TODO Auto-generated method stub
            
            System.out.println("before demo 2.....");
      }
}

//Actions after the method to implement the AfterReturningAdvice interface
public class AfterAdviceDemo implements AfterReturningAdvice
{
      @Override
      public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
            // TODO Auto-generated method stub
            System.out.println("after advice demo.....");
      }
      
}

//Test Class
public class App {
    public static void main(String[] args) {
        ApplicationContext appContext = new ClassPathXmlApplicationContext(
                new String[] { "beans.xml" });
    ServiceDemo demo =(ServiceDemo) appContext.getBean("ServiceProxy");
    System.out.println(demo.toString());
 //The demo here is taken out of the container and is actually a subclass of ServiceDemo created by the container configuration factory, as can be seen from the printed classes
     System.out.println(demo.getClass());
    System.out.println(demo.getClass().getSuperclass());
        //System.out.println("*************************");
        demo.printName();
       /* System.out.println("*************************");
        demo.printURL();
        System.out.println("*************************");*/
       
    }

The above approach is still cumbersome to configure and requires configuring the proxied functions and cut-in operations, as well as the factories that generate the proxy classes
So we introduced springAOP, which simplifies the configuration, hides the generation of proxy classes, and configures a surround class and interception condition directly for implementation.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

<!-- The class being proxied or the class that needs to be cut in -->
 <bean id="userInfo" class="com.hong.springaop.UserInfo"></bean>

<!-- Configure wrapping classes -->
 <bean id="roundDemo" class="com.hong.springaop.RoundAdviceDemo"></bean>

   <aop:config>
            <!--breakthrough point -->
            <aop:pointcut id="methodPoint"
                  expression="execution(* com.hong..*(..)) " />
                  <!--Using a custom Interceptor at this entry point, advisor:section-->
            <aop:advisor pointcut-ref="methodPoint" advice-ref="roundDemo" />
      </aop:config>
</beans>

summary

These are my own simple understanding of AOP and simple implementation analysis.

Posted by BlackenedSky on Thu, 02 May 2019 01:40:38 -0700