我们初次了解Spring,可能是老师傅给我们的第一份源码,可能里面的代码风格影响你今后的代码风格和架构风格,但是我们需要的是打破我们思维的桎梏,让Spring不再成为我们编码上的圣经,而是让Spring成为我们架构之路上的一名得力干将,那么你真的了解这个框架所使用的架构方式,下面我们各个方面全面分析Spring源码,让Spring不再神秘!
从最基本的Spring初始化配置文件开始。配置文件:
SimpleBean:
public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml"); SimpleBean bean = context.getBean(SimpleBean.class); bean.send(); context.close();}public class SimpleBean { public void send() { System.out.println("I am send method from SimpleBean!"); }}
启动代码:
I am send method from SimpleBean!
在代码中,通过ClassPathXmlApplicationContext类将bean注入到容器中,然后使用getBean方法从容器中获取Bean的实例对象。
ResourseLoader
这是Spring加载Bean的类图,这里我们可以看到在Spring在加载Bean的实例到容器中使用的是策略模式,我们可以看到在Bean加载的实现方法中有两个ClassPathXmlApplicationContect和FileSystemXmlApplicationContext,其作用都是加载配置文件,不同的是我们在使用的文件路径的区别!
首先我们看父类的构造器,沿着类图我们追溯到AbstractApplicationContext:
public AbstractApplicationContext(ApplicationContext parent) { this(); setParent(parent);}public AbstractApplicationContext() { this.resourcePatternResolver = getResourcePatternResolver();}
getResourcePatternResolver:
protected ResourcePatternResolver getResourcePatternResolver() { return new PathMatchingResourcePatternResolver(this);}
PathMatchingResourcePatternResolver支持Ant风格的路径解析。
设置配置文件路径
即AbstractRefreshableConfigApplicationContext.setConfigLocations:
public void setConfigLocations(String... locations) { if (locations != null) { Assert.noNullElements(locations, "Config locations must not be null"); this.configLocations = new String[locations.length]; for (int i = 0; i
getEnvironment方法来自于ConfigurableApplicationContext接口,源码很简单,如果为空就调用createEnvironment创建一个。AbstractApplicationContext.createEnvironment:
protected String resolvePath(String path) { return getEnvironment().resolveRequiredPlaceholders(path);}
此方法的目的在于将占位符(placeholder)解析成实际的地址。比如可以这么写: new ClassPathXmlApplicationContext("classpath:config.xml");那么classpath:就是需要被解析的。
protected ConfigurableEnvironment createEnvironment() { return new StandardEnvironment();}
Environment接口
类图:
Environmen接口代表了当前应用所处的环境。从此接口的方法可以看出,其主要和profile、Property相关。
Profile
Spring Profile特性是从3.1开始的,其主要是为了解决这样一种问题: 线上环境和测试环境使用不同的配置或是数据库或是其它。有了Profile便可以在 不同环境之间无缝切换。Spring容器管理的所有bean都是和一个profile绑定在一起的。使用了Profile的配置文件示例:
在启动代码中可以用如下代码设置活跃(当前使用的)Profile:
context.getEnvironment().setActiveProfiles("dev");
我们也可以在web.xml文件中配置不同的profile
spring.profiles.active develop
Property
这里的Property指的是程序运行时的一些参数,引用注释:
properties files, JVM system properties, system environment variables, JNDI, servlet context parameters, ad-hoc Properties objects,Maps, and so on.
Environment构造器
private final MutablePropertySources propertySources; public AbstractEnvironment() { this.propertySources = new MutablePropertySources(this.logger); this.propertyResolver = new PropertySourcesPropertyResolver(this.propertySources); this.customizePropertySources(this.propertySources); if(this.logger.isDebugEnabled()) { this.logger.debug(String.format("Initialized %s with PropertySources %s", new Object[]{this.getClass().getSimpleName(), this.propertySources})); } }
PropertySources接口
类图
这个接口实际上就是PropertySource的容器,默认的MutablePropertiesSources实现内部含有一个CopyOnWriteArrayList作为载体。
StandardEnvironment.customizePropertySources:
/** System environment property source name: {@value} */public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";/** JVM system properties property source name: {@value} */public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";@Overrideprotected void customizePropertySources(MutablePropertySources propertySources) { propertySources.addLast(new MapPropertySource (SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties())); propertySources.addLast(new SystemEnvironmentPropertySource (SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));}
PropertySource接口
PropertySource接口代表了键值对的Property来源。继承体系:
AbstractEnvironment.getSystemProperties:
@Overridepublic Map getSystemProperties() { try { return (Map) System.getProperties(); } catch (AccessControlException ex) { return (Map) new ReadOnlySystemAttributesMap() { @Override protected String getSystemAttribute(String attributeName) { try { return System.getProperty(attributeName); } catch (AccessControlException ex) { if (logger.isInfoEnabled()) { logger.info(format("Caught AccessControlException when accessing system " + "property [%s]; its value will be returned [null]. Reason: %s", attributeName, ex.getMessage())); } return null; } } }; }}
getSystemEnvironment方法也是一个套路,不过最终调用的是System.getenv,可以获取jvm和OS的一些版本信息。
这里的实现很有意思,如果安全管理器阻止获取全部的系统属性,那么会尝试获取单个属性的可能性,如果还不行就抛异常了。
路径Placeholder处理
AbstractEnvironment.resolveRequiredPlaceholders:
@Overridepublic String resolveRequiredPlaceholders(String text) throws IllegalArgumentException { //text即配置文件路径,比如classpath:config.xml return this.propertyResolver.resolveRequiredPlaceholders(text);}
propertyResolver是一个PropertySourcesPropertyResolver对象:
private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
PropertyResolver接口
PropertyResolver继承体系(排除Environment分支):
此接口正是用来解析PropertyResource。
解析
AbstractPropertyResolver.resolveRequiredPlaceholders:
@Overridepublic String resolveRequiredPlaceholders(String text) throws IllegalArgumentException { if (this.strictHelper == null) { this.strictHelper = createPlaceholderHelper(false); } return doResolvePlaceholders(text, this.strictHelper);}
PropertyPlaceholderHelper
private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) { //三个参数分别是${, }, : return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix, this.valueSeparator, ignoreUnresolvablePlaceholders);}
doResolvePlaceholders:
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) { //PlaceholderResolver接口依然是策略模式的体现 return helper.replacePlaceholders(text, new PropertyPlaceholderHelper.PlaceholderResolver() { @Override public String resolvePlaceholder(String placeholderName) { return getPropertyAsRawString(placeholderName); } });}
其实代码执行到这里的时候还没有进行xml配置文件的解析,那么这里的解析placeHolder是什么意思呢,原因在于可以这么写:
System.setProperty("spring", "classpath");ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("${spring}:config.xml");SimpleBean bean = context.getBean(SimpleBean.class);
这样就可以正确解析。placeholder的替换其实就是字符串操作,这里只说一下正确的属性是怎么来的。实现的关键在于PropertySourcesPropertyResolver.getProperty:
@Overrideprotected String getPropertyAsRawString(String key) { return getProperty(key, String.class, false);}protected T getProperty(String key, Class targetValueType, boolean resolveNestedPlaceholders) { if (this.propertySources != null) { for (PropertySource> propertySource : this.propertySources) { Object value = propertySource.getProperty(key); return value; } } return null;}
注意,classpath:XXX这种写法的classpath前缀到目前为止还没有被处理。
很明显了,就是从System.getProperty和System.getenv获取,但是由于环境变量是无法自定义的,所以其实此处只能通过System.setProperty指定。
refresh
Spring bean解析就在此方法,所以单独提出来。
AbstractApplicationContext.refresh:
public void refresh() throws BeansException, IllegalStateException { Object var1 = this.startupShutdownMonitor; synchronized(this.startupShutdownMonitor) { this.prepareRefresh(); ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory(); this.prepareBeanFactory(beanFactory); try { this.postProcessBeanFactory(beanFactory); this.invokeBeanFactoryPostProcessors(beanFactory); this.registerBeanPostProcessors(beanFactory); this.initMessageSource(); this.initApplicationEventMulticaster(); this.onRefresh(); this.registerListeners(); this.finishBeanFactoryInitialization(beanFactory); this.finishRefresh(); } catch (BeansException var9) { if(this.logger.isWarnEnabled()) { this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9); } this.destroyBeans(); this.cancelRefresh(var9); throw var9; } finally { this.resetCommonCaches(); } } }
prepareRefresh
protected void prepareRefresh() { this.startupDate = System.currentTimeMillis(); this.closed.set(false); this.active.set(true); // Initialize any placeholder property sources in the context environment //空实现 initPropertySources(); // Validate that all properties marked as required are resolvable // see ConfigurablePropertyResolver#setRequiredProperties getEnvironment().validateRequiredProperties(); // Allow for the collection of early ApplicationEvents, // to be published once the multicaster is available... this.earlyApplicationEvents = new LinkedHashSet();}
属性校验
AbstractEnvironment.validateRequiredProperties:
@Overridepublic void validateRequiredProperties() throws MissingRequiredPropertiesException { this.propertyResolver.validateRequiredProperties();}
AbstractPropertyResolver.validateRequiredProperties:
@Overridepublic void validateRequiredProperties() { MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException(); for (String key : this.requiredProperties) { if (this.getProperty(key) == null) { ex.addMissingRequiredProperty(key); } } if (!ex.getMissingRequiredProperties().isEmpty()) { throw ex; }}
requiredProperties是通过setRequiredProperties方法设置的,保存在一个list里面,默认是空的,也就是不需要校验任何属性。
BeanFactory创建
由obtainFreshBeanFactory调用AbstractRefreshableApplicationContext.refreshBeanFactory:
@Overrideprotected final void refreshBeanFactory() throws BeansException { //如果已经存在,那么销毁之前的 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } //创建了一个DefaultListableBeanFactory对象 DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; }}
BeanFactory接口
此接口实际上就是Bean容器,其继承体系:
eanFactory定制
AbstractRefreshableApplicationContext.customizeBeanFactory方法用于给子类提供一个自由配置的机会,默认实现:
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { if (this.allowBeanDefinitionOverriding != null) { //默认false,不允许覆盖 beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.allowCircularReferences != null) { //默认false,不允许循环引用 beanFactory.setAllowCircularReferences(this.allowCircularReferences); }}
Bean加载
AbstractXmlApplicationContext.loadBeanDefinitions,这个便是核心的bean加载了:
@Overrideprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) { // Create a new XmlBeanDefinitionReader for the given BeanFactory. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. //默认空实现 initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader);}
EntityResolver
此处只说明用到的部分继承体系:
EntityResolver接口在org.xml.sax中定义。DelegatingEntityResolver用于schema和dtd的解析。
BeanDefinitionReader
继承体系:
路径解析(Ant)
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) { Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); //here if (configLocations != null) { reader.loadBeanDefinitions(configLocations); }}
AbstractBeanDefinitionReader.loadBeanDefinitions:
@Overridepublic int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException { Assert.notNull(locations, "Location array must not be null"); int counter = 0; for (String location : locations) { counter += loadBeanDefinitions(location); } return counter;}
之后调用:
//第二个参数为空public int loadBeanDefinitions(String location, Set actualResources) { ResourceLoader resourceLoader = getResourceLoader(); //参见ResourceLoader类图,ClassPathXmlApplicationContext实现了此接口 if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); int loadCount = loadBeanDefinitions(resources); if (actualResources != null) { for (Resource resource : resources) { actualResources.add(resource); } } return loadCount; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } } else { // Can only load single resources by absolute URL. Resource resource = resourceLoader.getResource(location); int loadCount = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } return loadCount; }}
getResource的实现在AbstractApplicationContext:
@Overridepublic Resource[] getResources(String locationPattern) throws IOException { //构造器中初始化,PathMatchingResourcePatternResolver对象 return this.resourcePatternResolver.getResources(locationPattern);}
PathMatchingResourcePatternResolver是ResourceLoader继承体系的一部分。
@Overridepublic Resource[] getResources(String locationPattern) throws IOException { Assert.notNull(locationPattern, "Location pattern must not be null"); //classpath: if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) { // a class path resource (multiple resources for same name possible) //matcher是一个AntPathMatcher对象 if (getPathMatcher().isPattern(locationPattern .substring(CLASSPATH_ALL_URL_PREFIX.length()))) { // a class path resource pattern return findPathMatchingResources(locationPattern); } else { // all class path resources with the given name return findAllClassPathResources(locationPattern .substring(CLASSPATH_ALL_URL_PREFIX.length())); } } else { // Only look for a pattern after a prefix here // (to not get fooled by a pattern symbol in a strange prefix). int prefixEnd = locationPattern.indexOf(":") + 1; if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) { // a file pattern return findPathMatchingResources(locationPattern); } else { // a single resource with the given name return new Resource[] {getResourceLoader().getResource(locationPattern)}; } }}
isPattern:
@Overridepublic boolean isPattern(String path) { return (path.indexOf('*') != -1 || path.indexOf('?') != -1);}
可以看出配置文件路径是支持ant风格的,也就是可以这么写:
new ClassPathXmlApplicationContext("con*.xml");
配置文件加载
入口方法在AbstractBeanDefinitionReader的217行:
//加载Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);//解析int loadCount = loadBeanDefinitions(resources);
最终逐个调用XmlBeanDefinitionReader的loadBeanDefinitions方法:
@Overridepublic int loadBeanDefinitions(Resource resource) { return loadBeanDefinitions(new EncodedResource(resource));}
Resource是代表一种资源的接口,其类图:
EncodedResource扮演的其实是一个装饰器的模式,为InputStreamSource添加了字符编码(虽然默认为null)。这样为我们自定义xml配置文件的编码方式提供了机会。
之后关键的源码只有两行:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { InputStream inputStream = encodedResource.getResource().getInputStream(); InputSource inputSource = new InputSource(inputStream); return doLoadBeanDefinitions(inputSource, encodedResource.getResource());}
InputSource是org.xml.sax的类。
doLoadBeanDefinitions:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) { Document doc = doLoadDocument(inputSource, resource); return registerBeanDefinitions(doc, resource);}
doLoadDocument:
protected Document doLoadDocument(InputSource inputSource, Resource resource) { return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware());}
NamespaceAware默认false,因为默认配置了校验为true。
校验模型其实就是确定xml文件使用xsd方式还是dtd方式来校验,忘了的话左转度娘。Spring会通过读取xml文件的方式判断应该采用哪种。
documentLoader是一个DefaultDocumentLoader对象,此类是DocumentLoader接口的唯一实现。getEntityResolver方法返回ResourceEntityResolver,上面说过了。errorHandler是一个SimpleSaxErrorHandler对象。
DefaultDocumentLoader.loadDocument:
@Overridepublic Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) { //这里就是老套路了,可以看出,Spring还是使用了dom的方式解析,即一次全部load到内存 DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); return builder.parse(inputSource);}
createDocumentBuilderFactory比较有意思:
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware{ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(namespaceAware); if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) { //此方法设为true仅对dtd有效,xsd(schema)无效 factory.setValidating(true); if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) { // Enforce namespace aware for XSD... //开启xsd(schema)支持 factory.setNamespaceAware(true); //这个也是Java支持Schema的套路,可以问度娘 factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE); } } return factory;}
本文来自投稿,不代表本人立场,如若转载,请注明出处:http://www.sosokankan.com/article/2043443.html