@Configuration的代理保证bean单例

12 篇文章 0 订阅
文章探讨了Spring框架中,使用@Configuration注解的配置类如何确保@Bean创建的bean遵循单例模式。通过CGLIB动态代理,Spring在运行时对配置类进行增强,创建代理对象,保证了bean的单例性。在代理过程中,BeanMethodInterceptor起到了关键作用,避免了多次实例化。
摘要由CSDN通过智能技术生成

背景

先看下面问题:

类A、B,类AppConfig是配置类加了@Configuration,并且对A、B加了@Configuration,要把beanA、beanB创建到spring容器中,注意创建B时,调用了getA()。

此时按照代码正常理解逻辑会打印两遍“new A()”,因为调用了两次new A()。但是实际你可以去试试,只打印了一遍。。。这就很奇怪了。。。

是因为spring默认bean是单例的这里不会创建两遍,那么spring是如何保证自己的“单例原则”没有被打破的往下看。。

public class A {

    public A(){
        System.out.println("new A()");
    }
    
}

public class B {

    public B(){
        System.out.println("new B()");
    }
}

@ComponentScan("com.yonghui.yh")
@Configuration
public class AppConfig {

    @Bean
    public A getA(){
      	System.out.print("new A()")
        return new A();
    }

    @Bean
    public B getB(){
        //这里调用了getA()
        getA();
        return new B();
    }
}

public class ApplicationTest {
    public static void main(String[] args) {
        //初始化spring容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        // System.out.print("new A()")打印一遍
    }
}

其实源于我们AppConfig类中加了一个@Configuration,这个东西说明该类是一个配置类,spring会对这种加了@Configuration注解的类进行特殊处理,也就是传说中的代理,我们可以在容器启动时,看看这个类,可以发现他被cglib代理了。

AppConfig是一个配置类,任何一个类都可以被指定成为配置类,但是这个类并不一定需要加@Configuration注解,这个注解的作用就是能够是AppConfig能够产生一个代理对象,确保AppConfig类中@Bean创建的bean是单例的,如果没有AppConfig没有@Configuration就不是代理对象,那么出现@Bean方法相互调用会使单例原则被破坏。

public class ApplicationTest {
    public static void main(String[] args) {
        //初始化spring容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        System.out.println(context.getBean(AppConfig.class));
        // 因为AppConfig加了@Configuration: com.yonghui.yh.AppConfig$$EnhancerBySpringCGLIB$$4ce7fa7e@75881071
    }
}

有了思路去看看,到底是怎么做的,其实就是在ConfigurationClassPostProcessor bean工厂后置处理器中进行的。这个类实现了BeanDefinitionRegistryPostProcessor、而BeanDefinitionRegistryPostProcessor实现了BeanDefinitionPostProcessor。

(ConfigurationClassPostProcessor 这个处理器听说很重要很重要很重要,是spring开天辟地的五个BeanDefinition之一,我还没看,今天分享的是其中冰山一小🦶🏻,后面在学学)

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
      PriorityOrdered, ResourceLoaderAware, ApplicationStartupAware, BeanClassLoaderAware, EnvironmentAware {

说的这就要看看cglib代理的一个大概流程。

补充

这里的cglib代理原理如下:通过Enhancerr生成了一个继承了A类的子类,并创建对象(代理对象),你可以清楚的看到setSuperclass、setCallback、、、不就是在创建子类嘛,而setCallback方法就是设置一个MethodInterceptor拦截器,“增强”就是在这里面做的。

你可以回顾一下jdk动态代理得区别,可以在梳理梳理。

public class A {
    public void a(){
        System.out.println("aaaa");
    }
}

public class CglibA {
    public static void main(String[] args) {
        MethodInterceptor methodInterceptor = new MethodInterceptor() {
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("cglib before");
                Object invoke = methodProxy.invoke(o, null);
                System.out.println("cglib after");
                return invoke;
            }
        };
        //理解为通过Enhancer生成了一个继承了A类的子类,并创建对象(代理对象)
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(A.class);
        // 这里可以调用setCallbacks方法就传多个增强,行程链式增强(拦截器组)
        enhancer.setCallback(methodInterceptor);
        enhancer.setUseFactory(false);
        enhancer.setCallbackType(methodInterceptor.getClass());
        A a = (A) enhancer.create();
        a.a();
        /*
        打印:
        cglib before
        aaaa
        cglib after
        */
    }

}

解答

回到上面问题一开始的问题“为啥打印一遍”,直接看下源码:(我只截取了关键代码,并附上注释,可以进去点一点,跟着我的注释思路,很清晰的,最好是自己创建一个配置类,debug一哈)

ConfigurationClassPostProcessor实现的接口BeanDefinitionRegistryPostProcessor的父类接口的BeanFactoryPostProcessor的postProcessBeanFactory()

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
   int factoryId = System.identityHashCode(beanFactory);
   if (this.factoriesPostProcessed.contains(factoryId)) {
      throw new IllegalStateException(
            "postProcessBeanFactory already called on this post-processor against " + beanFactory);
   }
   this.factoriesPostProcessed.add(factoryId);
   if (!this.registriesPostProcessed.contains(factoryId)) {
      // BeanDefinitionRegistryPostProcessor hook apparently not supported...
      // Simply call processConfigurationClasses lazily at this point then.
      processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
   }
  
		// 从这开始
   enhanceConfigurationClasses(beanFactory);
   beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

enhanceConfigurationClasses(beanFactory)

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
   StartupStep enhanceConfigClasses = this.applicationStartup.start("spring.context.config-classes.enhance");
   Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
   for (String beanName : beanFactory.getBeanDefinitionNames()) {
   		//...省略部分源码.....
      //判断是否是一个全配置类 full
      if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
         //...省略部分源码.....
         // 全配置类进行记录,如果是lite非全配置类,那么不用管spring该怎么new就怎么new
         configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
      }
   }
   //无全配置类记录,直接结束
   if (configBeanDefs.isEmpty()) {
      // nothing to enhance -> return immediately
      enhanceConfigClasses.end();
      return;
   }
   if (IN_NATIVE_IMAGE) {
      throw new BeanDefinitionStoreException("@Configuration classes need to be marked as " +
            "proxyBeanMethods=false. Found: " + configBeanDefs.keySet());
   }

   //遍历全配置类configBeanDefs开始代理  
   ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
   for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
      AbstractBeanDefinition beanDef = entry.getValue();
      // If a @Configuration class gets proxied, always proxy the target class
      beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
      // Set enhanced subclass of the user-specified bean class
			//代理逻辑,返回代理类
			Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
			if (configClass != enhancedClass) {
				if (logger.isTraceEnabled()) {
					logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
							"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
				}
				//设置实际的beanClass为这个代理类,后面用这个bd时,就是使用的这个代理过的类了
				//要去看看,对他增强了什么
				beanDef.setBeanClass(enhancedClass);
			}
   //...省略部分源码.....
}

想知道上面打印一次那个原因,就需要看看,对他增强了什么。ConfigurationClassEnhancer封装了对这个全配置类增强的逻辑(即上面演示的cglib、enhance等,其中增强拦截器就是BeanMethodInterceptor、BeanFactoryAwareMethodInterceptor,主要在BeanMethodInterceptor中)

class ConfigurationClassEnhancer {

   // setCallbacks方法就传多个增强,行程链式增强(拦截器组)
   // BeanMethodInterceptor、BeanFactoryAwareMethodInterceptor是这个类的内部类
   private static final Callback[] CALLBACKS = new Callback[] {
         new BeanMethodInterceptor(),
         new BeanFactoryAwareMethodInterceptor(),
         NoOp.INSTANCE
   };

   private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);
   private static final String BEAN_FACTORY_FIELD = "$$beanFactory";
   private static final Log logger = LogFactory.getLog(ConfigurationClassEnhancer.class);
   private static final SpringObjenesis objenesis = new SpringObjenesis();

   /**
   * 调用enhancer.enhance(configClass, this.beanClassLoader);
   */
   public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
      if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
         if (logger.isDebugEnabled()) {
            logger.debug(String.format("Ignoring request to enhance %s as it has " +
                  "already been enhanced. This usually indicates that more than one " +
                  "ConfigurationClassPostProcessor has been registered (e.g. via " +
                  "<context:annotation-config>). This is harmless, but you may " +
                  "want check your configuration and remove one CCPP if possible",
                  configClass.getName()));
         }
         return configClass;
      }
      Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
      if (logger.isTraceEnabled()) {
         logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",
               configClass.getName(), enhancedClass.getName()));
      }
      return enhancedClass;
   }

   /**
    * Creates a new CGLIB {@link Enhancer} instance.
    */
   private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
      Enhancer enhancer = new Enhancer();
      enhancer.setSuperclass(configSuperClass);
      enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
      enhancer.setUseFactory(false);
      enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
      enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
      enhancer.setCallbackFilter(CALLBACK_FILTER);
      enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
      return enhancer;
   }

   /**
    * Uses enhancer to generate a subclass of superclass,
    * ensuring that callbacks are registered for the new subclass.
    */
   private Class<?> createClass(Enhancer enhancer) {
      Class<?> subclass = enhancer.createClass();
      // Registering callbacks statically (as opposed to thread-local)
      // is critical for usage in an OSGi environment (SPR-5932)...
      Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
      return subclass;
   }


   /**
    * Marker interface to be implemented by all @Configuration CGLIB subclasses.
    * Facilitates idempotent behavior for {@link ConfigurationClassEnhancer#enhance}
    * through checking to see if candidate classes are already assignable to it, e.g.
    * have already been enhanced.
    * <p>Also extends {@link BeanFactoryAware}, as all enhanced {@code @Configuration}
    * classes require access to the {@link BeanFactory} that created them.
    * <p>Note that this interface is intended for framework-internal use only, however
    * must remain public in order to allow access to subclasses generated from other
    * packages (i.e. user code).
    */
   public interface EnhancedConfiguration extends BeanFactoryAware {
   }


   /**
    * Conditional {@link Callback}.
    * @see ConditionalCallbackFilter
    */
   private interface ConditionalCallback extends Callback {

      boolean isMatch(Method candidateMethod);
   }


   /**
    * A {@link CallbackFilter} that works by interrogating {@link Callback Callbacks} in the order
    * that they are defined via {@link ConditionalCallback}.
    */
   private static class ConditionalCallbackFilter implements CallbackFilter {

      private final Callback[] callbacks;

      private final Class<?>[] callbackTypes;

      public ConditionalCallbackFilter(Callback[] callbacks) {
         this.callbacks = callbacks;
         this.callbackTypes = new Class<?>[callbacks.length];
         for (int i = 0; i < callbacks.length; i++) {
            this.callbackTypes[i] = callbacks[i].getClass();
         }
      }

      @Override
      public int accept(Method method) {
         for (int i = 0; i < this.callbacks.length; i++) {
            Callback callback = this.callbacks[i];
            if (!(callback instanceof ConditionalCallback) || ((ConditionalCallback) callback).isMatch(method)) {
               return i;
            }
         }
         throw new IllegalStateException("No callback available for method " + method.getName());
      }

      public Class<?>[] getCallbackTypes() {
         return this.callbackTypes;
      }
   }
		
   private static class BeanMethodInterceptor implements MethodInterceptor, ConditionalCallback {

      /**
       * 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
         if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
            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);
            }
         }
			/**
			 * 判断是否正在被调用,同理:生命周期循环依赖的正在创建的bean
			 * 这里和循环依赖相似:
			 * 		判断正在调用的方法 和 正在创建bean的方法
			 * 		1、如果	正在调用的方法 和 正在创建bean的方法 相同就会直接调用父类的方法(我们的@Bean方法)进行创建bean
			 * 		2、如果不相同就会先去getBean
			 *	这里不管是哪个方法都会进入代理的这个里面
			 *
			 * 	例如:
			 * 	创建beanA的生命周期中:
			 * 		调用@Bean getA()方法时,此时记录set集合中:getA(),调用代理类的方法也是getA(),就会直接创建beanA,清空set集合中:getA()
			 * 		调用	@Bean getB()方式时,此时记录set集合中:getB(),get()中又调用了getA(),(这里不管是哪个方法都会进入代理的这个里面),所以又会执行到
			 * 		这里的判断当前执行(调用)的是getA(),当时当前创建的却是getB(),就会执行下面的resolveBeanReference(),resolveBeanReference()中会去getBean(),
			 * 		保证了bean的单例,执行完getA(),后继续执行beanB的生命周期
			 *
			 */
         if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
            if (logger.isInfoEnabled() &&
                  BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
               logger.info(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()));
            }
            //如果	正在调用的方法 和 正在创建bean的方法 相同就会直接调用父类的方法进行创建bean
            return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
         }
			  	//如果不相同就会先去getBean
         return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
      }

      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;
                  }
               }
            }
           //getBean 去获取bean ,没有时就会先去创建那个bean的生命周期
            Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
                  beanFactory.getBean(beanName));
            if (!ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) {
               // Detect package-protected NullBean instance through equals(null) check
               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;
         }
         finally {
            if (alreadyInCreation) {
               beanFactory.setCurrentlyInCreation(beanName, true);
            }
         }
      }


}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值