Spring学习笔记2:Spring概述及IOC

Spring概述及IOC

本文将首先对Spring进行了简要的介绍,然后重点学习了SpringIOC,包括IOC基本概念、环境搭建、使用IOC容器创建对象,着重对获取bean的两种方式(id、Class)以及为属性赋值的三种方式(property属性、constructor-arg属性、p名称空间)进行了学习,同时也学习了对象的创建时机以及null值的使用。

1.为什么要学习Spring

因为Spring可以整合其他的框架,例如Struts2和Hibernate。
1. Spring中包含两个主要功能:IOC和AOP,可以使用IOC创建Struts2的Action类的对象以及Hibernate的SessionFactory对象,并自动装配。
2. 同时,对于数据库事物的问题,Spring以AOP为基础提供了声明式事务功能,在实际项目中,可以将事物操作交给Spring的声明式事务管理。
3. 为了学习声明式事务,需要先学习JdbcTemplate,JdbcTemplate是Spring提供的一个简单的访问数据库的功能。
图1

2.什么是IOC

  1. IOC(Inversion Of Control)又称“反转控制”。
  2. 传统的资源获取方向:应用去找环境要资源
    这里写图片描述
  3. IOC理念:环境主动把资源注入应用
    这里写图片描述
  4. 虽然目前我们还没有学习Spring的IOC,但是已经接触过一些由环境或者容器注入资源的例子。
    【例1】例如Servlet接口的init(ServletConfig config)方法,该方法由Servlet容器(例如Tomcat)负责调用,参数ServletConfig config是由Servlet容器负责创建对象并传入的,这就是一个典型的注入资源的例子。ServletConfig config这个资源并不是由我们创建,而是由Servlet容器创建并通过init()方法注入给我们。
    【例2】例如Servlet.service(request,response);方法。
    【例3】Struts2中Action类实现XxxAware接口,可以享受到Struts主动注入的Web资源对象。
  5. IOC的另一个表述方式:DI(Dependency Injection)又称“依赖注入”。
  6. IOC最主要的用途:创建组件对象并自动装配。
    这里写图片描述

3.IOC应用开发环境搭建

  1. 导入IOC容器所需要的jar包
    commons-logging-1.1.1.jar:日志
    spring-beans-4.0.0.RELEASE.jar:创建bean相关
    spring-context-4.0.0.RELEASE.jar:上下文先关
    spring-core-4.0.0.RELEASE.jar:核心
    spring-expression-4.0.0.RELEASE.jar:表达式相关
  2. 创建Spring配置文件:Spring Bean Configuration File
    在eclipse中选择“Window”菜单->“Perspective”->“Customisze Perspective…”
    这里写图片描述
    在弹出的窗口中选择“Menu Visibility”选项卡,将“File”->“New”下的“Spring Bean Configuration File”勾选,在右键“New”菜单中添加“Spring Bean Configuration File”选项。(不同版本的elipse这个设置的位置可能不同)
    这里写图片描述
    在src中新建“Spring Bean Configuration File”文件,文件名为“beans.xml”,创建完毕后如下图所示。
    这里写图片描述

4.IOC容器创建对象步骤(根据id获取Bean)

  1. 创建IOC容器对象
    在eclipse中按“ctrl shift” + “T”调出“Open Type”窗口,输入“ApplicationContext”,然后关联源码。
    这里写图片描述
    在接口名上按“ctrl+ T”,发现ApplicationContext接口有如下实现类,其中包含:
    • ApplicationContext:顶级接口。
    • ConfigurableApplicationContext:带有声明周期方法的接口,在涉及到带有生命周期的Bean时会使用。
    • ClassPathXmlApplicationContext:使用类路径下XML作为配置文件的实现类,通常使用这个类创建IOC容器对象
    • FileSystemXmlApplicationContext
    • GenericApplicationContext
      这里写图片描述
  2. 调用IOC容器对象的getBean()方法即可获取IOC容器中配置的对应的Bean对象。
    getBean()方法包含两种常用的重载形式,分别为:
//1.形参为Bean的ic,根据配置文件中指定的Bean的Id获取Bean。
getBean(String id)
//2.形参为Bean的class对象,根据Bean的类型获取Bean,如果想要根据类型获取Bean,
//需要保证IOC容器中指定类型的Bean只有一个。
getBean(Class<?> clazz)

测试:

//步骤1:在Spring01_IOC工程的src包下新建com.atguigu.ioc.bean包,在该包中新建Book.java
package com.atguigu.ioc.bean;

public class Book {

    //说明:在Hibernate中,要求持久化类必须提供OID属性,但是SpringIOC容器没有此要求,
    //这里只是为了照顾习惯使用了bookId属性。
    private Integer bookId;
    private String bookName;
    private String author;
    private double prive;

    public Book() {}

    public Book(Integer bookId, String bookName, String author, double prive) {
        super();
        this.bookId = bookId;
        this.bookName = bookName;
        this.author = author;
        this.prive = prive;
    }

    public Integer getBookId() {
        return bookId;
    }

    public void setBookId(Integer bookId) {
        this.bookId = bookId;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public double getPrive() {
        return prive;
    }

    public void setPrive(double prive) {
        this.prive = prive;
    }

    @Override
    public String toString() {
        return "Book [bookId=" + bookId + ", bookName=" + bookName + ", author=" + author + ", prive=" + prive + "]";
    }
}
//步骤2:在配置文件(beans.xml)中配置这个Bean,配置文件如下
<?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.xsd">

    <!-- 实验1:通过IOC容器创建对象,并为属性复制 -->
    <!-- id:Bean的Id -->
    <!-- class:Bean的全类名 -->
    <!-- 如果安装Spring插件的话,在填写全类名时只需要输入类名,就可以自动补全,非常方便 -->
    <!-- 同时id的值按alt + /,也可以自动生成 -->
    <bean id="book01" class="com.atguigu.ioc.bean.Book">
        <property name="bookId" value="1" />
        <property name="bookName" value="bookName01"/>
        <property name="author" value="author01" />
        <property name="prive" value="100" />
    </bean>

</beans>
//步骤3:测试,在src下新建com.atguigu.ioc.test包,在这个包下新建Junit Test Case类,类名:IOCTest
package com.atguigu.ioc.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class IOCTest {

    @Test
    public void test01() {
        //1.创建IOC容器对象
        ApplicationContext myIoc = new ClassPathXmlApplicationContext("beans.xml");

        //2.通过IOC容器对象获取Bean,使用getBean(String id)的重载形式
        Object bean = myIoc.getBean("book01");
        System.out.println(bean);
    }

}

运行该测试类,结果如下:
这里写图片描述
可以发现,在SpringIOC容器中,可以通过配置的方式创建对象,需要使用对象的时候从IOC容器中获取就可以。

5.对象在什么时候被创建

结论:默认情况下,对象在IOC容器创建的时候就被创建了

测试(只需要对上面的代码稍微修改)

  • 在Book类的空参构造器中添加如下代码
System.out.println("Book对象被创建了");
  • 将IOCTest类的test01方法改写为
    @Test
    public void test01() {
        ApplicationContext myIoc = new ClassPathXmlApplicationContext("beans.xml");

        //在IOC容器创建后,获取对象之前,添加下面一行代码。
        System.out.println("===Ioc容器已经被创建===");

        Object bean = myIoc.getBean("book01");
        System.out.println(bean);
    }

运行该测试类,结果如下:
这里写图片描述
可以发现,Book对象在IOC容器创建后便已经创建了。可以得到如下结论:

  • 默认情况下:在创建和初始化IOC容器本身的时候,IOC容器就会把所有配置好的Bean都创建出来。
  • 其他情况:是存在的

6.根据类型获取Bean

刚才我们提到,getBean()方法存在两种常用的重载形式,分别为:

getBean(String id)
getBean(Class<?> clazz)

在上面的例子中,我们使用的是第一种形式的getBean(String id)方法,该方法通过Bean的id获取Bean对象,下面我们来测试第二种方法:通过Bean的类型获取对象。

测试(只需要对上面代码稍微修改)

   //步骤1:在Junit Test Case 单元测试类IOCTest.java中,添加一个新的方法,test02()
    @Test
    public void test02()
    {
        //如果想要根据类型获取Bean,需要保证IOC容器中指定类型的Bean只有一个
        Book book = ioc.getBean(Book.class);
        System.out.println(book);
    }

同时,为了方便测试,我们将原先在test01()中定义的局部变量ApplicationContext ioc更改为一个私有的成员变量,以便被各个测试方法调用。

//步骤2:声明ApplicationContext局部变量
private ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");

运行该测试方法,结果如下:
这里写图片描述
至此,我们已经成功的通过Bean的类型获取Bean对象,且Junit没有抛出异常。

一个问题:在使用getBean(String id)方法获取Bean对象时,传入的参数为Bean的id,这个Id是惟一的,可以保证获取Bean的唯一性。但是,当使用getBean(Class<?> clazz)方法时,由于在SpringIOC配置文件中,同一个类型的实体,可以配置多个Bean,当这种情况发生的时候,会出现什么问题呢,下面我们进行测试。

//步骤:在beans.xml中添加如下配置,配置了Book类的另一个bean,id为book02。
    <!-- 实验2:根据Bean的类型从IOC容器中获取Bean的实例 -->
    <bean id="book02" class="com.atguigu.ioc.bean.Book">
        <property name="bookId" value="2" />
        <property name="bookName" value="bookName02"/>
        <property name="author" value="author02" />
        <property name="prive" value="200" />
    </bean>

重新运行test02()单元测试方法,结果如下:
这里写图片描述
这里写图片描述
可以发现,Junit抛出名为org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 的异常,该异常指出,Bean的配置不是惟一的。

因此可以得到结论:
- 如果想要根据类型获取Bean,需要保证IOC容器中指定类型的Bean只有一个。
- 该方法比较适合用于在一个工程中只存在一个实例的类

6.使用构造器创建对象

前面的例子中,beans.xml配置文件中的每一个bean都是通过设置property属性来确定对象的初始化值,这种方法实际上是调用了对象的无参构造器创建对象,并通过setXxx()方法给属性赋值,下面我们来学习一种新的创建对象的方法,该方法使用对象的有参构造器初始化对象,例子如下:

//步骤1:在beans.xml中添加新的配置
    <!-- 实验3:通过构造器为bean属性赋值 -->
    <bean id="book03" class="com.atguigu.ioc.bean.Book">
        <!-- 严格根据有参构造器的参数顺序创建 -->
        <constructor-arg value="3"/>
        <constructor-arg value="bookName3"/>
        <constructor-arg value="author03"/>
        <constructor-arg value="300"></constructor-arg>
    </bean>
//步骤2:在IOCTest类中添加新的单元测试方法test03()
    @Test
    public void test03()
    {
        Object bean = ioc.getBean("book03");
        System.out.println(bean);
    }

运行该单元测试方法,结果如下:
这里写图片描述
几个问题:
问题1:若bean按照上述格式进行配置,则每一个constructor-arg属性必须严格按照有参构造器的参数顺序赋值,否则会出现将作者姓名赋值给bookName的情况,所以通常需要采用如下改进型的配置:

    <!-- 实验3:通过构造器为bean属性赋值 -->
    <!-- 实验4:通过index属性指定参数位置-->
    <bean id="book03" class="com.atguigu.ioc.bean.Book">
        <!-- 根据有参构造器的参数顺序创建,同时使用index属性声明该constructor-arg是给构造器中
        第几个参数赋值 -->
        <constructor-arg value="3" index="0"/>
        <constructor-arg value="bookName3" index="1"/>
        <constructor-arg value="author03" index="2"/>
        <constructor-arg value="300" index="3"/>
    </bean>

问题2:如果出现重载的构造方法怎么办?

//步骤1:将Book.java重写,添加isbn属性(该属性目前没有get set方法),并创建重载的构造器
package com.atguigu.ioc.bean;

public class Book {

    private Integer bookId;
    private String bookName;
    private String author;
    private double prive;
    private String isbn;

    public Book() {
        System.out.println("Book对象被创建了");
    }


    public Book(Integer bookId, String bookName, String author, double prive) {
        super();
        this.bookId = bookId;
        this.bookName = bookName;
        this.author = author;
        this.prive = prive;
    }


    //重载构造器
    public Book(Integer bookId, String bookName, String author, String isbn) {
        super();
        this.bookId = bookId;
        this.bookName = bookName;
        this.author = author;
        this.isbn = isbn;
    }


    public Integer getBookId() {
        return bookId;
    }

    public void setBookId(Integer bookId) {
        this.bookId = bookId;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public double getPrive() {
        return prive;
    }

    public void setPrive(double prive) {
        this.prive = prive;
    }

    //重新toString()
    @Override
    public String toString() {
        return "Book [bookId=" + bookId + ", bookName=" + bookName + ", author=" + author + ", prive=" + prive
                + ", isbn=" + isbn + "]";
    }

}
//步骤2
    <!-- 实验3:通过构造器为bean属性赋值 -->
    <!-- 实验4:通过index属性指定参数位置-->
    <!-- 实验5:通过类型的不同指定重载的构造器 -->
    <bean id="book03" class="com.atguigu.ioc.bean.Book">
        <constructor-arg value="3" index="0"/>
        <constructor-arg value="bookName3" index="1"/>
        <constructor-arg value="author03" index="2"/>
        <!-- 使用type属性指定类型,可以为包装类型(Double)也可以为基本类型(double) -->
        <constructor-arg value="300" index="3" type="double"/>
    </bean>

运行test03(),结果如下:
这里写图片描述

更改type值为String,尝试使用重载的有参构造器创建对象:

    <bean id="book03" class="com.atguigu.ioc.bean.Book">
        <!-- 根据有参构造器的参数顺序创建 -->
        <constructor-arg value="3" index="0"/>
        <constructor-arg value="bookName3" index="1"/>
        <constructor-arg value="author03" index="2"/>
        <!-- 更改type值为String,尝试使用重载的有参构造器创建对象 -->
        <constructor-arg value="300" index="3" type="java.lang.String"/>
    </bean>

运行test03(),结果如下:
这里写图片描述
疑问:目前Book类中没有给新添加的isbn属性创建get set方法,但是测试发现依旧可以通过book03配置创建对象,我觉得应该是因为book03配置是通过重载的有参构造去创建对象的,没有使用set方法。在Book类中,声明了两个重载的有参构造方法,IOC容器通过判断constructor-arg属性的type来判断具体执行哪一个构造方法,既可创建对象。

7.通过p命名空间给属性赋值

在前面的例子中我们学习了两种给属性赋值的方法,分别为property方式和constructor-arg方式,接下来介绍另一种给属性赋值的方法,即通过p命名空间给属性赋值。

//步骤1:给beans.xml添加p名称空间
//步骤2:在beans.xml中添加如下配置
    <!-- 实验6:通过p名称空间为bean赋值 -->
    <bean id="book06" class="com.atguigu.ioc.bean.Book"
        p:bookId="6"
        p:bookName="bookName06"
        p:author="author06"
        p:prive="600"
        p:isbn="isbn06"
    />
//步骤3:给Book类的isbn属性添加get set方法(之前没有添加)。

在设置p名称空间时,同时按下alt + /可以看到,通过这种方法给属性赋值实际上是通过调用Book类的setXxx方法实现的,如下图所属。故每一个属性都需要定义set方法。
这里写图片描述

//步骤4:在IOCTest类中创建Junit Test case方法
    @Test
    public void test06()
    {
        Object bean = ioc.getBean("book06");
        System.out.println(bean);
    }

测试结果如下:
这里写图片描述
总结一下,目前共有三种方式给属性赋值,分别为:
1. property属性:通过setXxx()方法
2. constructor-arg属性:通过调用构造器
3. p名称空间:通过setXxx()方法

8.使用null值

测试null值的使用:

    <!-- 实验7:测试使用null值,在beans.xml中添加配置如下 -->
    <bean id="book07" class="com.atguigu.ioc.bean.Book">
        <property name="bookId" value="7" />
        <property name="bookName" value="bookName07"/>
        <property name="author">
            <null/>
        </property>
        <!-- 对于基本数据类型,此处不设置为默认值 -->
        <!-- <property name="prive" value="700" /> -->
        <!-- 对于引用数据类型,此处不设置也是null -->
        <!-- <property name="isbn">
            <null/>
        </property> -->
    </bean>
    //添加测试方法如下
    @Test
    public void test07()
    {
        Object bean = ioc.getBean("book07");
        System.out.println(bean);
    }

运行结果如下:
这里写图片描述

可以发现,对于基本数据类型来说,

  • 如果不显示设置属性值,IOC容器会将默认值赋给它。

对于引用数据类型来说

  • 如果不显示设置属性值或者使用了<null/>,IOC容器会将null值赋给它。

注意:不能给基本数据类型属性赋null值,否则会抛出org.springframework.beans.factory.BeanCreationException异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值