The process of creating proxy in Spring's AOP

Keywords: Java Spring mvc

AOP is actually a proxy model. All agents, because the code can not be read directly, are the hardest hit area of bug s.
case
A game system contains a CouponService class responsible for counting coupons and recharging, which contains a recharging method deposit():
service

package com.javaedge.spring.aop.service;
import org.springframework.stereotype.Service;
@Service
public class CouponService{
	public void deposit() throw Exception{
		System.out.println("Coupon depositing ... ");
		this.pay();
	}
	public void pay() throws Exception{
		System.out.println("Pay with WeChat Pay ...");
		//Simulating pay () method calls takes time
		Thread.sleep(1000);
	}
}

deposit() will recharge with wechat payment. Therefore, add the pay () method in the Service class class and adjust this.pay () in the deposit() method body.
As wechat payment is a third-party interface, it is necessary to record the interface call time.
Introduce @ Around surround enhancement, record the time before and after the execution of pay() method, and calculate the execution time of pay().
config

package com.javaedge.spring.aop.config;
@Aspect
@Service
@Slf4j
public class AopConfig{

	@Around("execution(*com.javaedge.spring.aop.service.CouponService.pay())")
	public Object recordPayPerformance(ProceedingJoinPoint joinPoint) throws Throwable{
	Object result;
	long start =System.currentTimeMillis();
	result = joinPoint.proceed();
	long end = System.currentTimeMillis();
	sout("Pay method time cost (ms):"+(end - start);
	return result;
	}
}

Controller

package com.javaedge.spring.aop.controller;
@RestController
public class GameController{
	@Autowired
	CouponService couponService;

	@GetMapping(path="deposit")
	public void deposit() throws Exception{
		couponService.deposit();
	}
}

When accessing the interface, you will find that the aspect of this calculation time has not been executed. The output log is as follows:The facet class clearly defines the facet corresponding method, but it is not implemented. It indicates that the method called through this within the class will not be enhanced by AOP.
analysis
The object corresponding to this is an ordinary CouponService object:
CouponService objects automatically assembled in the Controller layer:
It is a Bean enhanced by Spring, so when you execute deposit(), you will perform the enhanced operation of recording the interface call time. The object corresponding to this is just an ordinary object without any additional enhancement.

Why is the object referenced by this just an ordinary object? Are Spring enhanced beans enhanced objects?
From the process of Spring AOP enhancing objects.
realization
The underlying layer of AOP is dynamic agent. There are two ways to create an agent:
JDK mode
Agents can only be generated for classes that implement interfaces, not for ordinary classes.
CGLIB mode
Proxy can be implemented for classes, mainly to generate a subclass of the specified class and override the methods in it to implement the proxy object.
For non Spring Boot programs, in addition to adding relevant AOP dependencies, you can also use @ EnableAspectJAutoProxy to enable the AOP function.
This annotation class introduces aspectjautoproxyregister, which completes the preparation of AOP related beans by implementing the importbeandefinitionregister interface.
Now let's look at the process of creating a proxy object. Let's first look at the call stack:
When to create a proxy object
When creating a Bean
The key work of creating is done by the annotation awareaspectjauto proxycreator
AnnotationAwareAspectJAutoProxyCreator
A BeanPostProcessor. Therefore, its execution is in the process of initializing the Bean after completing the construction of the original Bean
AbstractAutoProxyCreator#postProcessAfterInitialization

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
   if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (this.earlyProxyReferences.remove(cacheKey) != bean) {
         return wrapIfNecessary(bean, beanName, cacheKey);
      }
   }
   return bean;
}

Key method wrapIfNecessary: when AOP needs to be used, it will wrap the created original Bean object into a proxy object and return it as a Bean.
AbstractAutoProxyCreator#wrapIfNecessary

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
   // Omit non critical code
   Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
   if (specificInterceptors != DO_NOT_PROXY) {
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
      Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
   }
   // Omit non critical code 
}

createProxy
Key to creating proxy objects:

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
      @Nullable Object[] specificInterceptors, TargetSource targetSource) {
  // ...
  // 1. Create an agent factory
  ProxyFactory proxyFactory = new ProxyFactory();
  if (!proxyFactory.isProxyTargetClass()) {
   if (shouldProxyTargetClass(beanClass, beanName)) {
      proxyFactory.setProxyTargetClass(true);
   }
   else {
      evaluateProxyInterfaces(beanClass, proxyFactory);
   }
  }
  Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
  // 2. Add the notifiers, proxied objects and other information to the agent factory
  proxyFactory.addAdvisors(advisors);
  proxyFactory.setTargetSource(targetSource);
  customizeProxyFactory(proxyFactory);
   // ...
   // 3. Obtain the proxy object through the proxy factory
  return proxyFactory.getProxy(getProxyClassLoader());
}

After such a process, a proxy object is created. All the objects we get from Spring are this proxy object, so it has AOP function. However, before using this directly, only an ordinary object was referenced, so there is no way to realize the function of AOP.

correct
Only when the reference is an object created by a dynamic proxy, it will be enhanced by Spring and have the functions of AOP.

What kind of object has such conditions?
Annotated by @ Autowired
Use @ Autowired to reference yourself inside the class:
Get the current Proxy directly from AopContext

AopContext is to bind the Proxy to the thread through a ThreadLocal, so that the Proxy bound by the current thread can be taken out at any time.

There is a prerequisite for using this scheme. You need to add the configuration item exposeProxy = true in @ EnableAspectJAutoProxy, which means to put the proxy object into ThreadLocal, which can be passed directly

AopContext.currentProxy()

Get, otherwise an error is reported:
Then modify the code:
Do not forget to modify the exposeProxy property of EnableAspectJAutoProxy:
Summary:
1.this is an object that replaces this class and cannot be used as an enhancement in AOP.
2.@Autowired automatically injects objects with AOP enhancements.
3.AopContext.currentProxy() the object created by the current proxy of AopContext can be enhanced by AOP, but it needs to add @ EnableAspectJAutoProxy (exposeProxy=true) annotation on the startup class.

Posted by kaupp on Wed, 22 Sep 2021 01:10:31 -0700