Spring系统架构

spring integration用例_spring

  • Core Container:核心容器
  • AOP:面向切面编程
  • Aspects:AOP思想实现
  • Data Access:数据访问
  • Data Integration:数据集成
  • Transaction:事务
  • Web:Web开发
  • Test:单元测试与集成测试

Spring学习线路

1.核心容器

        核心概念:IOC/DI

        容器基本操作

2.整合Mybatis

3.AOP

4.事务

一:配置文件

核心容器

由于在类里面写了其他的一些实现,导致耦合度偏高 。

spring integration用例_spring_02

  解决方案:使用对象时,在程序中不要主动使用new产生对象,转换由外部提供对象。引出IOC。

目标:充分解耦。使用IOC容器管理bean,在IOC容器内将有依赖关系的bean进行关系绑定。

        

1.核心概念

IOC  :使用对象时,由主动new产生对象转换为由外部提供对象,对象的创建控制权由程序转移到外部,这种思想称为控制反转。就是解耦。       

IOC容器  :(Core Container)Spring提供了这个容器,用来充当IOC思想中的”外部“。

这两个就是同一个”外部“。

Bean :IOC容器负责对象的创建,初始化等一系列工作,被创建或被管理的对象在IOC容器中统称为bean。

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

spring integration用例_后端_03

2.IOC案例

2.1 思路

2.1.1管理什么?Service和Dao

2.1.2 如何将被管理的对象告诉IOC容器?配置

2.1.3 被管理的对象交给IOC容器,如何获取到IOC容器?接口

2.1.4 IOC容器得到后,如何从容器中获取bean?接口方法

2.1.5 使用Spring导入哪些坐标?pom.xml

2.2 步骤

①在pom.xml导入spring的坐标spring-context

spring integration用例_后端_04

②定义sping管理的类

spring integration用例_spring_05

③ 在applicationContect.xml中写配置文件,把要管理的对象交给IOC容器(配置bean),配置对应类作为spring管理的Bean

spring integration用例_java_06

注意:bean定义时id属性在同一个上下文中不能重复

④ 要想拿到bean,就要获取IOC容器(接口),初始化IOC容器,通过容器获取bean

spring integration用例_加载_07

⑤ 从容器中获取bean,调用接口方法

spring integration用例_java_08

.idea是什么?

        .idea是IntelliJ IDEA工程的配置文件和目录。当在使用IntelliJ IDEA创建一个新的项目时,它会自动在项目的根目录下创建一个.idea目录。该目录包含了项目的配置信息,包括项目的结构、依赖项、编译选项、版本控制设置等。

        .idea目录中的文件和子目录通常是自动生成的,不需要手动编辑或修改。这些文件和目录的作用是帮助IntelliJ IDEA正确地加载和配置你的项目。

        .idea目录中常见的一些文件和目录:

  • workspace.xml:保存了IntelliJ IDEA工作区的全局配置,包括窗口布局、插件设置等。
  • modules.xml:保存了项目的模块配置信息,包括模块的依赖关系、源代码路径等。
  • libraries目录:保存了项目的依赖库配置信息,包括项目使用的外部库和框架。
  • runConfigurations目录:保存了运行和调试配置信息,包括启动项目的配置参数、环境变量等。
  • vcs.xml:保存了版本控制系统的配置信息,例如Git或SVN的设置。

spring integration用例_spring_09

spring integration用例_后端_10

Dependency 'org.springframework:spring-context:5.2.10.RELEASE' not found

spring integration用例_加载_11

解决办法

spring integration用例_加载_12

Maven的用户设置文件(User Settings File)是一个XML文件,用于配置Maven的全局设置和个人偏好。它包含了Maven运行时所需的一些配置信息,比如本地仓库路径、远程仓库的配置、代理设置、插件配置等。


将Maven的用户设置文件更改为Local repository后,pom.xml文件中的项目不再显示红色可能是由于以下原因:

  1. 本地仓库路径正确:如果你正确地将Maven的用户设置文件中的本地仓库路径更改为实际的本地仓库路径,那么Maven将能够正确地解析和检索项目所需的依赖项。这将消除在pom.xml文件中引用的依赖项无法解析而导致的红色警告。
  2. 依赖项被正确解析:Maven使用本地仓库来存储已下载的依赖项。当你将Maven的用户设置文件更改为本地仓库后,Maven将从本地仓库中解析和加载项目所需的依赖项。如果依赖项成功解析并可用,那么pom.xml文件中的项目将不再显示红色。

java: 错误: 不支持发行版本 5

上述代码放到maven\conf\setting.xml中

spring integration用例_后端_13

3.DI入门案例

3.1 思路

①基于IOC管理Bean

②Service中使用new形式创建的Dao对象不保留(如果保留,耦合度必然很高)

③Service中需要的Dao对象如何进入到Service中?提供方法

④Service与Dao之间的关系如何描述?配置

3.2 步骤

⑥ 删除业务层中使用new的方式创建的dao对象

spring integration用例_spring_14

⑦删除之后怎么创建dao对象呢?提供对应的set方法

⑧ 配置service与dao的关系

spring integration用例_java_15

注意:这两个dao不一样

spring integration用例_配置文件_16

Bean的配置

1.bean基础配置

id用来定义bean的名称

class用来定义bean的类型

spring integration用例_java_17

2.bean的别名配置

spring integration用例_spring_18

spring integration用例_spring_19

使用到未出现的别名时会爆出如下异常

spring integration用例_java_20

spring integration用例_后端_21

如果name中正确定义了别名,就不会显示这个异常。

3.bean作用范围控制

spring integration用例_加载_22

在不写这个scope的时候,默认就是singleton(单例对象)。

①为什么bean默认为单例?

Spring在帮我们管理对象的时候,就是帮助我们管理那些可以复用的对象。用一次还会用它,还会从容器中拿,这样效率才会高。

②哪些bean适合造单例?

表现层对象,业务层对象,数据层对象,工具对象


③不适合交给容器进行管理的bean?

封装实体的域对象

通过这个scope可以控制这个对象是否为单例对象。

spring integration用例_配置文件_23

spring integration用例_java_24

bean实例化/bean是如何创建出来的?

1.构造方法(常用)

bean本质上就是对象,创建bean使用构造方法完成。且调用的是无参构造方法。

spring integration用例_spring_25

无参构造方法如果不存在,将抛出异常BeanCreationException。

且用私有的构造方法也可以。

spring integration用例_java_26

报错怎么看?

对于Spring的报错信息,一般拉到异常的最后面,看Caused by

spring integration用例_spring_27

2.通过使用静态工厂创建对象(了解即可)

spring integration用例_后端_28

3.实例工厂(了解)

spring integration用例_后端_29

4.代替原始实例工厂创建对象的方法(实用)

spring integration用例_配置文件_30

spring integration用例_java_31

bean生命周期

生命周期:从创建到消亡的完整过程。

bean生命周期:bean从创建到销毁的整体过程。

bean生命周期控制:在bean创建后到销毁前做一些事情。

1.生命周期控制方法

一:配置生命周期控制方法

① 实现这两个方法

spring integration用例_配置文件_32

② 要想看到对应的destroy方法的执行,必须要关闭容器。

close关闭相对暴力,而钩子函数在虚拟机退出之前回调此函数。

spring integration用例_加载_33

二:接口控制(了解)

spring integration用例_后端_34

2.bean在整个初始化过程中经理了哪些阶段?

spring integration用例_加载_35

spring integration用例_后端_36

spring integration用例_spring_37

初始化容器

  • 创建对象(内存分配)
  • 执行构造方法
  • 执行属性注入(set操作)
  • 使用bean初始化方法

使用bean

  • 执行业务操作

③ 关闭/销毁容器

  • 执行bean销毁方法

3.bean销毁时机

容器关闭前触发bean的销毁。关闭容器方式:

spring integration用例_spring_38

依赖注入

spring integration用例_java_39

setter注入:

① 引用类型

spring integration用例_加载_40

② 简单类型

spring integration用例_spring_41

构造器注入:

① 引用类型:把setter方法删了换成构造器方法(了解)

spring integration用例_后端_42

并且在配置文件中改变标签,把property标签改成constructor-arg标签

spring integration用例_后端_43

② 简单类型同理(了解)

问题来了。

如果这里面的参数的名称变了

spring integration用例_java_44

那么配置文件里面这个也要跟着变,所以耦合度很高。

spring integration用例_spring_45

所以第一种解决方案:把name改成type,不就解耦了吗?但是。。。如果两种类型都是String或者int呢?

spring integration用例_spring_46

第二种解决:直接写形参下标?看似解决参数类型重复问题,使用位置解决参数匹配。但是不靠谱。

spring integration用例_加载_47

依赖注入方式选择

① 强制使用依赖使用构造器进行。使用setter注入有概率不进行注入导致null对象出现。

强制依赖:bean运行必须要用的构造器处理。

② 可选依赖使用setter注入进行,灵活性强。

③ Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的方式进行数据初始化,相对严谨。

④ 如果有必要可以两者同时使用,使用构造器注入完成依赖强制项的注入,使用setter注入完成可选依赖的注入。

⑤ 实际开发过程中话要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入。

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

依赖自动装配

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

自动装配方式:

① 按类型(常用) 

autowire属性开启自动装配。

spring integration用例_后端_48

配置bookDao的bean必须要写,而且也只能写一个。

你说如果你不写或者说写两个,它去装配谁?

spring integration用例_后端_49

spring integration用例_spring_50

提示:

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

② 按名称

③ 按构造方法

④ 不启动自动装配

集合注入

<?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 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>
</beans>

案例

数据源对象管理(Spring管理第三方资源)

①导入druid坐标,

spring integration用例_加载_51

②配置数据源对象作为spring管理的bean 。

spring integration用例_后端_52

引出问题:配置写在配置文件中不合适,应该单独抽出来。

加载properties文件

①在配置文件要想读取到properties的信息,得先去加载这个文件。Spring加载这个文件方式比较特殊,先去开启一个全新context的命名空间(灰色的)。

spring integration用例_配置文件_53

spring integration用例_配置文件_54

②使用context空间加载properties文件

spring integration用例_配置文件_55

③使用属性占位符¥{}读取properties文件中的属性。如上

spring integration用例_后端_56

问题:如果改成username,那么和系统中属性名称同名,在加载配置文件时就加一个属性并设置为NEVER。

spring integration用例_java_57

spring integration用例_加载_58

如果有多个properties,最标准的方式如下:

spring integration用例_配置文件_59

总结以上几种:

spring integration用例_加载_60

容器

1.创建容器

方式①:加载类路径下的配置文件

spring integration用例_配置文件_61

方式②:从文件系统下(绝对路径)加载配置文件 

2.获取bean

spring integration用例_配置文件_62

3.容器类层次结构

spring integration用例_后端_63

4.BeanFactory

spring integration用例_spring_64

核心容器总结

一.容器相关

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

二.bean相关

spring integration用例_spring_65

三.依赖注入相关

spring integration用例_加载_66

二.使用注解开发

注解开发

1.注解开发定义bean

①不采用配置文件的形式,采用注解的话,class里是全类名,既然对这个类进行配置(BookDaoImpl),就要考虑在这个类中去使用注解。

spring integration用例_加载_67

②在类上加上注解,把注解写上就代表了配置里面的<bean>,就配置好了bean,但是没有起名称。

spring integration用例_spring_68

③起名称

spring integration用例_加载_69

点开component的源码,有一个value()属性,由于可以默认不写,就直接如上图。

spring integration用例_java_70

④问题来了,Spring的配置文件怎么指知道它写了这一行话呢?在核心配置文件中通过扫描组件加载bean,指定这个注解所在的类的位置。

<context:component-scan base-package="com.itheima"/>

Spring提供@Component注解的三个衍生注解

  • @Controller  :用于表现层bean定义
  • @Service :用于业务层bean定义
  • @Repository (数据仓库):用于数据层bean定义

2.纯注解开发

Spring3.0升级了纯注解开发模式,使用Java类替代配置文件,开启了Spring快速开发赛道。

提问:有下面一句话,既然要用纯注解开发,所以要把这一行用类来代替。

spring integration用例_后端_71

所以现新建一个类,使用配置类。加上这个注解就代表是一个配置类。

spring integration用例_java_72

这个注解代表了这些东西。

spring integration用例_spring_73

但是这个还没有发过去。

spring integration用例_后端_74

再加上一个注解,扫描component。

spring integration用例_配置文件_75

现在这个程序不能运行了,因为它是加载配置文件的。

spring integration用例_spring_76

重新写一个程序加载配置类。

spring integration用例_spring_77

总结 :在纯注解开发中,用Java类代替Spring核心配置文件。

spring integration用例_配置文件_78

spring integration用例_后端_79

多个数据用数组形式,用大括号隔开。

spring integration用例_加载_80

bean管理

1.bean作用范围

spring integration用例_配置文件_81

2.bean生命周期

spring integration用例_加载_82

依赖注入

1.引用类型注入

spring integration用例_配置文件_83

spring integration用例_java_84

2.简单类型注入

spring integration用例_spring_85

spring integration用例_后端_86

第三方bean

1.第三方bean管理

管理第三方bena,由于不能把配置写在别人的源代码中,只能在配置类里搞了。

①定义一个方法获得bean,要管理的对象。

②添加Bean,表示当前方法的返回值是一个bean。

spring integration用例_spring_87

但是所有的配置类都写在这里面,写爆了咋整?

方法:使用独立的配置类管理第三方bean。如将上面的重新使用独立的配置类命名为JdbcConfig。

spring integration用例_配置文件_88

扫描式不建议。

spring integration用例_后端_89

问题来了,如果上面那个bean运行的时候,还需要依赖其他的bean怎么办?

2.第三方bean依赖注入

spring integration用例_加载_90

spring integration用例_配置文件_91

注解开发总结

spring integration用例_配置文件_92

三.整合Mybatis

spring integration用例_spring_93

spring integration用例_配置文件_94

将配置文件进行转换

spring integration用例_后端_95

spring integration用例_配置文件_96

整合junit

spring integration用例_加载_97

四.AOP

AOP,面向切面编程,一种编程范式,指导开发者如何组织程序结构。

作用:在不惊动原始设计的基础上为其进行功能增强

看例子。update,delete,select方法是不是没有业务执行万次的方法?

spring integration用例_java_98

现在来看打印结果。现在update没有那个方法为什么打印了上万次啊?说明通知和切入点之间绑定了关系

spring integration用例_后端_99

在不惊动原始设计的基础上,想为谁增加功能就为谁增加功能,这就是AOP。体现了Spring理念无入侵式/无侵入式

spring integration用例_spring_100

连接点:原始方法,或者说制作出来的功能,例如save(),update()...

切入点:要追加功能的方法。要执行对应通知的方法。

通知:要让大家都有的功能。

切面:上面说的,通知和切入点之间有关系,把他们绑到了一块。

说一下切入点和连接点的区别:连接点是所有方法,而切入点是匹配某些方法。

代理(Proxy):SpringAOP的核心本质是采用代理模式实现的。

它允许通过创建一个代理对象来控制对原始对象的访问。

代理模式的核心思想是:代理对象与原始对象实现相同的接口,从而可以完全替代原始对象。当客服端调用代理对象的方法时,代理对象可以在调用前后执行额外的逻辑。

标准AOP核心概念:

spring integration用例_配置文件_101

AOP入门案例

思路分析:

spring integration用例_配置文件_102

1.导入坐标(pom.xml) 

//导入aop的坐标,导入这个自动导入aop
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>

    //导入aspect的坐标
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.4</version>
    </dependency>

spring integration用例_spring_103

2.制作连接点方法

spring integration用例_spring_104

3.制作切入点方法

spring integration用例_后端_105

说明:切入点定义依托一个不具有实际意义的方法进行,即无参数,无返回值,方法无实际逻辑。

4.共性功能就是上面的method()

提出问题:现在能运行吗?不能,必须得受Spring控制,加个component,让他变成spring控制的bean。并且要告诉spring,扫描到以后,把这个当成aop处理。还要加个注解aspect

spring integration用例_spring_106

@Before,指定通知添加到原始连接点的具体执行位置。

在配置类里面还不知道aop是注解开发的。在配置类加上注解EnableAspectJAutoProxy。

spring integration用例_spring_107

这个EnableAspectJAutoProxy启动了@Aspect,而这个配置告诉你要识别哪些。

AOP工作流程

spring integration用例_加载_108

spring integration用例_后端_109

目标对象(Target):原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的。

代理(Proxy):目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现。

AOP切入点表达式

切入点:要进行增强的方法。

切入点表达式:要进行增强的方法的描述的表达式。

spring integration用例_java_110

标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)

eg:execution(public User com.itheima.service.UserService.findById(int))

标注颜色的可以省略。

通配符 :快速描述

*单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现。

execution(public * com.itheima.*.UserService.find*(*))         //有*必带参数

..:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写

execution(public User com..UserService.findById(..))

+

execution(* *..*Service+.*(..))

书写技巧

spring integration用例_加载_111

eg:execution(* com.itheima.*.*Service.save(..))

AOP通知类型

AOP通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要加到合理的位

前置通知:

后置通知

环绕通知

spring integration用例_加载_112

spring integration用例_加载_113

返回后通知

抛出异常后通知

AOP案例:测量业务层接口万次执行效率

业务功能:业务层接口执行前后分别记录时间,求差值得到执行效率

通知类型选择前后均可以增强的类型---环绕通知

spring integration用例_配置文件_114

补充说明:当前测试的接口执行效率仅仅时一个理论值,并不是一次完整的执行过程。

AOP通知获取数据

1.获取切入点方法的参数

JoinPoint:适用于前置,后置,返回后,抛出异常前通知

ProceedJoinPoint:适用于环绕通知

spring integration用例_spring_115

2.获取切入点方法返回值

返回后通知

环绕通知

spring integration用例_spring_116

3.获取切入点方法运行异常信息

抛出异常后通知

环绕通知

spring integration用例_后端_117

案例:百度网盘密码数据兼容处理

spring integration用例_配置文件_118

正常情况下在未处理空格时的结果应该是false。

现在使用AOP使得在不惊动原始设计的基础上,想为其增加处理空格的功能。典型的AOP技术。

① 开启注解

spring integration用例_后端_119

② 加上两注解,写切入点。

spring integration用例_配置文件_120

注意:要把处理后的参数传进去。

spring integration用例_后端_121

否则得到的结果就是未处理空格前的长度。

五:Spring事务

事务作用:在数据层保障一系列额数据库操作同时成功或者同时失败。

Spring事务作用:在数据层或业务层保障一系列额数据库操作同时成功或者同时失败。

案例:模拟银行转账

spring integration用例_配置文件_122

模拟异常后,操作失败。

spring integration用例_spring_123

引出事务。①在业务层接口上写@Transactional 

spring integration用例_配置文件_124

注意事项:Spring注解式事务通常添加到业务层接口中而不会添加到业务层实现类中,降低耦合。

注解式事务可以添加到业务方法上表示当前方法开启事务,也可以添加到接口上表示当前接口所有方法开启事务。

② 开了事务,但是用什么样的事务管理器未说。在配置类说明。

spring integration用例_spring_125

事务管理器要根据实现技术进行选择。

Mybatis框架使用的是JDBC事务。

③ Spring还不知道使用注解的形式进行事务操作。

spring integration用例_spring_126

Spring事务角色

spring integration用例_加载_127

事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法。

事务协调员:加入事务方,在Spring中通常指代数据层方法,也可以是业务层方法。

事务相关配置

spring integration用例_spring_128

看@Transactional注解源码。可以在注解上加上一些东西,属性。

spring integration用例_后端_129

重点掌握事务回滚异常,有些异常是不会默认回滚的。

①非受检异常(Unchecked Exception):当事务中发生非受检异常(如NullPointerExceptionIllegalArgumentException等)时,事务不会回滚。这是因为非受检异常通常表示程序错误或逻辑错误,回滚事务可能无法解决这些问题。

②Error类异常:当事务中发生Error类异常(如OutOfMemoryErrorStackOverflowError等)时,事务不会回滚。Error类异常通常表示严重的系统错误,回滚事务可能无法恢复系统的正常运行。

③事务提交前的异常:如果事务在提交之前发生异常,事务不会回滚。这是因为在提交之前,事务的修改操作尚未持久化到数据库中,因此无需回滚。

 ④不受事务管理的方法:如果一个方法没有被事务管理器管理,即没有被@Transactional注解标记或配置为事务处理的方法,那么其中的异常不会引发回滚。

以上是一般情况下的规则,具体的回滚行为还取决于所使用的事务管理器和数据库。

spring integration用例_java_130

spring integration用例_后端_131

所以转账日志必须保留,这个必须单独开启一个事务。引出事务传播行为

spring integration用例_java_132

事务传播行为:事务协调员对事务管理员所携带事务的处理态度。

在注解上加上属性,表示新开启一个事务。

spring integration用例_后端_133

propagation对应的值:

spring integration用例_加载_134

上面就是把默认值改成新开启一个事务。