Spring源码2 Bean标签的解析

本文的内容是读郝佳的《Spring源码深度解析》整理的笔记。

Bean的属性和子元素

属性: 可以和bean写在一个尖括号<>里面的,例如:下面bean标签中的class就是bean的属性。

<bean id="myTestBean" class="MyBean"/>

子元素: 写在<bean>和</bean>之间的标签元素,例如下面例子中meta就是bean的子元素。

<bean id="myTestBean" class="MyBean">
    <meta key="description" value="这是一个神奇的网站!"/>
</bean>

Bean常用属性

  • scope:默认是singleton,可选值为prototype、request、session、application、websocket。singleton是指一个Spring容器中,一个Bean定义只有一个对象实例;prototype允许Bean的定义可以被实例化多次,每次调用都创建一个实例;request在一次HTTP请求中,每个Bean定义对应一个实例,session表示在在土匪session中,每个Bean定义对应一个实例,request和session的作用域仅在基于Web的Spring上下文(例如:SpringMVC)中才有效。
  • abstract:默认为false,表示当前bean是一个抽象的bean,从而不会为它生成实例化对象。一般是用来声明抽象bean,将一些公共的属性放到一块,这样就能减少重复的代码。
  • lazy-init:默认为false,立即加载;true为用到时再加载。(lazy-init 设置只对scop属性为singleton的bean起作用)
  • autowire:默认为No,可选值为byName、byType、constructor、default。No表示不启用自动装配,Autowire默认的值;byName表示通过属性的名字的方式查找JavaBean依赖的对象并为其注入。比如说类Computer有个属性printer,指定其autowire属性为byName后,Spring IoC容器会在配置文件中查找id/name属性为printer的bean,然后使用Seter方法为其注入;byType表示通过属性的类型查找JavaBean依赖的对象并为其注入。比如类Computer有个属性printer,类型为Printer,那么,指定其autowire属性为byType后,Spring IoC容器会查找Class属性为Printer的bean,使用Seter方法为其注入;constructor与byType一样,也是通过类型查找依赖对象。与byType的区别在于它不是使用Seter方法注入,而是使用构造子注入。default表示由上级标签的default-autowire属性确定。
  • dependency-check:默认为none,可选值为simple、object、all。dependency-check=“simple” 就说 只检查简单类型属性以及集合类型属性 dependency-check=“objects” 就说 检查除简单类型属性以及集合类型属性外的引用类型属性 ,当然,all就是检查所有的 setter 。 所有的setter 方法对应的 属性, 必须在bean 的子元素property 中进行配置。
  • depends-on:只是表明依赖关系(不一定会引用),这个依赖关系决定了被依赖的bean必定会在依赖bean之前被实例化,反过来,容器关闭时,依赖bean会在被依赖的bean之前被销毁。
  • autowire-candidate:默认为true,设置为false,容器在查找自动装配对象时,将不考虑该bean,即该bean不会被作为其它bean自动装配的候选者,但该bean本身还是可以使用自动装配来注入其它bean的。
  • primary:默认为false,当使用@Autowired想要注入一个这个类型的bean时,就不会因为容器中存在多个该类型的bean而出现异常。而是优先使用primary为true的bean。
  • init-method:配置一个类的初始化方法,类完整的实例被创建出来后,才能走初始化方法。
  • destory-method:容器销毁之前所调用的方法。
  • factory-bean:实例化工厂类。
  • factory-method:调用工厂方法。

Bean常用子元素

  • meta:元数据。当需要使用里面的信息时可以通过key获取。通过BeanDefinition 的getAttribute方法获取。
  • lookup-method:当有一个父类A,两个继承A的两个子类分别B、C,在某个bean下有一个方法getA,getA的返回值类型的A类型,可以在这个bean的配置下面指定getA返回的实例是B或者C。
  • replaced-method:可以替换bean中的某一个方法,通过实现MethodReplacer声明一个类,将要替换的方法声明在该类下,然后在bean的配置下用该子元素指定哪一个方法要被替换。
  • constructor-arg:可以通过这个子元素给bean的构造方法传参数。index用来指定是第几个参数,type用来指定参数类型,name可以配置参数名字,与真实构造方法中的参数名字一样,ref如果参数类型是引用类型,用ref指定传入哪个bean的实例。
  • property:给bean的属性赋值。
  • qualifier:当一个A类有两个子类B和C时,如果一个bean被定义为A类型,spring会不知道该注入B和C中的哪一个,用qualifier指定注入哪一个。

Bean标签的解析

1.继续parseDefaultElement(ele, delegate);方法的跟进,该方法主要实现的是默认标签的解析。
在这里插入图片描述
2.进入该方法以后,先关注Bean相关标签的解析,也就是方法processBeanDefinition
在这里插入图片描述
3.继续往下走,进入processBeanDefinition方法中,该方法中首先利用parseBeanDefinitionElement方法将配置文件中关于Bean的信息封装到BeanDefinitionHolder类中。这里简单介绍一下BeanDefinitionHolder类(如下图),该类里面有3个属性,分别是真正的BeanDefinition,以及beanName的名字,和该bean的别名数组;其实有关bean的真正信息是存储在BeanDefinition类型的beanDefinition属性中。BeanDefinitionHolder主要是处理bean别名与bean之间的映射。

在这里插入图片描述在这里插入图片描述
4.继续跟进parseBeanDefinitionElement方法:
在这里插入图片描述
5.继续跟进parseBeanDefinitionElement方法,该方法主要是对前面提到Bean的默认标签进行解析,依次是:description、meta、lookup-method、replaced-method、constructor-arg、property、qualifier标签。
在这里插入图片描述
6.到这里为止,BeanDefinition中的信息已经装填,下面回到第3步中的decorateBeanDefinitionIfRequired方法中该方法,这个方法是对BeanDefinition进行装饰,这句话其实是针对Bean标签中的自定义标签进行解析,适用于这样的配置:

    <bean id="myTestBean" class="MyBean">
        <mybean:user username="aa"/>
    </bean>

该方法的流程主要为:首先获取属性或者元素的命名空间,以此来判断该元素或者属性是否适用于自的定义标签解析条件,找出自定义类型所对应的NamespaceHandler并进行进一步解析。
在这里插入图片描述

7.注册解析的BeanDefinition,也就是registerBeanDefinition方法。
在这里插入图片描述
8.跟进registerBeanDefinition方法,这个方法分别利用beanName和bean的别名对bean进行注册。
在这里插入图片描述
9.进入registerBeanDefinition方法中:
在这里插入图片描述
继续上面方法:在这里插入图片描述
10.继续第8步中的registry.registerAlias(beanName, alias);方法进入:
在这里插入图片描述

补充部前面提到的部分方法

校验bean中重载的validate方法((AbstractBeanDefinition) beanDefinition).validate();
在这里插入图片描述
上面涉及到的方法:

  • hasMethodOverrides()
	public boolean hasMethodOverrides() {
		return (this.methodOverrides != null && !this.methodOverrides.isEmpty());
	}
  • getFactoryMethodName()
	public String getFactoryMethodName() {
		return this.factoryMethodName;
	}
  • hasBeanClass()
	public boolean hasBeanClass() {
		return (this.beanClass instanceof Class);
	}
  • prepareMethodOverrides()
	/**
	 * Validate and prepare the method overrides defined for this bean.
	 * Checks for existence of a method with the specified name.
	 * @throws BeanDefinitionValidationException in case of validation failure
	 */
	public void prepareMethodOverrides() throws BeanDefinitionValidationException {
		// Check that lookup methods exists.
		if (hasMethodOverrides()) {
			Set<MethodOverride> overrides = getMethodOverrides().getOverrides();
			synchronized (overrides) {
				for (MethodOverride mo : overrides) {
					prepareMethodOverride(mo);
				}
			}
		}
	}
  • prepareMethodOverrides()中涉及到的prepareMethodOverride(mo)
	protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
		int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
		if (count == 0) {
			throw new BeanDefinitionValidationException(
					"Invalid method override: no method with name '" + mo.getMethodName() +
					"' on class [" + getBeanClassName() + "]");
		}
		else if (count == 1) {
			// Mark override as not overloaded, to avoid the overhead of arg type checking.
			mo.setOverloaded(false);
		}
	}
  • resetBeanDefinition(beanName)方法:
	protected void resetBeanDefinition(String beanName) {
		// Remove the merged bean definition for the given bean, if already created.
		clearMergedBeanDefinition(beanName);

		// Remove corresponding bean from singleton cache, if any. Shouldn't usually
		// be necessary, rather just meant for overriding a context's default beans
		// (e.g. the default StaticMessageSource in a StaticApplicationContext).
		destroySingleton(beanName);

		// Reset all bean definitions that have the given bean as parent (recursively).
		for (String bdName : this.beanDefinitionNames) {
			if (!beanName.equals(bdName)) {
				BeanDefinition bd = this.beanDefinitionMap.get(bdName);
				if (beanName.equals(bd.getParentName())) {
					resetBeanDefinition(bdName);
				}
			}
		}
	}
  • hasBeanCreationStarted()方法:
	protected boolean hasBeanCreationStarted() {
		return !this.alreadyCreated.isEmpty();
	}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章