IOC是用为用户创建、管理实例对象的。用户需要实例对象时只需要向IOC容器获取就行了,不用自己去创建,从而达到与具体类解耦。
简单点来讲就是Spring IOC就是一个Map集合,对象的名字就是集合中的key,值就是对应的对象。我们可以通过一个对象的名字到集合中获取对象。
Spring提供了多种方式来配置Bean定义,有Xml,JavaConfig
Xml:
<bean id="user" class="com.ranger.bean.User"> <constructor-arg type="String" value="ranger"></constructor-arg> <constructor-arg ref="cbean"></constructor-arg> </bean> <bean id="car" class="com.ranger.bean.Car"> <constructor-arg type="String" value="mazda"></constructor-arg> </bean>
JavaConfig的方法,这种方式要和注解配合
@Configuration public class AppConfig { @Bean public Service myService() { return new ServiceImpl(); } }
创建SpringIOC容器的时候指定一个配置文件(xml),或者指定包扫描的路径(JavaConfig)
// 通过指定classpath下的配置文件 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("springcontext.xml");
或者使用JavaConfig的方式
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); annotationConfigApplicationContext.register(AppConfig.class); annotationConfigApplicationContext.refresh(); System.out.println(annotationConfigApplicationContext.getBean(User.class));
大概介绍每个父接口对应的职责
ApplicationContext中定义的方法
ApplicationContext中定义了自己的几个方法,对这几个方法做简单的介绍:
我们先从ApplicationContext后面的子孙开始
从上面的类继承图可以看出,AbastractApplicationContext 后面有两个儿子,分别是 GernericApplicationContext
和 AbstractRefreshableApplicationContext
.这两个大儿子分别都持有了BeanFactory实例,这两个儿子所有的对于BeanDefiniton的注册,Bean的实例化都是基于这个BeanFactory实例的
它们分成了 两大派系 :
GenericApplicationContext实现了BeanDefinitionRegistry这个接口,这个接口干啥的呢,看名字是BeanDefinition的注册什么东东,没错,这个就是用来添加删除BeanDefiniton的。GenericApplicationContext也还有几个儿子,后面会简单分析一下他们的不同。
上面都提到了refresh方法,这个方法是AbastractApplicationContext实现的,用于配置Context.该类使用了模板方法模式,很多方法都留给了子类去实现。
在AbstractApplicationContext中,实现了大多数ApplicationContext接口从BeanFactroy接口继承来的方法。
我们还可以看到,AbstractApplicationContext的父类是ConfigurableApplicationContext,这个类提供了配置Context的方法,还提供了生命周期方法。
通过上面的分析我们看到,虽然都有一个共同的祖先叫做ApplicationContext,但是不同的子孙还是有不同的加载BeanDefinition的方法,但是其它方面都是一样的,SpringIOC中最重要的方法refresh就在他们共同的老爸AbstractApplicationContext中。refresh方法会根据BeanDefinition来创建Bean对象(除开lazy loading)
我们就从我们常见的ClasspathXmlApplicationContext开始分析:
现在项目中包含上面图中的类和配置,我们编写一个主方法:
public class Application { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("springcontext.xml"); Person person = applicationContext.getBean(Person.class); System.out.println(person); } }
springcontext.xml
<?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-3.0.xsd" default-lazy-init="true" default-init-method="" default-destroy-method=""> <bean id="car" class="com.ranger.spring.ioc.bean.Car"> <constructor-arg name="brand" value="mazda"></constructor-arg> </bean> <bean id="person" class="com.ranger.spring.ioc.bean.Person"> <property name="car" ref="car"></property> </bean> </beans>
调试启动Application类的main方法
因为最终BeanDefinition的注册都是在DefaultListableBeanFactory 中完成的,所以在registerBeanDefinition(String beanName, BeanDefinition beanDefinition)打断点就能看到下面的调用栈
从线程调用栈的下面向上看。
得到一个基本的流程:
这里面的解析Document获取BeanDefinition也比较复杂,如果有兴趣可以去看看。
那么GenericXmlApplicationContext加载BeanDefinition的流程是不是也和上面一样呢。
可以看到流程是一样的。
只是GenericXmlApplicationContext会先调用load来加载BeanDefinition,然后调用refresh完成配置。
而ClasspathXmlApplicationContext会在refresh方法调用的时候完成BeanDefinition的加载。
通过前面的分析我们可以看到,ApplicationContext的大部分操作其实都是基于DefaultListableBeanFactory来完成的。
DefaultListableBeanFactory是BeanFactory的一个实现类
现在我们来认识一下它:
通过读源码的doc,
BeanFactory的实现类应该尽可能支持bean的生命周期方法,比如BeanNameAware,BeanClassLoaderAware,等等。
对于这些生命周期方法的支持,BeanFacoty没有给出抽象的接口,需要实现类自己去实现
BeanFactory的源码:
public interface BeanFactory { // 用来区分FactoryBean和其产生的对象 String FACTORY_BEAN_PREFIX = "&"; // 通过BeanName获取Bean Object getBean(String name) throws BeansException; // 通过beanName和bean 的Class类型来获取Bean <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException; // 增加获取bean的参数 Object getBean(String name, Object... args) throws BeansException; // 通过类型获取 <T> T getBean(Class<T> requiredType) throws BeansException; // 和上面一样的道理 <T> T getBean(Class<T> requiredType, Object... args) throws BeansException; // 判断是否包含某个Bean boolean containsBean(String name); // bean是否是单例 boolean isSingleton(String name) throws NoSuchBeanDefinitionException; // bean是否是prototype boolean isPrototype(String name) throws NoSuchBeanDefinitionException; //查询指定了名字的Bean的Class类型是否与指定类型匹配 boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; // 同上 boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException; //获取指定名字bean的Class类型 @Nullable Class<?> getType(String name) throws NoSuchBeanDefinitionException; // 获取bean的别名 String[] getAliases(String name); }
BeanFactory 有三个子类接口:ListableBeanFactory、HierarchicalBeanFactory 和AutowireCapableBeanFactory,还有一个实现类SimpleJndiBeanFactory。
这里对于BeanFactory的体系介绍就先不说了,太多了,单独写吧。
ApplicationContext的体系很大,重要抓住了核心几个比较重要的几个类: AbstractApplicationContext
,以及它的两个大儿子 GenericApplicationContext
和 AbstractRefreshableApplicationContext
。大部分功能都在这里面实现了。
两个大儿子生下的儿子都是基于他们做了一些扩展。
阅读代码可以发现,ApplicationContext很多的方法都留到了子类去实现,这里用到了 模板方法 设计模式。
最终对于注册BeanDefinition和基于BeanDefinition创建bean实例都是归结到了DefaultListableBeanFactory中。
前面对Spring容器的体系做了整体的了解,接下来会分析bean的创建。