tiny-spring 源码分析

Published: Creative Commons Licence

一、前言

想研究 Spring 的源码,但是里面包和类众多、层级很深,不知道如何入手。后来发现了 tiny-spring 这个项目,可以认为是一个极简版本的 Spring,但是麻雀虽小五脏俱全,实现了最核心的 IOC 容器和 AOP 的功能,非常适合拿来入门研究和学习,本文就是对这个项目源码的一点分析。

二、Hello World

把代码下载下来,在IDE里打开,整体项目结构如下,是一个标准 Maven 工程:

1

我们可以从作者提供的测试代码上手,先来看一下 ApplicationContextTest ,很标准的 Spring IOC 容器使用范例:

public class ApplicationContextTest {
    @Test
    public void test() throws Exception {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");
        HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService");
        helloWorldService.helloWorld();
    }
}

再看一下 tinyioc.xml 的配置(省略了部分信息),这里的配置也是我们非常熟悉的写法:

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

    <bean id="outputService" class="us.codecraft.tinyioc.OutputServiceImpl">
    </bean>

    <bean id="helloWorldService" class="us.codecraft.tinyioc.HelloWorldServiceImpl">
        <property name="text" value="Hello World!"/>
        <property name="outputService" ref="outputService"/>
    </bean>

</beans>

HelloWorldService 是一个非常简单的服务,调用了 OutputService 来输出自己的成员变量 text:

public class HelloWorldServiceImpl implements HelloWorldService {

    private String text;

    private OutputService outputService;

    @Override
    public void helloWorld(){
        outputService.output(text);
    }

    public void setText(String text) {
        this.text = text;
    }

    public void setOutputService(OutputService outputService) {
        this.outputService = outputService;
    }

}

然后 OutputServiceImpl 就是直接调用系统输出:

public class OutputServiceImpl implements OutputService {

    @Override
    public void output(String text){
        System.out.println(text);
    }

}

是不是感觉很亲切?和我们平时使用的 Spring 是一样的,运行一下 ApplicationContextTest ,果然输出了:Hello World!

接下来看一下 Spring 两大核心 IOC 容器和 AOP 的实现。

三、IOC 容器的实现

IOC 容器的实现在beans包下面,结构如下图所示:

2

IOC容器的核心类是 BeanFactory,继续从测试用例入手,看看 BeanFactoryTest 的代码:

public class BeanFactoryTest {

    @Test
    public void testLazy() throws Exception {
        // 1.读取配置
        XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
        xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");

        // 2.初始化BeanFactory并注册bean
        AbstractBeanFactory beanFactory = new AutowireCapableBeanFactory();
        for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
            beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
        }

        // 3.获取bean
        HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
        helloWorldService.helloWorld();
    }
    
}

接下来一步一步看:

1. 读取配置

XmlBeanDefinitionReader 的作用是读取 XML 文件中配置的信息,解析为 BeanDefinition 并存储起来,看一下这个类的代码就可以明白大致流程,这里不再详细展开。

看 XmlBeanDefinitionReader 代码会发现,这里还牵涉到了 Spring 对于资源的抽象:

  • Resource: 代表资源实体,可以调用 getInputStream 接口获取资源的输入流。
  • ResourceLoader: 代表资源加载器,可以调用 getResource 接口从指定的位置加载资源。

tiny-spring 这里只提供了 UrlResource 和 UrlResourceLoader 实现。

这一步的最终产物 BeanDefinition 是 Spring 中非常重要的一个类,保存了 Bean 的元数据信息,是后面注册和实例化 Bean 必不可少的,这里贴一下其代码:

public class BeanDefinition {

	private Object bean;

	private Class beanClass;

	private String beanClassName;

	private PropertyValues propertyValues = new PropertyValues();

}

2. 初始化 BeanFactory 并注册 bean

BeanFactory 接口的定义是极简风格:

public interface BeanFactory {

    Object getBean(String name) throws Exception;

}

注册 bean 的方法是 registerBeanDefinition ,逻辑在 AbstractBeanFactory 里,这里其实就是把 BeadDefinition 转存一下:

public abstract class AbstractBeanFactory implements BeanFactory {

    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

    private final List<String> beanDefinitionNames = new ArrayList<String>();
    ...
    public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception {
        beanDefinitionMap.put(name, beanDefinition);
        beanDefinitionNames.add(name);
    }
    ...
}

3. 获取 bean

这里才是重头戏,来看一下 getBean 的流程:

public abstract class AbstractBeanFactory implements BeanFactory {
    ...
    @Override
    public Object getBean(String name) throws Exception {
        BeanDefinition beanDefinition = beanDefinitionMap.get(name);
        if (beanDefinition == null) {
            throw new IllegalArgumentException("No bean named " + name + " is defined");
        }
        Object bean = beanDefinition.getBean();
        if (bean == null) {
            bean = doCreateBean(beanDefinition);
            bean = initializeBean(bean, name);
            beanDefinition.setBean(bean);
        }
        return bean;
    }
    ...
}

代码逻辑简单明了:首先拿到 name 对应的 BeanDefinition,然后检查 Bean 实例,如果非空,直接返回即可;如果实例为空,则说明尚未初始化过,则执行一次初始化逻辑,包括 doCreateBeaninitializeBean 两步。

先来看看 doCreateBean 的实现:

public abstract class AbstractBeanFactory implements BeanFactory {
    ...
    protected Object doCreateBean(BeanDefinition beanDefinition) throws Exception {
		Object bean = createBeanInstance(beanDefinition);
		beanDefinition.setBean(bean);
		applyPropertyValues(bean, beanDefinition);
		return bean;
	}

    protected Object createBeanInstance(BeanDefinition beanDefinition) throws Exception {
        // 直接利用 Java 的反射机制创建实例
		return beanDefinition.getBeanClass().newInstance();
	}

    protected void applyPropertyValues(Object bean, BeanDefinition beanDefinition) throws Exception {

    }
    ...
}

applyPropertyValues 是在为成员变量赋值,AbstractBeanFactory 并未实现,具体逻辑放在了子类 AutowireCapableBeanFactory 里:


public class AutowireCapableBeanFactory extends AbstractBeanFactory {
    protected void applyPropertyValues(Object bean, BeanDefinition mbd) throws Exception {
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(this);
        }
        for (PropertyValue propertyValue : mbd.getPropertyValues().getPropertyValues()) {
            Object value = propertyValue.getValue();
            if (value instanceof BeanReference) {
                BeanReference beanReference = (BeanReference) value;
                value = getBean(beanReference.getName());
                // 这里其实是会有循环依赖的问题的,Spring 的解决方案可以参见:https://www.jianshu.com/p/6c359768b1dc
            }
            try {
                // 利用反射机制调用 setter 方法
                Method declaredMethod = bean.getClass().getDeclaredMethod("set"
                        + propertyValue.getName().substring(0, 1).toUpperCase() + propertyValue.getName().substring(1),
                        value.getClass());
                declaredMethod.setAccessible(true);
                declaredMethod.invoke(bean, value);
            } catch (NoSuchMethodException e) {
                Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
                declaredField.setAccessible(true);
                declaredField.set(bean, value);
            }
        }
    }
}

最后是 initializeBean 的实现,就是循环调用了一遍前置和后置的 BeanPostProcessor:

protected Object initializeBean(Object bean, String name) throws Exception {
    for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
        bean = beanPostProcessor.postProcessBeforeInitialization(bean, name);
    }

    // TODO:call initialize method
    for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
        bean = beanPostProcessor.postProcessAfterInitialization(bean, name);
    }
    return bean;
}

至此,获取 Bean 的流程就走完了,我们拿到了一个初始化过且注入了依赖的完整 Bean 实例。

此时再去看 context 包下面的几个类,会发现 ApplicationContext 其实就是把上面的1,2,3 步揉在了一起,没有什么新东西,不再赘述。

四、AOP 的实现

接下来分析一下 AOP 模块的实现。先来看一下整体包结构:

3

另外还要特别注意一下 aopalliance 这个三方包,这个项目的目的是规范和统一 AOP 中的概念,核心类如下所示(关于这些类的含义这里不再展开,想详细了解的可以去 官网):

4

继续从测试用例入手,我们来看 JdkDynamicAopProxyTest 这个类(Cglib2AopProxyTest和它几乎一致,只是创建代理时使用了cglib):

public class JdkDynamicAopProxyTest {

	@Test
	public void testInterceptor() throws Exception {
		// --------- helloWorldService without AOP
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");
		HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService");
		helloWorldService.helloWorld();

		// --------- helloWorldService with AOP
		// 1. 设置被代理对象(Joinpoint)
		AdvisedSupport advisedSupport = new AdvisedSupport();
		TargetSource targetSource = new TargetSource(helloWorldService, HelloWorldServiceImpl.class,
				HelloWorldService.class);
		advisedSupport.setTargetSource(targetSource);

		// 2. 设置拦截器(Advice)
		TimerInterceptor timerInterceptor = new TimerInterceptor();
		advisedSupport.setMethodInterceptor(timerInterceptor);

		// 3. 创建代理(Proxy)
		JdkDynamicAopProxy jdkDynamicAopProxy = new JdkDynamicAopProxy(advisedSupport);
		HelloWorldService helloWorldServiceProxy = (HelloWorldService) jdkDynamicAopProxy.getProxy();

		// 4. 基于AOP的调用
		helloWorldServiceProxy.helloWorld();

	}
}

helloWorldService without AOP 部分还是IOC容器的内容,不再多说,看下面的 helloWorldService with AOP 部分,一步步来分析:

1.设置被代理对象

首先创建了一个 AdvisedSupport ,这个类存储了代理的元数据,具体来说就是下面三个东东:

public class AdvisedSupport {

    /**
    * 被代理的对象信息
    */
    private TargetSource targetSource;

    /**
    * 方法拦截器信息
    */
    private MethodInterceptor methodInterceptor;

    /**
    * ?
    */
    private MethodMatcher methodMatcher;

    // ...
}

在这一步里设置代理对象,就是指 TargetSource ,这个类存储了被代理的对象的信息 :

public class TargetSource {

    // 被代理对象的类信息
    private Class<?> targetClass;

    // 被代理对象实现的接口(一个疑问:为什么要显式传递,而不是通过targetClass获取?)
    private Class<?>[] interfaces;

    // 被代理对象的实例
    private Object target;

    public TargetSource(Object target, Class<?> targetClass, Class<?>... interfaces) {
        this.target = target;
        this.targetClass = targetClass;
        this.interfaces = interfaces;
    }

    // ...
}

2.设置拦截器

设置 AdvisedSupportmethodInterceptor 属性,这里设置了一个简单的 TimerInterceptor ,只有计时功能:

public class TimerInterceptor implements MethodInterceptor {

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		long time = System.nanoTime();
		System.out.println("Invocation of Method " + invocation.getMethod().getName() + " start!");
		Object proceed = invocation.proceed();
		System.out.println("Invocation of Method " + invocation.getMethod().getName() + " end! takes " + (System.nanoTime() - time)
				+ " nanoseconds.");
		return proceed;
	}
	
}

3.创建代理

被代理的对象和拦截器准备就绪后,就可以执行创建代理了,这里是一个 JdkDynamicAopProxy

public class JdkDynamicAopProxy extends AbstractAopProxy implements InvocationHandler {

    public JdkDynamicAopProxy(AdvisedSupport advised) {
        super(advised);
    }

	@Override
	public Object getProxy() {
        // 使用 JDK 动态代理机制创建代理
		return Proxy.newProxyInstance(getClass().getClassLoader(), advised.getTargetSource().getInterfaces(), this);
	}

	@Override
	public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
		// 暂略
	}

}

父类 AbstractAopProxy 里没什么东西,就是存储了一个 AdvisedSupport 实例:

public abstract class AbstractAopProxy implements AopProxy {

    protected AdvisedSupport advised;

    public AbstractAopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }
    
}

public interface AopProxy {

    Object getProxy();
    
}

4.实际调用

通过上面可知,我们此时拿到的 HelloWorldService 实例其实是一个动态代理对象,当调用其方法时,实际上会调用 InvocationHandlerinvoke 方法:

   public class JdkDynamicAopProxy extends AbstractAopProxy implements InvocationHandler {
   
       // ..
   
   	@Override
   	public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
   		MethodInterceptor methodInterceptor = advised.getMethodInterceptor();
   		if (advised.getMethodMatcher() != null
   				&& advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) {
   			return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(),
   					method, args));
   		} else {
   			return method.invoke(advised.getTargetSource().getTarget(), args);
   		}
   	}
   
   }

终于在这里 MethodMatcher 派上了用场,其作用是判断当前调用的方法是否是需要进行拦截,如果是,则调用我们设置好的 MethodInterceptorinvoke 方法; 否则,就直接调用目标对象,什么额外逻辑也不做。

至此就把 JdkDynamicAopProxyTest 的主要逻辑也梳理完毕了。