The framework is to leave the complex to itself, the simple to coden, as simple as writing hello world
When developing Spring AOP programs in the early years, aop was configured with xml files (now xml is not popular, annotation @ EnableAspectJAutoProxy is popular), and then framework parsing,
For example:
How is this configuration resolved, how is the interception method obtained, how is it injected into the agent, how is the agent object generated,
See below, you can refer to my blog bean creation process first A process of Spring Bean from scratch,
xml element parsing is not specific. I'm interested in my own research
Since the tag I use is < AOP: config >, the parsing class is ConfigBeanDefinitionParser. When parsing, an AspectJAwareAdvisorAutoProxyCreator, a high-level BeanPostProcessor, will be registered
Then I parse the aop:config sub element. Due to the numerous methods, I only write large blocks
if (POINTCUT.equals(localName)) { parsePointcut(elt, parserContext); } else if (ADVISOR.equals(localName)) { parseAdvisor(elt, parserContext); } else if (ASPECT.equals(localName)) { parseAspect(elt, parserContext); }
Reference https://blog.csdn.net/dong19891210/article/details/105697175 Create the initializeBean phase of the bean, corresponding to the fileorg.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.java
/** * Initialize the given bean instance, applying factory callbacks * as well as init methods and bean post processors. * <p>Called from {@link #createBean} for traditionally defined beans, * and from {@link #initializeBean} for existing bean instances. * @param beanName the bean name in the factory (for debugging purposes) * @param bean the new bean instance we may need to initialize * @param mbd the bean definition that the bean was created with * (can also be {@code null}, if given an existing bean instance) * @return the initialized bean instance (potentially wrapped) * @see BeanNameAware * @see BeanClassLoaderAware * @see BeanFactoryAware * @see #applyBeanPostProcessorsBeforeInitialization * @see #invokeInitMethods * @see #applyBeanPostProcessorsAfterInitialization */ protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) { //...... Omitted if (mbd == null || !mbd.isSynthetic()) { System.out.println(beanName+" AOP 6666666666666666"); wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); System.out.println(wrappedBean.getClass()+" AOP 888888888888"); } return wrappedBean; }
@Override public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; System.out.println("object:"+existingBean+" The types of are:"+existingBean.getClass()); List<BeanPostProcessor> beanPostProcessorList = getBeanPostProcessors(); System.out.println("BeanPostProcessor list: "+beanPostProcessorList); for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { //After Bean initialization System.out.println(beanProcessor.getClass().getName()); result = beanProcessor.postProcessAfterInitialization(result, beanName); if (result == null) { return result; } } return result; }
Output before for:
calculator AOP 6666666666666666 //Object: spring.aop.CalculatorImp@906d29b type: class spring.aop.CalculatorImp BeanPostProcessor list: [org.springframework.context.support.ApplicationContextAwareProcessor@49d3c823, org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker@436bc36, org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor@3b8f0a79, org.springframework.context.annotation.ConfigurationClassPostProcessor$EnhancedConfigurationBeanPostProcessor@71e693fa, proxyTargetClass=false; optimize=false; opaque=false; exposeProxy=false; frozen=false, org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@4f6f416f, org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@409c54f, org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor@3e74829, org.springframework.context.support.PostProcessorRegistrationDelegate$ApplicationListenerDetector@5fe1ce85]
Get all the beanpostprocessors,
After entering the for phase, pay attention to the handling of an org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator
The processing method can be looked at in detail. Because there are too many codes, only large aspects of codes are displayed
Remember one of the registered bean s mentioned above: AspectJAwareAdvisorAutoProxyCreator, which inherits fromorg.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(Object bean, String beanName) throws BeansException
/** * Create a proxy with the configured interceptors if the bean is * identified as one to proxy by the subclass. * @see #getAdvicesAndAdvisorsForBean */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { // return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
/** * Wrap the given bean if necessary, i.e. if it is eligible for being proxied. * @param bean the raw bean instance * @param beanName the name of the bean * @param cacheKey the cache key for metadata access * @return a proxy wrapping the bean, or the raw bean instance as-is */ protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { //. . . . . slightly // Create proxy if we have advice Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { System.out.println("org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(Object bean, String beanName) "); this.advisedBeans.put(cacheKey, Boolean.TRUE); //Where spring aop generates "proxy objects" 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; }
Before creating a proxy object, you will be notified or intercepted
1. Take interception modeorg.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource)
2. Create agent Object org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource)
/** * Create an AOP proxy for the given bean. * @param beanClass the class of the bean * @param beanName the name of the bean * @param specificInterceptors the set of interceptors that is * specific to this bean (may be empty, but not null) * @param targetSource the TargetSource for the proxy, * already pre-configured to access the bean * @return the AOP proxy for the bean * @see #buildAdvisors */ protected Object createProxy( Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) { if (this.beanFactory instanceof ConfigurableListableBeanFactory) { AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass); } //Start to prepare raw materials ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this); if (!proxyFactory.isProxyTargetClass()) { if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { evaluateProxyInterfaces(beanClass, proxyFactory); } } //Very important Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); for (Advisor advisor : advisors) { proxyFactory.addAdvisor(advisor); } proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } //Create agent return proxyFactory.getProxy(getProxyClassLoader()); }
File org.springframework.aop.framework.proxyFactory.java
/** * Create a new proxy according to the settings in this factory. * <p>Can be called repeatedly. Effect will vary if we've added * or removed interfaces. Can add and remove interceptors. * <p>Uses the given class loader (if necessary for proxy creation). * @param classLoader the class loader to create the proxy with * (or {@code null} for the low-level proxy facility's default) * @return the proxy object */ public Object getProxy(ClassLoader classLoader) { return createAopProxy().getProxy(classLoader); }
public class ProxyCreatorSupport extends AdvisedSupport { //. . . slightly /** * Create a new ProxyCreatorSupport instance. */ public ProxyCreatorSupport() { this.aopProxyFactory = new DefaultAopProxyFactory(); } /** * Return the AopProxyFactory that this ProxyConfig uses. */ public AopProxyFactory getAopProxyFactory() { return this.aopProxyFactory; } /** * Subclasses should call this to get a new AOP proxy. They should <b>not</b> * create an AOP proxy with {@code this} as an argument. */ protected final synchronized AopProxy createAopProxy() { if (!this.active) { activate(); } return getAopProxyFactory().createAopProxy(this); } } public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { @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 is an interface, use JDK to generate proxy if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } //If it is not an interface, cglib is used to generate proxy class return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } } }
Since I use the interface, the proxy implementation class is JdkDynamicAopProxy.java
/** * JDK-based {@link AopProxy} implementation for the Spring AOP framework, * based on JDK {@link java.lang.reflect.Proxy dynamic proxies}. * * <p>Creates a dynamic proxy, implementing the interfaces exposed by * the AopProxy. Dynamic proxies <i>cannot</i> be used to proxy methods * defined in classes, rather than interfaces. * * <p>Objects of this type should be obtained through proxy factories, * configured by an {@link AdvisedSupport} class. This class is internal * to Spring's AOP framework and need not be used directly by client code. * * <p>Proxies created using this class will be thread-safe if the * underlying (target) class is thread-safe. * * <p>Proxies are serializable so long as all Advisors (including Advices * and Pointcuts) and the TargetSource are serializable. * * @author Rod Johnson * @author Juergen Hoeller * @author Rob Harrop * @author Dave Syer * @see java.lang.reflect.Proxy * @see AdvisedSupport * @see ProxyFactory */ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable { /** Config used to configure this proxy */ private final AdvisedSupport advised; /** * Construct a new JdkDynamicAopProxy for the given AOP configuration. * @param config the AOP configuration as AdvisedSupport object * @throws AopConfigException if the config is invalid. We try to throw an informative * exception in this case, rather than let a mysterious failure happen later. */ public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException { Assert.notNull(config, "AdvisedSupport must not be null"); if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) { throw new AopConfigException("No advisors and no TargetSource specified"); } this.advised = config; } @Override public Object getProxy() { return getProxy(ClassUtils.getDefaultClassLoader()); } @Override public Object getProxy(ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource()); } Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); //Tools of jdk return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); } /** * Implementation of {@code InvocationHandler.invoke}. * <p>Callers will see exactly the exception thrown by the target, * unless a hook method throws an exception. */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MethodInvocation invocation; Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Class<?> targetClass = null; Object target = null; try { if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // The target does not implement the equals(Object) method itself. return equals(args[0]); } else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { // The target does not implement the hashCode() method itself. return hashCode(); } else if (method.getDeclaringClass() == DecoratingProxy.class) { // There is only getDecoratedClass() declared -> dispatch to proxy config. return AopProxyUtils.ultimateTargetClass(this.advised); } else if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { // Service invocations on ProxyConfig with the proxy config... return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); } Object retVal; if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // May be null. Get as late as possible to minimize the time we "own" the target, // in case it comes from a pool. //Get target object target = targetSource.getTarget(); if (target != null) { targetClass = target.getClass(); } // Get the interception chain for this method. // Get a defined interceptor chain List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. if (chain.isEmpty()) { // We can skip creating a MethodInvocation: just invoke the target directly // Note that the final invoker must be an InvokerInterceptor so we know it does // nothing but a reflective operation on the target, and no hot swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // We need to create a method invocation... /* * If there is a definition of an interceptor, you need to call the interceptor before you can call the corresponding method of the target object * */ invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. // retVal = invocation.proceed(); } // Massage return value if necessary. Class<?> returnType = method.getReturnType(); if (retVal != null && retVal == target && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { // Special case: it returned "this" and the return type of the method // is type-compatible. Note that we can't help if the target sets // a reference to itself in another returned object. retVal = proxy; } else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) { throw new AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } return retVal; } finally { if (target != null && !targetSource.isStatic()) { // Must have come from TargetSource. targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }
Is now a proxy object
Then go back one level at a time:
Agent objects perform business methods, and add other operations by the way
Summary: the processing of Spring AOP depends on its AspectJAwareAdvisorAutoProxyCreator's handling of beans, or the control of the bean's life cycle, which stage to generate, which stage to initialize beans, and then to see how to generate proxy objects, what raw materials are needed (interceptors, notifications, facets, pointcuts, etc.), which way (two ways)
OK, the details are getting deeper. Do you believe that Spring aop has hundreds of nested calls to create proxy objects, and there are many new concepts. Fortunately, aop is as simple for developers as helloword.
Two figures:
reference resources:
0. https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#xsd-schemas-aop
1. https://www.springframework.org/schema/aop/spring-aop.xsd
3. https://github.com/seaswalker/spring-analysis/blob/master/note/spring-aop.md
4. Analysis of design concept and design pattern of spring framework https://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/index.html
5. Understanding Spring AOP https://www.codejava.net/frameworks/spring/understanding-spring-aop
6. Spring source learning (VIII) AOP use and implementation principle
http://www.justdojava.com/2019/07/17/spring-analysis-note-8/
7. Spring AOP source code (3) scan Advice and Bean match https://segmentfault.com/a/1190001054658
8. Introduction to the use of spring AOP, from previous life to this life https://www.javadoop.com/post/spring-aop-intro
9. AOP principle of spring source code analysis https://www.cnblogs.com/liuyk-code/p/9886033.html