天天看点

Spring框架IOC详解

Spring框架学习01

spring框架的概述以及spring中基于XML的IOC配置

1. 什么是spring?

来自百度:

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。

轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。

控制反转——Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。

面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。

2.Spring的优势

1.方便解耦,简化开发

2.AOP编程支持

3.声明式事务的支持

4.方便程序的测试

5.方便集成各种优秀框架

6.降低JavaEE API的使用难度

这里一说可能大家都看不懂优点都是什么意思,不过没关系,这些优势我们会在后来的博客中一点一点总结深入,今天我们主要详细了解的就是IOC,所以AOP我们暂时不谈,先讲一讲为什么需要引入IOC。

Spring框架IOC详解

上图讲的就是Spring框架的结构体系,今天我们详细讲解的就是 Core Container部分,就是Spring的核心容器,IOC相关的内容,Spring任何的其他部分要运行,必须得有核心容器的支持。

我们先用一个案例来入门,了解一下Spring框架的其中一个核心内容IOC的作用

案例:

正常情况下我们连接数据库进行查询操作的代码如下:

package com.jack.jdbc;

import java.sql.*;

/**
 * @Author: Jack
 * @date: 2019/10/21 19:25
 */
public class JdbcDemo01 {
    public static void main(String[] args) throws SQLException {
        //1.注册驱动
        DriverManager.registerDriver(new com.mysql.jdbc.Driver());
        //2.获取连接
        Connection connection = DriverManager.getConnection("jdbc:mysql:///day23","root","root");
        //3.创建执行sql语句的statement对象
        PreparedStatement preparedStatement = connection.prepareStatement("select * from account");
        //4.执行sql语句,获得结果集
        ResultSet resultSet = preparedStatement.executeQuery();
        //5.遍历结果集
        while (resultSet.next()){
            System.out.println("   id:"+resultSet.getString(1));
            System.out.println("  uid:"+resultSet.getString(2));
            System.out.println("Money:"+resultSet.getString(3));
        }
        //6.释放资源
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }
}

           

代码写完了我们可以从代码中看出

没有注册驱动,就不能获得连接,没有获得连接就不能创建PreparedStatement ,不能创建PreparedStatement 就不能执行sql语句。。。一环套一环,缺一不可。

所以一个功能的实现都是各种类和方法之间的依赖,这就叫做耦合性,分为类之间依赖和方法间依赖。

解耦:降低程序间的依赖关系

在实际开发中,真正的做到没有依赖完全独立不太可能,所以实际开放中的解耦就是要做到

编译期间不依赖,运行时才依赖

解耦的思路:

第一步:使用反射来创建对象,而不是使用new关键字。

//1.注册驱动
        DriverManager.registerDriver(new com.mysql.jdbc.Driver());
        Class.forName("com.mysql.jdbc.Driver");
           

这样做的好处就是,我们注册驱动的时候不再是需要一个具体的类,而是只要有一段正确的描述类名的字符串就行了。

但是也存在他的问题,我们注册驱动的时候,使用反射就已经把要注册的驱动类型写死,以后在使用其他数据库的时候,会相当的麻烦。

解决方案:使用读取配置文件来获取 要创建的对象的全限定类名

在我们平时做服务端开发的时候,我们一般都使用的是三层架构,也就是界面层,业务逻辑层,数据访问层三层,一般处理的业务逻辑就是界面层调用业务逻辑层,业务逻辑层调用数据访问层这样的逻辑,具有很强的耦合性,三层之间缺一不可,我们使用spring框架的核心内容IOC就是解决了这三层之间的耦合性的问题。

IOC控制反转

inversion of control,把对象创建的权力叫给框架,是框架的重要特征,大幅度降低了耦合。注意不是完全的消除耦合。今天我们主要介绍的就是spring的ioc。

入门案例:

1.导入jar包:

因为我使用的是maven项目,这里我就把坐标贴一下

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
           
Spring框架IOC详解

2.创建一个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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    
</beans>
           

3.把对象的创建交给spring来管理

<bean id="accountService" class="com.jack.service.imp.AccountServiceImpl"/>

<bean id="accountDao" class="com.jack.dao.impl.AccountDaoImpl"/>
           

4.代码测试一下

//获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //根据id获取bean对象
        AccountService accountService = (AccountService) ac.getBean("accountService");
        AccountDao accountDao = ac.getBean("accountDao",AccountDao.class);

        System.out.println(accountService);
        System.out.println(accountDao);
           
Spring框架IOC详解

根据运行结果我们可以发现,在操作过程中,我们并没有使用new关键字实例化任何一个对象,但是在打印结果中我们可以看到,两个对象都被实例化了,这就是springIOC的基本操作。

配置文件 -> 解析配置文件获得全类名 -> 反射创建对象。

符合我们通过解析配置文件,获取要创建对象的全限定类名,通过反射创建对象的降低耦合度的思路

所以IOC的就是一个容器,我们把需要用到的类全部都放在ioc中,在spring中登记,在适当的时候,spring会把你需要的类给你,并且同时也在适当的时候,把你交给其他需要你的类,一个类的生命周期不再决定于引用他的对象,而是全部交spring框架管理,对于对象来讲,以前是对象控制其他的对象,但是现在,是spring控制所有的对象,这就叫做控制反转。

Spring框架IOC详解

IOC的一些其他细节

—细节1

核心容器的两个接口:

1.ApplicationContext(单例对象适用)

构建核心容器时,创建对象采取的策略是立即加载的方式,也就是说,一读取完配置文件,就立刻创建配置文件中注册的对象。

2.BeanFactory(多例对象适用)

在构建核心容器的时,创建对象采取的是延迟加载的策略,什么时候根据id获取对象的时候,才真正的创建对象。

但是我们思考一个问题,ApplicationContext在我们配置文件读取的时候就把对象创建了,但是如果我们要第二次使用某个对象,肯定要再次的调用创建对象的方法,这样看来,我们还不如不着急的创建对象,干脆等到什么时候用,什么时候创建对象。所以根据应用场景来看,ApplicationContext和BeanFactory都有使用的必要,不过在实际情况中,spring框架具有强大的功能,非常智能,可以根据我们配置的不同,去改变创建对象的策略,所以两个接口创建对象的策略显得不是那么的重要,但是因为BeanFactory是一个顶级的接口,功能不够完善,所以在日常的开发中,多数情况下还是选择使用ApplicationContext接口来定义容器对象。

—细节2

创建Bean对象的方式(三种):

1.使用目标类中的默认构造函数来创建对象:

采用的是目标类中的构造方法来创建对象,如果目标类中没有默认的构造方法(空参构造方法),则无法创建对象。

2.使用某个类中的方法来创建目标类对象

某个类(工厂类)

public class ServiceFactory {
    public AccountService getAccountService(){
        return new AccountServiceImpl();
    }
}
           

spring配置文件:

<bean id="factory" class="com.jack.Utils.ServiceFactory"/>
    <bean id="accountService" factory-bean="factory" factory-method="getAccountService"/>
           

首先在配置文件中注册工厂类,也就是说实例化了工厂类,然后再注册要实例化的目标类(AccountService ),factory-bean指定哪个类中的方法可以创建目标对象,factory-method指定工厂类中的哪个方法来创建对象。

3.使用工厂类中的静态方法来创建目标对象

工厂类和静态方法

public class StaticFactory {
    public static AccountService getAccountService(){
        return new AccountServiceImpl();
    }
}
           

spring配置文件

—细节3

bean的作用范围

这个就非常简单了,只要设置一下scope标签就好了,我们来挨个说。

1.singleton:顾名思义,就是设置spring创建对象为单例的。

spring配置文件:

测试类:

//获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        AccountService accountService1 = ac.getBean("accountService", AccountService.class);
        AccountService accountService2 = ac.getBean("accountService", AccountService.class);
        System.out.println(accountService1 == accountService2);
           

执行结果如下:

Spring框架IOC详解

可以看到AccountService的构造函数仅执行了一次,并且两个AccountService对象的比较打印结果为true

2.prototype:多例的

执行结果如下:

Spring框架IOC详解

AccountService的构造函数执行了两次,也就是说,AccountService创建了两次。

3.request

作用域为 web应用的请求范围

4.session

作用域为 web应用的会话范围

5.global-session

作用域为集群web应用的会话范围

—细节4

bean对象的生命周期

分为单例对象和多例对象

单例对象的生命周期为:

出生:容器创建时,对象创建

生存:只要容器存在,对象一直存在

死亡:容器销毁时,对象死亡

单例对象和容器的生命周期相同,同生共死。

多例对象的生命周期:

出生:使用对象时,spring才为我们创建对象

生存:对象只要是在使用过程中就一直生存。

死亡:当对象长时间不用,并且没有别的对象引用的时候,java的垃圾回收机制把对象消亡。

继续阅读