本文的内容是读郝佳的《Spring源码深度解析》整理的笔记。
属性: 可以和bean写在一个尖括号<>里面的,例如:下面bean标签中的class就是bean的属性。
<bean id="myTestBean" class="MyBean"/>
子元素: 写在
<bean id="myTestBean" class="MyBean">
<meta key="description" value="这是一个神奇的网站!"/>
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
:调用工厂方法。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指定注入哪一个。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();
上面涉及到的方法:
public boolean hasMethodOverrides() {
return (this.methodOverrides != null && !this.methodOverrides.isEmpty());
}
public String getFactoryMethodName() {
return this.factoryMethodName;
}
public boolean hasBeanClass() {
return (this.beanClass instanceof Class);
}
/**
* 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);
}
}
}
}
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);
}
}
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);
}
}
}
}
protected boolean hasBeanCreationStarted() {
return !this.alreadyCreated.isEmpty();
}