As we all know, the various aspects of Aop are certainly created by proxy (Aop basic concepts should be heard by you, there is no need to elaborate here). But then the problem arises. We have analyzed the parsing and creation of ordinary beans. Where does AOP create proxy objects and how does it match pointcuts? This paper also analyzes these two problems. The analysis of dynamic proxy has been analyzed in the last article, you can take a look at it. portal

The question of this study

  • Creation of proxy objects
  • Match point of tangency

The test code

@Aspect
class AdviceUsingThisJoinPoint {

	private String lastEntry = "";

	public String getLastMethodEntered(a) {
		return this.lastEntry;
	}

	@Pointcut("execution(int *.getAge())")
	public void methodExecution(a) {}@Before("methodExecution()")
	public void entryTrace(JoinPoint jp) {
		this.lastEntry = jp.toString();
		System.out.println(this.lastEntry); }}Copy the code

public class TestBean implements BeanNameAware.BeanFactoryAware.ITestBean.IOther.Comparable<Object> {
  // The redundant part is deleted
	private String name;

	private int age;

	public TestBean(a) {}public TestBean(String name) {
		this.name = name;
	}
	
	@Override
	public String getName(a) {
		return name;
	}

	@Override
	public void setName(String name) {
		this.name = name;
	}

	@Override
	public int getAge(a) {
		return age;
	}

	@Override
	public void setAge(int age) {
		this.age = age;
	}

	/ * * *@see org.springframework.beans.testfixture.beans.ITestBean#exceptional(Throwable)
	 */
	@Override
	public void exceptional(Throwable t) throws Throwable {
		if(t ! =null) {
			throwt; }}@Override
	public void unreliableFileOperation(a) throws IOException {
		throw new IOException();
	}
	/ * * *@see org.springframework.beans.testfixture.beans.ITestBean#returnsThis()
	 */
	@Override
	public Object returnsThis(a) {
		return this;
	}

	/ * * *@see org.springframework.beans.testfixture.beans.IOther#absquatulate()
	 */
	@Override
	public void absquatulate(a) {}@Override
	public int haveBirthday(a) {
		return age++;
	}

	@Override
	public boolean equals(Object other) {
		if (this == other) {
			return true;
		}
		if (other == null| |! (otherinstanceof TestBean)) {
			return false;
		}
		TestBean tb2 = (TestBean) other;
		return (ObjectUtils.nullSafeEquals(this.name, tb2.name) && this.age == tb2.age);
	}

	@Override
	public int hashCode(a) {
		return this.age;
	}

	@Override
	public int compareTo(Object other) {
		if (this.name ! =null && other instanceof TestBean) {
			return this.name.compareTo(((TestBean) other).getName());
		}
		else {
			return 1; }}@Override
	public String toString(a) {
		return this.name; }}Copy the code

      
<! DOCTYPEbeans PUBLIC "- / / SPRING / / DTD BEAN / 2.0 / EN" "https://www.springframework.org/dtd/spring-beans-2.0.dtd">

<beans>

	<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>

	<bean id="aspect" class="org.springframework.aop.aspectj.autoproxy.AdviceUsingThisJoinPoint"/>

	<bean id="adrian" class="org.springframework.beans.testfixture.beans.TestBean" scope="prototype">
		<property name="name" value="adrian"/>
		<property name="age" value="34"/>
	</bean>

</beans>
Copy the code
	@Test
	public void testAdviceUsingJoinPoint(a) {
		ClassPathXmlApplicationContext bf = newContext("usesJoinPointAspect.xml");

		ITestBean adrian1 = (ITestBean) bf.getBean("adrian");
		adrian1.getAge();
		adrian1.getDoctor();
		AdviceUsingThisJoinPoint aspectInstance = (AdviceUsingThisJoinPoint) bf.getBean("aspect");
		assertThat(aspectInstance.getLastMethodEntered().indexOf("TestBean.getAge())") != 0).isTrue();
	}
Copy the code

Source code analysis

Create a proxy object entry for AbstractAutoProxyCreator# postProcessBeforeInstantiation, it can be seen that the core method for getAdvicesAndAdvisorsForBean, behind is to create a proxy object

@Override
	public Object postProcessBeforeInstantiation(Class
        beanClass, String beanName) {
		Object cacheKey = getCacheKey(beanClass, beanName);

		if(! StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
			if (this.advisedBeans.containsKey(cacheKey)) {
				return null;
			}
			if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
				this.advisedBeans.put(cacheKey, Boolean.FALSE);
				return null; }}// Create proxy here if we have a custom TargetSource.
		// Suppresses unnecessary default instantiation of the target bean:
		// The TargetSource will handle target instances in a custom fashion.
		TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
		if(targetSource ! =null) {
			if (StringUtils.hasLength(beanName)) {
				this.targetSourcedBeans.add(beanName);
			}
			Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
			Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		return null;
	}
Copy the code

This method is to get the corresponding Advisor

	protected List<Advisor> findEligibleAdvisors(Class
        beanClass, String beanName) {
		List<Advisor> candidateAdvisors = findCandidateAdvisors();// Get the candidate Advisor
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);// Get the Advisor applicable to the bean: for example, the Pointcut match
		extendAdvisors(eligibleAdvisors);// Special processing
		if(! eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors);/ / sorting
		}
		return eligibleAdvisors;
	}
Copy the code

FindCandidateAdvisors will eventually call buildAspectJAdvisors to get the corresponding Advisor. The first entry will find the @aspect-defined method, generate the corresponding Advisor(which encapsulates the Advice), and then fetch it from the cache

public List<Advisor> buildAspectJAdvisors(a) {
		List<String> aspectNames = this.aspectBeanNames;

		if (aspectNames == null) {
			synchronized (this) {
				aspectNames = this.aspectBeanNames;// Concurrency problems
				if (aspectNames == null) {
					List<Advisor> advisors = new ArrayList<>();
					aspectNames = new ArrayList<>();
					String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(//
							this.beanFactory, Object.class, true.false);
					for (String beanName : beanNames) {
						if(! isEligibleBean(beanName)) {continue;
						}
						// We must be careful not to instantiate beans eagerly as in this case they
						// would be cached by the Spring container but would not have been weaved.Class<? > beanType =this.beanFactory.getType(beanName, false);
						if (beanType == null) {
							continue;
						}
						if (this.advisorFactory.isAspect(beanType)) {// Whether the bean is decorated with @aspect
							aspectNames.add(beanName);
							AspectMetadata amd = new AspectMetadata(beanType, beanName);
							if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
								MetadataAwareAspectInstanceFactory factory =
										new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
								List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);/ / generated Adivsor
								if (this.beanFactory.isSingleton(beanName)) {
									this.advisorsCache.put(beanName, classAdvisors);
								}
								else {
									this.aspectFactoryCache.put(beanName, factory);
								}
								advisors.addAll(classAdvisors);
							}
							else {
								// Per target or per this.
								if (this.beanFactory.isSingleton(beanName)) {
									throw new IllegalArgumentException("Bean with name '" + beanName +
											"' is a singleton, but aspect instantiation model is not singleton");
								}
								MetadataAwareAspectInstanceFactory factory =
										new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
								this.aspectFactoryCache.put(beanName, factory);
								advisors.addAll(this.advisorFactory.getAdvisors(factory)); }}}this.aspectBeanNames = aspectNames;
					returnadvisors; }}}if (aspectNames.isEmpty()) {
			return Collections.emptyList();
		}
		List<Advisor> advisors = new ArrayList<>();
		for (String aspectName : aspectNames) {
			List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
			if(cachedAdvisors ! =null) {
				advisors.addAll(cachedAdvisors);
			}
			else {
				MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
				advisors.addAll(this.advisorFactory.getAdvisors(factory)); }}return advisors;
	}
Copy the code

The next step is to get the matching Advisor

	public static List<Advisor> findAdvisorsThatCanApply(List
       
         candidateAdvisors, Class
         clazz)
        {
		if (candidateAdvisors.isEmpty()) {
			return candidateAdvisors;
		}
		List<Advisor> eligibleAdvisors = new ArrayList<>();
		for (Advisor candidate : candidateAdvisors) {
			if (candidate instanceofIntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); }}booleanhasIntroductions = ! eligibleAdvisors.isEmpty();for (Advisor candidate : candidateAdvisors) {// Iterate through the Advisor to find a match
			if (candidate instanceof IntroductionAdvisor) {
				// already processed
				continue;
			}
			if(canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); }}return eligibleAdvisors;
	}
Copy the code

Specific match is in the org. Springframework. Aop. Support. AopUtils# canApply (org. Springframework. Aop Pointcut, Java. Lang. Class
, Boolean) check whether the class matches before checking whether the method matches

	public static boolean canApply(Pointcut pc, Class<? > targetClass,boolean hasIntroductions) {
		Assert.notNull(pc, "Pointcut must not be null");
		if(! pc.getClassFilter().matches(targetClass)) {/ / to match the class
			return false;
		}

		MethodMatcher methodMatcher = pc.getMethodMatcher();
		if (methodMatcher == MethodMatcher.TRUE) {
			// No need to iterate the methods if we're matching any method anyway...
			return true;
		}

		IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
		if (methodMatcher instanceofIntroductionAwareMethodMatcher) { introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher; } Set<Class<? >> classes =new LinkedHashSet<>();
		if(! Proxy.isProxyClass(targetClass)) { classes.add(ClassUtils.getUserClass(targetClass)); } classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));for(Class<? > clazz : classes) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);for (Method method : methods) {
				if(introductionAwareMethodMatcher ! =null ?
						introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
						methodMatcher.matches(method, targetClass)) {
					return true; }}}return false;
	}
Copy the code

The JDK proxy or Cglib dynamic proxy will be created based on whether or not the interface is the main condition

	@Override
	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

conclusion

Scan all @aspect annotated objects, encapsulate them as Advisor objects, cache them, and iterate to see if they match when creating the object.

Just say something

At this point, the SpringIoC and Aop parts are all analyzed. The previous few also to a portal “spring-IOC” source analysis of a bean information “spring-IOC” source analysis of two dependency injection & dependency cycle “spring-AOP” source analysis of three: JDK dynamic proxy &Cglib