AOP entry

In the previous section we on the analysis of analytical AOP tag, the first step is to register for a class AspectJAwareAdvisorAutoProxyCreator, we say it is the entry of the AOP classes. Why do you say that? Its subclass, AbstractautoXyCreator, inherits the BeanPostProcessor interface. So, there are two method must be invoked to postProcessBeforeInitialization, postProcessAfterInitialization. One is called before dependency injection is complete and one is called after.

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
	if(bean ! = null) { Object cacheKey = getCacheKey(bean.getClass(), beanName);if(! this.earlyProxyReferences.contains(cacheKey)) {returnwrapIfNecessary(bean, beanName, cacheKey); }}return bean;
}
Copy the code

The wrapIfNecessary method is where the proxy is actually generated, and let’s look at its internal implementation first.

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
	// Create proxy ifWe have advice. // We have advice. // We have advice. Bean.getclass () finds all notifications and advisors in bean.getClass() // First, find all advisors in the Bean factory To follow beanClass and Pointcut matching Object [] specificInterceptors = getAdvicesAndAdvisorsForBean (bean. GetClass (), beanName, null);if(specificInterceptors ! = DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); // Create the proxy and return the proxy class. Replace the real bean with Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;
	}
	this.advisedBeans.put(cacheKey, Boolean.FALSE);
	return bean;
}
Copy the code

Looking at the source code above, the whole process is divided into two steps, match and create return. This completes the substitution of the proxy class.

1, match,

First, it is easier to find all the advisors. As we know, configured notifications are wrapped as advisor registrations during parsing. Take beanDefinitionNames for all Beannames in the container, loop for bean types that are the type of the Advisor interface, and return if necessary.

public List<Advisor> findAdvisorBeans() {
	// Determine list of advisor bean names, ifnot cached already. String[] advisorNames = null; / / get the beanName advisorNames = BeanFactoryUtils. BeanNamesForTypeIncludingAncestors (enclosing the beanFactory, Advisor. The class,true.false);
		
	List<Advisor> advisors = new LinkedList<Advisor>();
	for (String name : advisorNames) {
		ifAdd (this.beanFactory.getBean(name, advisor.class)); }}} // The advisors returned are all the advice and advisors in the configuration filereturn advisors;
}
Copy the code

Finding advisors does not end with matching the pointcut to see if these advisors meet expression conditions. It ends up calling the match method of Pointcut. This method is too deeply nested to post code. The idea is to determine whether a class and its corresponding method match a pointcut expression.

2. Create the proxy

After finding the match above and determining that the current bean really needs a proxy, the createProxy method is called.

  • Set up the agent factory
protected Object createProxy(Class<? > beanClass, String beanName, Object[] specificInterceptors, TargetSource TargetSource) {// create ProxyFactory ProxyFactory ProxyFactory = new ProxyFactory(); // Copy our properties (proxyTargetClass etc) inherited from ProxyConfig. proxyFactory.copyFrom(this); EvaluateProxyInterfaces (beanClass, proxyFactory); // Set Advisor to broker factory Advisor[] Advisors = buildAdvisors(beanName, specificInterceptors);for(Advisor advisor : advisors) { proxyFactory.addAdvisor(advisor); } / / set the target object proxyFactory. SetTargetSource (targetSource);return proxyFactory.getProxy(this.proxyClassLoader);
}
Copy the code
  • create

Creating proxies is divided into JDK proxies and Cglib proxies. Here we will focus on JDK proxies. The getProxy method calls the JdkDynamicAopProxy class method. This class also implements the InvocationHandler interface, indicating that it is also a call handler. Namely the proxy class invoke method, the call is invoked to JdkDynamicAopProxy. Actually the invoke ().

Public Object getProxy(ClassLoader ClassLoader) {// Proxy <? >[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised); // This is an instance of JdkDynamicAopProxy.return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
Copy the code

Invocation of proxy classes

After the Bean is instantiated and dependency injection is complete, it determines whether the current Bean needs a proxy and, if so, generates a proxy class to replace the original class. In business method calls, will call to JdkDynamicAopProxy. The invoke ().

When executing invoke, we can divide it into two steps… -_ – | |, something to do with two very decree by destiny, every time is two steps.

1, get the method of interception chain

Matches all advisors from the broker factory to the PointcutAdvisor type. Check that the class’s methods are in the pointCut scope with matches targetClass and Method. Add the interceptorList and return it.

public List<Object> getInterceptorsAndDynamicInterceptionAdvice( Advised config, Method method, Class<? > targetClass) { List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length); //config is an instance of the broker factoryfor (Advisor advisor : config.getAdvisors()) {
		if (advisor instanceof PointcutAdvisor) {
			PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
			if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
				MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
				MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
				if(MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) { interceptorList.addAll(Arrays.asList(interceptors)); }}}}return interceptorList;
}
Copy the code

2, call

Get the method’s interceptor chain and call. There’s one interesting thing about this call. It creates an object ReflectiveMethodInvocation first. This object takes two arguments

CurrentInterceptorIndex / / the current call interceptor index, the default value of 1 interceptorsAndDynamicMethodMatchers / / the list of interceptorsCopy the code

And then look at how it calls.

Public Object proceed() throws Throwable {// If the two variables are equal, all interceptors have been calledif(this currentInterceptorIndex = = this. InterceptorsAndDynamicMethodMatchers. The size () - 1) {/ / execution be proxy methodreturninvokeJoinpoint(); } // Start at -1 and add 1 each time. InterceptorOrInterceptionAdvice is corresponding to each notification / / such as before, after, after - returning... // The corresponding instance classes are: / / MethodBeforeAdviceInterceptor, AspectJAfterAdvice, AfterReturningAdviceInterceptor Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); // When the invoke method of a specific notification is called, the current object is passed as an argument. Proceed (). // This is a chain of calls to invokeJoinpoint(). When all the calls are finished, invokeJoinpoint() is invoked.return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
Copy the code

After looking at its call flow, we still have a question. There are five types of notification: pre notification, post notification, method return notification, surround notification, and exception notification. So, how does it guarantee the order of calls? Let’s look at the concrete implementation of the two notification classes.

Pre notice
Public Object Invocation (MethodInvocation MI) throws invocation invocation { Call this.advice.before(mi.getMethod(), mi.getarguments (), mi.getThis());return mi.proceed();
}
Copy the code
The rear notice
Public Object invocation (MethodInvocation mi) throws invocation {invocation invocation (MethodInvocation mi) throws invocation invocation (MethodInvocation mi).returnmi.proceed(); } finally { invokeAdviceMethod(getJoinPointMatch(), null, null); }}Copy the code
Surrounding the notification
Public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {system.out.println ("Before surround notification"); // You can decide if you want to call back. Object result = joinPoint.proceed(); System.out.println("After surround notification");
	return result;
}
Copy the code

Annotation-based AOP

If you want to use annotation AOP, you need to turn on a configuration. < aop: aspectj autoproxy / >. Spring parsing configuration label, call to AspectJAutoProxyBeanDefinitionParser. The parse (). This method mainly do one thing: registering AnnotationAwareAspectJAutoProxyCreator entrance class. XML configuration mode of AOP, Spring entrance class called AspectJAwareAdvisorAutoProxyCreator registered.

Their invocation process is the same, are instantiated class called after grandpa AbstractAutoProxyCreator. PostProcessAfterInitialization (). Recall that the wrapIfNecessary method is where the proxy really comes in. It gets all the notifications and matches them with the current bean class. If so, the current bean needs a proxy and the proxy class is generated. As we know, in XML-configured AOP, Spring encapsulates configured notifications as Advisor registered in the container, so when fetched, just match beans of type Advisor directly in the Bean factory. However, when parsing annotation AOP, we see that it only registers an entry class, not an Advisor, so how do we get it here?

Look back at the method of querying the Advisor.

protected List<Advisor> findCandidateAdvisorsAdvoisor List<Advisor> advisors () {// Add all the Spring advisors found according to superclass rules  = super.findCandidateAdvisors(); // Build Advisorsfor all AspectJ aspects inThe bean factory. / / this is advisor of query annotation advisors. The addAll (this) aspectJAdvisorsBuilder) buildAspectJAdvisors ());return advisors;
}
Copy the code

When the buildAspectJAdvisors method queries an Advisor, it can be done in roughly three steps.

1. Get all beannames from the Bean factory

String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);

Loop through beanNames to determine whether the bean contains Aspect annotations

for(String beanName : beanNames) { Class<? > beanType = this.beanFactory.getType(beanName);if(this.advisorFactory.isAspect(beanType)) { aspectNames.add(beanName); . }}Copy the code

3. Get Advisors

Get all the Method objects on the Class object and return different Advice objects based on the Annotation type of the Method object. This is the same as the Advice object returned in XML.

switch (aspectJAnnotation.getAnnotationType()) {
	case AtBefore:
		springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
		break;
	case AtAfter:
		springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
		break;
	case AtAfterReturning:
		springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
		AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
		if (StringUtils.hasText(afterReturningAnnotation.returning())) {
			springAdvice.setReturningName(afterReturningAnnotation.returning());
		}
		break;
	case AtAfterThrowing:
		springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
		AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
		if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
			springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
		}
		break;
	case AtAround:
		springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
		break;
	case AtPointcut:
		if (logger.isDebugEnabled()) {
			logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
		}
		return null;
	default:
		throw new UnsupportedOperationException(
				"Unsupported advice type on method " + candidateAdviceMethod);
}
Copy the code

Last advice and pointcuts encapsulation into InstantiationModelAwarePointcutAdvisorImpl object to return. After you return, create a proxy class to replace it.

Thus, annotation AOP is parsed and generated when querying Advisors, and the rest of the configuration process is the same as XML.

Four,

This chapter focuses on three issues. That is, how to generate proxy classes, how to execute proxy classes, and how to process AnnotationAOP. The combination of this chapter and the previous section gives us a good impression of Spring AOP’s internal processing flow.

  • How do you create proxy classes? The Bean object is instantiated through a loop called beanNames to determine whether the object matches the Pointcut expression. If there is a match, generate a different Advisor object based on the advice, and then call JDK or CGLIB methods to generate the proxy class return.

  • The proxy class performs a call to JDK or CGLIB’s Invoke method to query the advisor’s call chain. Chained invocation, which calls different advice depending on the notification type, is enhanced.