Spring AOP instance resolution (the role of proxy parameter of InvocationHandler's invoke)

Aspect-oriented programming is implemented with dynamic proxy. The following example implements the function of adding logs and transactions before and after a specific method.

1) Three parameters of the Proxy class ( the proxy of the real object )

    The first two parameters are used to generate the $Proxy constructor, which then takes the invocationHandler as a parameter to generate the $Proxy instance.

  • classLoader: The class loader, which is obtained by the real object userService, but there is no one-to-one correspondence with userService; any object can be used to obtain the same class loader in the same way; in this case, the test class itself can also be used Get the class loader: this.getClass().getClassLoader(), and then omit this, it becomes getClass().getClassLoader() .
    The class loader prints out: sun.misc.Launcher$AppClassLoader@18b4aac2
  • interfaces: An array of interfaces, all the interfaces implemented by the real object userService. Proxy uses the two parameters of class loader and interface array to obtain the dynamically generated Proxy Class object (getProxyClass0(loader, intfs)), and then uses this Class object to obtain its constructor (cl.getConstructor(constructorParams)). Generate a Proxy instance (cons.newInstance(new Object[]{h}), where h is the third parameter invocationHandler of Proxy). 
  • invocationHandler: Literal translation is an invocation handler, and its object is essentially the proxy of the Proxy class , that is, the proxy of the real object's  proxy. The Proxy class implements the interface of the real object, so its object can call the method of the real object. When invoking, the invocationHandler is assigned to actually execute the system enhancement business (ie log, transaction, etc.) and the real business.

2) The role of the three parameters of invoke of InvocationHandler

  • proxy: is a dynamic instance of Proxy, which is displayed in Idea debug as: {$Proxy4@862} com.qf.service.impl.UserServiceImpl@5a8806ef. This parameter can be completely used in the invoke method body, so it is difficult to understand its function , and even the answers on Zhihu and Stackoverflow don't feel right. The previous answer on Stackoverflow said that the function is that the proxy object can be returned for continuous invocation, and wrote a demo (https://stackoverflow.com/questions/22930195/understanding-proxy-arguments-of-the-invoke-method-of- java-lang-reflect-invoca), is it useless if you don't need continuous invocation? After checking a lot of information, I finally found a more logical explanation: how does the second parameter of invoke come from method? After the dynamic instance of $Proxy (that is, the first parameter proxy) is generated (it is a class file), through reverse compilation, you can see that there is the following static code block (source: http://rejoy.iteye.com/blog/ 1627405), that is to say, only the proxy instance is loaded in the InvocationHandler implementation class to generate the second parameter method  ( the static code block is executed when the virtual machine loads the class, and it is executed only once), so the $Proxy instance must pass itself To the invoke method of the InvocationHandler.
m3 = Class.forName("dynamic.proxy.UserService").getMethod("add", new Class[0]);

  • method: The business method to be implemented by the real object, obtained from the static code block of the $Proxy instance.
  • args: Arguments for the second parameter method.

3) Two concepts: the system enhancement services (logs, transactions, permissions, etc.) are separated from the real business of the system, and these outgoing code snippets placed in the InvocationHandler implementation class invoke method are an aspect , and the real business add() in this Demo is the entry point .

The following is the code of the InvocationHandler implementation class and test class (including creating a dynamic instance of $Proxy).

public class DynamicProxy implements InvocationHandler {
	private TransactionManager tx;
	private LoggerManager log;
	private Object object;

	public DynamicProxy() {
	}
        //The object parameter is the real object, in this case the userService object
	public DynamicProxy(TransactionManager tx, LoggerManager log, Object object) {
		this.tx = tx;
		this.log = log;
		this.object = object;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		tx.begin();
		log.beginLog();
		Object invoke = method.invoke(object, args);
		log.endLog();
		tx.commit();
		return invoke;
	}
}
        @Test
	public void testDynamicAOP(){
		LoggerManager log = new LoggerManager();
		TransactionManager tm = new TransactionManager();
		IUserService userService = new UserServiceImpl();
		ClassLoader loader = userService.getClass().getClassLoader();
		Class<?>[] interfaces = userService.getClass().getInterfaces();
		InvocationHandler handler = new DynamicProxy(tm, log, userService);
		IUserService userServiceProxy = (IUserService) Proxy.newProxyInstance(loader, interfaces, handler);
		userServiceProxy.add(new User("Rock"));
	}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324820922&siteId=291194637