- 容器基本用法
测试类
public class MyTestBean {
private String testStr = "testStr";
public String getTestStr() {
return testStr;
}
public void setTestStr(String testStr) {
this.testStr = testStr;
}
}
XML文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myTestBean" class="com.xxy.core.ioc.MyTestBean"/>
</beans>
测试代码
@Test
public void test() {
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
MyTestBean bean = (MyTestBean) bf.getBean("myTestBean");
Assert.assertEquals("testStr", bean.getTestStr());
}
- 源码学习
从测试代码开始,第一行BeanFactory bf = new XmlBeanFactory(new ClassPathResource(“beanFactoryTest.xml”));
首先看ClassPathResource类:
private final String path;
@Nullable
private ClassLoader classLoader;
@Nullable
private Class<?> clazz;
public ClassPathResource(String path) {
this(path, (ClassLoader)null);
}
public ClassPathResource(String path, @Nullable ClassLoader classLoader) {
Assert.notNull(path, "Path must not be null");
String pathToUse = StringUtils.cleanPath(path);
if (pathToUse.startsWith("/")) {
pathToUse = pathToUse.substring(1);
}
this.path = pathToUse;
this.classLoader = classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader();
}
public ClassPathResource(String path, @Nullable Class<?> clazz) {
Assert.notNull(path, "Path must not be null");
this.path = StringUtils.cleanPath(path);
this.clazz = clazz;
}
一共有三个成员和三个构造方法,构造方法主要是对路径的检查和成员的赋值。
接下来进入XmlBeanFactory类:
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader;
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, (BeanFactory)null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader = new XmlBeanDefinitionReader(this);
this.reader.loadBeanDefinitions(resource);
}
}
两个构造方法,单参构造传入Resource之后调用双参构造,进入双参构造方法,首先是调用父类方法对parentBeanFactory进行赋值,单参构造parentBeanFactory直接设为null,暂时没有尝试使用双参构造。接下来初始化XmlBeanDefinitionReader并进行赋值,XmlBeanDefinitionReader的初始化过程并不是这里的重点,不进入源码进行分析了。XmlBeanDefinitionReader初始化完毕后,调用loadBeanDefinitions方法,传入resource:
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return this.loadBeanDefinitions(new EncodedResource(resource));
}
可以看到首先通过EncodedResource对Resource进行封装,接着进入到loadBeanDefinitions(EncodedResource encodedResource)方法:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (this.logger.isTraceEnabled()) {
this.logger.trace("Loading XML bean definitions from " + encodedResource);
}
//记录已经加载的资源
Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
//初始化HashSet
currentResources = new HashSet(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
//添加失败则抛出异常
if (!((Set)currentResources).add(encodedResource)) {
throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
} else {
int var5;
//从encodedResource获取Resource对象中的InputStream对象
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//通过InputSource类对inputStream进行封装
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//真正进入核心逻辑
var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} finally {
//关闭输入流
inputStream.close();
}
} catch (IOException var15) {
throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
} finally {
((Set)currentResources).remove(encodedResource);
if (((Set)currentResources).isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
return var5;
}
}
进入doLoadBeanDefinitions方法:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
Document doc = this.doLoadDocument(inputSource, resource);
int count = this.registerBeanDefinitions(doc, resource);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
} catch (BeanDefinitionStoreException var5) {
throw var5;
} catch (SAXParseException var6) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var6.getLineNumber() + " in XML document from " + resource + " is invalid", var6);
} catch (SAXException var7) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var7);
} catch (ParserConfigurationException var8) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var8);
} catch (IOException var9) {
throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var9);
} catch (Throwable var10) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var10);
}
}
首先获取Document,然后根据返回的Document实例注册bean信息。doLoadDocument部分略过,直接进入registerBeanDefinitions方法:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//首先实例化documentReader
BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
//实例化BeanDefinitionReader时会传入BeanDefinitionRegistry
//记录BeanDefinition的加载个数
int countBefore = this.getRegistry().getBeanDefinitionCount();
//开始注册bean
documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
//返回本次加载BeanDefinition的个数
return this.getRegistry().getBeanDefinitionCount() - countBefore;
}
进入registerBeanDefinitions方法,这个方法是定义在BeanDefinitionDocumentReader接口中的,这个接口只有唯一的实现类DefaultBeanDefinitionDocumentReader:
public static final String BEAN_ELEMENT = "bean";
public static final String NESTED_BEANS_ELEMENT = "beans";
public static final String ALIAS_ELEMENT = "alias";
public static final String NAME_ATTRIBUTE = "name";
public static final String ALIAS_ATTRIBUTE = "alias";
public static final String IMPORT_ELEMENT = "import";
public static final String RESOURCE_ATTRIBUTE = "resource";
public static final String PROFILE_ATTRIBUTE = "profile";
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
this.doRegisterBeanDefinitions(doc.getDocumentElement());
}
protected void doRegisterBeanDefinitions(Element root) {
//专门处理解析
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
//判断是否是默认的命名空间
if (this.delegate.isDefaultNamespace(root)) {
//处理profile关键字
String profileSpec = root.getAttribute("profile");
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
}
return;
}
}
}
//解析前处理
this.preProcessXml(root);
this.parseBeanDefinitions(root, this.delegate);
//解析后处理
this.postProcessXml(root);
this.delegate = parent;
}
首先可以发现这个类的成员定义了在写XML文件时的关键字。然后看doRegisterBeanDefinitions方法,其中profile是用于配置文件中部署两套环境来使用于生产环境和开发环境,这样可以方便的进行环境切换,最常用的就是更换不同的数据库。解析前后处理的代码都是空的,留给子类实现,spring并没有实现它的子类代码。因此这段代码的重点进入parseBeanDefinitions方法:
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element)node;
if (delegate.isDefaultNamespace(ele)) {
this.parseDefaultElement(ele, delegate);
} else {
delegate.parseCustomElement(ele);
}
}
}
} else {
delegate.parseCustomElement(root);
}
}
做过xml文件解析的应该很容易看懂这就是对xml文件标签的遍历解析,其中有对是否是默认命名空间的判断,spring中有两大类bean声明,一种是默认的,一种是自定义的,代码的逻辑还是很清晰的。自定义标签的解析这里不进行分析,进入默认标签的解析,也就是parseDefaultElement方法:
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//处理import标签
if (delegate.nodeNameEquals(ele, "import")) {
this.importBeanDefinitionResource(ele);
//处理alias标签
} else if (delegate.nodeNameEquals(ele, "alias")) {
this.processAliasRegistration(ele);
//处理bean标签
} else if (delegate.nodeNameEquals(ele, "bean")) {
this.processBeanDefinition(ele, delegate);
//处理beans标签
} else if (delegate.nodeNameEquals(ele, "beans")) {
this.doRegisterBeanDefinitions(ele);
}
}
进入对bean标签的解析流程:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//对标签中各种属性的解析及封装
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//下面这行代码是解决bean使用的是默认标签,但其子元素却使用了自定义标签的配置
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//注册BeanDefinition
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException var5) {
this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5);
}
//发出响应通知监听器,spring没有做逻辑处理
this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
同样不关注自定义标签,上面的代码有两个方法需要注意,首先看parseBeanDefinitionElement方法:
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return this.parseBeanDefinitionElement(ele, (BeanDefinition)null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
//解析id属性
String id = ele.getAttribute("id");
//解析name属性
String nameAttr = ele.getAttribute("name");
//分割name属性
List<String> aliases = new ArrayList();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, ",; ");
aliases.addAll(Arrays.asList(nameArr));
}
//有id属性则beanName为id
String beanName = id;
//没有id属性但是有name属性,则第一个name为beanName
if (!StringUtils.hasText(id) && !aliases.isEmpty()) {
beanName = (String)aliases.remove(0);
if (this.logger.isTraceEnabled()) {
this.logger.trace("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases");
}
}
//检测beanName是否已经被使用
if (containingBean == null) {
this.checkNameUniqueness(beanName, aliases, ele);
}
//进一步解析其他属性
AbstractBeanDefinition beanDefinition = this.parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
//如果不存在beanName,则按照spring命名规则生成beanName
beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
} else {
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (this.logger.isTraceEnabled()) {
this.logger.trace("Neither XML 'id' nor 'name' specified - using generated bean name [" + beanName + "]");
}
} catch (Exception var9) {
this.error(var9.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
} else {
return null;
}
}
进入进一步解析属性的parseBeanDefinitionElement方法:
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) {
//入栈
this.parseState.push(new BeanEntry(beanName));
//解析class属性
String className = null;
if (ele.hasAttribute("class")) {
className = ele.getAttribute("class").trim();
}
//解析parent属性
String parent = null;
if (ele.hasAttribute("parent")) {
parent = ele.getAttribute("parent");
}
try {
//GenericBeanDefinition对BeanDefinition的进一步封装
AbstractBeanDefinition bd = this.createBeanDefinition(className, parent);
//硬编码解析bean的各种属性
this.parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//提取description
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, "description"));
//解析元数据
this.parseMetaElements(ele, bd);
//解析lookup-method属性
this.parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//解析replace-method属性
this.parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析构造函数
this.parseConstructorArgElements(ele, bd);
//解析Property元素
this.parsePropertyElements(ele, bd);
//解析Qualifier元素
this.parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(this.extractSource(ele));
AbstractBeanDefinition var7 = bd;
return var7;
} catch (ClassNotFoundException var13) {
this.error("Bean class [" + className + "] not found", ele, var13);
} catch (NoClassDefFoundError var14) {
this.error("Class that bean class [" + className + "] depends on not found", ele, var14);
} catch (Throwable var15) {
this.error("Unexpected failure during bean definition parsing", ele, var15);
} finally {
//出栈
this.parseState.pop();
}
return null;
}
对于详细的标签解析过程和其他三种标签的解析这里不进行描述,这段代码中可以看出,如果没有发生异常则会返回填充了各种属性之后的GenericBeanDefinition。到此bean标签的解析就完成了。
而在解析完成之后,通过一系列的return返回processBeanDefinition方法,接下来是beanDifinition的注册过程,registerBeanDefinition方法,由BeanDefinitionReaderUtils类实现:
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
//首先获取beanName
String beanName = definitionHolder.getBeanName();
//根据beanName注册
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
//注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
String[] var4 = aliases;
int var5 = aliases.length;
for(int var6 = 0; var6 < var5; ++var6) {
String alias = var4[var6];
//注册别名
registry.registerAlias(beanName, alias);
}
}
}
可以看到一共有两种注册方法,第一种是根据beanName进行注册,一种是根据alias进行注册。先看registerBeanDefinition方法,根据register定义在BeanDefinitionRegistry接口中,如果仔细看源码的启动流程可以发现BeanDefinitionRegistry的实现类一般是DefaultListableBeanFactory:
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(256);
private volatile List<String> beanDefinitionNames = new ArrayList(256);
//手动注册的单例类名称,按照注册顺序排序
private volatile Set<String> manualSingletonNames = new LinkedHashSet(16);
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
//注册之前的最后一次校验,主要是对AbstractBeanDefinition中的methodOverrides校验
((AbstractBeanDefinition)beanDefinition).validate();
} catch (BeanDefinitionValidationException var8) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var8);
}
}
//beanDefinitionMap是全局变量,处理并发情况
BeanDefinition existingDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);
//处理注册已经注册的beanName的情况
if (existingDefinition != null) {
//不允许覆盖则抛出异常
if (!this.isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
//处理允许覆盖的逻辑
if (existingDefinition.getRole() < beanDefinition.getRole()) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");
}
} else if (!beanDefinition.equals(existingDefinition)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");
}
} else if (this.logger.isTraceEnabled()) {
this.logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");
}
this.beanDefinitionMap.put(beanName, beanDefinition);
} else {
//判断BeanFactory 中Bean的创建阶段是否已经开始
if (this.hasBeanCreationStarted()) {
Map var4 = this.beanDefinitionMap;
synchronized(this.beanDefinitionMap) {
// 加入beanDefinitionMap
this.beanDefinitionMap.put(beanName, beanDefinition);
创建List<String>
List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);
//将缓存的beanDefinitionNames和新解析的beanName加入集合
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
//更新集合
this.beanDefinitionNames = updatedDefinitions;
//从manualSingletonNames中移除
this.removeManualSingletonName(beanName);
}
//没有开始
} else {
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
//如果有同名的bean,重置
if (existingDefinition != null || this.containsSingleton(beanName)) {
this.resetBeanDefinition(beanName);
}
}
接下来看alias的注册,registerAlias方法定义在AliasRegistry接口中,DefaultListableBeanFactory是其实现类SimpleAliasRegistry的子类:
private final Map<String, String> aliasMap = new ConcurrentHashMap(16);
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
Map var3 = this.aliasMap;
synchronized(this.aliasMap) {
// 如果beanName与别名相同
if (alias.equals(name)) {
//移除别名
this.aliasMap.remove(alias);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
}
} else {
//尝试从缓存中获取别名
String registeredName = (String)this.aliasMap.get(alias);
//存在
if (registeredName != null) {
//缓存中的别名和beanName相同,不做任何操作,不需要再次注册
if (registeredName.equals(name)) {
return;
}
//缓存中存在别名,且不允许覆盖,抛出异常
if (!this.allowAliasOverriding()) {
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'.");
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding alias '" + alias + "' definition for registered name '" + registeredName + "' with new target name '" + name + "'");
}
}
//当A->B存在,若再次出现A->C->B,则抛出异常
this.checkForAliasCircle(name, alias);
//缓存别名
this.aliasMap.put(alias, name);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}
在注册完成之后就会返回processBeanDefinition方法,而processBeanDefinitionfa方法此时也已经完成了,因此完成了对一个bean标签的解析,对标签的解析则是在parseBeanDefinitions方法的循环中执行的,循环结束则意味着xml文件解析完成,所有的beanDifition都以及注册结束。接下来就是一步步返回并完成一些善后工作,比如关闭输入流。
对于默认标签的解析及BeanDifition的注册这里差不多就总结完毕了,如果有什么错误欢迎指正。