目录

Spring系统架构图

核心概念

IOC、IOC容器、Bean、DI

IOC实现

DI实现

setter注入:引用类型

setter注入:简单类型

构造器注入:引用类型

构造器注入:引用类型

配置重点applicationContext.xml

bean实例化

构造方法实例化

静态工厂实例化

实例工厂与FactoryBean

bean生命周期

1.添加初始化和销毁方法

2. 配置生命周期

3.close关闭容器

4.注册钩子关闭容器

5.生命周期

依赖自动装配

集合注入


Spring系统架构图

Spring Framework是Spring生态圈中最基础的项目,是其他项目的根基。
Spring Framework的发展也经历了很多版本的变更,每个版本都有相应的调整

spring项目结构 spring项目架构图_spring

spring项目结构 spring项目架构图_junit_02

(1)核心层
        Core Container:核心容器,这个模块是Spring最核心的模块,其他的都需要依赖该模块
(2)AOP层
        AOP:面向切面编程,它依赖核心层容器,目的是在不改变原有代码的前提下对其进行功能增强
        Aspects:AOP是思想,Aspects是对AOP思想的具体实现
(3)数据层
        Data Access:数据访问,Spring全家桶中有对数据访问的具体实现技术
        Data Integration:数据集成,Spring支持整合其他的数据层解决方案,比如Mybatis
        Transactions:事务,Spring中事务管理是Spring AOP的一个具体实现,也是后期学习的重点内容
(4)Web层
        这一层的内容将在SpringMVC框架具体学习
(5)Test层
        Spring主要整合了Junit来完成单元测试和集成测试

核心概念

IOC、IOC容器、Bean、DI

 1. IOC(Inversion of Control)控制反转
        使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转。

        Spring提供了一个容器,称为IOC容器,用来充当IOC思想中的"外部"
        IOC容器负责对象的创建、初始化等一系列工作,其中包含了数据层和业务层的类对象
        被创建或被管理的对象在IOC容器中统称为Bean

        IOC容器中放的就是一个个的Bean对象
        像这种在容器中建立对象与对象之间的绑定关系就要用到DI:
2. DI(Dependency Injection)依赖注入

        在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注

3.使用IOC容器管理的对象就是bean(IOC)

        在IOC容器内将有依赖关系的bean进行关系绑定(DI)
        最终结果为:使用对象时不仅可以直接从IOC容器中获取,并且获取到的bean已经绑定了所有的依赖关系

spring项目结构 spring项目架构图_spring_03

IOC实现

1.Spring的依赖jar包

<dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

2.获取ioc容器

ApplicationContext的继承体系
applicationContext:

接口类型,代表应用上下文,可以通过其实例获得 Spring 容器中的 Bean 对象

spring项目结构 spring项目架构图_maven_04

 ApplicationContext的实现类

1) ClassPathXmlApplicationContext
它是从类的根路径下加载配置文件 推荐使用这种

ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

2) FileSystemXmlApplicationContext
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。

FileSystemXmlApplicationContext fileSystemXmlApplicationContext = new FileSystemXmlApplicationContext("C:\\Users\\ChenQi\\Desktop\\Spring\\ioc\\src\\main\\resources\\applicationContext-product.xml");

3) AnnotationConfigApplicationContext
当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。

spring项目结构 spring项目架构图_junit_05

BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载
ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载
ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能
ApplicationContext接口常用初始化类

3.获取bean,getBean()方法使用

public Object getBean(String name) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(name);
}

public <T> T getBean(Class<T> requiredType) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(requiredType);
}
UserService userService =(UserService) classPathXmlApplicationContext.getBean("userService");

UserService bean = classPathXmlApplicationContext.getBean(UserService.class);

getBean有两个实现方法

        当参数的数据类型是字符串时,表示根据Bean的id从容器中获得Bean实例,返回是Object,需要强转。

        当参数的数据类型是Class类型时,表示根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时,则此方法会报错。

DI实现

传递数据有两种方式:1. 普通方法(set方法)   2.构造方法

spring项目结构 spring项目架构图_spring项目结构_06

 

 

setter注入
        简单类型
        引用类型
构造器注入
        简单类型
        引用类型

setter注入:引用类型

<!--name:为bean指定别名,别名可以有多个,使用逗号,分号,空格进行分隔-->
    <bean id="bookService" name="service service4 bookEbi" class="com.itheima.service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao"/>
    </bean>

    <!--scope:为bean设置作用范围,可选值为单例singloton,非单例prototype-->
    <bean id="bookDao" name="dao" class="com.itheima.dao.impl.BookDaoImpl" scope="prototype"/>
public class BookServiceImpl implements BookService {
    private BookDao bookDao;

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

setter注入:简单类型

<!--注入简单类型-->
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
        <!--property标签:设置注入属性-->
        <!--name属性:设置注入的属性名,实际是set方法对应的名称-->
        <!--value属性:设置注入简单类型数据值-->
        <property name="connectionNum" value="100"/>
        <property name="databaseName" value="mysql"/>
    </bean>
public class BookDaoImpl implements BookDao {

    private String databaseName;
    private int connectionNum;
    //setter注入需要提供要注入对象的set方法
    public void setConnectionNum(int connectionNum) {
        this.connectionNum = connectionNum;
    }
    //setter注入需要提供要注入对象的set方法
    public void setDatabaseName(String databaseName) {
        this.databaseName = databaseName;
    }

    public void save() {
        System.out.println("book dao save ..."+databaseName+","+connectionNum);
    }
}

构造器注入:引用类型

<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
    <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>

    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
        <constructor-arg name="userDao" ref="userDao"/>
        <constructor-arg name="bookDao" ref="bookDao"/>
    </bean>
public class BookServiceImpl implements BookService{
    private BookDao bookDao;
    private UserDao userDao;

    public BookServiceImpl(BookDao bookDao, UserDao userDao) {
        this.bookDao = bookDao;
        this.userDao = userDao;
    }

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
        userDao.save();
    }
}

构造器注入:引用类型

<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
        根据构造方法参数名称注入
        <constructor-arg name="connectionNum" value="10"/>
        <constructor-arg name="databaseName" value="mysql"/>
    </bean>
public class BookDaoImpl implements BookDao {
    private String databaseName;
    private int connectionNum;

    public BookDaoImpl(String databaseName, int connectionNum) {
        this.databaseName = databaseName;
        this.connectionNum = connectionNum;
    }

    public void save() {
        System.out.println("book dao save ..."+databaseName+","+connectionNum);
    }
}
解决形参名称的问题,与形参名不耦合
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
        根据构造方法参数类型注入
        <constructor-arg type="int" value="10"/>
        <constructor-arg type="java.lang.String" value="mysql"/>
    </bean>
    <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>

    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
        <constructor-arg name="userDao" ref="userDao"/>
        <constructor-arg name="bookDao" ref="bookDao"/>
  
    
       解决参数类型重复问题,使用位置解决参数匹配
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
        根据构造方法参数位置注入
        <constructor-arg index="0" value="mysql"/>
        <constructor-arg index="1" value="100"/>
    </bean>
    <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>

    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
        <constructor-arg name="userDao" ref="userDao"/>
        <constructor-arg name="bookDao" ref="bookDao"/>
    </bean>

 自己开发的模块推荐使用setter注入

配置重点applicationContext.xml

spring项目结构 spring项目架构图_junit_07

spring项目结构 spring项目架构图_spring_08

spring项目结构 spring项目架构图_System_09

spring项目结构 spring项目架构图_System_10

bean实例化

实例化bean的三种方式,构造方法、静态工厂、实例工厂 

构造方法实例化

public class BookDaoImpl implements BookDao {

    public BookDaoImpl() {
        System.out.println("book dao constructor is running ....");
    }

    public void save() {
        System.out.println("book dao save ...");
    }

}

spring项目结构 spring项目架构图_spring项目结构_11

显而易见Spring底层用的是反射

静态工厂实例化

在spring的配置文件application.properties中添加以下内容:

<bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factorymethod="getOrderDao"/>

spring项目结构 spring项目架构图_System_12

实例工厂与FactoryBean

<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>

spring项目结构 spring项目架构图_spring_13

 FactoryBean的使用

(1)创建一个UserDaoFactoryBean的类,实现FactoryBean接口,重写接口的方法 

import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
import org.springframework.beans.factory.FactoryBean;
//FactoryBean创建对象
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
    //代替原始实例工厂中创建对象的方法
    public UserDao getObject() throws Exception {
        return new UserDaoImpl();
    }

    public Class<?> getObjectType() {
        return UserDao.class;
    }

}

 (2)在Spring的配置文件中进行配置

<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>

spring项目结构 spring项目架构图_spring_14

bean生命周期

1.添加初始化和销毁方法

public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
    //表示bean初始化对应的操作
    public void init(){
        System.out.println("init...");
    }
    //表示bean销毁前对应的操作
    public void destory(){
        System.out.println("destory...");
    }

}

2. 配置生命周期

<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init"
destroy-method="destory"/>

spring项目结构 spring项目架构图_maven_15

        Spring的IOC容器是运行在JVM中
        运行main方法后,JVM启动,Spring加载配置文件生成IOC容器,从容器获取bean对象,然后调方
法执行main方法执行完后,JVM退出,这个时候IOC容器中的bean还没有来得及销毁就已经结束了所以没有来得及调用对应的destroy方法

3.close关闭容器
 

ApplicationContext中没有close方法

需要将ApplicationContext更换成ClassPathXmlApplicationContext

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

ctx.close();

spring项目结构 spring项目架构图_spring_16

4.注册钩子关闭容器

        以上方法过于暴力,所以推出另外一种 设置关闭钩子,在容器未关闭之前,提前设置好回调函数,让JVM在退出之前回调此函数来关闭容器

ctx.registerShutdownHook();

 注意:registerShutdownHook在ApplicationContext中也没有

 

spring项目结构 spring项目架构图_System_17

        不同点:close()是在调用的时候关闭,registerShutdownHook()是在JVM退出前调用关闭
 

         分析上面的实现过程,会发现添加初始化和销毁方法,即需要编码也需要配置,实现起来步骤比较多也比较乱。
        Spring提供了两个接口来完成生命周期的控制,好处是可以不用再进行配置init-method和
destroy-method

5.生命周期

初始化容器
        1.创建对象(内存分配)
        2.执行构造方法
        3.执行属性注入(set操作)
        4.执行bean初始化方法
使用bean
        1.执行业务操作
关闭/销毁容器
        1.执行bean销毁方法

public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
    private BookDao bookDao;

    public void setBookDao(BookDao bookDao) {
        System.out.println("set .....");
        this.bookDao = bookDao;
    }

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }

    public void destroy() throws Exception {
        System.out.println("service destroy");
    }

    public void afterPropertiesSet() throws Exception {
        System.out.println("service init");
    }
}

依赖自动装配

        IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配

<bean class="com.itheima.dao.impl.BookDaoImpl"/>
    <!--autowire属性:开启自动装配,通常使用按类型装配-->
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>

1. 自动装配用于引用类型依赖注入,不能对简单类型进行操作
2. 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
3. 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
4. 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效

集合注入

        环境准备项目中添加添加集合

public class BookDaoImpl implements BookDao {

    private int[] array;

    private List<String> list;

    private Set<String> set;

    private Map<String,String> map;

    private Properties properties;




    public void setArray(int[] array) {
        this.array = array;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }




    public void save() {
        System.out.println("book dao save ...");

        System.out.println("遍历数组:" + Arrays.toString(array));

        System.out.println("遍历List" + list);

        System.out.println("遍历Set" + set);

        System.out.println("遍历Map" + map);

        System.out.println("遍历Properties" + properties);
    }
}

        注入 

<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
        <!--数组注入-->
        <property name="array">
            <array>
                <value>100</value>
                <value>200</value>
                <value>300</value>
            </array>
        </property>
        <!--list集合注入-->
        <property name="list">
            <list>
                <value>itcast</value>
                <value>itheima</value>
                <value>boxuegu</value>
                <value>chuanzhihui</value>
            </list>
        </property>
        <!--set集合注入-->
        <property name="set">
            <set>
                <value>itcast</value>
                <value>itheima</value>
                <value>boxuegu</value>
                <value>boxuegu</value>
            </set>
        </property>
        <!--map集合注入-->
        <property name="map">
            <map>
                <entry key="country" value="china"/>
                <entry key="province" value="henan"/>
                <entry key="city" value="kaifeng"/>
            </map>
        </property>
        <!--Properties注入-->
        <property name="properties">
            <props>
                <prop key="country">china</prop>
                <prop key="province">henan</prop>
                <prop key="city">kaifeng</prop>
            </props>
        </property>
    </bean>

spring项目结构 spring项目架构图_spring_18