typora-copy-images-to: img

Spring _01

学习目标

  • 能够描述spring框架
  • 能够编写spring的IOC的入门案例
  • 能够说出spring的bean标签的配置
  • 能够理解Bean的属性注入方法
  • 能够理解复杂类型的属性注入
  • 能够实现spring框架整合JUnit

第一章-Spring概述

知识点-Spring介绍

1.目标

  • 能够描述spring框架

2.路径

  1. 什么是Spring
  2. Spring 的发展历程
  3. Spring的优点
  4. Spring的体系结构

3.讲解

3.1什么是Spring

  1. Spring 是一个开源框架,是为了解决企业应用程序开发复杂性而创建的(解耦)。
  2. 框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。
  3. 简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。
  4. 一站式:Spring提供了三层解决方案.

3.2Spring 的发展历程

1997 年 IBM 提出了 EJB 的思想

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)

3.3 Spring的优点

1.方便解耦,简化开发(基础重要功能)

  1. 通过Spring提供的IOC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。有了Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
  1. AOP编程的支持(亮点)

    通过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。

  2. 声明式事务的支持 (简化事务)

    在Spring中, 我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量

  3. 方便程序的测试 (集成Junit测试框架)

    可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随手可做的事情。例如:Spring对Junit4支持,可以通过注解方便的测试Spring程序。

5.方便集成各种优秀框架 (亮点、优势)

  1. Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts,HibernateHessianQuartz)等的直接支持。

6.降低Java EE API的使用难度(了解)

  1. Spring对很多难用的Java EE API(如JDBCJavaMail,远程调用等)提供了一个薄薄的封装层,通过Spring的简易封装,这些Java EE API的使用难度大为降低

3.4Spring的体系结构

Spring_01 - 图1

4.小结

  1. Spring是JavaSE/JavaEE 一系列的框架. 提供了三层开发里面的技术解决方案, 是一站式框架

  2. 我们这三天学的是Spring的核心部分

    • IOC
    • AOP
  • 声明式事务

第二章-IOC

知识点-工厂模式解耦

1.目标

  • 能够使用工厂模式进行解耦

2.路径

  1. 程序耦合的概述

  2. 使用工厂模式解耦

3.讲解

3.1程序的耦合

3.1.1 程序耦合的概述
  1. 耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。 耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。<br />
  2. 在软件工程中, 耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计<br />

应使类和构件之间的耦合最小。 软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。 划分模块的一个
准则就是高内聚低耦合。
总结:
耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。内聚与耦合内聚标志一个模块内各个元素彼此结合的紧密程度,它是信息隐蔽和局部化概念的自然扩展。内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。耦合是软件
结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。 程序讲究的是低耦合,高内聚。就是同一个模块内的各个元素之间要高度紧密,但是各个模块之
间的相互依存度却要不那么紧密。

  1. 内聚和耦合是密切相关的,同其他模块存在高耦合的模块意味着低内聚,而高内聚的模块意味着该模块同其他<br />

模块之间是低耦合。在进行软件设计时,应力争做到高内聚,低耦合。

3.1.2在代码中体现
  1. 早期我们的 JDBC 操作(面向接口的编程),注册驱动时,我们为什么不使用 DriverManager register 方法,而是采<br />

用 Class.forName 的方式?

  1. 原因就是: 我们的类依赖了数据库的具体驱动类(MySQL ,如果这时候更换了数据库(比如 Oracle ,<br />

需要修改源码来重新数据库驱动。这显然不是我们想要的。

  1. /**
  2. * 程序的耦合
  3. * 耦合:程序间的依赖关系
  4. * 包括:
  5. * 类之间的依赖
  6. * 方法间的依赖
  7. * 解耦:
  8. * 降低程序间的依赖关系
  9. * 实际开发中:
  10. * 应该做到:编译期不依赖,运行时才依赖。
  11. * 解耦的思路:
  12. * 第一步:使用反射来创建对象,而避免使用 new 关键字。
  13. * 第二步:通过读取配置文件来获取要创建的对象全限定类名
  14. */
  15. public class JdbcDemo1 {
  16. public static void main(String[] args) throws Exception{
  17. //1.注册驱动
  18. //DriverManager.registerDriver(new Driver());//第一种注册驱动的方式
  19. //第一种注册驱动的方式,缺点是耦合性强
  20. Class.forName("com.mysql.jdbc.Driver");//这是第二种注册驱动的方式
  21. //第二种注册驱动的方式,缺点是"字符串硬编码",可以使用配置文件解决
  22. //2.获取连接
  23. //3.获取操作数据库的预处理对象
  24. //4.执行 SQL,得到结果集
  25. //5.遍历结果集
  26. //6.释放资源
  27. }
  28. }

第一种耦合:一个类直接依赖另外一个类

第二种耦合:将字符串直接写死在Java代码中(字符串的硬编码)

3.1.3解决程序耦合的思路
  1. **产生类与类之间的耦合的根本原因是:在一个类中new了另外一个类的对象**
  2. **解决耦合的思路:不在类中创建另外一个类的对象,但是我们还需要另一个类的对象;由别人(Spring)把那个类的对象创建好之后给我用就可以了**
  3. 当是我们讲解 jdbc 时,是通过反射来注册驱动的,代码如下:
  4. `Class.forName("com.mysql.jdbc.Driver");//此处只是一个字符串`
  5. 此时的好处是,我们的类中不再依赖具体的驱动类,此时就算删除 mysql 的驱动 jar 包,依然可以编译(运行就不要想了没有驱动不可能运行成功的)
  6. 同时,也产生了一个新的问题, mysql 驱动的全限定类名字符串是在 java 类中写死的,一旦要改还是要修改源码。解决这个问题也很简单,使用配置文件配置

3.2.自定义IOC(工厂模式解耦)

  1. 原始方式

    • 方式: 创建类, 直接根据类new对象
    • 优点: 好写, 简单
    • 缺点: 耦合度太高, 不好维护
  2. 接口方式

    • 方式: 定义接口, 创建实现类. 接口=子类的对象
    • 优点: 耦合度相对原始方式 减低了一点
    • 缺点: 多写了接口, 还是需要改源码 不好维护
  3. 自定义IOC

    • 方式: 使用对象的话, 不直接new()了,直接从工厂里面取; 不需要改变源码

3.2.1原始方式

代码略

3.2.2接口方式
  • UserService.java
  1. package com.itheima.service;
  2. /**
  3. * 包名:com.itheima.service
  4. *
  5. * @author Leevi
  6. * 日期2020-08-09 10:22
  7. */
  8. public interface UserService {
  9. String getName();
  10. }
  • UserServiceImpl.java
  1. package com.itheima.service.impl;
  2. import com.itheima.service.UserService;
  3. /**
  4. * 包名:com.itheima.service.impl
  5. *
  6. * @author Leevi
  7. * 日期2020-08-09 10:22
  8. */
  9. public class UserServiceImpl implements UserService{
  10. public void init(){
  11. System.out.println("UserServiceImpl对象创建了...");
  12. }
  13. public void destroy(){
  14. System.out.println("UserServiceImpl对象销毁了...");
  15. }
  16. @Override
  17. public String getName() {
  18. return "周杰棍";
  19. }
  20. }
  • TestSpring.java
  1. public class TestSpring {
  2. public void test01() {
  3. AccountService accountService = new AccountServiceImpl();
  4. accountService.getName();
  5. }
  6. }

3.2.3自定义IOC(使用工厂模式解耦)

3.2.2.1工厂模式解耦思路
  1. 在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候, 让一个类中的方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。那么,这个读取配置文件, 创建和获取三层对象的类就是工厂

3.2.2.2实现
  • 添加坐标依赖
  1. <dependencies>
  2. <dependency>
  3. <groupId>junit</groupId>
  4. <artifactId>junit</artifactId>
  5. <version>4.12</version>
  6. <scope>test</scope>
  7. </dependency>
  8. <!-- 解析 xml 的 dom4j -->
  9. <dependency>
  10. <groupId>dom4j</groupId>
  11. <artifactId>dom4j</artifactId>
  12. <version>1.6.1</version>
  13. </dependency>
  14. <!-- dom4j 的依赖包 jaxen -->
  15. <dependency>
  16. <groupId>jaxen</groupId>
  17. <artifactId>jaxen</artifactId>
  18. <version>1.1-beta-8</version>
  19. </dependency>
  20. </dependencies>
  • UserService.java
  1. package com.itheima.service;
  2. /**
  3. * 包名:com.itheima.service
  4. *
  5. * @author Leevi
  6. * 日期2020-08-09 10:22
  7. */
  8. public interface UserService {
  9. String getName();
  10. }
  • UserServiceImpl.java
  1. package com.itheima.service.impl;
  2. import com.itheima.service.UserService;
  3. /**
  4. * 包名:com.itheima.service.impl
  5. *
  6. * @author Leevi
  7. * 日期2020-08-09 10:22
  8. */
  9. public class UserServiceImpl implements UserService{
  10. public void init(){
  11. System.out.println("UserServiceImpl对象创建了...");
  12. }
  13. public void destroy(){
  14. System.out.println("UserServiceImpl对象销毁了...");
  15. }
  16. @Override
  17. public String getName() {
  18. return "周杰棍";
  19. }
  20. }
  • Client.java
  1. package com.itheima.service.impl;
  2. import com.itheima.service.UserService;
  3. /**
  4. * 包名:com.itheima.service.impl
  5. *
  6. * @author Leevi
  7. * 日期2020-08-09 09:14
  8. */
  9. public class UserServiceImpl implements UserService{
  10. @Override
  11. public String getName() {
  12. return "张三";
  13. }
  14. }
  • BeanFactory.java
    下面的通过 BeanFactory 中 getBean 方法获取对象就解决了我们代码中对具体实现类的依赖。
  1. package com.itheima.factory;
  2. import org.dom4j.Document;
  3. import org.dom4j.Element;
  4. import org.dom4j.io.SAXReader;
  5. import java.io.InputStream;
  6. import java.util.HashMap;
  7. import java.util.List;
  8. import java.util.Map;
  9. /**
  10. * 包名:com.itheima.factory
  11. * @author Leevi
  12. * 日期2020-08-09 09:18
  13. */
  14. public class BeanFactory {
  15. private static Map<String,Object> map = new HashMap();
  16. static {
  17. try {
  18. //将读取配置文件的代码,放到静态代码块中
  19. //解析配置文件beans.xml,根据id的值,获取class的值
  20. SAXReader saxReader = new SAXReader();
  21. InputStream is = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
  22. Document document = saxReader.read(is);
  23. //获取所有的bean标签
  24. List<Element> elementList = document.selectNodes("//bean");
  25. if (elementList != null) {
  26. for (Element element : elementList) {
  27. //获取标签的id属性
  28. String idAttr = element.attributeValue("id");
  29. String className = element.attributeValue("class");
  30. //根据类的全限定名,创建出该类的对象
  31. Object obj = Class.forName(className).newInstance();
  32. //以id作为key,以对象作为value存入map中
  33. map.put(idAttr,obj);
  34. }
  35. }
  36. } catch (Exception e) {
  37. e.printStackTrace();
  38. }
  39. }
  40. public static Object getBean(String id){
  41. try {
  42. //根据传入的id,获取到对应的className
  43. Object obj = map.get(id);
  44. if (obj != null) {
  45. return obj;
  46. }
  47. throw new RuntimeException("找不到对应的类!!!");
  48. } catch (Exception e) {
  49. e.printStackTrace();
  50. throw new RuntimeException(e.getMessage());
  51. }
  52. }
  53. }
  • spring.xml
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans>
  3. <bean class="com.itheima.service.impl.UserServiceImpl" id="userService"></bean>
  4. </beans>

4.小结

  1. 程序初始化的时候, 解析xml, 全部解析好 存到容器【Map】里面
  2. 把bean标签的id作为map的key, bean标签的class属性值反射创建对象之后作为map的value
  3. 要使用对象, 不再直接new了, 而是从容器里面直接取

知识点-IOC概念

1.目标

  • 能够理解IOC概念

2.路径

  1. 分析和IOC的引入
  2. IOC概述

3.讲解

3.1分析和IOC的引入

上一小节我们通过使用工厂模式,实现了解耦。
它的核心思想就是:
1、通过Bean工厂读取配置文件使用反射创建对象。
2、把创建出来的对象都存起来,当使用者需要对象的时候,不再自己创建对象,而是调用Bean工厂的方法从容器中获取对象
这里面要解释两个问题:
第一个:存哪去?
分析:由于我们是很多对象,肯定要找个集合来存。这时候有 Map 和 List 供选择。
到底选 Map 还是 List 就看我们有没有查找需求。有查找需求,选 Map。
所以我们的答案就是:在应用加载时,创建一个 Map,用于存放三层对象。我们把这个 map 称之为容器。
第二个: 什么是工厂?
工厂就是负责给我们从容器中获取指定对象的类。这时候我们获取对象的方式发生了改变。
原来:我们在获取对象时,都是采用 new 的方式。 是主动的。

  • 老方式

Spring_01 - 图2

  • 现在 我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象。 是被动的。
    Spring_01 - 图3

3.2IOC概述

  1. IOC(inversion of control)的中文解释是“控制反转”,对象的使用者不是创建者. 作用是将对象的创建 反转给spring框架来创建和管理。
  2. 控制反转怎么去理解呢。 其实它反转的是什么呢,是对象的创建工作。 举个例子:平常我们在servlet或者service里面创建对象,都是使用new 的方式来直接创建对象,现在有了spring之后,我们就再也不new对象了,而是把对象创建的工作交给spring容器去维护。我们只需要问spring容器要对象即可
  3. ioc 的作用:削减计算机程序的耦合(解除我们代码中的依赖关系)。

4.小结

  1. IOC: 控制反转. 对象的使用者不是创建者. 把对象的创建和管理, 交给Spring框架. 要使用的时候, 直接从Spring工厂(容器)里面取
  2. 作用: 解耦.

案例-Spring的IOC快速入门

1.需求

  • 能够编写spring的IOC的入门案例

2.分析

  1. 本章我们使用的案例是, 账户的业务层和持久层的依赖关系解决。在开始 spring 的配置之前,我们要先准备一下环境。由于我们是使用 spring 解决依赖关系,并不是真正的要做增删改查操作,所以此时我们没必要写实体类。并且我们在此处使用的是 java 工程,不是 java web 工程。

3.实现

3.0 步骤

  1. 创建Maven工程, 添加坐标

  2. 准备好接口和实现类

  3. 创建spring的配置文件 (applicationContext.xml), 配置bean标签

  4. 创建工厂对象 获得bean 调用

3.1准备工作

3.1.1 引入spring的依赖(使用5.0.2版本)

3.1.2接口和实现类
  • UserService.java
  1. package com.itheima.service;
  2. /**
  3. * 包名:com.itheima.service
  4. *
  5. * @author Leevi
  6. * 日期2020-08-09 10:22
  7. */
  8. public interface UserService {
  9. String getName();
  10. }
  • UserServiceImpl.java
  1. package com.itheima.service.impl;
  2. import com.itheima.service.UserService;
  3. /**
  4. * 包名:com.itheima.service.impl
  5. *
  6. * @author Leevi
  7. * 日期2020-08-09 10:22
  8. */
  9. public class UserServiceImpl implements UserService{
  10. public void init(){
  11. System.out.println("UserServiceImpl对象创建了...");
  12. }
  13. public void destroy(){
  14. System.out.println("UserServiceImpl对象销毁了...");
  15. }
  16. @Override
  17. public String getName() {
  18. return "周杰棍";
  19. }
  20. }

3.2代码实现

  • 创建 maven 工程并导入坐标

  • 坐标

  1. <dependencies>
  2. <!--引入相关依赖-->
  3. <dependency>
  4. <groupId>org.springframework</groupId>
  5. <artifactId>spring-context</artifactId>
  6. <version>5.0.2.RELEASE</version>
  7. </dependency>
  8. <dependency>
  9. <groupId>junit</groupId>
  10. <artifactId>junit</artifactId>
  11. <version>4.12</version>
  12. <scope>test</scope>
  13. </dependency>
  14. </dependencies>
  • 在类的根路径下创建spring的配置文件

  • 配置文件为任意名称的 xml 文件(不能是中文)

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <!--
  7. 每一个实现类就对应一个bean标签
  8. id属性: 对象的唯一标识,根据这个唯一标识,就可以从核心容器中获取对象
  9. class属性: 对象所属的实现类的全限定名
  10. -->
  11. <bean class="com.itheima.service.impl.UserServiceImpl"
  12. id="userService"></bean>
  13. </beans>
  • 编写Java代码测试
  1. public class TestSpring {
  2. @Test
  3. public void test01(){
  4. //调用UserServiceImpl类的方法
  5. //1. 创建spring的核心容器(加载类路径下的xml配置文件的核心容器)
  6. //在创建核心容器的时候,就已经读取了整个spring.xml配置文件,就已经创建好了它里面的bean标签对应的对象
  7. //并且对象都放到核心容器中了
  8. ApplicationContext act = new ClassPathXmlApplicationContext("classpath:spring.xml");
  9. //ApplicationContext act = new FileSystemXmlApplicationContext("d:/a/spring.xml");
  10. //2. 调用核心容器的方法,根据id获取对象
  11. UserService userService = (UserService) act.getBean("userService");
  12. System.out.println(userService.getName());
  13. }
  14. }

4.小结

4.1步骤

  1. 创建Maven工程, 添加坐标
  2. 准备接口和实现类
  3. 创建applicationContext.xml 配置bean
  4. 创建工厂, 从工厂获得对象

4.2注意事项

我们当前使用的Spring版本是5.0.2 jdk要求>=8的

知识点-Spring 基于 XML 的 IOC 细节

1.目标

  • 能够说出spring的bean标签的配置

2.路径

  1. 配置文件详解(Bean标签)
  2. bean的作用范围和生命周期

3.讲解

3.1配置文件详解(Bean标签)

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <!--
  7. 每一个实现类就对应一个bean标签
  8. id属性: 对象的唯一标识,根据这个唯一标识,就可以从核心容器中获取对象
  9. class属性: 对象所属的实现类的全限定名
  10. scope属性: 对象的范围
  11. 1. singleton 单例(默认)
  12. 2. prototype 多例
  13. lazy-init: 配置懒加载,核心容器创建的时候是否创建出该类对象
  14. init-method: 配置类的对象初始化的时候,要调用哪个方法
  15. destroy-method: 配置这个类的对象销毁的时候,要调用哪个方法
  16. 单例模式下(默认没有开启懒加载),由核心容器进行管理的对象什么时候创建什么时候销毁?
  17. 1. 核心容器创建的时候,会创建出它所配置的所有类的对象
  18. 2. 核心容器销毁的时候,它里面的对象才会被销毁
  19. 多例模式下,由spring管理的对象什么时候创建什么时候销毁
  20. 1. 当核心容器调用getBean(id)的时候,创建对象
  21. 2. 垃圾回收机制才能销毁这个对象
  22. -->
  23. <bean class="com.itheima.service.impl.UserServiceImpl"
  24. id="userService"
  25. scope="prototype" lazy-init="false"
  26. init-method="init"
  27. destroy-method="destroy"></bean>
  28. </beans>
  • id或者name属性
    用于标识bean , 其实id 和 name都必须具备唯一标识 ,两种用哪一种都可以。但是一定要唯一、 一般开发中使用id来声明.

  • class属性: 用来配置要实现化类的全限定名

  • scope属性: 用来描述bean的作用范围
    singleton: 默认值,单例模式。spring创建bean对象时会以单例方式创建。(默认)
    prototype: 多例模式。spring创建bean对象时会以多例模式创建。
    request: 针对Web应用。spring创建对象时,会将此对象存储到request作用域。(不用管)
    session: 针对Web应用。spring创建对象时,会将此对象存储到session作用域。(不用管)

  • init-method属性:spring为bean初始化提供的回调方法

  • destroy-method属性:spring为bean销毁时提供的回调方法. 销毁方法针对的都是单例bean , 如果想销毁bean , 可以关闭工厂

3.2bean的作用范围和生命周期

  • 单例对象: scope=”singleton”,一个应用只有一个对象的实例。它的作用范围就是整个引用。

    1. 核心容器创建的时候,会创建出它所配置的所有类的对象
    2. 核心容器销毁的时候,它里面的对象才会被销毁
  • 多例对象: scope=”prototype”,每次访问对象时,都会重新创建对象实例。

    1. 当核心容器调用getBean(id)的时候,创建对象
    2. 垃圾回收机制才能销毁这个对象

4.小结

知识点-Spring工厂详解(了解)

1.目标

  • 掌握Spring常见的工厂类

2.路径

  1. spring 中工厂的类结构图
  2. BeanFactory 和 ApplicationContext 的区别

3.讲解

3.1 ApplicationContext接口的三种实现类

  • ClassPathXmlApplicationContext:它是从类的根路径下加载配置文件
  • FileSystemXmlApplicationContext:它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
  • AnnotationConfigApplicationContext:当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。

3.2 BeanFactory 和 ApplicationContext 的区别(了解)

  • ApplicationContext 是现在使用的工厂
    1. ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  • XmlBeanFactory是老版本使用的工厂,目前已经被废弃【了解】
    1. BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

两者的区别:

  • ApplicationContext加载方式是框架启动时就开始创建所有单例的bean,存到了容器里面

  • 非懒加载: 在核心容器创建的时候,创建出所有的bean对象,存到核心容器中

  • 懒加载: 第一次调用getBean()的时候,创建出bean对象,存到核心容器中

  • BeanFactory加载方式是用到bean时再加载(目前已经被废弃)

    • 懒加载: 第一次调用getBean()的时候,创建出bean对象,存到核心容器中
  • ApplicationContext是BeanFactory的子接口,子接口的功能肯定比父接口更强大

4.小结

  1. 工厂类继承关系

    • BeanFactory

      • ApplicationContext

        • FileSystemXmlApplicationContext xml方式 它是从磁盘路径上加载配置文件
        • ClassPathXmlApplicationContext xml方式 它是从类的根路径下加载配置文件
        • AnnotationConfigApplicationContext 注解方式的
  2. 新旧工厂

    • 新工厂: 在工厂初始化的话的时候, 就会创建配置的所有的单例bean,存到Spring容器里面
    • 旧工厂(已经废弃了): 等用到对象的时候, 再创建

知识点-实例化Bean的三种方式【了解】

1.目标

  • 了解实例化 Bean 的三种方式

2.路径

  1. 无参构造方法方式
  2. 静态工厂方式
  3. 实例工厂实例化的方法

3.讲解

3.1方式一: 无参构造方法方式 (只需要掌握这种)

  1. 需要实例化的类,提供无参构造方法
  2. 配置代码
  1. <bean class="com.itheima.service.impl.UserServiceImpl"
  2. id="userService"></bean>

3.2方式二: 静态工厂方式

  1. 需要额外提供工厂类 工厂方法是静态方法
  1. package com.itheima.service.utils;
  2. import com.alibaba.druid.pool.DruidDataSourceFactory;
  3. import javax.sql.DataSource;
  4. import java.io.InputStream;
  5. import java.util.Properties;
  6. /**
  7. * 包名:com.itheima.utils
  8. *
  9. * @author Leevi
  10. * 日期2020-07-06 11:45
  11. */
  12. public class DruidUtil {
  13. private static DataSource dataSource;
  14. static {
  15. try {
  16. //1. 创建Properties对象
  17. Properties properties = new Properties();
  18. //2. 将配置文件转换成字节输入流
  19. InputStream is = DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties");
  20. //3. 使用properties对象加载is
  21. properties.load(is);
  22. //druid底层是使用的工厂设计模式,去加载配置文件,创建DruidDataSource对象
  23. dataSource = DruidDataSourceFactory.createDataSource(properties);
  24. } catch (Exception e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. public static DataSource getDataSource(){
  29. System.out.println("调用静态工厂的getDataSource方法...");
  30. return dataSource;
  31. }
  32. }
  1. 配置代码
  1. <!--
  2. 配置使用静态工厂实例化Bean
  3. id就是创建的这个对象的唯一标识
  4. class就是要调用的那个静态工厂的全限定名
  5. factory-method就是要调用的那个静态工厂中的方法名
  6. -->
  7. <bean id="dataSource" class="com.itheima.service.utils.DruidUtil"
  8. factory-method="getDataSource"></bean>

3.3方式三:实例工厂实例化Bean的方法

创建实例化工厂类

  1. package com.itheima.service.utils;
  2. import com.alibaba.druid.pool.DruidDataSourceFactory;
  3. import javax.sql.DataSource;
  4. import java.io.InputStream;
  5. import java.util.Properties;
  6. /**
  7. * 包名:com.itheima.utils
  8. *
  9. * @author Leevi
  10. * 日期2020-07-06 11:45
  11. */
  12. public class DruidUtil {
  13. private static DataSource dataSource;
  14. static {
  15. try {
  16. //1. 创建Properties对象
  17. Properties properties = new Properties();
  18. //2. 将配置文件转换成字节输入流
  19. InputStream is = DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties");
  20. //3. 使用properties对象加载is
  21. properties.load(is);
  22. //druid底层是使用的工厂设计模式,去加载配置文件,创建DruidDataSource对象
  23. dataSource = DruidDataSourceFactory.createDataSource(properties);
  24. } catch (Exception e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. //非静态方法
  29. public DataSource getDataSource(){
  30. System.out.println("调用实例工厂的getDataSource方法...");
  31. return dataSource;
  32. }
  33. }

配置

  1. <!--
  2. 配置实例工厂实例化Bean
  3. 第一步: 配置实例工厂对象
  4. 第二步: 配置bean对象
  5. factory-bean 表示要调用哪个实例工厂对象的方法
  6. factory-method 表示要调用实例工厂对象的哪个方法
  7. -->
  8. <bean id="druidUtil" class="com.itheima.service.utils.DruidUtil"></bean>
  9. <bean id="dataSource" factory-bean="druidUtil" factory-method="getDataSource"></bean>

4.小结

  1. 一般情况下不会使用第二和三种, 主要使用第一种

第三章 - 依赖注入

知识点-依赖注入概念

1.目标

  • 掌握什么是依赖注入

2.讲解

  1. 依赖注入全称是 dependency Injection 翻译过来是依赖注入.其实就是如果我们托管的某一个类中存在属性,需要spring在创建该类实例的时候,顺便给这个对象里面的属性进行赋值。 这就是依赖注入。
  2. 现在, Bean的创建交给Spring了, 需要在xml里面进行注册
  3. 我们交给Spring创建的Bean里面可能有一些属性(字段), Spring帮我创建的同时也把Bean的一些属性(字段)给赋值, 这个赋值就是注入.

3.小结

  1. 注册: 把bean的创建交给Spring
  2. 依赖注入: bean创建的同时, bean里面可能有一些字段需要赋值, 这个赋值交给Spring, 这个过程就是依赖注入

知识点-依赖注入实现

1.目标

  • 能够理解Bean的属性注入方法
  • 能够理解复杂类型的属性注入

2.路径

  1. 构造方法方式注入
  2. set方法方式的注入
  3. P名称空间注入

3.讲解

3.1构造方法方式注入【掌握】

  • UserController ```java package com.itheima.controller;

import com.itheima.service.UserService;

/**

  • 包名:com.itheima.controller *
  • @author Leevi
  • 日期2020-08-09 12:05
  • 给对象的属性赋值的方式
    1. 有参构造
    1. set方法 */ public class UserController{ private UserService userService;

      public UserController() { }

      public UserController(UserService userService) { this.userService = userService; }

      public String getName(){ return userService.getName(); } } ```

  • UserServiceImpl ```java package com.itheima.service.impl;

import com.itheima.dao.UserDao; import com.itheima.service.UserService;

/**

  • 包名:com.itheima.service.impl *
  • @author Leevi
  • 日期2020-08-09 12:05 */ public class UserServiceImpl implements UserService{ private UserDao userDao;

    public UserServiceImpl() { } public UserServiceImpl(UserDao userDao) {

    1. this.userDao = userDao;

    }

    @Override public String getName() {

    1. return userDao.getName();

    } } ```

  • 配置文件

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://www.springframework.org/schema/beans
    5. http://www.springframework.org/schema/beans/spring-beans.xsd">
    6. <!--
    7. 它里面的UserService属性要进行赋值
    8. 使用有参构造进行属性的注入,使用<constructor-arg>标签注入
    9. -->
    10. <bean id="userController" class="com.itheima.controller.UserController">
    11. <constructor-arg name="userService" ref="userService"></constructor-arg>
    12. </bean>
    13. <!--
    14. 它里面的userDao属性要进行赋值
    15. -->
    16. <bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
    17. <constructor-arg name="userDao" ref="userDao"></constructor-arg>
    18. </bean>
    19. <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
    20. </beans>

3.2 set方法方式的注入【重点】

3.2.1注入对象类型(重点)
  • UserController ```java package com.itheima.controller;

import com.itheima.service.UserService;

/**

  • 包名:com.itheima.controller *
  • @author Leevi
  • 日期2020-08-09 12:05
  • 给对象的属性赋值的方式
    1. 有参构造
    1. set方法 *
  • 注入的属性类型:
    1. 对象(spring核心容器中的)类型:使用ref=”对象的id” / public class UserController{ private UserService userService;

      public void setUserService(UserService userService) { this.userService = userService; }

      public String getName(){ return userService.getName(); } } ```

  • UserServiceImpl ```java package com.itheima.service.impl;

import com.itheima.dao.UserDao; import com.itheima.service.UserService;

/**

  • 包名:com.itheima.service.impl *
  • @author Leevi
  • 日期2020-08-09 12:05 */ public class UserServiceImpl implements UserService{ private UserDao userDao;

    public void setUserDao(UserDao userDao) {

    1. this.userDao = userDao;

    }

    @Override public String getName() {

    1. return userDao.getName();

    } } ```

  • 配置文件 ```xml <?xml version=”1.0” encoding=”UTF-8”?> <beans xmlns=”http://www.springframework.org/schema/beans

    1. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    2. xmlns:p="http://www.springframework.org/schema/p"
    3. xsi:schemaLocation="http://www.springframework.org/schema/beans
    4. http://www.springframework.org/schema/beans/spring-beans.xsd">
  1. <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
  2. </bean>

  1. <a name="5996752e"></a>
  2. ##### 3.2.2 注入数组类型(了解一下)
  3. -
  4. java代码
  5. ```java
  6. package com.itheima.dao.impl;
  7. import com.itheima.dao.UserDao;
  8. import java.util.Arrays;
  9. import java.util.Map;
  10. /**
  11. * 包名:com.itheima.dao.impl
  12. *
  13. * @author Leevi
  14. * 日期2020-08-09 12:05
  15. */
  16. public class UserDaoImpl implements UserDao{
  17. private String[] stringArray;
  18. public void setStringArray(String[] stringArray) {
  19. this.stringArray = stringArray;
  20. }
  21. @Override
  22. public String getName() {
  23. System.out.println("数组stringArray:" + Arrays.toString(stringArray));
  24. //模拟调用数据库方法获取name
  25. return "奥巴马";
  26. }
  27. }
  • 配置文件
    1. <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
    2. <!--注入数组-->
    3. <property name="stringArray">
    4. <array>
    5. <value>hello1</value>
    6. <value>hello2</value>
    7. <value>hello3</value>
    8. <value>hello4</value>
    9. </array>
    10. </property>
    11. </bean>

3.2.3 注入Map类型(了解)
  • Java代码 ```java package com.itheima.dao.impl;

import com.itheima.dao.UserDao;

import java.util.Arrays; import java.util.Map;

/**

  • 包名:com.itheima.dao.impl *
  • @author Leevi
  • 日期2020-08-09 12:05 */ public class UserDaoImpl implements UserDao{ private Map map; public void setMap(Map map) {
    1. this.map = map;
    } @Override public String getName() {
    1. System.out.println("map的值为:" + map);
    2. //模拟调用数据库方法获取name
    3. return "奥巴马";
    } } ```
  • 配置文件
    1. <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"">
    2. <!--注入map-->
    3. <property name="map">
    4. <map>
    5. <entry key="username" value="aobama"></entry>
    6. <entry key="pwd" value="123456"></entry>
    7. <entry key="address" value="召唤师峡谷"></entry>
    8. </map>
    9. </property>
    10. </bean>
  1. ##### 3.2.4 注入简单类型数据(掌握)
  2. + Java代码
  3. ```java
  4. package com.itheima.dao.impl;
  5. import com.itheima.dao.UserDao;
  6. import java.util.Arrays;
  7. import java.util.Map;
  8. /**
  9. * 包名:com.itheima.dao.impl
  10. *
  11. * @author Leevi
  12. * 日期2020-08-09 12:05
  13. */
  14. public class UserDaoImpl implements UserDao{
  15. private String username;
  16. private Integer age;
  17. public void setAge(Integer age) {
  18. this.age = age;
  19. }
  20. public void setUsername(String username) {
  21. this.username = username;
  22. }
  23. @Override
  24. public String getName() {
  25. System.out.println("年龄是:" + age);
  26. //模拟调用数据库方法获取name
  27. return username;
  28. }
  29. }
  • 配置文件 ```xml
<a name="dc44b2e9"></a> #### 3.3 P名称空间注入[了解] 其实底层也是调用set方法,只是写法有点不同 <a name="856a3967"></a> ##### 3.3.1 p名称空间的方式 - 提供属性的set方法 - 在applicationContext.xml引入p命名空间`xmlns:p="http://www.springframework.org/schema/p"` xml <?xml version=”1.0” encoding=”UTF-8”?> - 使用xml

  1. <a name="dd631cbe-7"></a>
  2. ### 4.小结
  3. 1.
  4. 构造方法方式
  5. - 定义变量 ,提供构造方法
  6. - 在bean标签里面配置子标签constructor-arg
  7. ```xml
  8. <bean id="" class="">
  9. <constructor-arg name="" value=""></constructor-arg>
  10. <constructor-arg name="" ref=""></constructor-arg>
  11. </bean>
  1. Set方法方式

    • 2.1 步骤

      • 定义变量 ,提供Set方法
      • bean标签里面配置子标签property
    • 2.2 简单类型(基本类型, String)
  1. <bean id="" class="">
  2. <property name="" value=""></property>
  3. </bean>
  • 2.3 数组类型

    1. <bean id="" class="">
    2. <property name="">
    3. <array>
    4. <value></value>
    5. </array>
    6. </property>
    7. </bean>
  • 2.3 Map类型

    1. <bean id="" class="">
    2. <property name="">
    3. <map>
    4. <entry key="" value=""></entry>
    5. </map>
    6. </property>
    7. </bean>
  • 2.4 对象类型

    1. <bean id="" class="">
    2. <property name="" ref="">
    3. </property>
    4. </bean>

第四章-IOC和依赖注入练习

使用Spring的IOC的实现账户的CRUD

1.目标

  • 够实现spring基于XML的IoC案例

2.分析

  1. 环境搭建
  2. 创建applicationContext.xml

    • new的地方, 就注册
    • 需要用到的字段(对象), 就注入
  3. 启动Spring工厂

3.实现

3.1环境搭建

  1. 创建Maven工程,导入坐标
  1. <dependencies>
  2. <!--引入相关依赖-->
  3. <dependency>
  4. <groupId>org.springframework</groupId>
  5. <artifactId>spring-context</artifactId>
  6. <version>5.0.2.RELEASE</version>
  7. </dependency>
  8. <dependency>
  9. <groupId>junit</groupId>
  10. <artifactId>junit</artifactId>
  11. <version>4.12</version>
  12. <scope>test</scope>
  13. </dependency>
  14. <dependency>
  15. <groupId>org.projectlombok</groupId>
  16. <artifactId>lombok</artifactId>
  17. <version>1.18.8</version>
  18. </dependency>
  19. <!--druid的依赖-->
  20. <dependency>
  21. <groupId>com.alibaba</groupId>
  22. <artifactId>druid</artifactId>
  23. <version>1.1.18</version>
  24. </dependency>
  25. <!--mysql的依赖-->
  26. <dependency>
  27. <groupId>mysql</groupId>
  28. <artifactId>mysql-connector-java</artifactId>
  29. <version>5.1.25</version>
  30. </dependency>
  31. <!--dbutils的依赖-->
  32. <dependency>
  33. <groupId>commons-dbutils</groupId>
  34. <artifactId>commons-dbutils</artifactId>
  35. <version>1.7</version>
  36. </dependency>
  37. </dependencies>

实体

  1. package com.itheima.pojo;
  2. import lombok.Data;
  3. /**
  4. * 包名:com.itheima.pojo
  5. *
  6. * @author Leevi
  7. * 日期2020-08-09 14:55
  8. */
  9. @Data
  10. public class Account {
  11. private String name;
  12. private Integer id;
  13. private Double money;
  14. }
  1. 编写持久层代码
    AccountDao.java
  1. package com.itheima.dao;
  2. import com.itheima.pojo.Account;
  3. import java.sql.SQLException;
  4. import java.util.List;
  5. /**
  6. * 包名:com.itheima.dao
  7. *
  8. * @author Leevi
  9. * 日期2020-08-09 14:56
  10. */
  11. public interface AccountDao {
  12. /**
  13. * 查询所有账号信息
  14. * @return
  15. */
  16. public List<Account> findAll() throws SQLException;
  17. /**
  18. * 根据id查询账号信息
  19. * @param id
  20. * @return
  21. */
  22. public Account findById(int id) throws SQLException;
  23. /**
  24. * 根据id删除账号信息
  25. * @param id
  26. */
  27. public void deleteById(int id) throws SQLException;
  28. /**
  29. * 添加账号信息
  30. * @param account
  31. */
  32. public void add(Account account) throws SQLException;
  33. /**
  34. * 修改账号信息
  35. * @param account
  36. */
  37. public void update(Account account) throws SQLException;
  38. }
  1. AccountDaoImpl.java
  1. package com.itheima.dao.impl;
  2. import com.itheima.dao.AccountDao;
  3. import com.itheima.pojo.Account;
  4. import org.apache.commons.dbutils.QueryRunner;
  5. import org.apache.commons.dbutils.handlers.BeanHandler;
  6. import org.apache.commons.dbutils.handlers.BeanListHandler;
  7. import java.sql.SQLException;
  8. import java.util.List;
  9. /**
  10. * 包名:com.itheima.dao.impl
  11. * @author Leevi
  12. * 日期2020-08-09 14:58
  13. * 使用DBUtils
  14. * 1. 创建QueryRunner对象
  15. */
  16. public class AccountDaoImpl implements AccountDao {
  17. private QueryRunner queryRunner;
  18. public void setQueryRunner(QueryRunner queryRunner) {
  19. this.queryRunner = queryRunner;
  20. }
  21. @Override
  22. public List<Account> findAll() throws SQLException {
  23. String sql = "select * from account";
  24. List<Account> accountList = queryRunner.query(sql, new BeanListHandler<>(Account.class));
  25. return accountList;
  26. }
  27. @Override
  28. public Account findById(int id) throws SQLException {
  29. String sql = "select * from account where id=?";
  30. Account account = queryRunner.query(sql, new BeanHandler<>(Account.class), id);
  31. return account;
  32. }
  33. @Override
  34. public void deleteById(int id) throws SQLException {
  35. String sql = "delete from account where id=?";
  36. queryRunner.update(sql,id);
  37. }
  38. @Override
  39. public void add(Account account) throws SQLException {
  40. String sql = "insert into account values (null,?,?)";
  41. queryRunner.update(sql,account.getName(),account.getMoney());
  42. }
  43. @Override
  44. public void update(Account account) throws SQLException {
  45. String sql = "update account set name=?,money=? where id=?";
  46. queryRunner.update(sql,account.getName(),account.getMoney(),account.getId());
  47. }
  48. }
  1. 编写业务层代码
    AccountService.java
  1. package com.itheima.service;
  2. import com.itheima.pojo.Account;
  3. import java.sql.SQLException;
  4. import java.util.List;
  5. /**
  6. * 包名:com.itheima.service
  7. *
  8. * @author Leevi
  9. * 日期2020-08-09 15:05
  10. */
  11. public interface AccountService {
  12. /**
  13. * 查询所有账号信息
  14. * @return
  15. */
  16. public List<Account> findAll() throws SQLException;
  17. /**
  18. * 根据id查询账号信息
  19. * @param id
  20. * @return
  21. */
  22. public Account findById(int id) throws SQLException;
  23. /**
  24. * 根据id删除账号信息
  25. * @param id
  26. */
  27. public void deleteById(int id) throws SQLException;
  28. /**
  29. * 添加账号信息
  30. * @param account
  31. */
  32. public void add(Account account) throws SQLException;
  33. /**
  34. * 修改账号信息
  35. * @param account
  36. */
  37. public void update(Account account) throws SQLException;
  38. }
  1. AccountServiceImpl.java
  1. package com.itheima.service.impl;
  2. import com.itheima.dao.AccountDao;
  3. import com.itheima.pojo.Account;
  4. import com.itheima.service.AccountService;
  5. import java.sql.SQLException;
  6. import java.util.List;
  7. /**
  8. * 包名:com.itheima.service.impl
  9. *
  10. * @author Leevi
  11. * 日期2020-08-09 15:05
  12. */
  13. public class AccountServiceImpl implements AccountService {
  14. private AccountDao accountDao;
  15. public void setAccountDao(AccountDao accountDao) {
  16. this.accountDao = accountDao;
  17. }
  18. @Override
  19. public List<Account> findAll() throws SQLException {
  20. return accountDao.findAll();
  21. }
  22. @Override
  23. public Account findById(int id) throws SQLException {
  24. return accountDao.findById(id);
  25. }
  26. @Override
  27. public void deleteById(int id) throws SQLException {
  28. accountDao.deleteById(id);
  29. }
  30. @Override
  31. public void add(Account account) throws SQLException {
  32. accountDao.add(account);
  33. }
  34. @Override
  35. public void update(Account account) throws SQLException {
  36. accountDao.update(account);
  37. }
  38. }
  1. 编写控制层AccountController的代码
  1. package com.itheima.controller;
  2. import com.itheima.pojo.Account;
  3. import com.itheima.service.AccountService;
  4. import java.sql.SQLException;
  5. import java.util.List;
  6. /**
  7. * 包名:com.itheima.controller
  8. *
  9. * @author Leevi
  10. * 日期2020-08-09 15:06
  11. */
  12. public class AccountController {
  13. private AccountService accountService;
  14. public void setAccountService(AccountService accountService) {
  15. this.accountService = accountService;
  16. }
  17. public List<Account> findAll() throws SQLException {
  18. return accountService.findAll();
  19. }
  20. public Account findById(int id) throws SQLException {
  21. return accountService.findById(id);
  22. }
  23. public void deleteById(int id) throws SQLException {
  24. accountService.deleteById(id);
  25. }
  26. public void add(Account account) throws SQLException {
  27. accountService.add(account);
  28. }
  29. public void update(Account account) throws SQLException {
  30. accountService.update(account);
  31. }
  32. }

3.2 拷贝Druid的工具类以及Druid的配置文件

  • DruidUtil
  1. package com.itheima.utils;
  2. import com.alibaba.druid.pool.DruidDataSourceFactory;
  3. import javax.sql.DataSource;
  4. import java.io.InputStream;
  5. import java.util.Properties;
  6. /**
  7. * 包名:com.itheima.utils
  8. *
  9. * @author Leevi
  10. * 日期2020-07-06 11:45
  11. */
  12. public class DruidUtil {
  13. private static DataSource dataSource;
  14. static {
  15. try {
  16. //1. 创建Properties对象
  17. Properties properties = new Properties();
  18. //2. 将配置文件转换成字节输入流
  19. InputStream is = DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties");
  20. //3. 使用properties对象加载is
  21. properties.load(is);
  22. //druid底层是使用的工厂设计模式,去加载配置文件,创建DruidDataSource对象
  23. dataSource = DruidDataSourceFactory.createDataSource(properties);
  24. } catch (Exception e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. public static DataSource getDataSource(){
  29. return dataSource;
  30. }
  31. }
  • jdbc.properties
  1. # 数据库连接参数
  2. url=jdbc:mysql://localhost:3306/day20
  3. username=root
  4. password=123
  5. driverClassName=com.mysql.jdbc.Driver
  6. # 连接池的参数
  7. initialSize=10
  8. maxActive=10
  9. maxWait=2000

3.3 配置步骤

  • 创建applicationContext.xml

Spring_01 - 图4

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <!--
  7. 进行IOC:
  8. AccountController
  9. AccountServiceImpl
  10. AccountDaoImpl
  11. QueryRunner
  12. DataSource: 调用静态工厂的方法
  13. 进行依赖注入
  14. 将AccountServiceImpl的对象注入进AccountController中
  15. 将AccountDaoImpl的对象注入进AccountServiceImpl中
  16. 将QueryRunner的对象注入到AccountDaoImpl中
  17. 将DataSource注入到QueryRunner中
  18. -->
  19. <bean id="accountController" class="com.itheima.controller.AccountController">
  20. <property name="accountService" ref="accountService"></property>
  21. </bean>
  22. <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
  23. <property name="accountDao" ref="accountDao"></property>
  24. </bean>
  25. <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
  26. <property name="queryRunner" ref="queryRunner"></property>
  27. </bean>
  28. <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
  29. <!--采用构造函数方式注入-->
  30. <constructor-arg name="ds" ref="dataSource"></constructor-arg>
  31. </bean>
  32. <!--静态工厂方式实例化bean-->
  33. <bean id="dataSource" class="com.itheima.utils.DruidUtil" factory-method="getDataSource"></bean>
  34. </beans>

3.3测试案例

  1. package com.itheima.test;
  2. import com.itheima.controller.AccountController;
  3. import com.itheima.pojo.Account;
  4. import org.junit.Before;
  5. import org.junit.Test;
  6. import org.springframework.context.ApplicationContext;
  7. import org.springframework.context.support.ClassPathXmlApplicationContext;
  8. import java.sql.SQLException;
  9. import java.util.List;
  10. /**
  11. * 包名:com.itheima.test
  12. *
  13. * @author Leevi
  14. * 日期2020-08-09 15:16
  15. */
  16. public class TestSpring {
  17. private AccountController accountController;
  18. @Before
  19. public void init(){
  20. //1. 创建spring的核心容器对象
  21. ApplicationContext act = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
  22. //2. 从核心容器中获取AccountController的对象
  23. accountController = (AccountController) act.getBean("accountController");
  24. }
  25. @Test
  26. public void testFindAll() throws SQLException {
  27. List<Account> accountList = accountController.findAll();
  28. System.out.println(accountList);
  29. }
  30. @Test
  31. public void testFindById() throws SQLException {
  32. Account account = accountController.findById(1);
  33. System.out.println(account);
  34. }
  35. @Test
  36. public void testDeleteById() throws SQLException {
  37. accountController.deleteById(3);
  38. }
  39. @Test
  40. public void testAdd() throws SQLException {
  41. Account account = new Account();
  42. account.setName("ww");
  43. account.setMoney(1000.0);
  44. accountController.add(account);
  45. }
  46. @Test
  47. public void testUpdate() throws SQLException {
  48. Account account = accountController.findById(5);
  49. account.setName("aobama");
  50. account.setMoney(1500.0);
  51. accountController.update(account);
  52. }
  53. }

4.小结

第五章-Spring5 的新特性【了解】

知识点-Spring5 的新特性

1.目标

  • 了解Spring5 的新特性

2.路径

  1. 与 JDK 相关的升级
  2. 核心容器的更新
  3. JetBrains Kotlin 语言支持
  4. 响应式编程风格
  5. Junit5 支持
  6. 依赖类库的更新

3.讲解

3.1与 JDK 相关的升级

3.1.1jdk 版本要求
  1. spring5.0 2017 9 月发布了它的 GA(通用)版本。
  2. 该版本是基于 jdk8 编写的, 所以 jdk8 以下版本将无法使用。 同时,可以兼容 jdk9 版本。<br />

注:
我们使用 jdk8 构建工程,可以降版编译。但是不能使用 jdk8 以下版本构建工程。

  1. Spring 大量使用了反射

3.1.2 利用 jdk8 版本更新的内容
  • 基于 JDK8 的反射增强
  1. public class Test {
  2. //循环次数定义: 10 亿次
  3. private static final int loopCnt = 1000 * 1000 * 1000;
  4. public static void main(String[] args) throws Exception {
  5. //输出 jdk 的版本
  6. System.out.println("java.version=" + System.getProperty("java.version"));
  7. t1();
  8. t2();
  9. t3();
  10. }
  11. // 每次重新生成对象
  12. public static void t1() {
  13. long s = System.currentTimeMillis();
  14. for (int i = 0; i < loopCnt; i++) {
  15. Person p = new Person();
  16. p.setAge(31);
  17. }
  18. long e = System.currentTimeMillis();
  19. System.out.println("循环 10 亿次创建对象的时间: " + (e - s));
  20. }
  21. // 同一个对象
  22. public static void t2() {
  23. long s = System.currentTimeMillis();
  24. Person p = new Person();
  25. for (int i = 0; i < loopCnt; i++) {
  26. p.setAge(32);
  27. }
  28. long e = System.currentTimeMillis();
  29. System.out.println("循环 10 亿次给同一对象赋值的时间: " + (e - s));
  30. }
  31. //使用反射创建对象
  32. public static void t3() throws Exception {
  33. long s = System.currentTimeMillis();
  34. Class<Person> c = Person.class;
  35. Person p = c.newInstance();
  36. Method m = c.getMethod("setAge", Integer.class);
  37. for (int i = 0; i < loopCnt; i++) {
  38. m.invoke(p, 33);
  39. }
  40. long e = System.currentTimeMillis();
  41. System.out.println("循环 10 亿次反射创建对象的时间: " + (e - s));
  42. }
  43. static class Person {
  44. private int age = 20;
  45. public int getAge() {
  46. return age;
  47. }
  48. public void setAge(Integer age) {
  49. this.age = age;
  50. }
  51. }
  52. }

Spring_01 - 图5

  • @NonNull 注解和@Nullable 注解的使用
    @Nullable @NotNull 注解来显示表明可为空的参数和以及返回值。这样就够在编译的时候处理空值而不是在运行时抛出 NullPointerExceptions。

  • 日志记录方面
    Spring Framework 5.0 带来了 Commons Logging 桥接模块的封装, 它被叫做 spring-jcl 而不是标准的 Commons Logging。当然,无需任何额外的桥接,新版本也会对 Log4j 2.x, SLF4J, JUL( java.util.logging) 进行自动检测。

3.2.核心容器的更新

  1. Spring Framework 5.0 现在支持候选组件索引作为类路径扫描的替代方案。该功能已经在类路径扫描器中添加,以简化添加候选组件标识的步骤。应用程序构建任务可以定义当前项目自己的 META-INF/spring.components 文件。在编译时,源模型是自包含的, JPA 实体和 Spring 组件是已被标记的。从索引读取实体而不是扫描类路径对于小于 200 个类的小型项目是没有明显差异。但对大型项目影响较大。加载组件索引开销更低。因此,随着类数的增加,索引读取的启动时间将保持不变。 加载组件索引的耗费是廉价的。因此当类的数量不断增长,加上构建索引的启动时间仍然可以维持一个常数,不过对于组件扫描而言,启动时间则会有明显的增长。 这个对于我们处于大型 Spring 项目的开发者所意味着的,是应用程序的启动时间将被大大缩减。虽然 20 或者 30 秒钟看似没什么,但如果每天要这样登上好几百次,加起来就够你受的了。使用了组件索引的话,就能帮助你每天过的更加高效。
  2. 你可以在 Spring Jira 上了解更多关于组件索引的相关信息。

3.3. JetBrains Kotlin 语言支持

  1. Kolin概述:谷歌公司研发的,是一种支持函数式编程编程风格的面向对象语言。 Kotlin 运行在 JVM 之上,但运行环境并不<br />

限于 JVM。

  • Kolin 的示例代码:
  1. {
  2. ("/movie" and accept(TEXT_HTML)).nest {GET("/", movieHandler::findAllView)
  3. GET("/{card}", movieHandler::findOneView)
  4. }
  5. ("/api/movie" and accept(APPLICATION_JSON)).nest {
  6. GET("/", movieApiHandler::findAll)
  7. GET("/{id}", movieApiHandler::findOne)
  8. }
  9. }
  • Kolin 注册 bean 对象到 spring 容器:
  1. val context = GenericApplicationContext {
  2. registerBean()
  3. registerBean { Cinema(it.getBean()) }
  4. }

3.4. 响应式编程风格

  1. 此次 Spring 发行版本的一个激动人心的特性就是新的响应式堆栈 WEB 框架。这个堆栈完全的响应式且非阻塞,适合于事件循环风格的处理,可以进行少量线程的扩展。<br />
  2. Reactive Streams 是来自于 Netflix, Pivotal, Typesafe, Red Hat, Oracle, Twitter 以及Spray.io 的工程师特地开发的一个 API。它为响应式编程实现 的实现提供一个公共 API,好实现Hibernate JPA。这里 JPA 就是这个 API, Hibernate 就是实现。Reactive Streams API Java 9 的官方版本的一部分。在 Java 8 中, 你会需要专门引入依赖来使用 Reactive Streams APISpring Framework 5.0 对于流式处理的支持依赖于 Project Reactor 来构建, 其专门实现了Reactive Streams API。<br />
  3. Spring Framework 5.0 拥有一个新的 spring-webflux 模块,支持响应式 HTTP WebSocket 客<br />

户端 Spring Framework 5.0 还提供了对于运行于服务器之上,包含了 REST, HTML, 以及 WebSocket 风格交互的响应式网页应用程序的支持。在 spring-webflux 中包含了两种独立的服务端编程模型:基于注解:使用到了@Controller 以及 Spring MVC 的其它一些注解;使用 Java 8 lambda 表达式的函数式风格的路由和处理。有 了 Spring Webflux, 你 现 在 可 以 创 建 出 WebClient, 它 是 响 应 式 且 非 阻 塞 的 , 可 以 作 为RestTemplate 的一个替代方案。

  • 这里有一个使用 Spring 5.0 的 REST 端点的 WebClient 实现:
  1. WebClient webClient = WebClient.create();
  2. Movie movie = webClient.get().uri("http://localhost:8080/movie/42").accept(MediaType.APPLICATION_JSON).exchange().then(response -> response.bodyToMono(Movie.class));

3.5. Junit5 支持

  1. 完全支持 JUnit 5 Jupiter,所以可以使用 JUnit 5 来编写测试以及扩展。此外还提供了一个编程以及扩展模型, Jupiter 子项目提供了一个测试引擎来在 Spring 上运行基于 Jupiter 的测试。<br />
  2. 另外, Spring Framework 5 还提供了在 Spring TestContext Framework 中进行并行测试的扩展。针对响应式编程模型, spring-test 现在还引入了支持 Spring WebFlux WebTestClient 集成测试的支持,类似于 MockMvc,并不需要一个运行着的服务端。使用一个模拟的请求或者响应, WebTestClient就可以直接绑定到 WebFlux 服务端设施。<br />
  3. 你可以在这里找到这个激动人心的 TestContext 框架所带来的增强功能的完整列表。当然, Spring Framework 5.0 仍然支持我们的老朋友 JUnit! 在我写这篇文章的时候, JUnit 5 还只是发展到了 GA 版本。对于 JUnit4 Spring Framework 在未来还是要支持一段时间的。

3.6.依赖类库的更新

  • 终止支持的类库
    Portlet.
    Velocity.
    JasperReports.
    XMLBeans.
    JDO.
    Guava.

  • 支持的类库
    Jackson 2.6+
    EhCache 2.10+ / 3.0 GA
    Hibernate 5.0+
    JDBC 4.0+
    XmlUnit 2.x+
    OkHttp 3.x+
    Netty 4.1+