IoC全称为控制反转(Inverse of Control),也叫依赖注入,是spring容器的内核,AOP、事务等都依赖于此技术。IoC说白了,就是将对象与对象之间的依赖关系从代码中转移到spring的配置文件中(默认为ApplicationContext.xml 也可以自定义名字),从而由spring进行管理。这样的好处就是降低了对象与对象之间的依赖。IoC的工作原理就是利用Java的反射功能实例化对象与对象的依赖。除此之外,IoC容器还提供了对象的实例缓存、生命周期管理、对象实例代理、事件发布、资源装载等高级功能。
BeanFactory接口也就是大家熟知的Bean工厂,它是spring框架最核心的接口,它提供了高级的IoC配置机制,可以管理任何不同类型的Java对象。ApplicationContext接口是继BeanFactory接口之外,另一个重要的接口。它的功能是建立在BeanFactory接口之上的。它除了已有的功能外,还提供国际化支持和框架事件等更多面向应用的功能。对于两者的区别用最直观最简介的方式来说就是BeanFactory是Spring框架的内核,它是给Spring用的,而ApplicationContext接口是给使用Spring框架的开发者用的。
BeanFactory是一个类工厂,但它和一般的类工厂不同,一般的类工厂是实例化一个类或几个类,而BeanFactory是个超级工厂,可以创建管理各种类。所有被BeanFactory管理的对象在Spring中统称为Bean.下面我们初始化BeanFactory工厂,并在工厂中获取某个对象。
spring配置文件:bean.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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 给car的所有属性赋值 -->
<bean id="car" class="com.xiaobai.spring.mark.Car" p:brand="众泰T600S" p:color="白色" p:speed="120"></bean>
</beans>
实体类Car.java
public class Car {
/** 品牌 */
private String brand;
/** 颜色 */
private String color;
/** 速率 */
private int speed;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", color='" + color + '\'' +
", speed=" + speed +
'}';
}
}
测试类CarTest.java
public class CarTest {
/**
* new 方式实例化car对象
*/
@Test
public void newCreateCar() {
Car car = new Car();
car.setBrand("捷豹xjl");
car.setColor("黑色");
car.setSpeed(200);
System.out.println(car);
}
/**
* BeanFactory 方式实例化car对象
*/
@Test
public void beanFactoryCreateCar() {
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
Resource resource = resourcePatternResolver.getResource("classpath:xiaobai/beans.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
Car car = beanFactory.getBean("car", Car.class);
System.out.println(car);
}
/**
* applicationContext 方式实例化car对象
*/
@Test
public void applicationContextCreateCar() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:xiaobai/beans.xml");
Car car = applicationContext.getBean("car", Car.class);
System.out.println(car);
}
}
XmlBeanFactory通过Resource装载Spring配置信息并启动IoC容器,然后通过BeanFactory中的getBean()方法从IoC容器中获取Bean.根据Spring的内部机制,启动IoC容器时,并不会初始化配置文件中的Bean.当第一次调用getBean()时初始化。对于单例的Bean来说,BeanFactory会缓存Bean实例,在第二次调用getBean()时,直接从IoC容器的缓存中获取Bean实例。接下来我们通过下面的例子来证明BeanFactory的缓存机制。
BeanFactory的缓存测试
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
Resource resource = resourcePatternResolver.getResource("classpath:xiaobai/beans.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
Car car1 = beanFactory.getBean("car", Car.class);
Car car2 = beanFactory.getBean("car", Car.class);
System.out.println(car1);
System.out.println(car2);
上述代码我们从IoC容器中获取了两个Car,通过输出对象的内存地址信息来判断这两个car是否为同一个
输出结果
com.xiaobai.spring.mark.Car@1d96f4b5
com.xiaobai.spring.mark.Car@1d96f4b5
通过输出结果证明了我们之前所说的,BeanFactory的缓存机制。但在各别情况下我们不希望它有缓存,我们希望每次访问时返回的都是一个新的对象。那怎么办呢。我们可以通过scope属性来配置。
beanx.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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 给car添加scope属性并值设置为prototype -->
<bean id="car" class="com.xiaobai.spring.mark.Car" p:brand="众泰T600S" p:color="白色" p:speed="120" scope="prototype"></bean>
</beans>
输出结果
com.xiaobai.spring.mark.Car@6ecf829d
com.xiaobai.spring.mark.Car@79884a40
通过结果我们看,通过给Bean设置scope=”prototype”时,IoC容器每次返回的都是一个新对象了,如果想每次返回的都是同一个对象,可以设置为scope=”singleton”,默认不设置scope属性时scope=”singleton”
Spring3.0以后支持类注解的配置方式,可以通过@Configuration注解为Spring提供配置信息,Spring为配置的注解类提供了一个专门的实现类AnnotationConfigApplicationContext,下面我们来看一下怎么来加载配置信息
Beans.java
@Configuration
public class Beans {
@Bean(name = "car")
public Car buildCar() {
Car car = new Car();
car.setBrand("东风标志508");
car.setColor("白色");
car.setSpeed(300);
return car;
}
}
测试用例
/**
* @Configuration 方式实例化car对象
*/
@Test
public void configurationCreateCar() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Beans.class);
Car car = applicationContext.getBean("car", Car.class);
System.out.println(car);
}
输出结果
Car{brand='东风标志508', color='白色', speed=300}
WebApplicationContext接口是Spring专门为Web应用准备的,它继承了ApplicationContext接口,并为Bean的scope属性增加了三个值分别是request、session、globalSession.它允许从Web根目录的路径中装载配置文件,完成初始化工作。因为它是为Web服务的,所以它必须依赖于Web容器。根据Web项目开发经验,我们应该在web.xml配置自启动的Servlet或者Web容器监听器,来配置Web项目开发环境。Spring为这两种都提供了相应的类,来启动Spring容器。它们分别为org.springframework.web.context.ContextLoaderListener和org.springframework.web.context.ContextLoaderServlet.我们可以根据这两种中的任何一种方式来启动WebApplicationContext。虽然两种没有什么太大的区别,都可以启动WebApplicationContext,但是只有Servlet2.3以上的版本的Web容器才支持Web容器的监听器。(包括当前版本),所以可以根据项目需要配置启动WebApplicationContext的方式。下面我们来看一下具体的配置代码
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--
下面的代码是选择Spring配置文件的路径,也可以定义Spring配置文件的名字
如果不配置Spring默认在项目根目录下查找applicationContext.xml文件
-->
<context-param>
<param-name>contextConfigLocation</param-name>
<!--
如果配置多个文件可以用逗号分隔,也可以用资源前缀的方式配置
? : 匹配文件名中的一个字符
* : 匹配文件名中的任意个字符
** : 匹配多层路径
classpath:com/t?st.xml 匹配com类路径下的com/tast.xml、tbst.xml 等
classpath:com/*.xml 匹配com类路径下的所有xml文件
classpath:com/**.xml 匹配com类路径及其子目录下的所有xml文件
-->
<param-value>classpath:beans.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
在SpringIoC容器中一个Bean对应配置文件中的一个<bean>,id为这个Bean的名称,通过容器的getBean()方法获取对应的Bean。id在IoC容器中必须是唯一的,它的命名还要满足XML对id的命名规范:必须以字母开始,后机可以是字母、数字、连字符、下划线、等。但是,在特殊情况下,我们需要特别的名字,那id就不允许了。这时我们可以用name属性。name属性没有字符限制,几乎可以使用任何字符,并可以设置多个名字。如下所示
<bean name="#1, $2" class="com.xiaobai.spring.mark.Car" p:brand="众泰T600S" p:color="白色" p:speed="120"/>
Spring配置文件虽然不允许出现两个id的<bean>,但却可以出现两个name的<bean> 如果有多个name相同的<bean> 获取时,将返回最后声明的那个Bean,原因是后面的<bean>覆盖了前面的<bean>.如果id和name两个属性都未指定,Spring自动将class做为Bean的名称。如下所示
<bean class="com.xiaobai.spring.mark.Car" p:brand="众泰T600S" p:color="白色" p:speed="120"/>
@Test
public void applicationContextCreateCar() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:xiaobai/beans.xml");
Car car = applicationContext.getBean("com.xiaobai.spring.mark.Car", Car.class);
System.out.println(car);
}
执行结果
Car{brand='众泰T600S', color='白色', speed=120}