2
  • 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}

吉林乌拉
456 声望31 粉丝