我们通过讲Spring的容器是怎样启动的,它在启动过程中做了些什么,以此来引发我们IOC原理的解析,在这里我们直接使用编程式的方式来启动容器,而不会涉及到SpringMVC,如果你担心你大多数时候是SpringMVC与Spring一起使用的,看这文章没有用,请放心,在后面解析SpringMVC的时候,你会发现,无论怎么变化,无非是启动容器的触发代码位置不同而已,这里是我们直接写代码,而SpringMVC会通过对Servlet的上下文监听而启动容器...
我们先来写一个例子,这个例子注重点在于讲清楚以下几点,你可以记下这个例子,后面的一系列解析过程都是围绕着它展开的喔!
1:编程式容器的启动;
2:资源文件的定位与加载;
3:资源文件的解析;
4:Bean的生成与注册;
5:依赖注入的全过程;
/**短裤*/
public class Pants {
private String color;
//演示构造器注入过程
public Pants( String color ) {
this.color = color;
}
@Override
public String toString() {
return "Pants [color=" + color + "]";
}
}
/**人类*/
public class Person {
//演示引用依赖注入过程
private Pants pants;
private String name ;
private int age;
//演示集合注入过程,其它几种就不用演示了,原理一样,可举一反三
private List<String> girls;
public Pants getPants() {
return pants;
}
public void setPants(Pants pants) {this.pants = pants;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
public List<String> getGirls() {return girls;}
public void setGirls(List<String> girls) {this.girls = girls;}
@Override
public String toString() {
return "Person [pants=" + pants + ", name=" + name + ", age=" + age
+ ", girls=" + girls + "]";
}
}
在src/main/resources下放入以下资源文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd" default-lazy-init="true">
<bean id="pants" class="com.fyrj.compoment.spring.test.ioc.Pants">
<constructor-arg index="0" name="color" value="黄色"/>
</bean>
<bean id="person" class="com.fyrj.compoment.spring.test.ioc.Person">
<property name="pants" ref="pants"/>
<property name="name" value="张三丰"/>
<property name="age" value="18"/>
<property name="girls">
<list>
<value>景甜</value>
<value>刘亦菲</value>
<value>克拉拉</value>
</list>
</property>
</bean>
</beans>
OK,就这两个类以及资源文件,接下来就正式开始原理解析了,你可以将源码准备好,然后跟着我一步步进行调试,进入Spring作者的思维之中,我相信,看完整个Spring系列文章,对你的帮助肯定是会有的!
我们先来看一段代码:
public class TestSpringIOC {
public static void main(String[] args) {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
Resource resource = new ClassPathResource("testSpringIOC.xml");
reader.loadBeanDefinitions(resource);
Person person = factory.getBean(Person.class);
System.out.println(person);
//上面的4行代码相当于下面的1行代码
ApplicationContext context = new ClassPathXmlApplicationContext("testSpringIOC.xml");
Person newPerson = context.getBean(Person.class);
System.out.println(newPerson);
}
}
Person [pants=Pants [color=黄色], name=张三丰, age=18, girls=[景甜, 刘亦菲, 克拉拉]]
Person [pants=Pants [color=黄色], name=张三丰, age=18, girls=[景甜, 刘亦菲, 克拉拉]]
*************************************************************************************
我们知道,我们的Bean都是由Spring来进行管理的,包括Bean的创建与依赖关系的管理,那么管理它的容器是什么呢?就是BeanFactory类,如果把Bean比作水滴,那么BeanFactory类就相当于一个桶子,它装载着大量的Bean;
我们来看看代码,上面首先声明了一个DefaultListableBeanFactory,它就是一个BeanFactory,接下来是XmlBeanDefinitionReader,见名知意,这是通过XML文件来读取BeanDefinition的类,(BeanDefinition是Spring对Bean的抽象描述类,这个类中维护折后期实例化一个Bean时所有的配置,包括我们在XML中声明的构造参数,方法参数等等...)而后声明了Resource,这是Spring对资源文件的抽象,最后调用XML读取器来载入bean定义;关于这些类的详细描述,后面解析,这里只需要有个印象即可;
由此我们看到,bean的创建过程大致是这样的,首先我们在XML中声明对POJO的配置,而后通过Resource接口转换为Spring的资源类,再通过bean读取器对XML内容进行读取,它将读取到的各种配置转换解析为BeanDefintion类,而转换后的类放入哪里,当然是它持有的引用DefaultListableBeanFactory之中; 实际上,Spring已经帮我们封装好了这一切,通过下面那一行代码,就可以完成;
说到这里,是时候来了解下Spring中BeanFactory的体系结构了;
看不清楚的话放大看吧,我们看到,BeanFactory的类关系有大量的类,还有些我没有画出来,那些我们先不去关心,通过图我们发现,Spring的Bean工厂大概沿着3条主线走,首先是最终实现类为DefaultListableBeanFactory的基础实现,而后是以上下文形式存在的的ApplicationContext的实现,而上下文又分为普通环境上下文以及WEB环境上下文,普通环境上下文以FileSystemXmlApplicationContext以及ClassPathXmlApplicationContext类来进行实现,通过名字就能知道,一个是通过从本地文件系统中读取XML配置来形成上下文另一个是通过扫描类路径中的XML配置来形成上下文环境;WEB环境是以XmlWebApplicationContext来实现的;这个类也是SpringMVC启动时构建的web上下文类,后面我们再做解析;
接下来,我们来看看BeanFactory接口的代码;
public interface BeanFactory {
//通过名字获得一个Bean
Object getBean(String name) throws BeansException;
//通过名字与类型类获得一个Bean
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
//通过Bean名称与创建参数来获得一个Bean
Object getBean(String name, Object... args) 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
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
}
这些方法我们经常用到,就不多说了,接下来是它的子接口AutowireCapableBeanFactory,这是一个支持通过注解来发现与注册Bean的bean工厂,然后是ListableBeanFacrory接口,这是用来管理bean的依赖关系图的接口,然后是ConfigurableListableBeanFactory,这是用来对Bean工厂做额外配置的接口,最后它的最终实现类是DefaultListableBeanFactory,这是Spring中提供的一个成熟的Bean工厂,通过它的类关系我们知道它能做到管理bean的依赖关系,配置bean,根据注解生成bean等等功能...
如果我们使用DefaultListableBeanFactory来完成对Bean的管理,那么就像先前的代码一样,它需要借助于其它的类来加载资源,解析载入资源等等...这些都需要我们手动来完成,而ApplicationContext则不同,它虽然也要这样做但它已经帮我们将这些步骤封装好了,至于它的内部具体是怎么工作的,是怎么将XML描述转换为BeanDefinition的...这就是我们接下来要做的事情了!