最近比较闲,所以想研究下开源的框架,之前研究过基于NIO写的一个分布式框架,但是一直没有空写出心得体会。很快又忘记了,所以想把最近研究的spring开源框架写出来。其实每研究或者说读一个框架,都会受益良多。对自己的技术有很大的提升。
今天先写spring的IOC。IOC的概念控制反转,我的体会是spring通过xml的配置,把一些属性的实例化本来是由我们自己程序做的事情交给了spring的IOC容器。不过这是最简单的,spring还帮我们做了其他很多的工作。不过我认为IOC最核心的工作也就是这个了。
开始我读spring的源代码是根据下载的spring技术内幕pdf资料。由于我下载的是spring3.0的,而这个pdf是2.0的。然后又懵懵懂懂的读下去,遇到不懂的就看源代码和调试。这样下来几天了还没有什么进展。后面我想了一下,觉得不应该这样研究。应该是找到目标,然后再有目的性的研究。这样下来,效率高多了。虽然我以前研究也都是根据问题然后有目的性的研究,但是这些都没有很好的规范。所以执行起来还是比较乱。
今天先从以下两个角度细分
- 怎么读取xml配置文件的
- 怎么设置其属性的
读取配置文件的入口是XmlBeanDefinitionReader这个类。其中有一个这样的方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { int validationMode = getValidationModeForResource(resource); Document doc = this.documentLoader.loadDocument( inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); return registerBeanDefinitions(doc, resource); }
其中documentLoader是其属性,并有初始化。再到loadDocument方法代码如下:
* Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured * XML parser. */ public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); if (logger.isDebugEnabled()) { logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]"); } DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); return builder.parse(inputSource); } /** * Create the {@link DocumentBuilderFactory} instance. * @param validationMode the type of validation: {@link XmlValidationModeDetector#VALIDATION_DTD DTD} * or {@link XmlValidationModeDetector#VALIDATION_XSD XSD}) * @param namespaceAware whether the returned factory is to provide support for XML namespaces * @return the JAXP DocumentBuilderFactory * @throws ParserConfigurationException if we failed to build a proper DocumentBuilderFactory */ protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware) throws ParserConfigurationException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(namespaceAware); if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) { factory.setValidating(true); if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) { // Enforce namespace aware for XSD... factory.setNamespaceAware(true); try { factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE); } catch (IllegalArgumentException ex) { ParserConfigurationException pcex = new ParserConfigurationException( "Unable to validate using XSD: Your JAXP provider [" + factory + "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " + "Upgrade to Apache Xerces (or Java 1.5) for full XSD support."); pcex.initCause(ex); throw pcex; } } } return factory; } /** * Create a JAXP DocumentBuilder that this bean definition reader * will use for parsing XML documents. Can be overridden in subclasses, * adding further initialization of the builder. * @param factory the JAXP DocumentBuilderFactory that the DocumentBuilder * should be created with * @param entityResolver the SAX EntityResolver to use * @param errorHandler the SAX ErrorHandler to use * @return the JAXP DocumentBuilder * @throws ParserConfigurationException if thrown by JAXP methods */ protected DocumentBuilder createDocumentBuilder( DocumentBuilderFactory factory, EntityResolver entityResolver, ErrorHandler errorHandler) throws ParserConfigurationException { DocumentBuilder docBuilder = factory.newDocumentBuilder(); if (entityResolver != null) { docBuilder.setEntityResolver(entityResolver); } if (errorHandler != null) { docBuilder.setErrorHandler(errorHandler); } return docBuilder; }
从中可以看出spring解析xml并没有用到像dom4j和jdom这样的框架,而是直接用java的API方式。其中创建DocumentBuilderFactory的时候设置validating为true,就是说读取的时候会验证xml的配置正确性。其中又是根据xsd的模式。这里可能有很多其他的同学会遇到过这样的问题:有时候spring的项目运行很好,但是断网的情况下却出错了。其实就是spring的验证在作怪。当然默认情况下spring肯定是不会上网去验证了,肯定是放xsd放在其中的一个地方。那么放在什么地方呢?docBuilder.setEntityResolver(entityResolver);就在这个地方。如下entitiResolver是是返回一个InputSource类。
InputSource source = super.resolveEntity(publicId, systemId); if (source == null && systemId != null) { String resourcePath = null; try { String decodedSystemId = URLDecoder.decode(systemId); String givenUrl = new URL(decodedSystemId).toString(); String systemRootUrl = new File("").toURL().toString(); // Try relative to resource base if currently in system root. if (givenUrl.startsWith(systemRootUrl)) { resourcePath = givenUrl.substring(systemRootUrl.length()); } }
很明显看得到如果souce为空的时候就会通过URL读取xsd文件,其中的systemId为http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tool/spring-tool-3.0.xsd
等。所以没有网,那肯定访问不了就会出错了。
其中spring的默认实现是到PluggableSchemaResolver类的
private Map<String, String> getSchemaMappings() { if (this.schemaMappings == null) { synchronized (this) { if (this.schemaMappings == null) { if (logger.isDebugEnabled()) { logger.debug("Loading schema mappings from [" + this.schemaMappingsLocation + "]"); } try { Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.schemaMappingsLocation, this.classLoader); if (logger.isDebugEnabled()) { logger.debug("Loaded schema mappings: " + mappings); } Map<String, String> schemaMappings = new ConcurrentHashMap<String, String>(); CollectionUtils.mergePropertiesIntoMap(mappings, schemaMappings); this.schemaMappings = schemaMappings; } catch (IOException ex) { throw new IllegalStateException( "Unable to load schema mappings from location [" + this.schemaMappingsLocation + "]", ex); } } } } return this.schemaMappings; }
初始化了这些systemId对应的xsd位置。其中的DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas"。这是静态的不可变的类成员变量。所以spring会读取classpath路径下所有jar下META-INF/spring.schemas。而其中org.springframework.beans-3.0.5.RELEASE.jar中spring.schemas内容如下:
http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd http\://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd http\://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool-2.5.xsd http\://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd http\://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd http\://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util-2.5.xsd http\://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd
所以现在就可以理解到为什么会出现那个错误了。可能是你classpath路径下没有这些文件,所以到网上去找,结果就出错了。其中还有一个地方也是和这个类似,那就是aop,spring对每个xml的命名空间会有不同的类来解析。而读到其中的文件为spring.handlers。
以上为xml的验证分析。
验证通过,就是怎么读取xml然后的问题了。而这里会最终到DefaultBeanDefinitionDocumentReader类中的以下方法
* Parse the elements at the root level in the document: * "import", "alias", "bean". * @param root the DOM root element of the document */ 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)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
其中可以看到如果解析xml中Element的命名空间是默认的和其他的是不同的处理方式。而默认即为http://www.springframework.org/schema/beans,也就是标签为<bean>的Element。如果是<aop>,<tx>等则就会是不同的处理,这里的也就是上面提到的在META-INF/spring.handlers读配置文件信息了。如是<aop>的话则是一下的内容http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
。
如果是像aop,tx(事务)这些标签的话会到这个方法,在BeanDefinitionParserDelegate类中,这其中的handler就是上面配置文件的AopNamespaceHandler了。
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
这里把重点先放到bean的读取上。aop的等到下次讲解aop才详细讲解。还记得书上好像有说程序就是算法+数据结构。
以前我一直会把算法放在最前面。这就有点像面向过程一样,有时候会比较快,面对较小的程序。但是如果程序复杂起来的话,先看数据结构反而会更加的容易。所以我先暂时研究一下<bean>,这个对应的类的数据结构,等下看代码起来也就更加的容易了。<bean>都会对应这个类AbstractBeanDefinition。而这个类会在DefaultListableBeanFactory类中有一个private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(); 线程安全的Map,然后通过BeanFactory类getBean的时候实际都是访问到这个map,拿到BeanDefinition初始化并且设置好属性。所以理解BeanDefinition数据结构是至关重要的。其数据结构如下:
/**
* Constant for the default scope name: "", equivalent to singleton status
* but to be overridden from a parent bean definition (if applicable).
*/
public static final String SCOPE_DEFAULT = "";
/**
* Constant that indicates no autowiring at all.
* @see #setAutowireMode
*/
public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;
/**
* Constant that indicates autowiring bean properties by name.
* @see #setAutowireMode
*/
public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
/**
* Constant that indicates autowiring bean properties by type.
* @see #setAutowireMode
*/
public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
/**
* Constant that indicates autowiring a constructor.
* @see #setAutowireMode
*/
public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;
/**
* Constant that indicates determining an appropriate autowire strategy
* through introspection of the bean class.
* @see #setAutowireMode
* @deprecated as of Spring 3.0: If you are using mixed autowiring strategies,
* use annotation-based autowiring for clearer demarcation of autowiring needs.
*/
@Deprecated
public static final int AUTOWIRE_AUTODETECT = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;
/**
* Constant that indicates no dependency check at all.
* @see #setDependencyCheck
*/
public static final int DEPENDENCY_CHECK_NONE = 0;
/**
* Constant that indicates dependency checking for object references.
* @see #setDependencyCheck
*/
public static final int DEPENDENCY_CHECK_OBJECTS = 1;
/**
* Constant that indicates dependency checking for "simple" properties.
* @see #setDependencyCheck
* @see org.springframework.beans.BeanUtils#isSimpleProperty
*/
public static final int DEPENDENCY_CHECK_SIMPLE = 2;
/**
* Constant that indicates dependency checking for all properties
* (object references as well as "simple" properties).
* @see #setDependencyCheck
*/
public static final int DEPENDENCY_CHECK_ALL = 3;
private volatile Object beanClass;
private String scope = SCOPE_DEFAULT;
private boolean singleton = true;
private boolean prototype = false;
private boolean abstractFlag = false;
private boolean lazyInit = false;
private int autowireMode = AUTOWIRE_NO;
private int dependencyCheck = DEPENDENCY_CHECK_NONE;
private String[] dependsOn;
private boolean autowireCandidate = true;
private boolean primary = false;
private final Map<String, AutowireCandidateQualifier> qualifiers =
new LinkedHashMap<String, AutowireCandidateQualifier>(0);
private boolean nonPublicAccessAllowed = true;
private boolean lenientConstructorResolution = true;
private ConstructorArgumentValues constructorArgumentValues;
private MutablePropertyValues propertyValues;
private MethodOverrides methodOverrides = new MethodOverrides();
private String factoryBeanName;
private String factoryMethodName;
private String initMethodName;
private String destroyMethodName;
private boolean enforceInitMethod = true;
private boolean enforceDestroyMethod = true;
private boolean synthetic = false;
private int role = BeanDefinition.ROLE_APPLICATION;
private String description;
private Resource resource;
看到这个其实发现不就是<bean>配置文件中的属性么,lazyInit,autowireMode等这些,完全对。其中最重要的又是标红色的部分了。它即为其中<property>标签的数据结构了。MutablePropertyValues类中会有一个这样的成员变量,private final List<PropertyValue> propertyValueList;而PropertyValue的数据结构如下:
private final String name; private final Object value; private Object source; private boolean optional = false; private boolean converted = false; private Object convertedValue; /** Package-visible field that indicates whether conversion is necessary */ volatile Boolean conversionNecessary; /** Package-visible field for caching the resolved property path tokens */ volatile Object resolvedTokens; /** Package-visible field for caching the resolved PropertyDescriptor */ volatile PropertyDescriptor resolvedDescriptor;
这就很像property的类型了。其中name就是name了,Object即为value或者ref了。至于为什么不直接用一个map保存bean里面的属性呢?官方的解释是用PropertyValue更多的灵活性,并能以一种优化的方式处理索引的属性等。可能是多了这里的PropertyValue多了几个属性吧。
了解到xml的验证读取和<bean>标签对应的数据结构后,还没有说怎么读xml,怎么生成这些BeanDefinition的呢。这里就省略前面的分析,生成BeanDefinition是在BeanDefinitionParserDelegate这个类,其实spring的类命名还是很好的,看到就知道这个是BeanDefinition解析的代表,那肯定是跟BeanDefinition有着莫大的关系了。其实每个生成的BeanDefinition就都是在这个类产生的。在这个方法中
public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } try { String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } AbstractBeanDefinition bd = createBeanDefinition(className, parent); parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); parseMetaElements(ele, bd); parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); parseConstructorArgElements(ele, bd); parsePropertyElements(ele, bd); 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; }
其中看到会根据class的属性和parent的属性创建一个AbstractBeanDefinition类。实际是创建的GenericBeanDefinition,而GenericBeanDefinition类是继承了AbstractBeanDefinition的。其中又调用了自己本身的parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);方法,这个方法介绍实例化这些bean的属性的,像重要的scope,abstract,lazy-init,autowire,depends-on,init-method,factory-method等。如果没有配置就会是默认的方式了。再重要的就是解析值了。
相关推荐
在Spring4.0中,XML配置仍然是一个选项,但更推崇使用注解驱动的配置,如`@Component`、`@Service`、`@Repository`和`@Controller`,这些注解简化了配置并提高了代码的可读性。 2. **面向切面编程(AOP)**:AOP...
以下是对Spring 4.0源码的详细分析: 1. **核心容器**: - **BeanFactory与ApplicationContext**:Spring的核心是BeanFactory,它是管理对象(beans)的工厂。在4.0中,对BeanFactory和ApplicationContext进行了...
《Spring Framework 4.0源码深度解析》 Spring Framework是Java开发中广泛使用的轻量级框架,它为创建高效、灵活且可测试的应用程序提供了基础。Spring 4.0版本是一个重要的里程碑,引入了许多改进和新特性,使得...
总之,Spring 3.0源码的深度分析有助于我们理解Spring框架的设计理念,掌握其核心功能的实现原理,从而更好地利用Spring进行软件开发。通过学习源码,我们可以学到如何优雅地实现IoC、AOP、MVC等设计模式,以及如何...
- **Bean的创建与管理**:解析XML或注解配置,通过DefaultListableBeanFactory实现bean的实例化、初始化和依赖注入。 - **AOP实现**:通过ProxyFactoryBean或AspectJ自动代理实现切面编程。 - **Spring MVC的执行...
根据提供的文件信息,本文将对“javassm源码Extjs4.0通用后台管理系统源码”进行深入解析,重点围绕其技术特点、应用场景及扩展性等方面展开。 ### javassm框架简介 #### JavaSSM JavaSSM是指基于Java语言开发的...
《深入理解Jakarta Servlet API 4.0源码》 Servlet技术是Java Web开发的核心,它为Web应用程序提供了服务器端的编程接口。Jakarta Servlet API 4.0是Servlet规范的最新版本,它包含了对HTTP协议处理、生命周期管理...
Java相关jar包是一个重要的软件开发资源集合,主要涵盖了Spring框架的4.0版本,DOM处理库dom4j,以及AOP(面向切面编程)相关的类库,还有用于查看Java源码的MyEclipse反编译工具。这些组件在Java开发中扮演着不可或...
2. **解析配置文件**:研究Spring的bean配置、Spring MVC的控制器配置以及MyBatis的映射文件。 3. **学习业务逻辑**:深入代码,了解每个服务类和DAO类是如何实现具体业务功能的。 4. **实践动手**:尝试修改部分...
这个压缩包“SSM框架_bootstrap_智能制造系统(说明及源码)”提供了基于SSM框架构建的智能制造系统的详细说明和源代码,旨在帮助开发者理解如何将这些技术应用于实际的工业4.0场景。 首先,Spring框架作为基础,它...
《Hessian RPC技术详解与实践:基于hessian_test02.rar源码分析》 Hessian是一种高效的二进制RPC协议,由Caucho Technology开发,主要用于Java应用之间进行远程调用。它通过二进制序列化的方式,实现了简单、快速的...
7. **改进的XML处理**:例如,StAX(Streaming API for XML)被引入,提供了基于事件的低内存消耗的XML解析和生成。 8. **XPath API**:JAXP(Java API for XML Processing)在JDK 6中增加了对XPath 1.0的支持,...
开发者可以通过分析源码,了解如何在实际项目中实现这些功能。例如,用户管理模块涉及到用户注册、登录、个人信息管理等,而购物车模块则展示了如何处理商品的添加、删除、数量修改以及结账过程。 文件...
Simple Weather源码 4.7. 添加资源 4.8. 运行Simple Weather项目 4.8.1. Maven Exec 插件 4.8.2. 浏览你的项目依赖 4.9. 编写单元测试 4.10. 添加测试范围依赖 4.11. 添加单元测试资源 4.12. 执行单元...