spring 源码分析_从底层分析Spring源码,原来Spring比你想象的简单的多

我们初次了解Spring,可能是老师傅给我们的第一份源码,可能里面的代码风格影响你今后的代码风格和架构风格,但是我们需要的是打破我们思维的桎梏,让Spring不再成为我们编码上的圣经,而是让Spring成为我们架构之路上的一名得力干将,那么你真的了解这个框架所使用的架构方式,下面我们各个方面全面分析Spring源码,让Spring不再神秘!

从最基本的Spring初始化配置文件开始。配置文件:

<?xml version="1.0" encoding="UTF-8"?>    

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的实例对象。

d62b256e1bebdfd99ee6737f16cd2fcc.png

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 < locations.length; i++) {           this.configLocations[i] = resolvePath(locations[i]).trim();      }  } else {       this.configLocations = null;  }}

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接口

类图:

c2deeb31df1ce3c2cc6c93ad5a032e6a.png

Environmen接口代表了当前应用所处的环境。从此接口的方法可以看出,其主要和profile、Property相关。

Profile

Spring Profile特性是从3.1开始的,其主要是为了解决这样一种问题: 线上环境和测试环境使用不同的配置或是数据库或是其它。有了Profile便可以在 不同环境之间无缝切换。Spring容器管理的所有bean都是和一个profile绑定在一起的。使用了Profile的配置文件示例:

在启动代码中可以用如下代码设置活跃(当前使用的)Profile:

context.getEnvironment().setActiveProfiles("dev");

我们也可以在web.xml文件中配置不同的profile

spring.profiles.activedevelop

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接口

类图

92d3129032b16afdd743656736c28462.png

这个接口实际上就是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来源。继承体系:

a92c3e0b940b1d75562256fc8b2be44b.png

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分支):

2f8f6417532983926b61574117059de4.png

此接口正是用来解析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容器,其继承体系:

60b4b406eb0a91e3a5c91bc6b914605f.png

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

此处只说明用到的部分继承体系:

fae2840f31d4a30be60d6eea6ac1ff77.png

EntityResolver接口在org.xml.sax中定义。DelegatingEntityResolver用于schema和dtd的解析。

BeanDefinitionReader

继承体系:

fa33011f189cbb92e1a0935a30ac4ce9.png

路径解析(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是代表一种资源的接口,其类图:

8530b56b80a90846928c4d736280e70a.png

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;}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值