Object-oriented programming (OOP) is what we use most in business development, because its code is logical and intuitive, and you can see the complete execution link from the top down.

Extending this foundation, there is the emergence of section-oriented programming (AOP), which extracts repeatable crosscutting logic into unified modules.

For example, log printing, security monitoring, if the IDEA of OOP is to add duplicate code before and after each method, then there will be too many changes to maintain. So AOP programming came along, and AOP’s focus was horizontal, as opposed to OOP’s vertical.

So let’s learn how AOP is used and the processing logic in the Spring container


How to use

Before the business development needs to use AOP, so I also compiled a Spring custom annotations to implement AOP, interested students can go to see ~

Here’s an example from the book:

Create the bean for interception

public class TestAopBean {

	private String testStr = "testStr";

	public void testAop(a) {
	    // The intercepted method, simply print
		System.out.println("I am the true aop bean"); }}Copy the code

Create the Advisor

@Aspect
public class AspectJTest {

	@Pointcut("execution(* *.testAop(..) )")
	public void test(a) {}@Before("test()")
	public void beforeTest(a) {
		System.out.println("before Test");
	}


	@After("test()")
	public void afterTest(a) {
		System.out.println("after Test");
	}

	@Around("test()")
	public Object aroundTest(ProceedingJoinPoint joinPoint) {
		System.out.println("around Before");
		Object o = null;
		try {
			// Call the method of the aspect
			o = joinPoint.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		System.out.println("around After");
		returno; }}Copy the code

First, the @aspect annotation makes Spring realize that this is a Aspect bean and marks the method @pointcut (“execution(* *.testaop (..)). The expression inside execution() indicates the intercepted method. Before, After, and Around indicate execution Before and After the intercepted method, respectively.


Create the configuration file aop.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:aop="http://www.springframework.org/schema/aop"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <! Enable AOP functionality -->
	<aop:aspectj-autoproxy />

	<bean id="aopTestBean" class="aop.TestAopBean"/>

	<bean class="aop.AspectJTest" />
</beans>
Copy the code

Test the Demo

public class AopTestBootstrap {
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("aop/aop.xml");
		TestAopBean bean = (TestAopBean) context.getBean("aopTestBean");
		bean.testAop();
		// The output sequence of the enhancement method is as follows:
		// Around proceed -> Before -> Around proceed -> After
		//around Before
		//before Test
		//I am the true aop bean
		//around After
		//after Test}}Copy the code

Based on the startup example above, we found that the core business method testAop() we wrote simply printed I am the True AOP Bean, but the execution result printed something else, indicating that the class has been enhanced. We extended it without modifying the core business method. It is proved that AOP can make the auxiliary functions independent of the core business, which facilitates the expansion and decoupling of programs.

Easy to use, let’s see how Spring implements AOP functionality


Dynamic AOP custom tags

The AOP implementation also uses custom annotations, according to the IDEA of custom tags: for each custom tag, there is a corresponding parser, and then with the help of the powerful development tool IDEA positioning function, find where the parser registered:

  1. Hold down thectrl, corresponding to the positioning labelxsdfile
  2. According to the named file, inMETA-INFI found it in the directoryspring.handlersfile
  3. The processor was found in the processor fileAopNamespaceHandler
public class AopNamespaceHandler extends NamespaceHandlerSupport {
	@Override
	public void init(a) {
		// In 2.0 XSD as well as in 2.1 XSD.
		registerBeanDefinitionParser("config".new ConfigBeanDefinitionParser());
		// Note 8.1 Custom annotations, registered parser, element name is AspectJ-AutoProxy
		registerBeanDefinitionParser("aspectj-autoproxy".new AspectJAutoProxyBeanDefinitionParser());
		registerBeanDefinitionDecorator("scoped-proxy".new ScopedProxyBeanDefinitionDecorator());
		// Only in 2.0 XSD: moved to context namespace as of 2.1
		registerBeanDefinitionParser("spring-configured".newSpringConfiguredBeanDefinitionParser()); }}Copy the code

The processor inherits fromNamespaceHandlerSupport, will be executed during loadinginitThe initialization method, in this case, registersaspectj-autoproxyType resolverAspectJAutoProxyBeanDefinitionParser

You’ve already seen how to register a custom parser, so let’s take a quick look at how the application resolves a bean of type AspectJ-AutoProxy.


Registered AnnotationAwareAspectJAutoProxyCreator

When parsing, its entry method is as follows:

public BeanDefinition parse(Element element, ParserContext parserContext) {
	/ / aop annotation parsing entrance, registered AnnotationAwareAspectJAutoProxyCreator
	AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
	// Handling of subclasses in annotations
	extendBeanDefinition(element, parserContext);
	return null;
}
Copy the code

The entry method is as concise as ever, telling you what to do, and then handing over the complex logic to the utility class or subclass to implement it, so the next thing to look at is how to registerAnnotationAwareAspectJAutoProxyCreator.

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary( ParserContext parserContext, Element sourceElement) {
	/ / by tools, AspectJAnnotationAutoProxyCreator registration or upgrade
	BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			parserContext.getRegistry(), parserContext.extractSource(sourceElement));
	// Handle proxy-target-class and expose-proxy properties
	useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
	// Register the component and notify it for the listener to process
	registerComponentIfNecessary(beanDefinition, parserContext);
}
Copy the code

We can see that there are three processing logic in this method, so we will analyze them one by one:


AnnotationAwareAspectJAutoProxyCreator register or upgrade

For the realization of AOP, basically all is by AnnotationAwareAspectJAutoProxyCreator to complete, it can automatically according to the tangent Point of the @ Point annotations define agent matching bean.

Due to theSpringDid a lot of work for us, so developmentAOPBusiness can be so simple, even the configuration is also simplified a lot, so take a lookSpringHow to use custom configuration to help us automatically registerAnnotationAwareAspectJAutoProxyCreator.

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @Nullable Object source) {
	/ / AnnotationAwareAspectJAutoProxyCreator actually registered type of bean
	return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

private static BeanDefinition registerOrEscalateApcAsRequired( Class
        cls, BeanDefinitionRegistry registry, @Nullable Object source) {
	Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
	if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
		// If an automatic proxy creator already exists in Registry and the type of the agent passed in is not the same as the registered one, determine whether it needs to be modified based on priority
		BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
		if(! cls.getName().equals(apcDefinition.getBeanClassName())) {// Select which one to use according to the priority
			int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
			int requiredPriority = findPriorityForClass(cls);
			if (currentPriority < requiredPriority) {
				// Change the registered beanName to use the passed proxy creatorapcDefinition.setBeanClassName(cls.getName()); }}// Since the proxy already exists, the default Settings are not required
		return null;
	}
	RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
	beanDefinition.setSource(source);
	// The default is the minimum priority
	beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
	beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
	Automatic proxy creator / / registered name is always org. Springframework. Aop. Config. InternalAutoProxyCreator
	registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
	return beanDefinition;
}
Copy the code

In this step, and realized automatic registration AnnotationAwareAspectJAutoProxyCreator class, at the same time can see involves the concept of the priority has always been AUTO_PROXY_CREATOR_BEAN_NAME and registered name.


Handles proxy-target-class and expose-proxy properties

private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
	if(sourceElement ! =null) {
		// This method simply resolves the following two properties and, if true, adds them to the properties list of the proxy registry
		// definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE)
		boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
		if (proxyTargetClass) {
			// Handle proxy-target-class attributes
			// Depending on how the code is generated, use JDK dynamic proxy or Cglib in the next step
			AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
		}
		boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
		if (exposeProxy) {
			// Handle the expose-proxy property
			// Extend the enhancement, sometimes the target object internal self-call does not implement the enhancement in the aspect, through this property can be enhanced to both methodsAopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); }}}Copy the code

About AopConfigUtils. ForceAutoProxyCreatorToUseClassProxying (registry); Method, which is the process of setting properties. If the resolved properties are true, they are added to the property list of the proxy registry, which is not covered here.

Familiarize these two properties separately:


proxy-target-class

Spring AOPSome useJDKDynamic Proxy + InvocationHandler, orCGLIB(Code Generation LIB) to create a proxy for the target object. According to the book, the recommended use isJDKDynamic proxy.

JDK dynamic proxies are used if the proxied target object implements at least one interface. All interfaces implemented by this target type will be proxied.

If the target object ** does not implement any interface, a CGLIB proxy is created. ** If you want to proxy all the methods of the target object, not just those implemented from the interface, you can use this proxy-target-class property to enable the mandatory use of the CGLIB proxy.

However, forcibly enabling CGLIB causes the following two problems:

  • Final methods cannot be advised at the same time because they cannot be overwritten
  • The CGLB binary distribution needs to be placed under the CLASspath

With these two aspects in mind, there are two ways to force the CGLIB agent on:

<! -- one -->
<aop:config proxy-target-class="true">.</aop:config>
<! -- two -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
Copy the code

The relevantCGLIBAgent, the elder brother spoke very thoroughly, suggested that we can go to understand ~Cglib and its basic use


expose-proxy

Sometimes the internal self-invocation of the target object will not be able to implement the enhancement in the aspect.

For example, both methods have the transaction annotation @transactional but the transaction type is different:

public interface TestService {
	void a(a);
	void b(a);
}

public class TestServiceImpl implements TestService {

	@Override
	@Transactional(propagation = Propagation.REQUIRED)
	public void a(a) {
		this.b();
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void b(a) {
		System.out.println("Hello world"); }}Copy the code

Where this refers to the target object, the this.b() method will not perform the slice of the B transaction, that is, will not perform transaction enhancement.

To solve this problem, the a() and B () methods can be enhanced simultaneously with expose-proxy:

<! -- one -->
<aop:config expose-proxy="true">.</aop:config>
<! -- two -->
<aop:aspectj-autoproxy expose-proxy="true"/>
Copy the code

Register the component and notify

Emmmm, the internal logic of this method is as clear as its name, so I won’t go into details.


Creating an AOP proxy

Mainly around in front of the automatic proxy AnnotationAwareAspectJAutoProxyCreator registration process to explain that the next see agent is done automatically to complete the operation of AOP.

The following is AnnotationAwareAspectJAutoProxyCreator inheritance system:

In the upper right corner of the image, you can see that it implements the BeanPostProcessor interface, which, as mentioned in the previous article, is a post-processor that can be extended before and after bean instantiation. Reviewed the implements the interface of the two methods, postProcessBeforeInitialization didn’t do, return the object directly.

What we’re actually dealing with ispostProcessAfterInitializationMethods,beanAfter instantiation, the proxy enhancement is performed in this step, so take a look at this method:

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
	if(bean ! =null) {
		/ / assembly key
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		if (this.earlyProxyReferences.remove(cacheKey) ! = bean) {// Encapsulate the specified bean if it is suitable to be proxied
			returnwrapIfNecessary(bean, beanName, cacheKey); }}return bean;
}

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
	// If it has already been processed
	if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
		return bean;
	}
	// No enhancement is required
	if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
		return bean;
	}
	/ / whether the given bean class represents a class of infrastructure, infrastructure class should not be acting | | configuration specified beans do not need to agent
	if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}
	// Create a proxy if there is an enhancement method
	Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
	if(specificInterceptors ! = DO_NOT_PROXY) {// The enhancement method is not null
		this.advisedBeans.put(cacheKey, Boolean.TRUE);
		// Create the proxy
		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

To extract the core flow:

  1. Gets the enhancer method or enhancerWe just wrote that@Before@AfterSomething like that, which is the enhancement method,AOPIdentify these enhancements first.
  2. Proxy based on the obtained enhancementWhen enhancements are found, they need to be enhanced proxies, actually thisbeanIt’s not exactly the old type anymore, it’s going to be the proxied type.

Gets the enhancer method or enhancer

The entry method is here:

protectedObject[] getAdvicesAndAdvisorsForBean( Class<? > beanClass, String beanName,@Nullable TargetSource targetSource) {
	// Find a matching slice
	List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
	if (advisors.isEmpty()) {
		return DO_NOT_PROXY;
	}
	return advisors.toArray();
}

protected List<Advisor> findEligibleAdvisors(Class
        beanClass, String beanName) {
	// Get classes declared as AspectJ annotations from beanFactory, and extract those classes from the enhancer
	/ / delegated to subclass implementation org. Springframework. Aop. Aspectj. Autoproxy. AspectJAwareAdvisorAutoProxyCreator. ExtendAdvisors
	List<Advisor> candidateAdvisors = findCandidateAdvisors();
	// Find the matching enhancer
	List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
	extendAdvisors(eligibleAdvisors);
	if(! eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); }return eligibleAdvisors;
}
Copy the code

Getting an enhancement method for a given bean consists of getting all the enhancements and finding all the enhancements that apply to the bean and applying them. This corresponds to the two methods findCandidateAdvisors and findAdvisorsThatCanApply. If no corresponding enhancer is found, DO_NOT_PROXY is returned, indicating that no enhancement is required.

Due to too much logic, so the next post code will not be too much, mainly to understand its general process, there is a need to follow the source code engineering notes to track the complete process ~ :


Find the corresponding enhancer, findCandidateAdvisors

protected List<Advisor> findCandidateAdvisors(a) {
	List<Advisor> advisors = super.findCandidateAdvisors();
	if (this.aspectJAdvisorsBuilder ! =null) {
		/ / comment the actual call is 8.3 org. Springframework. Aop). Aspectj annotation. BeanFactoryAspectJAdvisorsBuilder. BuildAspectJAdvisors
		advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
	}
	return advisors;
}
Copy the code

Practical perspective, the key is this. This method aspectJAdvisorsBuilder. BuildAspectJAdvisors () this method looks simple, but the actual processing logic, depth of the code is very much also, so in order to avoid too much code, I list the main processes, And what are the key solutions

The main process is as follows:

  1. Fetching all beannames extracts beans previously registered in the beanFactory.
  2. Walk through the list of beans extracted in the previous step to find classes annotated with @AspectJ for further processing
  3. Proceed with the enhancer extraction of the @AspectJ annotated class extracted in the previous step
  4. Add the extracted results to the cache

Comments in the code can be queried, from [Comment 8.3] to [Comment 8.8 generating enhancers from pointcut information] is the processing logic of this method

Does does in the last step in this process, will identify to the point of contact information (PointCut) and enhanced method (Advice) encapsulation, concrete is carried out by the realization of the Advisor class InstantiationModelAwarePointcutAdvisorImpl unified encapsulation.

public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
	// Simple assignment
	this.declaredPointcut = declaredPointcut; .if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
		Pointcut preInstantiationPointcut = Pointcuts.union(aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
		this.pointcut = new PerTargetInstantiationModelPointcut(
		this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
		this.lazy = true;
	}
	else {
		// A singleton aspect.
		this.pointcut = this.declaredPointcut;
		this.lazy = false;
		// Initializes the enhancer
		this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut); }}Copy the code

The first half of the package logic is simply assignment. The key is the method instantiateAdvice(this.declaredpointcut), in which the logic is different for different enhancements (Before/After/Around). In ReflectiveAspectJAdvisorFactory# getAdvice difference in method realized according to the different types of annotations encapsulate different enhancer.

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
			MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {...// Comment 8.7 encapsulates different enhancers for different annotation types
	switch (aspectJAnnotation.getAnnotationType()) {
		case AtPointcut:
			
		}
		return null;
	case AtAround:
		springAdvice = new AspectJAroundAdvice(
				candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
		break;
	case AtBefore:
		springAdvice = new AspectJMethodBeforeAdvice(
				candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
		break;
	case AtAfter:
		springAdvice = new AspectJAfterAdvice(
				candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
		break;
	case AtAfterReturning:
		springAdvice = new AspectJAfterReturningAdvice(
		    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
		AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
		if (StringUtils.hasText(afterReturningAnnotation.returning())) {
			springAdvice.setReturningName(afterReturningAnnotation.returning());
		}
		break;
	case AtAfterThrowing:
		springAdvice = new AspectJAfterThrowingAdvice(
		    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
		AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
	    if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
		    springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
		}
		break;
	default:}}Copy the code

Finally, the pointcut method is parsed and encapsulated into Advisor, and the extracted results are added to the cache. If you are careful, you may notice that there are two other types of enhancers in addition to the regular enhancer: synchronous instantiation enhancer and introduction enhancer. ** Due to the use of less, so I see the source code of the two branch processing did not in-depth study, interested students please continue to in-depth study of the two enhancer ~


Get the matching enhancer findAdvisorsThatCanApply

In the previous process, all of the enhancers were resolved, but not all of the previously resolved enhancers are applicable to the bean currently being processed, so you need a method to pick out the appropriate enhancers.

protected List<Advisor> findAdvisorsThatCanApply(List
       
         candidateAdvisors, Class
         beanClass, String beanName)
        {
	ProxyCreationContext.setCurrentProxiedBeanName(beanName);
	try {
		// Perform the filter enhancer in this step
		return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
	}
	finally {
		ProxyCreationContext.setCurrentProxiedBeanName(null); }}Copy the code

As you can see, the concrete implementation of filtration is a tool of class methods AopUtils. FindAdvisorsThatCanApply:

public static List<Advisor> findAdvisorsThatCanApply(List
       
         candidateAdvisors, Class
         clazz)
        {
	if (candidateAdvisors.isEmpty()) {
		return candidateAdvisors;
	}
	List<Advisor> eligibleAdvisors = new ArrayList<>();
	// Iterate over all enhancers
	for (Advisor candidate : candidateAdvisors) {
		// Handle the introduction enhancement first
		if (candidate instanceofIntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); }}booleanhasIntroductions = ! eligibleAdvisors.isEmpty();for (Advisor candidate : candidateAdvisors) {
		// Skip it
		if (candidate instanceof IntroductionAdvisor) {
			// already processed
			continue;
		}
		// Handle normal enhancer types
		if(canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); }}return eligibleAdvisors;
}
Copy the code

In the canApply() method, if eligibleAdvisors are qualified, add them to eligibleAdvisors, and return a list of enhancers suitable for the bean.


To create the agent

After obtaining the enhancers for all the corresponding beans from the previous process, you can begin the creation of the proxy.

protected Object createProxy(Class
        beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) {
	ProxyFactory proxyFactory = new ProxyFactory();
	// Copy to get related attributes in the current class
	proxyFactory.copyFrom(this);
	// Determine whether targetClass should be used instead of its interface proxy for a given bean
	if(! proxyFactory.isProxyTargetClass()) {// Check the proxyTargetClass setting and the preserveTargetClass attribute
		if (shouldProxyTargetClass(beanClass, beanName)) {
			proxyFactory.setProxyTargetClass(true);
		}
		else {
			// Add a proxy interfaceevaluateProxyInterfaces(beanClass, proxyFactory); }}// In this step, the interceptor is primarily encapsulated as an enhancer
	Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
	proxyFactory.addAdvisors(advisors);
	proxyFactory.setTargetSource(targetSource);
	// Customize the proxy
	customizeProxyFactory(proxyFactory);
	// used to control whether the agent factory is allowed to change after it has been configured
	// The default value is false and the configuration of the proxy is not allowed to be modified
	proxyFactory.setFrozen(this.freezeProxy);
	if (advisorsPreFiltered()) {
		proxyFactory.setPreFiltered(true);
	}
	// Generate proxy, delegate to ProxyFactory to handle.
	return proxyFactory.getProxy(getProxyClassLoader());
}
Copy the code

For the creation and processing of proxy classes,SpringEntrusted to theProxyFactoryTo handle, the function posted above is mainly onProxyFactoryTo prepare for the actual creation of the agent, the main process is as follows:

  1. Gets the attributes of the current class
  2. Adding a Proxy Interface
  3. Encapsulate the Advisor and add it to ProxyFactory
  4. Sets the class to proide
  5. Provide a custom function customizeProxyFactory to subclasses, which further encapsulates the ProxyFactory
  6. Get the agent

The key steps are the third and sixth, where the interceptor wrapper is performed in the third step. See [Annotation 8.9 Creating an AOP proxy for a given bean] and [Annotation 8.10 Wrapping an Interceptor as an Advisor] for the code flow.

Then, all the enhancers are wrapped, and the last step of the parsing is the creation and retrieval of the proxy.

public Object getProxy(@Nullable ClassLoader classLoader) {
	return createAopProxy().getProxy(classLoader);
}
Copy the code

Create proxy createAopProxy()

Locate the code to create the agent:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
	if(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<? > targetClass = config.getTargetClass();if (targetClass == null) {
			throw new AopConfigException("TargetSource cannot determine target class: " +
					"Either an interface or a target is required for proxy creation.");
		}
		if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
			return new JdkDynamicAopProxy(config);
		}
		return new ObjenesisCglibAopProxy(config);
	}
	else {
		return newJdkDynamicAopProxy(config); }}Copy the code

As you can see from the code above, you use several key attributes to determine what type of AopProxy you are creating. One is JDK dynamic proxy, and the other is CGLIB dynamic proxy.

The proxy-target-class and targetClass attributes mentioned earlier determine which proxy should be created.


GetProxy getProxy()

[JDK dynamic proxy] and [CGLIB dynamic proxy]

At the same time, the implication of dynamic proxy is that abstract classes are not subclassed at compile time and the final object is generated at run time.


JDK dynamic proxy

JDKProxy is the default recommended proxy, usingProxy + InvocationHandler.

This can be done by defining an interface, an implementation class, and a handler that inherits from InvocationHandler, and then overriding the invoke method in the handler to enhance the proxy object.

JdkDynamicAopProxy.java

public Object getProxy(@Nullable ClassLoader classLoader) {
	// Comments 8.11 JDK Dynamic Proxy
	if (logger.isTraceEnabled()) {
		logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource()); } Class<? >[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
	findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
	return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
Copy the code

NewProxyInstance (classLoader, proxiedInterfaces, this), the third parameter is JdkDynamicAopProxy itself, And it implements the InvocationHandler interface, overloading the Invoke method.

org.springframework.aop.framework.JdkDynamicAopProxy#invoke

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	// Comment 8.12 JDK dynamic proxy overloaded invoke method
	MethodInvocation invocation;
	Object oldProxy = null;
	boolean setProxyContext = false;
	TargetSource targetSource = this.advised.targetSource;
	Object target = null;
	try {
		Object retVal;
		if (this.advised.exposeProxy) {
			// Make invocation available if necessary.
			oldProxy = AopContext.setCurrentProxy(proxy);
			setProxyContext = true; } target = targetSource.getTarget(); Class<? > targetClass = (target ! =null ? target.getClass() : null);
		// Get the interception chain for this method.
		// Get the interceptor chain for this method
		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

		// Check if we have any aspect logic. If we don't do this, we can fall back on the direct reflection invocation target and avoid creating the MethodInvocation.
		if (chain.isEmpty()) {
			Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
			retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
		}
		else {
			/ / packages interceptors in ReflectiveMethodInvocation, easy to use proceed to perform the interceptor
			invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);

			// Execute interceptor chainretVal = invocation.proceed(); }...return retVal;
	}
	finally {
		if(target ! =null && !targetSource.isStatic()) {
			targetSource.releaseTarget(target);
		}
		if(setProxyContext) { AopContext.setCurrentProxy(oldProxy); }}}Copy the code

createJDKThe main work in the proxy process is when an interceptor chain is created and usedReflectiveMethodInvocationClass is encapsulated, and after encapsulation, its arguments are called one by oneproceedMethod used to achieve the pre-enhancement and post-enhancement of the target method.

org.springframework.aop.framework.ReflectiveMethodInvocation#proceed

public Object proceed(a) throws Throwable {
	// Execute the pointcut method after executing all enhancers
	if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
		return invokeJoinpoint();
	}
	// Get the next interceptor to execute
	Object interceptorOrInterceptionAdvice =
			this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
	if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
		// Dynamic matchingInterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; Class<? > targetClass = (this.targetClass ! =null ? this.targetClass : this.method.getDeclaringClass());
		if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
			return dm.interceptor.invoke(this);
		}
		else {
			// Fail to match, skip interceptor, return directly
			returnproceed(); }}else {
		return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); }}Copy the code

Refer to this method for specific code and comments. About JDK dynamic proxy, in-depth study can also be carried out separately, so it is recommended to see this information leopards take you to see the source code: JDK dynamic proxy, and learn


CGLIB dynamic proxy

CGLIB[Code Generation LIB] is a powerful high-performance Code Generation package. It is widely used in many AOP frameworks.

Once again, this brother willCGLIBAgent, introduced in detailCGLIBIn what scenarios, and in what order code is processed by it,Cglib and its basic use.

Hopefully, by the end of this article, you can see how the CGLIB code generation package specifically enhances classes.


Proxy enhancement results

Through the previous series of steps, you parse tags, attributes, enhance methods, and finally get a CGLIB proxy from which to create beans

Take a look inside the bean that was last proxied:

As you can see from the figure, the final creation is decoratedbeanThe inside obviously isCGGLIBFor the code generated by the agent, we implemented method enhancement without modifying the business code.


Static AOP

If there are dynamic proxies, there will also be static proxies.

When using static AOP, you use LTW (load-time Weaving), which refers to dynamically Weaving AspectJ facets as the virtual machine loads bytecode files.

AOPStatic agent mainly in the virtual machine is started by changing the way target byte code to complete the target object, it has higher efficiency compared with the dynamic proxy, because in the process of dynamic proxy invocation, also need a dynamically create the proxy class and proxy target steps, and the static agent is at startup is complete bytecode increase or decrease, When the system calls the target class again, it is no different from invoking the normal class, so it is relatively efficient.

For static AOP usage and learning, see this article: From Proxy Mechanisms to Spring AOP


conclusion

Dynamic AOP is simple to use, but there are two main points to summarize on how to implement it:

  1. The dynamic analysisAOPThe label
  2. createAOPThe agent

But in theSpringThe underlying implementation logic is extremely complex, fromSpringAs you can see in the framework, this is good code design, the top level of entry as simple as possible, users can easily master the function, complex implementation logic is hidden.

It took me nearly a week to write this AOP learning summary. I read books first, and spent a night after work to explain the general process. The next night, I went through the code, and found that there were still doubts in some places, such as JDK and Cglib dynamic proxy.

Add the code comments, analyze what the dynamic proxy does at each step, and comb through it again with the knowledge of the post-processor BeanPostProcessor and custom tag parsing. Scattered, finally finishing finishing.

In the staticAOPKnowledge point, according to my understanding, the deeper into the bottom of the system, its execution efficiency is higher, so reduce the dynamic creation of proxy class and proxy target object steps, static proxy speed will be improved. At the same time, the complexity of code writing will also increase after getting close to the bottom layer, so I am weighing high frequency usage scenarios (dynamic proxy), this study did not understand in detail, leave this hole, later have a chance to fill in ~


Due to limited personal skills, if there is any misunderstanding or mistake, please leave a comment, and I will correct it according to my friends’ suggestions

Gitee address https://gitee.com/vip-augus/spring-analysis-note.git

Making the address https://github.com/Vip-Augus/spring-analysis-note


The resources

  1. Cglib and its basic use

  2. Talk about cGLIb dynamic proxy

  3. Spring-aop automatically creates proxies

  4. Small leopard with you to see the source code: JDK dynamic proxy

  5. From proxy mechanisms to Spring AOP

  6. — Beijing: Posts and Telecommunications Press


Portal:

  • Spring source learning – environment preparation

  • (1) The infrastructure of the container

  • Spring source code learning (2) default tag parsing

  • Spring source code learning (3) custom tags

  • Spring source code learning (four) bean loading

  • Spring source code learning (5) loop dependency

  • Spring source code learning (six) extension function part 1

  • Spring source code learning (seven) extension features part 2

  • Spring source learning (eight) AOP use and implementation principle

  • Spring source code learning (9) Transaction Transaction

  • (10) Spring MVC