Spring 版本 分支5.0.x Spring git地址
代码运行说明
-
git clone git@github.com:spring-projects/spring-framework.git
cd spring-framework -
gradlew.bat build
读head first,讲述如何更好的去提高效率,通过交互来提高,来引导整个流程,我也仿制一下
1.Spring 是怎么用的
Spring使用非常广泛,从基础来讲,肯定是容器为基础,有了容器才有操作的空间,下面的代码是容器的基础加载
这段代码是容器的的基础使用
public class SpringIocTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
EasyBean bean = context.getBean(EasyBean.class);
bean.sayHello();
context.close();
}
}
<?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-2.0.xsd">
<bean class="com.cn.smartqq.EasyBean"></bean>
</beans>
下面这个会失败
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean class="com.cn.smartqq.EasyBean"></bean>
</beans>
这里贴的XML代码里面包括了beans的定义,我非常奇怪,有一些的源码解读里面为什么不带这个能运行起来,这个下面会解析的
代码贴出来了,那么现在关注代码的运行
- 首先将config.xml加载进来,进行初始化工作
- 然后通过传入class获取bean
- 剩下就是调用
- 关闭容器
下面有两个问题,从实际出发,先看这2个问题
- 当beans不声明的时候,无法加载成功,为什么?
- 传入class就能获取bean,为什么?
直接拿代码了,主要代码是这个类,注释里面写出了问题1的答案
public class DefaultDocumentLoader implements DocumentLoader {
省略N多
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); 这里是关键,但是这里又是工厂模式建立的,看下面的关键方法
}
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) {
最后就是进入了这里,感慨一句,代码是越来越方便维护,以前这里都是硬写的参数3,1之类的,写成true了,所以必须要有
写成true之后是必须读xlmns里面的内容的
// 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;
}
}
再来看问题2,首先个人猜了一下实现,可能放到一个hash表里面,可能是类似于concurrenthashmap,线程安全,然后通过反射的形势生成对象,因为spring这里不一定是jvm加载就生成所有对象,spring使用中也有单例,非单例的形式。所以肯定有生成对象的代码只是if条件不符合,忽略过去了(大概猜了个3成)
看代码如何getbean,但是跳入getbean就会发现,竟然不是ClassPathXmlApplicationContext这个类的实际获取,这里就看下代码的整个继承关系
右点击打开可能看的更清楚,我这里就不换图,因为后面的代码都是通过这里,getBean方法调用的是
AbstractApplicationContext这个类里面的内容,然后这里需要开始插入设计的好处了,非常明显,下面是2个代码的具体,其他省略
设计非常清晰,我这个方法就是子类实现,那么因为这里除了xml的这个bean加载,还有其他的加载
说到这里,我就要吐槽,前端设计真的还是比不上后端,就像腾讯去设计前端,改动也非常多,什么对继承开放,对修改关闭都不是很严格,更不用说更菜的设计了,不过前端也改变非常快
@Override
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
//---------------------------------------------------------------------
// Implementation of BeanFactory interface
//---------------------------------------------------------------------
@Override
public Object getBean(String name) throws BeansException {
assertBeanFactoryActive();//这里只是一个监控是否已经关掉容器,assert断言就能说明了,命名很棒
return getBeanFactory().getBean(name);
}
然后又是追踪了,虽然设计很好,但是追踪起来一样吐血
AbstractRefreshableApplicationContext 这个类里面看到这段语句
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
synchronized (this.beanFactoryMonitor) {//同步锁,虽然concurrent包有很多好用的锁工具,还是syn这个简洁明了
if (this.beanFactory == null) {
throw new IllegalStateException("BeanFactory not initialized or already closed - " +
"call 'refresh' before accessing beans via the ApplicationContext");
}
return this.beanFactory;
}
}
为什么看这个方法了,因为这个类里面只有这个方法去复制beanFactory,这里可能有问题说,有没有可能还是子类去实现
set beanFactory这个动作,的确有可能,并且这个方法的type是protected的,那么最多子类去实现了,但是这里已经有
default实现,那么我们先看看default实现
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();//看这步
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
下面的跳转,其实普通是猜不错来的,但是这里还是继续下去,后面,再讲这里具体的实现,下面代码省略很多,文章篇幅会太长
其实是到了DefaultListBeanFactory这个类里面,下面开始删代码,太长了
public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
NamedBeanHolder<T> namedBean = this.resolveNamedBean(requiredType, args);
通过namedBean返回实例
}
NamedBeanHolder 这个结构是name,加实例,进入这个方法,这个方阿飞的
private <T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType, Object... args) throws BeansException {
省略
这句很关键,getbean
return new NamedBeanHolder(beanName, this.getBean(beanName, requiredType, args));
省略
}
关键代码到了
sharedInstance = this.getSingleton(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
try {
return AbstractBeanFactory.this.createBean(beanName, mbd, args);
} catch (BeansException var2) {
AbstractBeanFactory.this.destroySingleton(beanName);
throw var2;
}
}
});
其实这个createBean里面会包含非常多的信息,有一个非常有趣的类叫做RootBeanDefinition这个就会包含我们
最爱的scope,lazyinit信息之类的信息
然后经过非常长的追踪到了
Class<?> resolvedClass = ClassUtils.forName(className, classLoader);
这里就是获取bean了,其实最后,猜了个5成,直接用的hashmap,名字叫做commonClassCache,所以只有公共的才去获取的到
所以不用管获取
然后获取是反射,这段代码
return clToUse != null?clToUse.loadClass(name):Class.forName(name);
其实上面看不出啥了refreshBeanFactory这个方法你只能连蒙带猜的去看怎么实现,那么这个refreshBeanFactory又是什么时候去实现的?
是ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(“config.xml”);这里去实现的。
这段代码关键在于对于父类的构造方法的调用,其次是refresh();
public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, @Nullable ApplicationContext parent)
throws BeansException {
super(parent); //首先是父类的调用,但是请注意,我们这里传入的是null
Assert.notNull(paths, "Path array must not be null");
Assert.notNull(clazz, "Class argument must not be null");
this.configResources = new Resource[paths.length];
for (int i = 0; i < paths.length; i++) {
this.configResources[i] = new ClassPathResource(paths[i], clazz);
}
refresh();
}
父类的构造函数一直向上追寻到AbstractApplicationContext这个类
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
this();
setParent(parent);
}
这个this()里面去构造了一个支持ant location的的一个resourceload的解析器,这里只是发生的set操作
然后使用setParent,注意这里传入的null,所以里面的代码是没执行的,功能是将父的contentxt,merge到现有的context里面去,有兴趣的可以去看看
现在回过来看看refresh()这个方法
再来定几个疑问
1.怎么初始化xml文件
2.bean工厂初始化干了什么?
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses. 上下文对beanfactory的一些处理,但是现在调用是没有任何处理的
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context. 国际化用的,在beanfacoty里面判断是否包含messageSource字符串来判断是否开启国际化
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
prepareRefresh()这个没啥好说的就是准备
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
这里有一个写法,在AbstractApplicationContext里面去调用了一个abstract方法refreshBeanFactory,但是实现在于子类AbstractRefreshableApplicationContext中
这个方法我关注的是beanfactory的刷新/创建,并且读取xml文件读bean就隐藏在这里面
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory); //读取xml文件
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory()); //这里面的不用管,现在的调用是空,那么只用考虑DefaultListableBeanFactory的实现
}
那么我们拿到的factory类就是DefaultListableBeanFactory实现
prepareBeanFactory(beanFactory); 这个方法也就是对factory进行参数设置
这里有一个写法也值得讲一下
AbstractAutowireCapableBeanFactory,ConfigurableListableBeanFacotry有相同的方法ignoreDependencyType同
注意了,这个方法再DefaultListableBeanFactory是没有实现的,而是直接调用的AbstractAutowireCapableBeanFactory里面的实现方法
loadBeanDefinitions(beanFactory) 就是读取xml文件,会校验xml填写的对不对,bean的一些数量信息都会有提现
-----------------------我是分割线-------------------------------
这里还要讲一下代码设计中的策略模式和工厂模式
Define a family of algorithms,encapsulate each one,and make them
interchangeable.(定义一组 算法,将每个算法都封装起来,并且使它们之间可以互换。)
就拿ClassPathXmlApplicationContext 这个类的继承图来说,他继承实现AbstractApplicationContext类,这个类里面将 resourceLoader作为一批算法,然后DefaultResourceLoader作为算法簇中的一个,context作为调用算法的类,这里写的非常灵活,用继承方式,将具体实现引入,而不是通过New的方式去引入
工厂模式
Define an interface for creating an object,but let subclasses decide
which class to instantiate.Factory Method lets a class defer
instantiation to subclasses.(定义一个用于创建对象的
接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。)
工厂模式然后会有抽象工厂模式,也就是更复杂的情况下的工厂模式,但是其根本定义还是这个
来我们看下BeanFactory的一个部分继承图,其实这里根本没有实现,实现在其他的里面,在下一张图里面
这里,把具体实现扔到了这里
那么这是工厂模式吗?看起来和工厂模式没有关系啊,
设计模式是告诉我们如何组织类和对象以解决某种问题
出自headFirst,这本书真推荐,比国内的设计模式讲的更清晰,我之前看的一些设计模式的书和资料,从心啊在看来都太死板了
他这里是使用反射的方式去生成类的,是传入参数,class,而不是传入具体类,请注意这个虚线号,不是继承,也不是实现,是一个构成。