Finally to AOP, the front did not understand must follow the code to read more, otherwise this article is easy to get lost!
If you don't say much, you can't change without its sect. First, the picture above.
AOP sequence diagram
Source code analysis
1. Initialization phase - > method of matching tangent points
- Go back to abstractautowirecapablebeanfactory in the previous article Docreatebean mainly focuses on the code behind dependency injection
//Find here //Dependency injection populateBean(beanName, mbd, instanceWrapper); //Initialize the AOP aspect entry of the initial method in the bean. This article focuses on this exposedObject = initializeBean(beanName, exposedObject, mbd); //The Bean object created when the container is initialized, and add a BeanPostProcessor post processor to it protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareMethods(beanName, bean); return null; }, getAccessControlContext()); } else { //Call all callbacks that implement BeanNameAware, BeanClassLoaderAware, beanfactory aware interface methods //Wrap the Bean instance object with relevant attributes, such as name, class loader, container, etc invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { //Call the callback method to do some processing for the Bean instance before initialization wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { //Specified in the file through the init method attribute or @ PostConstruct //Some implementations of InitializingBean call the post notification afterpropertieset //MVC mapping is explained here when the callback triggers the next MVC invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { //Call the callback method to do some processing for the Bean instance after initialization. The AOP agent mainly does this wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } //Return object return wrappedBean; } //Call the processing method after the initialization of the BeanPostProcessor post processor instance object public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; //Traverse the container and call back all post processors that implement the BeanPostProcessor interface for the created Bean for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { //Do some custom processing operations for the Bean instance object after initialization. Next, let's go Object current = beanProcessor.postProcessAfterInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; }
- Next, go to abstractautoproxycreator #postprocessafterinitialization - > wrapifnecessary under the spring AOP package
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // Create proxy if we have advice. //If there is advice, create a proxy class. First look at obtaining the advice chain, and then look at creating a proxy object Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); //If this array is not equal to null //protected static final Object[] DO_NOT_PROXY = null; if (specificInterceptors != DO_NOT_PROXY) { //This bean is identified as having advice this.advisedBeans.put(cacheKey, Boolean.TRUE); //Create proxy object Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); //The proxy object is returned return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
- Next, let's look at the logic of obtaining interceptor chain advice, and go down to abstractadvisor autoproxycreator getAdvicesAndAdvisorsForBean
protected Object[] getAdvicesAndAdvisorsForBean( Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) { //Qualified interceptors found List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName); if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray(); } protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { //Load the currently configured AOP list List<Advisor> candidateAdvisors = findCandidateAdvisors(); //Match according to beanName. Let's briefly analyze this List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { //sort eligibleAdvisors = sortAdvisors(eligibleAdvisors); } //Return to execution responsibility chain return eligibleAdvisors; }
- Findadvisors that can apply all the way down to aoputils canApply
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) { Assert.notNull(pc, "Pointcut must not be null"); if (!pc.getClassFilter().matches(targetClass)) { 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 instanceof IntroductionAwareMethodMatcher) { 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) { //Get all methods Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); for (Method method : methods) { //Breakpoint debugging go to introductionawaremethodmatcher Matches, next look here if (introductionAwareMethodMatcher != null ? introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) : methodMatcher.matches(method, targetClass)) { return true; } } } return false; }
- introductionAwareMethodMatcher.matches -> AspectJExpressionPointcut.getShadowMatch
//Find this line and verify whether the method meets the pointcut through AOP rules shadowMatch = obtainPointcutExpression().matchesMethodExecution(methodToMatch);
If the currently configured pointcut is satisfied, create a proxy object in AbstractAutoProxyCreator#wrapIfNecessary and return. The initialization phase is completed here. Next, continue to analyze the proxy source code, that is, the code weaving phase
2. Code weaving phase - > create dynamic agent
- Go back to abstractautoproxycreator wrapIfNecessary
//Find this paragraph //Get interceptor chain Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); //If it is not empty, a proxy object is created if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); //Create a proxy object. Let's go here! Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } //Create proxy object protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) { if (this.beanFactory instanceof ConfigurableListableBeanFactory) { AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass); } //Create agent factory ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this); if (!proxyFactory.isProxyTargetClass()) { if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { evaluateProxyInterfaces(beanClass, proxyFactory); } } //Packaging actuator chain Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); proxyFactory.addAdvisors(advisors); //The information of the original class object encapsulates a layer of SingletonTargetSource proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } //Create proxy - > there are two methods to create proxy: CGLIB and JDK. Let's choose JdkDynamicAopProxy return proxyFactory.getProxy(getProxyClassLoader()); }
- Next, go to ProxyFactory#getProxy
public Object getProxy(@Nullable ClassLoader classLoader) { return createAopProxy().getProxy(classLoader); } //createAopProxy -> ProxyCreatorSupport#createAopProxy protected final synchronized AopProxy createAopProxy() { if (!this.active) { activate(); } //It's this return getAopProxyFactory().createAopProxy(this); } //Get jdk or Cglib object public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (!NativeDetector.inNativeImage() && (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) { //Original class information 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)) { //Save advice information AdvisedSupport through construction method return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } } //The jdk agent has been used as an example public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException { Assert.notNull(config, "AdvisedSupport must not be null"); if (config.getAdvisorCount() == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) { throw new AopConfigException("No advisors and no TargetSource specified"); } this.advised = config; //Get the interface of the original class. The JKD dynamic proxy must have an interface this.proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); findDefinedEqualsAndHashCodeMethods(this.proxiedInterfaces); }
- Next, analyze getproxy - > jdkdynamicaopproxy getProxy
public Object getProxy(@Nullable ClassLoader classLoader) { if (logger.isTraceEnabled()) { logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource()); } Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); //Very familiar with the way to create an agent. If you don't understand it, look at the dynamic agent first return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }
At this point, the proxy object is created, and the proxy object is returned to the IOC container.
3. Runtime - > reflection call of dynamic agent
- When you call a method, you get the proxy object and paste the JdkDynamicAopProxy proxy class
//As you can see above, if the execution callback method is this, it means that the proxy object will call the invoke method of the current class Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); //Next, look at the invoker method public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; //Gets the SingletonTargetSource encapsulated by the original class TargetSource targetSource = this.advised.targetSource; Object target = null; try { //A series of judgments are deleted ...... //Get original class target = targetSource.getTarget(); //Class information Class<?> targetClass = (target != null ? target.getClass() : null); //The meaning of chain / pipeline obtains the interceptor pipeline //All the way down is actually wrapping it and saving the cache interceptorlist add(new InterceptorAndDynamicMethodMatcher(interceptor, mm)); //interceptor is registry getInterceptors(advisor); Initialize the pre and post exceptions through the constructor List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); if (chain.isEmpty()) { //The pipe is empty Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); //Call the original class directly through reflection retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // We need to create a method invocation... MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); //Start calling the pipe. Let's go here retVal = invocation.proceed(); } //After that, the return refers to quota processing, which is removed first . . . } finally { if (target != null && !targetSource.isStatic()) { // Must have come from TargetSource. targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }
- Next, invoke proceed() ->ReflectiveMethodInvocation. proceed()
//Member variable //The executed pipeline call chain is assigned by the construction method protected final List<?> interceptorsAndDynamicMethodMatchers; private int currentInterceptorIndex = -1; //Start responsibility chain call public Object proceed() throws Throwable { // We start with an index of -1 and increment early. //If the subscript of the current execution is equal to the size-1 of the execution, it indicates that the last execution has occurred, and the native calling method is executed if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { //This is the return method invoke(target, args); return invokeJoinpoint(); } //Subscript + 1 starts from 0. The default value is - 1 Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); //If you want to dynamically match joinpoints if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // Evaluate dynamic method matcher here: static part will already have // been evaluated and found to match. InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass()); //Dynamic matching: whether the runtime parameters meet the matching conditions if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) { //The parameter passed in is this //For example, the method will be called again after the execution of the front section return dm.interceptor.invoke(this); } else { //Continue recursion return proceed(); } } else { //If the MethodInterceptor is not called directly return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
- dm. interceptor. invoke(this) -> AspectJAfterAdvice. Invoke calls the method of notification. Pick one and see
//After calling, you can understand others by yourself public Object invoke(MethodInvocation mi) throws Throwable { try { //Reflection calls the original class return mi.proceed(); } finally { //Call the facet method after return invokeAdviceMethod(getJoinPointMatch(), null, null); } }
summary
After the container initializes the bean, it calls back to the post notification method. aop just implements the post notification method. If it is judged that the current configuration pointcut is met, the proxy class is generated and returned to the IOC container.
When creating a proxy class, the original class, call link and other information will be saved through the construction method. When creating a dynamic proxy, the callback handler class passed in when newInstance is also this, so the invoke method of the handler class will be called when calling the proxy class
In the invoke method, recursive and subscript adding methods are used to make the calling link execute reflection calls. If the corresponding Advice class cannot be matched normally, the original class will be directly reflected and called when the last one is reached and returned
If it can be matched normally, for example, the AspectJAfterAdvice above is to call the original class by reflection first, and then call the post aspect method, and the whole process ends
So far, the source code of the whole AOP aspect has been analyzed. After reading these codes, there should be no problem with handwriting!
The next section of MVC source code is based on IOC container, and the idea is similar to AOP.
That is the whole content of this chapter.
Previous: Spring source code analysis bullet 2 - DI dependency injection analysis
Next: Spring source code analysis fourth bullet - MVC analysis
Don't take it easy, white young head, empty sad