Discussion on jdk dynamic agent at the bottom of spring AOP

1, What is a dynamic agent

What is dynamic proxy and why it is used? Where is dynamic proxy used in normal programming?
To understand what a dynamic agent is, you first need to know what an agent is. In fact, it is very simple. Just as literally, an agent: taking care of affairs instead of others.

In programming, agent is an implementation of aspect oriented programming, and then we need to know what aspect oriented programming is. For example, for the method we write, we need to print a log or execute transactions before and after calling, or calculate the time required to call the method, so we will add duplicate code to each method, which will make the code very bloated, At this time, we need to use the idea of aspect oriented programming.

Aspect oriented programming is to extract the repeated code and put it separately in a proxy class to add the required functions to each method through proxy

Then the spring AOP we usually use is an implementation of aspect oriented programming, and dynamic agent is the underlying principle of AOP

2, Agent classification

  • Static proxy: the bytecode file of the proxy class already exists before the program runs, and the relationship between the proxy object and the real object is determined before the program runs. (that is, the proxy class and object should be created by ourselves)
  • Dynamic proxy: the proxy class is dynamically generated by the JVM through reflection and other mechanisms during the program running, so there is no bytecode file of the proxy class. The bytecode object is dynamically generated. The relationship between the proxy object and the real object is determined during the program running. (that is, we should not create proxy classes and objects ourselves)

3, Dynamic agent implementation

  • Using JDK dynamic proxy for real class interfaces requires Java Proxy class in lang.reflect package and InvocationHandler interface;
  • For real classes that do not implement interfaces, use CGLIB or Javassist components.

4, Differences between the two:

Advantages of static proxy:

  • Business classes only need to pay attention to the business logic itself, which ensures the reusability of business classes.
  • Hide the real object and protect the real object.

shortcoming

  • An interface of a proxy object only serves a certain type of object, that is to create a proxy class for each real class. For example, there are other business classes in the project.
  • If there are many proxy methods, proxy processing should be performed for each method.
  • If a method is added to the interface, in addition to all implementation classes, the proxy class also needs to implement this method.

Advantages of dynamic agent:

  • Compared with static agents, it is found that there is no need to provide so many agent classes manually.

shortcoming

  • The real object must implement the interface (unique to JDK dynamic agent);
  • The smallest unit of dynamic agent is the class (some methods in the class will be processed). If you only want to intercept some methods, you can judge the name of the method to be executed in the invoke method;
  • If Spring is used to proxy multiple real objects, there are too many configurations. It is troublesome to manually create proxy objects.

5, Code

1.jdk

Transaction call handler

public class TransactionInvocationHandler implements InvocationHandler {

    private Object object;              //Used to receive classes that need to be proxied
    private TransactionManager tx;      //Used to provide transactions

    public Object getObject() {
        return object;
    }

    public void setObject(Object object) {
        this.object = object;
    }

    public TransactionManager getTx() {
        return tx;
    }

    public void setTx(TransactionManager tx) {
        this.tx = tx;
    }

    /**
     * @param proxy  Real proxy object of real object
     * @param method Method called by proxy object
     * @param args   Method
     * @return       Return what you want to return, according to your needs
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object o = null;
        try {
            tx.begin();
            o = method.invoke(object, args);
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
            tx.rollback();
        }
        return o;
    }
}

Simulate the business requirements

public class EmployeeServiceImpl implements EmployeeService {
    @Override
    public void save(String name, String password) {

        System.out.println("Saved"+name+":"+password);
    }
}

Transaction simulation

public class TransactionManager {
    public void begin(){
        System.out.println("The transaction began");
    }

    public  void commit(){
        System.out.println("Transaction committed");
    }

    public  void rollback(){
        System.out.println("Transaction rollback");
    }
}

Test class, add transaction to business

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class EmployeeServiceImplTest {
    /**
     * Dynamic agent
     */
    @Autowired
    private TransactionInvocationHandler transactionInvocationHandler;

    @Test
    public void save() {
        EmployeeService proxy = (EmployeeService) Proxy.newProxyInstance(
                this.getClass().getClassLoader(),//Gets the class loader of the current class
                transactionInvocationHandler.getObject().getClass().getInterfaces(),//Get all interfaces of the proxy class,
                                                                                    // The proxy class is passed into the obj attribute in the tih object from the configuration file
                transactionInvocationHandler);//The object is passed in to the field in the created proxy class
        proxy.save("JNX", "666");
    }/*The object receiving transactionInvocationHandler in the created proxy class calls the invoke method
    When the created proxy object calls the save method
        Method in the method and then call the save method in the interface
    */
}

Inject bean s into spring, and spring boot does not need to write 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--    Configure transaction manager objects-->
    <bean class="cn.xxx.handler.TransactionManager" id="tx"/>
    <!--    Configure processor execution object-->
    <bean class="cn.xxx.handler.TransactionInvocationHandler"
          id="transactionInvocationHandler">
        <property name="object">
            <bean class="cn.xxx.service.impl.EmployeeServiceImpl"/>
        </property>
        <property name="tx" ref="tx"/>
</bean>
</beans>

2.CGlib

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class EmployeeServiceTest {
	
	@Autowired
	private TransactionInvocationHandler transactionInvocationHandler;
	
	@Test
	public void testSave() {
		Enhancer enhancer = new Enhancer();
		// Sets the class inherited by the generation proxy class
		enhancer.setSuperclass(transactionInvocationHandler.getTarget().getClass());
		// Set what to do to generate proxy class objects
		enhancer.setCallback(transactionInvocationHandler);
		// Generate proxy class and create proxy object
		EmployeeServiceImpl proxy = (EmployeeServiceImpl)enhancer.create();
		proxy.save("Miss Luo", "666");
	}

}

3. Choose

JDK dynamic proxy is based on implementation interface, CGLIB and Javassit are based on inheriting real classes.

In terms of performance: javassit > cglib > JDK.

The selection is as follows:

  • If the real class implements the interface, JDK dynamic agent is preferred. (because it will produce a more loosely coupled system and more in line with interface oriented programming)
  • If you do not use any Java cgssit and dynamic lib interfaces.

In fact, I think it can be understood that the real class is the landlord and the agent class is the intermediary. So to understand the relationship between them, the landlord needs to collect rent, and the intermediary can help him collect rent, but it is not limited to collecting rent

AOP

1, AOP terminology

  • Joinpoint: connection point, which generally refers to the method that needs to be enhanced. Where: where to enhance.
  • Pointcut: pointcut, which packages, which classes and which methods can be considered as a collection of join points. Where: where to enhance.
  • Advice: enhancement: when the Joinpoint is intercepted, what kind of enhancement is made at what time when the method is executed. According to the timing, it is divided into: pre enhancement, post enhancement, abnormal enhancement, final enhancement and surround enhancement.
  • Aspect: aspect, Pointcut + Advice, where to go + when to + what to enhance.
  • Target: the target object being represented.
  • Weaving: the process of creating a Proxy object after weaving in and adding Advice to the Target.
  • Proxy: a proxy class generated after a class is woven and enhanced by AOP.

2, Use

The advantage of AOP is that you can use annotations to customize the transactions you need

@Component
@Aspect
public class MyTransactionManager {
	
	// Define pointcut WHERE
	@Pointcut("execution(* cn.xxx.service.impl.*ServiceImpl.*(..))")
	public void txPointcut() {}
	
	@Before("txPointcut()")
	public void begin() {
		System.out.println("Open transaction");
	}
	
	@AfterReturning("txPointcut()")
	public void commit() {
		System.out.println("Commit transaction");
	}
	
	@AfterThrowing("txPointcut()")
	public void rollback() {
		System.out.println("Rollback transaction");
	}
}

For additional information on AOP, see:

My other commonly used notes on AOP

Keywords: Java Spring Back-end

Added by stopblackholes on Fri, 04 Mar 2022 15:56:33 +0200