Spring AOP source code learning (AOP portal)

Posted by RobertSubnet on Wed, 05 Jan 2022 11:09:01 +0100

preface

  how the logic of AOP starts after the AOP function is enabled.

1, Bean life cycle

  to talk about AOP, you must understand the life cycle of spring beans. The logic of AOP starts at this stage after Bean initialization. The life cycle of Spring Bean is simple, as shown in the figure above:
 

2, AOP

(1) @ EnableAspectJAutoProxy

  use this annotation in Spring to enable the AOP function. This annotation will register a class related to AutoProxyCreator [AnnotationAwareAspectJAutoProxyCreator, AspectJAwareAdvisorAutoProxyCreator] into the IOC container. The class related to AutoProxyCreator implements the instantiaawarebeanpostprocessor interface, that is, some pre preparation operations of AOP will be performed before and after Bean instantiation.

  • AbstractAutoProxyCreator implements the BeanFactoryAware interface, which is the top-level parent class of the post processor imported from our transaction and aop;
  • In the method before instantiation of abstractautoproxycreator postprocessbeforeinstance, AOP aspect beans will be cached (if they are Advice, PointCut and Advisor basic Bean types, they will be skipped directly without resolution);
     

    /**  AbstractAutoProxyCreator
       * In our bean creation process, we call it before calling the constructor to instantiate the bean (before and after instantiation)
       * Our aop parsing aspect and transaction annotation are completed here
       * @param beanClass class object of the bean currently being created
       * @param beanName beanName
       * @return
       * @throws BeansException
       */
      @Override
      public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
          //Build our cache key
          Object cacheKey = getCacheKey(beanClass, beanName);
    
          if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
              //If it has been parsed, it will be returned directly
              if (this.advisedBeans.containsKey(cacheKey)) {
                  return null;
              }
              /**
               * Determine whether it is a basic bean
               * Judge whether it should be skipped (aop parsing directly parses our facet information (and saves our facet information), while transactions will not be parsed here)
               */
              if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
                  this.advisedBeans.put(cacheKey, Boolean.FALSE);
                  return null;
              }
          }
    
          /**
           * Generally, proxy objects will not be generated in this place unless there is TargetSourceCreator in our container and our bean needs to be implemented
           * TargetSource Interface
           */
          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;
      }

    (2) Create Proxy object

  • In the method after the initialization of AbstractAutoProxyCreator postProcessAfterInitialization [each Bean will be called when it is created], the AOP proxy object will be created. The main method is wrapIfNecessary;
     

    /**  AbstractAutoProxyCreator
       * In this post method, the proxy object of AOP is generated here
       * @param bean bean example
       * @param beanName bean Name of
       * @return
       * @throws BeansException
       */
      @Override
      public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
          if (bean != null) {
              //Get cache key
              Object cacheKey = getCacheKey(bean.getClass(), beanName);
              if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                  //If you find the right one, you will be represented
                  return wrapIfNecessary(bean, beanName, cacheKey);
              }
          }
          return bean;
      }

    The wrapIfNecessary method is as follows:
     

    /**  AbstractAutoProxyCreator wrapIfNecessary
       * 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) {
          //It has been processed
          if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
              return bean;
          }
          //No enhancement required
          if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
              return bean;
          }
          //Is it the basic bean that needs to be skipped
          if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
              this.advisedBeans.put(cacheKey, Boolean.FALSE);
              return bean;
          }
    
          //If we have a notification, create a proxy object
          Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
          //Our appropriate notifier is not empty
          if (specificInterceptors != DO_NOT_PROXY) {
              //Indicates that the current object has been processed by proxy mode
              this.advisedBeans.put(cacheKey, Boolean.TRUE);
              //Create our real proxy object
              Object proxy = createProxy(
                      bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
              //Add to cache
              this.proxyTypes.put(cacheKey, proxy.getClass());
              return proxy;
          }
    
          this.advisedBeans.put(cacheKey, Boolean.FALSE);
          return bean;
      }
  • The core method of creating proxy: createProxy (based on ProxyFactory). The real method to create proxy objects is ProxyFactory Getproxy method;
     

    /**  
       * 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(@Nullable ClassLoader classLoader) {
          //createAopProxy() is used to get our proxy factory
          return createAopProxy().getProxy(classLoader);
      }

    The createAopProxy method determines whether to use JDK dynamic proxy or CGLIB
     

    • CGLIB proxy may take a long time to execute AOP for the first time, because it needs to generate a new bytecode file;
    • In general, the performance of CGLIB and JDK is similar; However, the performance is worse than using native AspectJ. AspectJ is based on bytecode programming technology and is not implemented with the help of Proxy objects.
       

    /**
       * Implementation class DefaultAopProxyFactory createAopProxy
       * @param config Used to specify advisor information for us
       * This method is used to create our proxy object
       * So our targetClass object implements the interface, and ProxyTargetClass does not specify a mandatory cglib proxy, so it is to create a jdk proxy
       * If our proxy class does not implement the interface, we will go directly to the cglib proxy
       * If ProxyTargetClass is specified as false and the proxy class is an interface, jdk proxy will be used. Is it cglib proxy
       * @return
       * @throws AopConfigException
       */
      @Override
      public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
          //Judge whether we specify to use cglib proxy proxytargetclass = true fasle
          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.");
              }
              //The targetClass interface uses the jdk proxy
              if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                  return new JdkDynamicAopProxy(config);
              }
              //cglib proxy
              return new ObjenesisCglibAopProxy(config);
          }
          else {
              //Dynamic agent
              return new JdkDynamicAopProxy(config);
          }
      }
    // Getproxy method of implementation class jdkdynamicaopproxy
    @Override
      public Object getProxy(@Nullable 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);
          return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
      }
    
      /**
       * Finds any {@link #equals} or {@link #hashCode} method that may be defined
       * on the supplied set of interfaces.
       * @param proxiedInterfaces the interfaces to introspect
       */
      private void findDefinedEqualsAndHashCodeMethods(Class<?>[] proxiedInterfaces) {
          for (Class<?> proxiedInterface : proxiedInterfaces) {
              Method[] methods = proxiedInterface.getDeclaredMethods();
              for (Method method : methods) {
                  if (AopUtils.isEqualsMethod(method)) {
                      this.equalsDefined = true;
                  }
                  if (AopUtils.isHashCodeMethod(method)) {
                      this.hashCodeDefined = true;
                  }
                  if (this.equalsDefined && this.hashCodeDefined) {
                      return;
                  }
              }
          }
      }

    The created proxy object will be saved to singletonObjects in the singleton pool of DefaultSingletonBeanRegistry, and then getBean() will get the proxy object in the singleton pool.

In the follow-up, some optimization measures for AOP, especially surround notification, are basically surround notification when encountering all AOP performance bottlenecks.

Topics: Java Spring AOP