欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解配置類為什么要添加@Configuration注解

 更新時(shí)間:2020年05月15日 09:51:07   作者:程序員DMZ  
這篇文章主要介紹了詳解配置類為什么要添加@Configuration注解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

不加@Configuration導(dǎo)致的問題

我們先來(lái)看看如果不在配置類上添加@Configuration注解會(huì)有什么問題,代碼示例如下:

@ComponentScan("com.dmz.source.code")
//@Configuration
public class Config{
	@Bean
	public A a(){
	return new A(dmzService());
	}

	@Bean
	public DmzService dmzService(){
		return new DmzService();
	}
}

public class A {
	public A(DmzService dmzService){
		System.out.println("create A by dmzService");
	}
}

@Component
public class DmzService {
	public DmzService(){
		System.out.println("create dmzService");
	}
}

不添加@Configuration注解運(yùn)行結(jié)果:

create dmzService
create A by dmzService
create dmzService

添加@Configuration注解運(yùn)行結(jié)果:

create dmzService
create A by dmzService

在上面的例子中,我們會(huì)發(fā)現(xiàn)沒有添加@Configuraion注解時(shí)dmzService被創(chuàng)建了兩次, 這是因?yàn)榈谝淮蝿?chuàng)建是被Spring容器所創(chuàng)建的,Spring調(diào)用這個(gè)dmzService()創(chuàng)建了一個(gè)Bean被放入了單例池中(沒有添加其它配置默認(rèn)是單例的),第二次創(chuàng)建是Spring容器在創(chuàng)建a時(shí)調(diào)用了a(),而a()又調(diào)用了dmzService()方法。

這樣的話,就出現(xiàn)問題了。

第一,對(duì)于dmzService而言,它被創(chuàng)建了兩次,單例被打破了

第二,對(duì)于a而言,它所依賴的dmzService不是Spring所管理的,而是直接調(diào)用的一個(gè)普通的java method創(chuàng)建的普通對(duì)象。這個(gè)對(duì)象不被Spring所管理意味著,首先它的域(Scope)定義失效了,其次它沒有經(jīng)過一個(gè)完整的生命周期,那么我們所定義所有的Bean的后置處理器都沒有作用到它身上,其中就包括了完成AOP的后置處理器,所以AOP也失效了。

上面的分析不能說服你的話,我們可以看看官方在@Bean上給出的這一段注釋

首先,Spring就在注釋中指出了,通常來(lái)說,BeanMethod一般都申明在一個(gè)被@Configuration注解標(biāo)注的類中,在這種情況下,BeanMethod可能直接引用了在同一個(gè)類中申明的beanMethod,就像本文給出的例子那樣,a()直接引用了dmzService(),我們重點(diǎn)再看看劃紅線的部分,通過調(diào)用另外一個(gè)beanMethod進(jìn)入的Bean的引用會(huì)被保證是遵從域定義以及AOP語(yǔ)義的,就像getBean所做的那樣。這是怎么實(shí)現(xiàn)的呢?在最后被紅線標(biāo)注的地方也有說明,是通過在運(yùn)行時(shí)期為沒有被@Configuration注解標(biāo)注的配置類生成一個(gè)CGLIB的子類。

源碼分析

Spring是在什么時(shí)候創(chuàng)建的代理呢?到目前為止我們應(yīng)該沒有落掉Spring整個(gè)啟動(dòng)流程的任何關(guān)鍵代碼,那么我們不妨帶著這個(gè)問題繼續(xù)往下看。目前來(lái)說我們已經(jīng)閱讀到了Spring執(zhí)行流程圖中的3-5步,也就是org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors方法,在之前的分析中我們已經(jīng)知道了,這個(gè)方法的主要作用就是執(zhí)行BeanFactoryPostProcessor中的方法,首先執(zhí)行的是BeanDefinitionRegistryPostProcessor(繼承了BeanFactoryPostProcessor)的postProcessBeanDefinitionRegistry方法,然后執(zhí)行postProcessBeanFactory方法。而到目前為止我們并沒有向容器中注冊(cè)bean工廠的后置處理器(BeanFactoryPostProcessor),這就意味著當(dāng)前容器中只有一個(gè)ConfigurationClassPostProcessor會(huì)被執(zhí)行,在前文中我們已經(jīng)分析過了它的postProcessBeanDefinitionRegistry方法,緊接著我們就來(lái)看看它的postProcessBeanFactory方法做了什么。其源碼如下:

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
  int factoryId = System.identityHashCode(beanFactory);
  // 防止重復(fù)處理
  if (this.factoriesPostProcessed.contains(factoryId)) {
    throw new IllegalStateException(
      "postProcessBeanFactory already called on this post-processor against " + beanFactory);
  }
  this.factoriesPostProcessed.add(factoryId);
  // 在執(zhí)行postProcessBeanDefinitionRegistry方法的時(shí)就已經(jīng)將這個(gè)id添加到registriesPostProcessed集合中了
  if (!this.registriesPostProcessed.contains(factoryId)) {
    processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
  }
	// 看起來(lái)這個(gè)方法就是完成了代理
  enhanceConfigurationClasses(beanFactory);
  // 添加了一個(gè)后置處理器
  beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

enhanceConfigurationClasses源碼分析

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {		// map中放置的是所有需要被代理的類
		Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
		for (String beanName : beanFactory.getBeanDefinitionNames()) {
			BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
			if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
				// 省略異常跟日志代碼....
        // 這個(gè)代碼的含義就是如果是一個(gè)被@Configuration注解標(biāo)注的類,那么將其放入到configBeanDefs這個(gè)集合中
				configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
			}
		}
    
		if (configBeanDefs.isEmpty()) {
			// nothing to enhance -> return immediately
			return;
		}
		
    // 對(duì)配置類進(jìn)行代理的核心類
		ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
		for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
			AbstractBeanDefinition beanDef = entry.getValue();
			// 對(duì)于配置類永遠(yuǎn)使用cglib代理
			beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
			try {
				// cglib代理是基于類實(shí)現(xiàn)的,所以在這之前要明確代理的類是什么
				Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
				if (configClass != null) {
          // 通過ConfigurationClassEnhancer獲取到一個(gè)經(jīng)過代理的class
					Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
         // 省略日志....
          
          // 將原有的配置類的bd中的beanClass屬性替換成代理后的class
						beanDef.setBeanClass(enhancedClass);
					}
				}
			}
			catch (Throwable ex) {
				throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
			}
		}
	}

這段代碼非常簡(jiǎn)單,核心的代碼在ConfigurationClassEnhancer中,所以我們要分析下ConfigurationClassEnhancer的源碼,在分析它的源碼前,我們需要對(duì)cglib有一定的了解。

1、cglib原理分析

1.1、使用示例

public class Target{
  public void f(){
    System.out.println("Target f()");
  }
  public void g(){
    System.out.println("Target g()");
  }
}

public class Interceptor implements MethodInterceptor {
  @Override
  public Object intercept(Object obj, Method method, Object[] args,  MethodProxy proxy) throws Throwable {
    System.out.println("I am intercept begin");
//Note: 此處一定要使用proxy的invokeSuper方法來(lái)調(diào)用目標(biāo)類的方法
    proxy.invokeSuper(obj, args);
    System.out.println("I am intercept end");
    return null;
  }
}

public class Test {
  public static void main(String[] args) {
    // 設(shè)置這個(gè)屬性,將代理類的字節(jié)碼文件生成到F盤的code目錄下
  System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\code");
    //實(shí)例化一個(gè)增強(qiáng)器,也就是cglib中的一個(gè)class generator
    Enhancer eh = new Enhancer();
    //設(shè)置目標(biāo)類
    eh.setSuperclass(Target.class);
    // 設(shè)置攔截對(duì)象
    eh.setCallback(new Interceptor());
    // 生成代理類并返回一個(gè)實(shí)例
    Target t = (Target) eh.create();
    t.f();
    t.g();
  }
}

運(yùn)行結(jié)果為:

I am intercept begin
Target f()
I am intercept end
I am intercept begin
Target g()
I am intercept end

1.2、原理分析

查看F盤的code目錄,會(huì)發(fā)現(xiàn)多了以下幾個(gè)文件

其中第二個(gè)文件就是我們的代理類字節(jié)碼,將其直接用IDEA打開

// 省略多余的方法,我們就關(guān)注g方法
public class Target$$EnhancerByCGLIB$$788444a0 extends Target implements Factory
{
  
  final void CGLIB$g$0()
  {
   super.g();
  }
  
  // 經(jīng)過代理過的g方法
  public final void g()
  {
  
  // 查看是否有攔截器存在
   MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
   if (tmp4_1 == null)
   {
     CGLIB$BIND_CALLBACKS(this);
     tmp4_1 = this.CGLIB$CALLBACK_0;
   }
   
   // 如果有攔截器的存在的話,直接調(diào)用攔截器的方法
   if (this.CGLIB$CALLBACK_0 != null) {
     tmp4_1.intercept(this, CGLIB$g$0$Method, CGLIB$emptyArgs, CGLIB$g$0$Proxy);
   }
   
   // 如果沒有攔截器,說明不需要代理,直接調(diào)用父類方法,也就是目標(biāo)類的方法
   else{
     super.g();
   }
  }
}

可以看到,代理類繼承了目標(biāo)類(Target),代理類為每個(gè)目標(biāo)類的方法生成兩個(gè)方法,例如針對(duì)目標(biāo)類中的每個(gè)非private方法,代理類會(huì)生成兩個(gè)方法,以g方法為例:一個(gè)是@Override的g方法,一個(gè)是CGLIB$g$0(CGLIB$g$0相當(dāng)于目標(biāo)類的g方法)。我們?cè)谑纠a中調(diào)用目標(biāo)類的方法t.g()時(shí),實(shí)際上調(diào)用的是代理類中的g()方法。

從這里就能看出,跟JDK動(dòng)態(tài)代理不同的是,cglib代理采用的是繼承的方式生成的代理對(duì)象。

在上面的例子中,我們實(shí)現(xiàn)了對(duì)cglib中方法的攔截,但是就目前而言我們沒有辦法選擇性的攔截目標(biāo)類中的某一個(gè)方法,假設(shè)現(xiàn)在我們只想攔截Target中的g方法而不攔截f方法有什么方法呢?我們看下面這個(gè)例子

public class Main {
	public static void main(String[] args) {
		System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\code");
		//實(shí)例化一個(gè)增強(qiáng)器,也就是cglib中的一個(gè)class generator
		Enhancer eh = new Enhancer();
		//設(shè)置目標(biāo)類
		eh.setSuperclass(Target.class);
		// 設(shè)置攔截對(duì)象
		eh.setCallbacks(new Callback[]{new Interceptor(), NoOp.INSTANCE});
		eh.setCallbackFilter(new CallbackFilter() {
			@Override
			public int accept(Method method) {
				if(method.getName().equals("g"))
       // 這里返回的是上面定義的callback數(shù)組的下標(biāo),0就是我們的Interceptor對(duì)象,1是內(nèi)置的NoOp對(duì)象,代表不做任何操作
				return 0;
				else return 1;
			}
		});
		// 生成代理類并返回一個(gè)實(shí)例
		Target t = (Target) eh.create();
		t.f();
		t.g();
	}
}

運(yùn)行結(jié)果:

Target f()
I am intercept begin
Target g()
I am intercept end

此時(shí)f方法已經(jīng)不會(huì)被代理了

2、ConfigurationClassEnhancer源碼分析

2.1、創(chuàng)建代理過程分析

在對(duì)cglib的原理有了一定了解后,我們?cè)賮?lái)看ConfigurationClassEnhancer的源碼就輕松多了

我們就關(guān)注其中核心的幾個(gè)方法,代碼如下:

public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
  // 如果已經(jīng)實(shí)現(xiàn)了EnhancedConfiguration接口,說明被代理過了,直接返回
  if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
    return configClass;
  }
  // 否則調(diào)用newEnhancer方法先創(chuàng)建一個(gè)增強(qiáng)器,然后直接使用這個(gè)增強(qiáng)器生成代理類的字節(jié)碼對(duì)象
  Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
  if (logger.isDebugEnabled()) {
    logger.debug(String.format("Successfully enhanced %s; enhanced class name is: %s",
                 configClass.getName(), enhancedClass.getName()));
  }
  return enhancedClass;
}

private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
  Enhancer enhancer = new Enhancer();
  // 設(shè)置目標(biāo)類
  enhancer.setSuperclass(configSuperClass);
  // 讓代理類實(shí)現(xiàn)EnhancedConfiguration接口,這個(gè)接口繼承了BeanFactoryAware接口
  // 主要兩個(gè)作用:1.起到標(biāo)記作用,如果實(shí)現(xiàn)了,代表已經(jīng)被代理過了
  // 2.代理類需要訪問BeanFactory,所有實(shí)現(xiàn)了BeanFactoryAware接口
  enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
  // 設(shè)置生成的代理類不實(shí)現(xiàn)factory接口
  enhancer.setUseFactory(false);
  // 設(shè)置代理類名稱的生成策略
  enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
  // 代理類中引入一個(gè)BeanFactory字段
  enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
  // 設(shè)置過濾器,CALLBACK_FILTER中也同時(shí)設(shè)置了攔截器
  enhancer.setCallbackFilter(CALLBACK_FILTER);
  enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
  return enhancer;
}

// 使用增強(qiáng)器生成代理類的字節(jié)碼對(duì)象
private Class<?> createClass(Enhancer enhancer) {
  Class<?> subclass = enhancer.createClass();
  Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
  return subclass;
}

并且我們會(huì)發(fā)現(xiàn),在最開始這個(gè)類就申明了三個(gè)攔截器

// 聲明的三個(gè)攔截器
private static final Callback[] CALLBACKS = new Callback[] {
  new BeanMethodInterceptor(),
  new BeanFactoryAwareMethodInterceptor(),
  NoOp.INSTANCE
};

2.2、攔截器源碼分析

基于我們之前對(duì)cglib的學(xué)習(xí),肯定能知道,代理的核心邏輯就是依賴于攔截器實(shí)現(xiàn)的。其中NoOp.INSTANCE代表什么都沒做,我們就關(guān)注前面兩個(gè)。

BeanFactoryAwareMethodInterceptor

之所以把這個(gè)攔截器放到前面分析是因?yàn)檫@個(gè)攔截器的執(zhí)行時(shí)機(jī)是在創(chuàng)建配置類的時(shí)候,其源碼如下:

private static class BeanFactoryAwareMethodInterceptor implements MethodInterceptor, ConditionalCallback {

		@Override
		@Nullable
		public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
			// 在生成代理類的字節(jié)碼時(shí),使用了BeanFactoryAwareGeneratorStrategy策略
			// 這個(gè)策略會(huì)在代理類中添加一個(gè)字段,BEAN_FACTORY_FIELD = "$$beanFactory"
			Field field = ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD);
			Assert.state(field != null, "Unable to find generated BeanFactory field");
			// 此時(shí)調(diào)用的方法是setBeanFactory方法,
			// 直接通過反射將beanFactory賦值給BEAN_FACTORY_FIELD字段
			field.set(obj, args[0]);

			// Does the actual (non-CGLIB) superclass implement BeanFactoryAware?
			// If so, call its setBeanFactory() method. If not, just exit.
			// 如果目標(biāo)配置類直接實(shí)現(xiàn)了BeanFactoryAware接口,那么直接調(diào)用目標(biāo)類的setBeanFactory方法
			if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) {
				return proxy.invokeSuper(obj, args);
			}
			return null;
		}

		@Override
		// 在調(diào)用setBeanFactory方法時(shí)才會(huì)攔截
		// 從前文我們知道,代理類是實(shí)現(xiàn)了實(shí)現(xiàn)EnhancedConfiguration接口的,
		// 這就意味著它也實(shí)現(xiàn)了BeanFactoryAware接口,那么在創(chuàng)建配置類時(shí),
		// setBeanFactory方法就會(huì)被調(diào)用,之后會(huì)就進(jìn)入到這個(gè)攔截器的intercept方法邏輯中
		public boolean isMatch(Method candidateMethod) {
			return isSetBeanFactory(candidateMethod);
		}

		public static boolean isSetBeanFactory(Method candidateMethod) {
			return (candidateMethod.getName().equals("setBeanFactory") &&
					candidateMethod.getParameterCount() == 1 &&
					BeanFactory.class == candidateMethod.getParameterTypes()[0] &&
					BeanFactoryAware.class.isAssignableFrom(candidateMethod.getDeclaringClass()));
		}
	}

BeanMethodInterceptor

相比于上面一個(gè)攔截器,這個(gè)攔截器的邏輯就要復(fù)雜多了,我們先來(lái)看看它的執(zhí)行時(shí)機(jī),也就是isMatch方法

public boolean isMatch(Method candidateMethod) {
  // 第一個(gè)條件,不能是Object,這個(gè)必定是滿足的
  // 第二個(gè)條件,不能是setBeanFactory方法,顯而易見的嘛,我們要攔截的方法實(shí)際只應(yīng)該是添加了@Bean注解的方法
  // 第三個(gè)條件,添加了@Bean注解
  return (candidateMethod.getDeclaringClass() != Object.class &&
      !BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) &&
      BeanAnnotationHelper.isBeanAnnotated(candidateMethod));
}

簡(jiǎn)而言之,就是攔截@Bean標(biāo)注的方法,知道了執(zhí)行時(shí)機(jī)后,我們?cè)賮?lái)看看它的攔截邏輯,代碼其實(shí)不是很長(zhǎng),但是理解起來(lái)確很不容易,牽涉到AOP以及Bean的創(chuàng)建了,不過放心,我會(huì)結(jié)合實(shí)例給你講明白這段代碼,下面我們先看源碼:

public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
								MethodProxy cglibMethodProxy) throws Throwable {
			// 之前不是給BEAN_FACTORY_FIELD這個(gè)字段賦值了BeanFactory嗎,這里就是反射獲取之前賦的值
			ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
			// 確定Bean的名稱
			String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

			// Determine whether this bean is a scoped-proxy
			// 判斷這個(gè)Bean是否是一個(gè)域代理的類
			Scope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class);
			// 存在@Scope注解,并且開啟了域代理模式
			if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
				String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
				// 域代理對(duì)象的目標(biāo)對(duì)象正在被創(chuàng)建,什么時(shí)候會(huì)被創(chuàng)建?當(dāng)然是使用的時(shí)候嘛
				if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
					// 使用的時(shí)候調(diào)用@Bean方法來(lái)創(chuàng)建這個(gè)域代理的目標(biāo)對(duì)象,所以@Bean方法代理的時(shí)候針對(duì)的是域代理的目標(biāo)對(duì)象,目標(biāo)對(duì)象需要通過getBean的方式創(chuàng)建
					beanName = scopedBeanName;
				}
			}

			// 判斷這個(gè)bean是否是一個(gè)factoryBean
			if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
					factoryContainsBean(beanFactory, beanName)) {
				Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
				if (factoryBean instanceof ScopedProxyFactoryBean) {
					// ScopedProxyFactoryBean還記得嗎?在進(jìn)行域代理時(shí)使用的就是這個(gè)對(duì)象
					// 對(duì)于這個(gè)FactoryBean我們是不需要進(jìn)行代理的,因?yàn)檫@個(gè)factoryBean的getObject方法
					// 只是為了得到一個(gè)類似于占位符的Bean,這個(gè)Bean只是為了讓依賴它的Bean在創(chuàng)建的過程中不會(huì)報(bào)錯(cuò)
					// 所以對(duì)于這個(gè)FactoryBean我們是不需要進(jìn)行代理的
					// 我們只需要保證這個(gè)FactoryBean所生成的代理對(duì)象的目標(biāo)對(duì)象是通過getBean的方式創(chuàng)建的即可
				} else {
					// 而對(duì)于普通的FactoryBean我們需要代理其getObject方法,確保getObject方法產(chǎn)生的Bean是通過getBean的方式創(chuàng)建的
					// It is a candidate FactoryBean - go ahead with enhancement
					return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
				}
			}
			// 舉個(gè)例子,假設(shè)我們被@Bean標(biāo)注的是A方法,當(dāng)前創(chuàng)建的BeanName也是a,這樣就符合了這個(gè)條件
			// 但是如果是這種請(qǐng)求,a(){b()},a方法中調(diào)用的b方法,那么此時(shí)調(diào)用b方法創(chuàng)建b對(duì)象時(shí)正在執(zhí)行的就是a方法
			// 此時(shí)就不滿足這個(gè)條件,會(huì)調(diào)用這個(gè)resolveBeanReference方法來(lái)解決方法引用
			if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
				// 如果當(dāng)前執(zhí)行的方法就是這個(gè)被攔截的方法,(說明是在創(chuàng)建這個(gè)Bean的過程中)
				// 那么直接執(zhí)行目標(biāo)類中的方法,也就是我們?cè)谂渲妙愔杏聾Bean標(biāo)注的方法
				return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
			}
  // 說明不是在創(chuàng)建中了,而是別的地方直接調(diào)用了這個(gè)方法,這時(shí)候就需要代理了,實(shí)際調(diào)用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.
			// 什么時(shí)候會(huì)是alreadyInCreation?就是正在創(chuàng)建中,當(dāng)Spring完成掃描后得到了所有的BeanDefinition
			// 那么之后就會(huì)遍歷所有的BeanDefinition,根據(jù)BeanDefinition一個(gè)個(gè)的創(chuàng)建Bean,在創(chuàng)建Bean前會(huì)將這個(gè)Bean
			// 標(biāo)記為正在創(chuàng)建的,如果是正在創(chuàng)建的Bean,先將其標(biāo)記為非正在創(chuàng)建,也就是這行代碼beanFactory.setCurrentlyInCreation(beanName, false)
			// 這是因?yàn)橹笥謺?huì)調(diào)用getBean方法,如果已經(jīng)被標(biāo)記為創(chuàng)建中了,那么在調(diào)用getBean時(shí)會(huì)報(bào)錯(cuò)
			boolean alreadyInCreation = beanFactory.isCurrentlyInCreation(beanName);
			try {
        // 如果是正在創(chuàng)建的Bean,先將其標(biāo)記為非正在創(chuàng)建,避免后續(xù)調(diào)用getBean時(shí)報(bào)錯(cuò)
				if (alreadyInCreation) {
					beanFactory.setCurrentlyInCreation(beanName, false);
				}
        
        // 在調(diào)用beanMthod的時(shí)候,也就是被@Bean注解標(biāo)注的方法的時(shí)候如果使用了參數(shù),只要有一個(gè)參數(shù)為null,就直接調(diào)用getBean(beanName),否則帶參數(shù)調(diào)用getBean(beanName,args),后面通過例子解釋這段代碼
				boolean useArgs = !ObjectUtils.isEmpty(beanMethodArgs);
				if (useArgs && beanFactory.isSingleton(beanName)) {
					for (Object arg : beanMethodArgs) {
						if (arg == null) {
							useArgs = false;
							break;
						}
					}
				}
				Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
						beanFactory.getBean(beanName));
        // 這里發(fā)現(xiàn)getBean返回的類型不是我們方法返回的類型,這意味著什么呢?
        // 在《你知道Spring是怎么解析配置類的嗎?》我有提到過BeanDefinition的覆蓋
        // 這個(gè)地方說明beanMethod所定義的bd被覆蓋了
				if (!ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) {

					if (beanInstance.equals(null)) {
						beanInstance = null;
					} else {
						// 省略日志
						throw new IllegalStateException(msg);
					}
				}
        // 注冊(cè)Bean之間的依賴關(guān)系
        // 這個(gè)method是當(dāng)前執(zhí)行的一個(gè)創(chuàng)建bean的方法
				Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
        // 不等于null意味著currentlyInvoked這個(gè)方法創(chuàng)建的bean依賴了beanName所代表的Bean
     		// 在開頭的例子中,currentlyInvoked就是a(),beanName就是dmzService,outBeanName就是a
				if (currentlyInvoked != null) {
					String outerBeanName = BeanAnnotationHelper.determineBeanNafanhr(currentlyInvoked);
          // 注冊(cè)的就是a跟dmzService的依賴關(guān)系,注冊(cè)到容器中的dependentBeanMap中
          // key為依賴,value為依賴所在的bean
					beanFactory.registerDependentBean(beanName, outerBeanName);
				}
				return beanInstance;
			} finally {
				if (alreadyInCreation) {
          // 實(shí)際還在創(chuàng)建中,要走完整個(gè)生命周期流程
					beanFactory.setCurrentlyInCreation(beanName, true);
				}
			}
		}

3、結(jié)合例子講解難點(diǎn)代碼

這部分內(nèi)容非常細(xì)節(jié),不感興趣可以跳過,主要是BeanMethodInterceptor中的方法。

3.1、判斷這個(gè)Bean是否是一個(gè)域代理的類示例代碼

@Configuration
@EnableAspectJAutoProxy
public class Config {
  @Bean
  @Scope(value = WebApplicationContext.SCOPE_REQUEST,proxyMode = ScopedProxyMode.TARGET_CLASS)
  public DmzService dmzService() {
    return new DmzService();
  }
}

@RestController
@RequestMapping("/test")
public class Controller {

  DmzService dmzService;

  @Autowired
  public void setDmzService(DmzService dmzService) {
    this.dmzService = dmzService;
  }

  @GetMapping("/get")
  public ResponseEntity<?> get() {
    System.out.println(dmzService);
    return ResponseEntity.ok().build();
  }
}

我們需要調(diào)試兩種情況

創(chuàng)建Controller時(shí),注入dmzService,因?yàn)閐mzService是一個(gè)request域的對(duì)象,正常情況下注入肯定是報(bào)錯(cuò)的,但是我們?cè)谂渲妙惿蠈?duì)域?qū)ο箝_啟了代理模式,所以在創(chuàng)建Controller時(shí)會(huì)注入一個(gè)代理對(duì)象。

端點(diǎn)調(diào)試,也確實(shí)如我們所料,這個(gè)地方注入的確實(shí)是一個(gè)代理對(duì)象,因?yàn)槲覀冊(cè)谂渲妙惿仙昝髁?code>proxyMode = ScopedProxyMode.TARGET_CLASS,所以這里是一個(gè)cglib的代理對(duì)象。

使用dmzService的時(shí)候,這個(gè)時(shí)候使用的應(yīng)該是實(shí)際的目標(biāo)對(duì)象。所以按照我們的分析應(yīng)該通過getBean(targetBeanName)的方式來(lái)獲取到這個(gè)Bean,執(zhí)行流程應(yīng)該是代理對(duì)象cglibDmzService調(diào)用了toString方法,然后調(diào)用getBean,getBean要根據(jù)BeanDefinition創(chuàng)建Bean,而根據(jù)BeanDefinition的定義,需要使用配置類中的BeanMethod來(lái)創(chuàng)建Bean,所以此時(shí)會(huì)進(jìn)入到BeanMethodInterceptor的intecept方法。

我們直接在intecept方法中進(jìn)行斷點(diǎn),會(huì)發(fā)現(xiàn)此時(shí)的調(diào)用棧如下

  • 打印時(shí),調(diào)用了toString方法
  • 實(shí)際將會(huì)去創(chuàng)建目標(biāo)Bean,所以此時(shí)getBean時(shí)對(duì)應(yīng)的BeanName為targetBeanName(“scopedTarget.”+beanName)
  • 在getBean時(shí)根據(jù)BeanDefinition的定義會(huì)通過執(zhí)行配置類中的beanMethod方法來(lái)創(chuàng)建Bean
  • 最終就進(jìn)入了攔截器中這個(gè)方法

這種情況下就會(huì)進(jìn)入到下面這段代碼的邏輯中

// 判斷這個(gè)Bean是否是一個(gè)域代理的類
Scope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class);
// 存在@Scope注解,并且開啟了域代理模式
if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
  String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
  // 域代理對(duì)象的目標(biāo)對(duì)象正在被創(chuàng)建,什么時(shí)候會(huì)被創(chuàng)建?當(dāng)然是使用的時(shí)候嘛
  if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
    // 使用的時(shí)候調(diào)用@Bean方法來(lái)創(chuàng)建這個(gè)域代理的目標(biāo)對(duì)象,所以@Bean方法代理的時(shí)候針對(duì)的是域代理的目標(biāo)對(duì)象
    beanName = scopedBeanName;
  }
}

3.3、方法引用的情況下,為什么會(huì)出現(xiàn)Bean正在創(chuàng)建中(isCurrentlyInCreation)?

也就是下面這段代碼什么時(shí)候會(huì)成立

if (alreadyInCreation) {
  beanFactory.setCurrentlyInCreation(beanName, false);
}

示例代碼

@ComponentScan(value = "com.dmz.spring.first")
@Configuration
public class Config {
	@Bean
	public A a(){
		return new A();
	}

	@Bean
	public B b(){
		a();
		return new B();
	}
}

class A{
	B b;

	@Autowired
	public void setB(B b) {
		this.b = b;
	}
}
class B{

}

上面這種配置,在啟動(dòng)的時(shí)候就會(huì)進(jìn)入到if條件中,在創(chuàng)建a的時(shí)候發(fā)現(xiàn)需要注入b,那么Spring此時(shí)就會(huì)去創(chuàng)建b,b在創(chuàng)建的過程中又調(diào)用了a方法,此時(shí)a方法在執(zhí)行時(shí)又被攔截了,然后就會(huì)進(jìn)入到if判斷中去。對(duì)Spring有一定了解的同學(xué)應(yīng)該能感覺到,這個(gè)其實(shí)跟循環(huán)依賴的原理是一樣的。關(guān)于循環(huán)依賴,在后面我單獨(dú)寫一篇文章進(jìn)行說明。

3.4、if (arg == null) {useArgs = false;}是什么意思?

這個(gè)代碼我初看時(shí)也很不明白,為什么只要有一個(gè)參數(shù)為null就直接標(biāo)記成不使用參數(shù)呢?我說說自己的理解。

beanMethodArgs代表了調(diào)用beanMethod時(shí)傳入的參數(shù),正常Spring自身是不會(huì)傳入這個(gè)參數(shù)的,因?yàn)闆]有必要,創(chuàng)建Bean時(shí)其依賴早就通過BeanDefinition確定了,但是可能出現(xiàn)下面這種情況

示例代碼

@Configuration
public class AnotherConfig {
	@Bean
	public DmzService dmzService(IndexService indexService) {
		return new DmzService(indexService);
	}

	@Bean
	public OrderService orderService() {
		DmzService dmzService = dmzService(null);
		return dmzService.createOrder();
	}
}


@Component
public class IndexService {
}

public class DmzService {
	public DmzService(IndexService indexService) {

	}

	public OrderService createOrder() {
		return new OrderService();
	}
}

public class OrderService {
}

這種情況下,我們?cè)趏rderService()為了得到當(dāng)前容器中的dmzService調(diào)用了對(duì)應(yīng)的BeanMethod,但是按照方法的定義我們不得不傳入一個(gè)參數(shù),但是實(shí)際上我們知道BeanMethod等價(jià)于getBean,所以上面這段代碼可以等價(jià)于

@Configuration
public class AnotherConfig {
	
	@Autowired
	ApplicationContext applicationContext;
	
	@Bean
	public DmzService dmzService(IndexService indexService) {
		return new DmzService(indexService);
	}

	@Bean
	public OrderService orderService() {
		DmzService dmzService = (DmzService) applicationContext.getBean("dmzService");
		return dmzService.createOrder();
	}
}

對(duì)于getBean而言,傳入?yún)?shù)跟不傳參數(shù)在創(chuàng)建Bean時(shí)是有區(qū)別的,但是創(chuàng)建后從容器中獲取Bean時(shí)跟傳入的參數(shù)沒有一毛錢關(guān)系(單例情況),因?yàn)檫@是從緩存中獲取嘛。也就是說單例下,傳入的參數(shù)只會(huì)影響第一次創(chuàng)建。正因?yàn)槿绱耍琯etBean在單純的做獲取的時(shí)候不需要參數(shù),那就意味著beanMthod在獲取Bean的時(shí)候也可以不傳入?yún)?shù)嘛,但是beanMthod作為一個(gè)方法又定義了形參,Spring就說,這種情況你就傳個(gè)null吧,反正我知道要去getBean,當(dāng)然,這只是筆者的個(gè)人理解。

4、結(jié)合Spring整體對(duì)ConfigurationClassEnhancer相關(guān)源碼分析總結(jié)

4.1、Bean工廠后置處理器修改bd,對(duì)應(yīng)enhance方法執(zhí)行流程

修改bd的整個(gè)過程都發(fā)生在Bean工廠后置處理器的執(zhí)行邏輯中

執(zhí)行邏輯

在上文中我們已經(jīng)知道了,在執(zhí)行bean工廠后置處理器前,Spring容器的狀態(tài)如下:

那么執(zhí)行完成Bean工廠后置處理器后(不考慮程序員自定義的后置處理器),容器的狀態(tài)應(yīng)該是這樣的

4.2、BeanFactoryAwareMethodInterceptor執(zhí)行流程

在容器中的bd就緒后,Spring會(huì)通過bd來(lái)創(chuàng)建Bean了,會(huì)先創(chuàng)建配置類,然后創(chuàng)建配置類中beanMethod定義的bean。在創(chuàng)建配置類的過程中在初始化Bean時(shí),如果實(shí)現(xiàn)了Aware接口,會(huì)調(diào)用對(duì)于的setXxx方法,具體代碼位于org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean

在調(diào)用setBeanFactory方法時(shí),會(huì)被攔截,進(jìn)入到攔截器的邏輯中

執(zhí)行邏輯

4.3、BeanMethodInterceptor執(zhí)行流程

以下面這段代碼為例:

@Configuration
public class AnotherConfig {
	@Bean
	public DmzService dmzService(){
		return new DmzService();
	}

	@Bean
	public OrderService orderService(){
		return new OrderService(dmzService());
	}
}

Spring會(huì)根據(jù)beanMethod在配置類中定義順序來(lái)創(chuàng)建Bean,所以上面這段配置會(huì)先創(chuàng)建dmzServcice,之后在創(chuàng)建orderService。

那么BeanMethodInterceptor的攔截將會(huì)發(fā)生在兩個(gè)地方

  • 直接創(chuàng)建dmzService的過程中,攔截的是dmzService()方法
  • 創(chuàng)建orderService過程中,第一次攔截的是orderService()方法
  • orderService()方法調(diào)用了dmzService()方法,dmzService()方法又被攔截

在直接創(chuàng)建dmzService時(shí),由于isCurrentlyInvokedFactoryMethod(beanMethod)這句代碼會(huì)成立,所以會(huì)直接調(diào)用目標(biāo)類的方法,也就是cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs),就是我們?cè)谂渲妙愔卸x的dmzService()方法,通過這個(gè)方法返回一個(gè)dmzService

而創(chuàng)建orderService時(shí),方法的調(diào)用就略顯復(fù)雜,首先它類似于上面的直接創(chuàng)建dmzService的流程,orderService()方法會(huì)被攔截,但是由于正在執(zhí)行的方法就是orderService()方法,所以orderService()也會(huì)被直接調(diào)用。但是orderService()中又調(diào)用了dmzService()方法,dmzService()方法又被攔截了,此時(shí)orderService()還沒被執(zhí)行完成,也就是說正在執(zhí)行的方法是orderService()方法,所以isCurrentlyInvokedFactoryMethod(beanMethod)這句代碼就不成立了,那么就會(huì)進(jìn)入org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#resolveBeanReference這個(gè)方法的邏輯中,在這個(gè)方法中,最終又通過getBean方法來(lái)獲取dmzService,因?yàn)?code>dmzService之前已經(jīng)被創(chuàng)建過了,所以在單例模式下,就直接從單例池中返回了,而不會(huì)再次調(diào)用我們?cè)谂渲妙愔卸x的dmzService()方法。

執(zhí)行邏輯

總結(jié)

這里就在上篇文章的基礎(chǔ)上對(duì)流程圖再做一次完善吧,因?yàn)閳D片太大了,就放個(gè)鏈接~

Spring創(chuàng)建bean前的執(zhí)行流程

到此這篇關(guān)于詳解配置類為什么要添加@Configuration注解 的文章就介紹到這了,更多相關(guān)配置類添加@Configuration注解 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論