spring小結(6)-細看@Configuration的appconfig代理類

之前提過@Configuration的appconfig會被cglib代理,生存代理類,然後是對象,在進行含有@Bean方法調用進項1攔截,

這裏需要細看詳細的代理類和攔截細節,爲了看是一次調用 new,還是getbean,,(isCurrentlyInvokedFactoryMethod(beanMethod)) 起了重要作用

 * Enhance a {@link Bean @Bean} method to check the supplied BeanFactory for the
 * existence of this bean object.
 * @throws Throwable as a catch-all for any exception that may be thrown when invoking the
 * super implementation of the proxied method i.e., the actual {@code @Bean} method
 */
@Override
@Nullable
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
         MethodProxy cglibMethodProxy) throws Throwable {

   ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
   String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

   // Determine whether this bean is a scoped-proxy
   Scope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class);
   if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
      String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
      if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
         beanName = scopedBeanName;
      }
   }

   // To handle the case of an inter-bean method reference, we must explicitly check the
   // container for already cached instances.

   // First, check to see if the requested bean is a FactoryBean. If so, create a subclass
   // proxy that intercepts calls to getObject() and returns any cached bean instance.
   // This ensures that the semantics of calling a FactoryBean from within @Bean methods
   // is the same as that of referring to a FactoryBean within XML. See SPR-6602.
   if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
         factoryContainsBean(beanFactory, beanName)) {
      Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
      if (factoryBean instanceof ScopedProxyFactoryBean) {
         // Scoped proxy factory beans are a special case and should not be further proxied
      }
      else {
         // It is a candidate FactoryBean - go ahead with enhancement
         return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
      }
   }

   if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
      // The factory is calling the bean method in order to instantiate and register the bean
      // (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
      // create the bean instance.
      if (logger.isWarnEnabled() &&
            BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
         logger.warn(String.format("@Bean method %s.%s is non-static and returns an object " +
                     "assignable to Spring's BeanFactoryPostProcessor interface. This will " +
                     "result in a failure to process annotations such as @Autowired, " +
                     "@Resource and @PostConstruct within the method's declaring " +
                     "@Configuration class. Add the 'static' modifier to this method to avoid " +
                     "these container lifecycle issues; see @Bean javadoc for complete details.",
               beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
      }
      return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
   }

   return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}

細看,此方法是判斷當前調用方法中是否是執行的方法

private boolean isCurrentlyInvokedFactoryMethod(Method method) {
   Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
   return (currentlyInvoked != null && method.getName().equals(currentlyInvoked.getName()) &&
         Arrays.equals(method.getParameterTypes(), currentlyInvoked.getParameterTypes()));
}

例子解釋

public class lmqDao {


    public lmqDao()
    {
        System.out.println("lmqdao1-init");
    }

    public void print()
    {
        System.out.println("lmqdao - print ------");
    }
}
  public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        System.out.println("method ----");
        return methodProxy.invokeSuper(o,objects);


    }
}
@Configuration
@ComponentScan("com.lmq.BaseService")
//@Import(MyImportBeanDefinitionRegistrar.class)
@Import(MyImportSelector.class)
public class AppConfig {


    @Bean
     public    lmqDao indexdao()
    {
        return new lmqDao();
    }

    @Bean
    public lmqDao1 indexdao1()
    {
        indexdao();//沒有@configuration會輸出2次,在加了@configuration,只會輸出1遍“lmqdao-init",因爲被cglib代理,只會創建一個lmqdao,構造函數也就輸出一次
        return new lmqDao1();  
    }

''''''''''''''''''''''''''

當只調外層的indexdao()時,Method method=MethodProxy methodProxy,即代理方法=調用方法,

當調用indexdao1(),由於其內部調用了indexdao(),此時Method method=indexdao(),MethodProxy methodProxy是indexdao1(),

假設啓動,第一次時,方法一樣,

此時,後面執行父類,

return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs)

同理調用indedao1()(還未執行到indexdao1中的indexdao()時)一樣,

查看後續執行,同樣直接返回父類,

但當執行到indexdao1()中的indexdao()調用時,如下圖就不一樣了,

後面的流程也不一樣,本質從被cglib代理的appconfig對象從beanfactory getbean,

private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
      ConfigurableBeanFactory beanFactory, String beanName) {

   // The user (i.e. not the factory) is requesting this bean through a call to
   // the bean method, direct or indirect. The bean may have already been marked
   // as 'in creation' in certain autowiring scenarios; if so, temporarily set
   // the in-creation status to false in order to avoid an exception.
   boolean alreadyInCreation = beanFactory.isCurrentlyInCreation(beanName);//是否正在創建
   try {
      if (alreadyInCreation) { 
         beanFactory.setCurrentlyInCreation(beanName, false);
      }
      boolean useArgs = !ObjectUtils.isEmpty(beanMethodArgs);
      if (useArgs && beanFactory.isSingleton(beanName)) {
         // Stubbed null arguments just for reference purposes,
         // expecting them to be autowired for regular singleton references?
         // A safe assumption since @Bean singleton arguments cannot be optional...
         for (Object arg : beanMethodArgs) {
            if (arg == null) {
               useArgs = false;
               break;
            }
         }
      }
      Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
            beanFactory.getBean(beanName));
      if (!ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) {
         if (beanInstance.equals(null)) {
            if (logger.isDebugEnabled()) {
               logger.debug(String.format("@Bean method %s.%s called as bean reference " +
                     "for type [%s] returned null bean; resolving to null value.",
                     beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(),
                     beanMethod.getReturnType().getName()));
            }
            beanInstance = null;
         }
         else {
            String msg = String.format("@Bean method %s.%s called as bean reference " +
                  "for type [%s] but overridden by non-compatible bean instance of type [%s].",
                  beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(),
                  beanMethod.getReturnType().getName(), beanInstance.getClass().getName());
            try {
               BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(beanName);
               msg += " Overriding bean of same name declared in: " + beanDefinition.getResourceDescription();
            }
            catch (NoSuchBeanDefinitionException ex) {
               // Ignore - simply no detailed message then.
            }
            throw new IllegalStateException(msg);
         }
      }
      Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
      if (currentlyInvoked != null) {
         String outerBeanName = BeanAnnotationHelper.determineBeanNameFor(currentlyInvoked);
         beanFactory.registerDependentBean(beanName, outerBeanName);
      }
      return beanInstance;
   }

如下圖可以拿到已創建的beaninstance,就不用new了,實現了攔截,

 

至此,invokeBeanFactoryPostProcessors(beanFactory);大部分執行完畢

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);

try {
   // Allows post-processing of the bean factory in context subclasses.
   postProcessBeanFactory(beanFactory);

   // Invoke factory processors registered as beans in the context.
   invokeBeanFactoryPostProcessors(beanFactory);

   // Register bean processors that intercept bean creation.
   registerBeanPostProcessors(beanFactory);

   // Initialize message source for this context.
   initMessageSource();

   // Initialize event multicaster for this context.
   initApplicationEventMult
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章