【Java中级】25.0 SSM之Spring框架(一)——Spring框架思想解析及入门

1.0 这里说的SSM,指的是

(Spring+SpringMVC+MyBatis)框架集,由Spring、MyBatis两个开源框架整合而成(SpringMVC是Spring中的部分内容)。

2.0 Sring学习内容
    1. spring框架的概述以及spring中基于XML的IOC配置
    1. spring中基于注解的IOC和ioc的案例
    1. spring中的aop和基于XML以及注解的AOP配置
    1. spring中的JdbcTemlate以及Spring事务控制

Spring学习使用的是IntelliJ IDEA。我用的是当下最新版,官网下的。激活方式很多,自行百度。

3.0 spring是什么

Spring 是分层的 Java SE/EE 应用 full-stack(全栈级别) 轻量级开源框架,以 IoC(Inverse Of Control:反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。

4.0 spring的两大核心
image.png
5.0 spring的发展历程和优势

这一节主要就是些废话。

5.1 发展历程
1997 年 IBM 提出了 EJB 的思想(23年前)
1998 年,SUN 制定开发标准规范 EJB1.0
1999 年,EJB1.1 发布
2001 年,EJB2.0 发布
2003 年,EJB2.1 发布
2006 年,EJB3.0 发布
Rod Johnson(spring 之父) 
Expert One-to-One J2EE Design and Development(2002)
阐述了 J2EE 使用 EJB 开发设计的优点及解决方案
Expert One-to-One J2EE Development without EJB(2004)
阐述了 J2EE 开发不使用 EJB 的解决方式(Spring 雏形)
 2017 年 9 月份发布了 spring 的最新版本 spring 5.0 通用版(GA)
5.2 spring 的优势
<方便解耦,简化开发>
通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
<  AOP编程的支持>
通过 Spring 的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付。
<声明式事务的支持>
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。
<方便程序的测试>
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
<方便集成各种优秀框架>
Spring 可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。
<降低 JavaEE API 的使用难度>
Spring 对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的使用难度大为降低。
< Java 源码是经典学习范例 >
Spring 的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对 Java 设计模式灵活运用以及对 Java 技术的高深造诣。它的源代码无意是 Java 技术的最佳实践的范例。
6.0 spring体系结构
image.png

所有的其他部分都离不开核心容器,所有必须先学懂容器,才能说会使用Spring。

7.0 新建一个项目

打开IntelliJ IDEA,创建一个新的Project:


image.png

我已经创建过了,所有提示我不能创建同一个项目名称。


image.png

等待一会,创建完毕。
image.png

新建一个数据库。即使是示范代码也是操作真实数据库。


image.png

先看一个简单的实现。
image.png

在pom.xml中配置一下mysql的驱动:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.edp</groupId>
    <artifactId>spring_study</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <!--打包方式选择jar-->

    <dependencies>
        <!--设置mysql依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.18</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
</project>

启动类就一个jdbc操作:

package com.edp.jdbc;

import java.sql.*;

/**
 * 程序的耦合
 */
public class JdbcDemo1 {
    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        //1.注册驱动
        //DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.获取连接
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/eesy?useSSL=false&serverTimezone=UTC", "root", "bywwcnll");
        //3.获取操作数据库的预处理对象
        PreparedStatement pstm = connection.prepareStatement("select  * from  account");
        //4.执行SQL,得到结果集
        ResultSet rs = pstm.executeQuery();
        //5.遍历结果集
        while (rs.next()) {
            System.out.println(rs.getString("name"));
        }
        //6.释放资源,先开的后关
        rs.close();
        pstm.close();
        connection.close();
    }
}

运行:

image.png

但是,如果我们把mysql的依赖注释掉,会发现,程序无法运行,报编译错误。
这里涉及到的一个重点——耦合

8.0 程序的耦合及解耦

官方意思超级长,如下:

耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调
用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关
系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立
性)。耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。
在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计
应使类和构件之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个
准则就是高内聚低耦合。
它有如下分类:
(1)内容耦合。当一个模块直接修改或操作另一个模块的数据时,或一个模块不通过正常入口而转入另
一个模块时,这样的耦合被称为内容耦合。内容耦合是最高程度的耦合,应该避免使用之。
(2)公共耦合。两个或两个以上的模块共同引用一个全局数据项,这种耦合被称为公共耦合。在具有大
量公共耦合的结构中,确定究竟是哪个模块给全局变量赋了一个特定的值是十分困难的。
(3) 外部耦合 。一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传
递该全局变量的信息,则称之为外部耦合。
(4) 控制耦合 。一个模块通过接口向另一个模块传递一个控制信号,接受信号的模块根据信号值而进
行适当的动作,这种耦合被称为控制耦合。
(5)标记耦合 。若一个模块 A 通过接口向两个模块 B 和 C 传递一个公共参数,那么称模块 B 和 C 之间
存在一个标记耦合。
(6) 数据耦合。模块之间通过参数来传递数据,那么被称为数据耦合。数据耦合是最低的一种耦合形
式,系统中一般都存在这种类型的耦合,因为为了完成一些有意义的功能,往往需要将某些模块的输出数据作为另
一些模块的输入数据。
(7) 非直接耦合 。两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实
现的。
总结:
耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须
存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。
内聚与耦合
内聚标志一个模块内各个元素彼此结合的紧密程度,它是信息隐蔽和局部化概念的自然扩展。内聚是从
功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。耦合是软件
结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通
过接口的数据。 程序讲究的是低耦合,高内聚。就是同一个模块内的各个元素之间要高度紧密,但是各个模块之
间的相互依存度却要不那么紧密。
内聚和耦合是密切相关的,同其他模块存在高耦合的模块意味着低内聚,而高内聚的模块意味着该模块同其他
模块之间是低耦合。在进行软件设计时,应力争做到高内聚,低耦合

现在主要考虑类之间的依赖问题(还有方法间的依赖,)

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

实际开发中,应该做到:
编译器不依赖,运行时才依赖。
我们在开发中,有些依赖关系是必须的,有些依赖关系可以通过优化代码来解除的。

解耦的思路:

    1. 使用反射来创建对象,而避免使用new关键字。
    1. 读取配置文件读取要创建的对象全限定类名。
8.2 曾经案例中问题

以下案例是一种反射加注解的方式搭建的工厂模式,模拟有web层,业务层,和持久层(Dao层),并创建一个工厂类来创建新的业务层和dao层的类:


image.png

ui包下是模拟web层的意思。
AccountDaoImpl.java

package com.edp.dao.impl;

import com.edp.dao.IAccountDao;
import org.omg.Messaging.SyncScopeHelper;

/**
 * @author EdPeng
 * @Title:
 * @Package
 * @Description: 账户持久层实现类
 * @date 2020/2/23下午 5:45
 */
public class AccountDaoImpl implements IAccountDao {
    @Override
    public void saveAccount() {
        System.out.println("保存了账户。");
    }
}

AccountDao.java

package com.edp.dao;

/**
 * @author EdPeng
 * @Title:
 * @Package
 * @Description: 账户持久层接口
 * @date 2020/2/23下午 5:43
 */
public interface IAccountDao {
    /**
     * @Description: 模拟保存账户
     * @author EdPeng
     * @date 2020/2/23 下午 5:44
     */
    void saveAccount();
}

IAccountServiceImpl .java

package com.edp.service.impl;/**
 * @Title:
 * @Package
 * @Description:
 * @author EdPeng
 * @date 2020/2/23下午 5:40
 */

import com.edp.dao.IAccountDao;
import com.edp.dao.impl.AccountDaoImpl;
import com.edp.service.IAccountService;
/**
 * @Description: 账户业务层实现类
 * @author EdPeng
 * @date 2020/2/23 下午 6:33
 */
public class IAccountServiceImpl implements IAccountService {
    private IAccountDao accountDao = new AccountDaoImpl();

    @Override
    public void saveAccount() {
        accountDao.saveAccount();
    }
}

IAccountService .java

package com.edp.service;

/**
 * 账户业务层的接口
 */

public interface IAccountService {
    /**
     * @param
     * @return
     * @throws
     * @Description: 模拟账户保存
     * @author EdPeng
     * @date 2020/2/23 下午 5:38
     */
    void saveAccount();
}

Client.java

package com.edp.ui;

import com.edp.service.IAccountService;
import com.edp.service.impl.IAccountServiceImpl;

/**
 * @author EdPeng
 * @Title:
 * @Package
 * @Description: 模拟一个表现层,用于调用业务层
 * @date 2020/2/23下午 5:48
 */
public class Client {
    public static void main(String[] args) {
        IAccountService accountService = new IAccountServiceImpl();
        accountService.saveAccount();
    }
}

重点来了,配置一个properties文件,bean.properties

accountService =com.edp.service.impl.IAccountServiceImpl
accountDao =com.edp.dao.impl.AccountDaoImpl

在工厂类,通过反射,实现一定程度的解耦。
BeanFactory.java

package com.edp.factory;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * @author EdPeng
 * @Title:
 * @Package
 * @Description: 一个创建Bean对象的工厂
 * JavaBean  >>  实体类
 * @date 2020/2/23下午 5:51
 * 一个javabean类,可重用组件
 * 创建我们的service和dao对象
 * 第一个:需要一个配置文件来配置文明的service和dao
 * 配置内容:唯一标识=全限定类名(key=value)
 * 第二个:通过读取配置文件中的配置的内容,来反射创建内容。
 * <p>
 * 配置文件有两种方式:xml和 properties,这里先采用properties,
 */
public class BeanFactory {
    //定义一个Properties对象
    private static Properties props;

    //使用静态代码块为Properties对象赋值
    static {
        try {
            props = new Properties();
            //获取properties文件的流对象
            //getClassLoader:类加载器
            InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            //load:读取一个流
            props.load(inputStream);
        } catch (IOException e) {
            throw new ExceptionInInitializerError("初始化properties失败");
        }
    }

    /**
     * @param
     * @return
     * @throws
     * @Description: 根据Bean的名称获取bean对象
     * @author EdPeng
     * @date 2020/2/23 下午 6:06
     */
    public static Object getBean(String beanName) {
        Object bean = null;
        try {
            String beanPath = props.getProperty((beanName));
            //通过反射来创建对象
            bean = Class.forName(beanPath).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }
}

注意,到现在为止,我们还没有开始编写Spring的代码,只是进行编程原理和思想分析。

8.3下面开始进一步解耦合:

现在,service中依赖了一个具体的实现类,Dao层也依赖了一个具体的实现类。UI中依赖了一个具体的service。
既然已经配置好了 工厂类,那么我们可以先把Client进行解耦。

package com.edp.ui;

import com.edp.factory.BeanFactory;
import com.edp.service.IAccountService;
import com.edp.service.impl.IAccountServiceImpl;

/**
 * @author EdPeng
 * @Title:
 * @Package
 * @Description: 模拟一个表现层,用于调用业务层
 * @date 2020/2/23下午 5:48
 */
public class Client {
    public static void main(String[] args) {
//        IAccountService accountService = new IAccountServiceImpl();
        IAccountService accountService = (IAccountService) BeanFactory.getBean("accountService");
        accountService.saveAccount();
    }
}

同理,
这时候,IAccountServiceImpl 可以修改为:

public class IAccountServiceImpl implements IAccountService {
    //    private IAccountDao accountDao = new AccountDaoImpl();
    private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");

    @Override
    public void saveAccount() {
        accountDao.saveAccount();

    }
}

运行:


image.png

如果我们把IAccountDaoImpl.java 删除,编译是不会报错的,只有运行的时候才会报错。

8.4 针对工厂模式的问题进行升级改造
8.5 针对解耦再进行升级改造

假设,我们修改ui的Client类。

public class Client {
    public static void main(String[] args) {
//        IAccountService accountService = new IAccountServiceImpl();
        for (int i = 0;i<5;i++){
            IAccountService accountService = (IAccountService) BeanFactory.getBean("accountService");
            System.out.println(accountService);
            accountService.saveAccount();
        }

    }
}

这就存在一个问题,单例模式和多例模式。当我们for语句执行几次之后,程序在多里模式下,会new很多新对象。

解决方法:定义一个容器,存放第一次创建的对象,反复使用,减少内存消耗。整体思路就是,原本由APP(应用程序)控制资源的创建和使用,改为由工厂来管理资源,APP向工厂申请资源使用,通过单例模式实现降低内存和性能消耗。
首先在工厂类中定义一个容器:

 //*****定义一个Map,用于存放我们要创建的对象,称之为容器
    private static Map<String, Object> beans;

然后对其进行赋值:


            //*****实例化容器
            beans = new HashMap<String, Object>();
            //*****通过枚举,取出配置文件中所有的key
            Enumeration<Object> keys = props.keys();
            //*****遍历枚举
            while (keys.hasMoreElements()) {
                String key = keys.nextElement().toString();
                //*****根据key获取valve
                String beanPath = props.getProperty(key);
                //*****反射创建对象
                Object value = Class.forName(beanPath).newInstance();
                //*****把key和value存入容器中
                beans.put(key, value);

再设置一个get方法调用即完成单例模式:

 /**
     * *****
     * @Description: 根据Bean的名称获取bean对象:改造(变成单例模式)
     * @author EdPeng
     * @date 2020/2/24 下午 11:23
     */
    public static Object getBean(String beanName) {
        return beans.get(beanName);
    }

BeanFactory .java完整代码:

package com.edp.factory;

import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * @author EdPeng
 * @Title:
 * @Package
 * @Description: 一个创建Bean对象的工厂
 * JavaBean  >>  实体类
 * @date 2020/2/23下午 5:51
 * 创建文明的service和dao对象
 * 第一个:需要一个配置文件来配置文明的service和dao
 * 配置内容:唯一标识=全限定类名(key=value)
 * 第二个:通过读取配置文件中的配置的内容,来反射创建内容。
 * <p>
 * 配置文件有两种方式:xml和 properties
 */
public class BeanFactory {
    //定义一个Properties对象
    private static Properties props;

    //*****定义一个Map,用于存放我们要创建的对象,称之为容器
    private static Map<String, Object> beans;

    //使用静态代码块为Properties对象赋值
    static {
        try {
            props = new Properties();
            //获取properties文件的流对象
            //getClassLoader:类加载器
            InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            //load:读取一个流
            props.load(inputStream);

            //*****实例化容器
            beans = new HashMap<String, Object>();
            //*****通过枚举,取出配置文件中所有的key
            Enumeration<Object> keys = props.keys();
            //*****遍历枚举
            while (keys.hasMoreElements()) {
                String key = keys.nextElement().toString();
                //*****根据key获取valve
                String beanPath = props.getProperty(key);
                //*****反射创建对象
                Object value = Class.forName(beanPath).newInstance();
                //*****把key和value存入容器中
                beans.put(key, value);
            }
        } catch (Exception e) {
            throw new ExceptionInInitializerError("初始化properties失败");
        }
    }

    /**
     * *****
     * @Description: 根据Bean的名称获取bean对象:改造(变成单例模式)
     * @author EdPeng
     * @date 2020/2/24 下午 11:23
     */
    public static Object getBean(String beanName) {
        return beans.get(beanName);
    }
    /**
     * @param
     * @return
     * @throws
     * @Description: 根据Bean的名称获取bean对象
     * @author EdPeng
     * @date 2020/2/23 下午 6:06

    public static Object getBean(String beanName) {
    Object bean = null;
    try {
    String beanPath = props.getProperty((beanName));
    //通过反射来创建对象
    bean = Class.forName(beanPath).newInstance();
    } catch (Exception e) {
    e.printStackTrace();
    }
    return bean;
    }*/
}

从代码中可以看到Spring中一种先进的思想:即被动接受的方式获得对象,这就叫控制反转(IOC),它是Spring框架的核心之一。包括依赖注入和依赖查找。IOC的作用就是:削减计算机程序的耦合(解除代码中的依赖关系。)

最后,修改业务层调用即可。

public class IAccountServiceImpl implements IAccountService {
    //放弃自主寻找Dao的权利,把权利交给工厂类
    //    private IAccountDao accountDao = new AccountDaoImpl();
    private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");
    private int i = 1;

    @Override
    public void saveAccount() {
        accountDao.saveAccount();
        System.out.println(i);
        i++;
    }
}

运行:


image.png
9.0 使用spring前,下载jar包并做介绍。

首先,下载Sping的开发包,目前Spring的官网下载东西很难下,所以这里就不留链接了,自行百度。
目前我用的是这个版本(spring-framework-5.0.2.RELEASE-dist.zip):

image.png

解压缩:
image.png

我的一般习惯:
image.png

打开,在浏览器中收藏:
image.png

或者采用谷歌浏览器,如下就是它为什么是全球最火的浏览器的原因之一:


image.png

当然,使用Spring目的就是取代上面案例中的BeanFactory工厂类。

10.0 spring中基于XML的IOC环境搭建

看图说话(IDEA软件):


image.png

image.png

image.png

首先修改pom.xml文件,设置打包格式为jar包:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.edp</groupId>
    <artifactId>first_project_spring</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

</project>

把上面案例中的代码,从com目录开始拷贝到新项目下:


image.png

修改基本结构和代码:


image.png

Client.java
public class Client {
    public static void main(String[] args) {
        IAccountService accountService = new IAccountServiceImpl();
        accountService.saveAccount();
    }
}

IAccountServiceImpl .java

public class IAccountServiceImpl implements IAccountService {
    private IAccountDao accountDao = new AccountDaoImpl();

    public void saveAccount() {
        accountDao.saveAccount();

    }
}

AccountDaoImpl .java

public class AccountDaoImpl implements IAccountDao {
    public void saveAccount() {
        System.out.println("保存了账户。");
    }
}

当前,整个项目是不报错的。设置Maven配置:


image.png

配置Spring的配置,修改pom.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.edp</groupId>
    <artifactId>first_project_spring</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
    </dependencies>
</project>

稍等一会,包就导入了(左下角有个import package的话记得点击一下),maven为我们自动搭建环境:

image.png

我们也可以看到依赖关系:
image.png

说白了Spring就是一个Map,Map里面封装这我们要用的对象。

创建一个xml文件:


image.png

image.png

原则上命名只要不带中文即可。(有一个固定的名称,但现在不重要。)
在bean.xml文件中配置约束,在上面说的API的html文档中寻找:


image.png

image.png

约束如下:
<?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">

完善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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--    把对象的创建交给spring来管理-->
    <!--    id:获取时的唯一标志-->
    <!--    class:反射要创建的权限定位名称-->
    <bean id="accountService" class="com.edp.service.impl.IAccountServiceImpl"></bean>
    <bean id="accountDao" class="com.edp.dao.impl.AccountDaoImpl"></bean>

</beans>

这就是依赖注入(Dependency Injection)的配置文件。

11.0 修改启动类

既然配置文件都改用spring的配置文件,当然启动类也需要改为spring的启动方式。
修改Client.java

package com.edp.ui;

import com.edp.service.IAccountService;
import com.edp.service.impl.IAccountServiceImpl;
import org.springframework.context.ApplicationContext;

public class Client {
    
    /**
     * @Description: 获取spring的Ioc核心容器,并根据id获取对象
     * @author EdPeng
     * @date 2020/2/26 上午 12:09 
     */
    public static void main(String[] args) {
//1.获取核心容器
        ApplicationContext applicationContext = null;

    }
}

查看ApplicationContext的源代码,可以找到这个借口抽象类和实现类,抽象类自然是没法继承和实现,那么我们可以看到下图框起来的3个实现类:


image.png

先用第一个(ClassPathXmlApplicationContext):

public class Client {
    
    /**
     * @Description: 获取spring的Ioc核心容器,并根据id获取对象
     * @param 
     * @return 
     * @throws 
     * @author EdPeng
     * @date 2020/2/26 上午 12:09 
     */
    public static void main(String[] args) {
//1.获取核心容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
        //根据id获取bean对象
        IAccountService accountService = (IAccountService) applicationContext.getBean("accountService");
        IAccountDao accountDao = applicationContext.getBean("accountDao",IAccountDao.class);

        System.out.println(accountService);
        System.out.println(accountDao);
    }

运行:


image.png

所以我们只需要创建一个配置文件,把应该配置的信息交给spring,然后使用一个对象得到核心容器,再根据唯一对象取出想用的对象。

11.0 ApplicationContext的3个常用实现类

打开ApplicationContext的继承关系视图:


image.png

image.png

可以看到ApplicationContext还不是“老大”,BeanFactory才是。查看ApplicationContext的实现类:


image.png

image.png

注意看最前面的图标,“I”表示接口,两边带灰色的是抽象来,正常的“C”就是普通类。
image.png

三个常用实现类分别是:

    1. ClassPathXmlApplicationContext 可以加载类路径下的配置文件
    1. FileSystemXmlApplicationContext 可以加载磁盘任意路径下的配置文件(前提是必须有访问权限)
    1. AnnotationConfigApplicationContext 用于读取注解创建容器

以上Spring代码就是对ClassPathXmlApplicationContext的演示。FileSystemXmlApplicationContext可以依据原理类推:

        ApplicationContext applicationContext = 
new FileSystemXmlApplicationContext("F:\\acm\\workspace\\first_project_spring\\src\\main\\resources\\bean.xml");

AnnotationConfigApplicationContext类内容丰富有点大,会有一个专章节来介绍。

11.0 核心容器的两个接口引发出的问题:
  • ApplicationContext 在构建核心容器时,创建对象采取的策略是采用立即加载的方式。只要读取完配置文件马上创建配置文件中配置的对象。
  • BeanFactory 在构建核心容器时,创建对象采取的策略是延迟加载的方式。什么时候根据id获取对象,什么时候才真正的创建对象。

什么时候适合立即加载的方式,什么时候适合延迟加载的方式

  • ApplicationContext 单例模式适用
  • BeanFactory 多例对象适用

在实际开发中,更多的采用ApplicationContext接口定义容器对象。

END

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容