Spring的配置文件
在分析源码中,首先需要了解它的使用,再读源码时就可以了解到,功能对应的源码是怎么运行的,怎么编写的。
首先来看DefaultBeanDefinitionDocumentReader中的parseDefaultElement方法用来对配置文件中的4大默认标签做解析处理,该方法也是在创建bean的之前会调用
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//import 处理
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//alias 处理
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//bean 处理
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//对beans 处理
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
其中我们看一下对bean标签的处理
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//元素解析
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//如果在默认标签的子节点下面,还有自定义标签,就要再次解析
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
//解析完成后,进行注册
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
//通知相关的监听器,这个bean已经加载完成了
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
从上面的代码中可以看到,bean的解析是第一步,如果有子标签还要解析子标签的属性。同事我们从这段代码中可以看到一些编程思想影子,BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
该方法本身不处理解析bean标签的属性,但是却委派给了其他类去处理 用到了委派模式。Spring中使用了大量的委派模式,这里有一个方法,凡是看到delegate结尾的类名、Dispatcher结尾的类名,在Spring中都使用了委派模式。例如大名鼎鼎的Dispatcherservlet,就是使用了委派模式来完成的(后期的文章中也会仔细分析Dispatcherservlet这个类)。
往下走,可以看到如下代码:
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
//解析id属性
String id = ele.getAttribute(ID_ATTRIBUTE);
//解析name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//分割name属性
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
//检测唯一性beanname
checkNameUniqueness(beanName, aliases, ele);
}
//检测其他的属性
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
//如果不存在beanName 那么根据spring中提供的命名规则为当前bean生成对应的beanName
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
//根据spring规则,自动生成beanName
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
//封装为一个BeanDefinitionHolder实例
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
从上面的代码中,我们终于看Spring在解析bean标签的属性了,但是只是解析了id和name,其他的属性则又跑到了其他的方法中去解析。我们看到它这里调用了这个方法AbstractBeanDefinition beanDefinition=parseBeanDefinitionElement(ele, beanName, containingBean);
这里需要明确一个规律,在Spring中严格采用了接口化编程,当看到Abstractxxxx的类的时候,可以点开idea的设计,看下这个的顶层接口和最底层的实现接口,如上篇文章中的Resource接口。这个是Spring的惯用手段,采用了编程思想中的模板模式,来构建一套可以面对不同情况去实现各种对应类的方法。
BeanDefinition
在这里的BeanDefinition,就是载体,承载了bean的属性,也就是我们熟知的Spring 中的bean。这里也可以看到,Spring这个框架的优越,对于我们本身需要的bean,Spring并没有去入侵我们的代码,只是适配了一个接口,包装了一下,既满足了我们的需求,又提供了Spring管理的方法。(后期注册的时候,可以看到AOP的容器中放的,就是这个)。有如下3中实现类:
- RootBeanDefinition:最常用的实现类,对应一般的bean标签
- GenericBeanDefinition:是2.5版本后加入的,是一站式服务类
- ChildBeanDefinition:当存在父子关系的bean时,父的bean使用RootBeanDefinition,子bean使用ChildBeanDefinition
bean的属性
我们接下去再来看,bean的属性解析,如下面的源码:
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
//解析class属性
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
//解析parent属性
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
//创建父类的一个bean
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//解析默认bean的各种属性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//提取description
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//解析元数据
parseMetaElements(ele, bd);
//解析lookup-method属性
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//解析replaced-method属性
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//
parseConstructorArgElements(ele, bd);
//解析property子元素
parsePropertyElements(ele, bd);
//解析qualifier子元素
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
return null;
}
其中parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
这部分代码就在文中看了,太多了,里面涉及了一些bean 的属性,大致为以下几种,介绍一下
scope属性:
可以选的值有:
- singleton:单例,默认情况下为singleton
- prototype:多例,每次都会创建一个新的实例。这里我个人的理解是设计模式中的原型模式,因为原型模式的最经典的例子就是深度克隆,也就是每一份对象的属性相同,但是对象本身的实例却不是同一个。有兴趣的可以考虑去看下原型模式。
- request:每次请求会创建一个新的实例
- session:在不同session中创建新的实例
global session:在整个session范围内使用同一个实例
abstract属性:
用法如下:
<bean id="person" class="spring.extend.Person" abstract="true"> <property name="name" value="张三"></property> </bean> <bean id="student" class="spring.extend.Student" parent="person"></bean>
或者如下
<bean id="person" abstract="true"> <property name="name" value="张三"></property> </bean> <bean id="student" class="spring.extend.Student" parent="person"></bean>
可以看到情况2中,没有定义class,仅仅作为一个模板来使用。
lazy-init属性: 懒加载属性
autowier属性: 自动注入属性
depends-on属性:Spring会按照depend-on中定义的顺序来处理Bean
autowire-candidate属性:
如下例子:
<!-- 两个对象继承自同一接口 autowire-candidate="false" 表示该对象不参与自动注入 --> <bean class="shangbo.spring.example38.MessageServiceDBImpl" autowire-candidate="false" /> <bean class="shangbo.spring.example38.MessageServiceFileImpl"/>
primary属性:
当设置为true时,那该bean在autowired是byType时 就是首选。spring根据primary的信息就会把这个bean拿去注入