Spring启示录
OCP开闭原则
OCP是软件七大开发原则中最基本也是最核心的一个原则:开闭原则。
- 对扩展开放
- 对修改关闭
OCP开闭原则的核心: 在扩展系统功能的时候,没有修改以前写好的代码,那么就是符合OCP原则的。反之,如果在扩展系统的时候,修改了之前的代码,那么这个设计就是失败的,违背了OCP原则。
当进行系统功能扩展的时候,如果动了之前稳定的程序,修改了之前的程序,那之前的程序都需要进行重新测试,这将非常麻烦。
依赖倒置原则DIP
凡是上依赖下的,就是违背了依赖倒置原则。((上)表示层->业务层->持久层(下))
依赖倒置原则的核心: 倡导面向接口编程和面向抽象编程,而不是面向具体编程。
依赖倒置原则的目的: 降低程序的耦合度,提高扩展力。
控制反转IoC(编程思想/新型设计模式)
IoC控制反转 (Inversion of Control)。
反转是什么:
- 不在程序中采用硬编码的方式new对象(new对象的权利交付出去)
- 不在程序中采用硬编码的方式来维护对象的关系(关系的维护权也交付出去)
Spring框架
- Spring框架实现了控制反转的IoC这种思想。
- Spring是一个实现了IoC思想的容器。
- 控制反转的实现方式有多种:
- 依赖注入(Dependency Injection, DI)
- set注入
- 构造方法注入
- 依赖注入(Dependency Injection, DI)
- 控制反转是思想,依赖注入是这种思想的实现。
Spring的特点:
- 轻量
- 控制反转:通过控制反转技术促进了松耦合。
- 面向切面
- 容器:Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器。
- 框架:Spring可以将简单的组件配置,组合成复杂的应用。
依赖注入DI
- 依赖:A对象和B对象的关系
- 注入:是一种手段
Spring通过依赖注入的方式来完成Bean管理。
Bean管理是说:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)。
set注入
在类中写set方法,并在.xml文件中相应配置(注意方法名)。
构造注入
核心原理:通过调用构造方法来给属性赋值。
Spring的配置文件(重点、难点)
在resources下创建spring配置文件xx.xml。
其中bean标签的两个重要属性:
- id:是bean的身份证号,不能重复,是唯一的标识。
- class:必须填写类的全路径,全限定类名(带包名的类名)。
在测试类中:
- 第一步:获取Spring容器对象
- 第二步:根据bean的id从Spring容器中获取这个对象
Spring是怎么实例化对象的?
- 默认情况下Spring会通过反射机制,调用类的无参构造方法来实例化对象。
Spring框架中的简单类型:
- 基本数据类型
- String类型
- Enum枚举
- Number
- Date(java.util.Date)
- URL
- Class等
级联属性赋值:
- 在Spring配置文件中,注意顺序
- 在Spring配置文件中,相应属性必须提供getter方法
自动装配根据类型注入的本质也是根据set注入。
Bean的作用域
- Spring默认情况下是如何管理Bean的
- 默认情况下Bean是单例的
- 在Spring上下文初始化的时候实例化
- 每一次调用getBean()方法的时候,都返回那个单例的对象
- Scope属性(未全部列举)
- singleton单例(默认)
- prototype 原型/多例
- request:一次请求当中一个bean
- session:一次会话中只有一个bean
设计模式
设计模式:一种可以被重复利用的解决方案。
GoF(Gang of Four)23种设计模式。
其中,工厂模式是解决对象创建问题的,所以工厂模式属于创建型设计模式。而Spring框架底层使用了大量的工厂模式。
工厂模式的三种形态
- 简单工厂模式(Simple Factory):不属于23种设计模式之一。简单工厂模式又叫做:静态工厂方法模式。
- 工厂方法模式(Factory Method):23种设计模式之一。
- 抽象工厂模式(Abstract Factory):是23种设计模式之一。
简单工厂模式
三个角色:
- 抽象产品角色
- 具体产品角色
- 工厂类角色(静态方法)
简单工厂模式的优点:
简单工厂模式实现了职责分离,客户端不需要关心产品的生产细节。 客户端只负责消费,工厂类负责生产(生产者消费者分离)。
简单工厂模式的缺点:
- 如果需要扩展新的产品,需要修改工厂类代码,违背了OCP原则。
- 工厂类责任比较重大,不能出现任何问题,这个工厂类负责所有产品的生产,成为全能类。这个工厂类出现问题,系统将会瘫痪。
工厂方法模式(Factory Method Pattern)
工厂方法模式可以解决简单工厂模式中的OCP问题。
- 一个工厂对应生产一种产品。
四个角色:
- 抽象产品角色
- 具体产品角色
- 抽象工厂角色
- 具体工厂角色
工厂方法模式的优点:
当需要扩展一个产品的时候,符合OCP原则,因为只需要添加两个类:
- 具体产品类
- 具体工厂类
工厂方法模式的缺点:
- 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加。
- 在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
抽象工厂模式
一定程度上弥补了工厂方法模式的缺点。
Bean的实例化方式(底层都是构造方法)
- 通过构造方法实例化
- 通过简单工厂模式实例化
- 通过factory-bean实例化
- 通过工厂方法模式,使用factory-bean属性 + factory-method属性来共同完成
- 通过FactoryBean接口实例化
BeanFactory和FactoryBean的区别
BeanFactory -- 是工厂
SpringIoC容器的顶级对象,BeanFactory被翻译为“Bean工厂”,在Spring的IoC容器中,“Bean工厂”负责创建Bean对象。
FactoryBean -- 是Bean
FactoryBean:它是一个Bean,是一个能够辅助Spring实例化其他Bean对象的一个Bean。
在Spring中,Bean可以分为两类:
- 普通Bean
- 工厂Bean
Bean的生命周期
什么是Bean的生命周期
Spring其实就是一个管理Bean对象的工厂。它负责对象的创建和销毁。
所谓的生命周期就是:对象从创建开始到最终销毁的整个过程。本质是在哪个时间节点上调用了哪个类的哪个方法。
Bean的生命周期之五步
- 实例化Bean(调用无参构造方法)
- Bean属性赋值(调用set方法)
- 初始化Bean(调用Bean的init方法,需要自己写并手动指定)
- 使用Bean
- 销毁Bean(调用Bean的destroy方法,需要自己写并手动指定)
Bean的生命周期之七步
- 实例化Bean
- Bean属性赋值
执行“BeanPostProcessor”的before方法- 初始化Bean
执行“BeanPostProcessor”的after方法- 使用Bean
- 销毁Bean
Bean的生命周期之十步
- 实例化Bean
- Bean属性赋值
检查Bean是否实现了Aware接口,并设置相关依赖- 执行“BeanPostProcessor”的before方法
检查Bean是否实现了InitializingBean接口,并调用接口方法- 初始化Bean
- 执行“BeanPostProcessor”的after方法
- 使用Bean
检查Bean是否实现了DisposableBean接口,并调用接口方法- 销毁Bean
注意
Spring容器只对singleton的Bean进行完整的生命周期管理。
如果是prototype作用域的Bean,Spring容器只负责将Bean初始化完毕。等客户端程序一旦获取到该Bean之后,Spring容器就不再管理该对象的生命周期了。
Bean的循环依赖
单例和set模式下
Spring没有问题。
多例和set模式下(两个Bean的scope都是prototype)
Spring中会陷入死循环,一直new对象,出现BeanCurrentlyInCreationException(Bean正在处于创建中)异常。
构造注入模式下
创建对象的同时需要对属性赋值,不分实例化和属性赋值两个阶段,所以很显然,循环依赖也会出现BeanCurrentlyInCreationException异常,而且无法解决。
解决循环依赖的本质
在Spring + Setter模式下,为什么循环依赖不会出现问题,Spring是如何应对的?
- 主要原因是,在这种模式下Spring对Bean的管理主要分为清晰的两个阶段:
- 第一个阶段:在Spring容器加载的时候,实例化Bean,只要其中任何一个Bean实例化之后,马上进行“曝光”(即使还没有进行属性赋值)。
- 第二个阶段:Bean“曝光”之后,再进行属性的赋值(调用set方法)。
- 核心解决方案是:实例化对象和对象的属性赋值分为两个阶段来完成。
- 也就是说,Bean都是单例的,我们可以先把所有的单例Bean实例化出来,放到一个集合当中(可以称为缓存),所有的单例Bean全部实例化完成之后,再慢慢调用setter方法给属性赋值,以解决循环依赖的问题。
反射机制
调用一个方法(无论是否使用反射机制),涉及四要素:
- 调用哪个对象
- 调用哪个方法
- 调用方法的时候传什么参数
- 方法执行结束之后的返回结果
IoC注解
声明Bean的注解
- @Component
- @Controller
- @Service
- @Repository
Spring注解的使用
- 加入aop的依赖
- 在配置文件中添加context命名空间
- 在配置文件中指定扫描的包
- 在Bean类上使用注解
负责注入的注解
给Bean属性赋值需要用到这些注解:
- @Value
- 注入简单类型
- @Autowired
- 默认根据类型byType进行注入
- 接口下有多个实现类时不好用
- @Qualifier
- 和@Autowired联合使用可以根据名字进行注入
- @Qualifier指定名字
- @Resource
- 推荐使用
- 默认根据名字byName进行注入,如果找不到,启动byType
代理模式(重点、难点)
在java程序中使用代理模式的作用:
- 当一个对象需要受到保护的时候,可以考虑使用代理对象来完成某个行为
- 需要给某个对象的功能进行功能增强的时候,可以考虑找一个代理进行增强
- A对象无法和B对象直接交互时,也可以使用代理模式来解决
代理模式中的三个角色:
- 目标对象
- 代理对象
- 目标对象和代理对象的公共接口
使用代理模式的时候,对于客户端程序来说是透明的,客户端在使用代理对象的时候就像在使用目标对象。
代理模式在代码上的实现:
- 静态代理
- 将目标对象作为代理对象的一个属性,这种关系叫做关联关系,比继承关系的耦合度低。
- 代理对象中含有目标对象的引用。
- 缺点:类爆炸,假设系统中有1000个接口,那么每个接口都需要对应代理类,这样类会急剧膨胀,不好维护。
- 动态代理
- 在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目的是为了减少代理类的数量,解决代码复用的问题。
- 在内存当中动态生成类的技术包括:
- JDK动态代理技术:只能代理接口
- CGLIB动态代理技术:CGLIB(Code Generation Library)是一个开源项目,是一个强大的、高性能的、高质量的Code生成类库,它可以在运行扩展Java类与实现Java接口。它既可以代理接口,又可以代理类,底层是通过继承的方式实现的。性能比JDK动态代理要好。(底层有一个小而快的字节码处理框架ASM)
- Javassist动态代理技术:Javassist是一个开源的分析,编辑和创建Java字节码的类库。它已加入了开放源代码JBoss应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态“AOP”框架。
AOP面向切面编程(重点、难点)
IoC使软件组件松耦合。AOP让你能够捕捉系统中经常使用的功能,把它转化成组件。
AOP(Aspect Oriented Programming):底层就是使用动态代理来实现。
- JDK动态代理 +
- CGLIB动态代理技术
AOP介绍
一般一个系统当中都会有一些系统服务,例如:日志、事务管理、安全等。这些通用的系统服务被称为:交叉业务。
- 交叉业务:在业务流程当中和业务不挂钩的这种非业务逻辑通用代码
一句话总结:将与核心业务无关的代码独立的抽取出来,形成一个独立的组件,然后以横向交叉的方式应用到业务流程当中的过程被称为AOP。
AOP的优点
- 代码复用性增强
- 代码易维护
- 使开发者更关注业务逻辑
AOP七大术语
- 连接点Joinpoint -- 描述位置
- 在程序的整个执行流程中,可以织入切面的位置。方法的执行前后,异常抛出之后等位置。
- 切点Pointcut -- 本质是方法
- 在程序执行流程中,真正织入切面的方法。(一个切点对应多个连接点)
- 通知Advice -- 描述代码
- 通知又叫增强,就是具体要织入的代码。
- 通知包括:
- 前置通知 @Before
- 后置通知 @AfterReturning
- 环绕通知 @Around 环绕的通知是最大范围
- 异常通知 @AfterThrowing
- 最终通知 @After
- 切面Aspect -- 逻辑概念
- 切点 + 通知 就是切面
- 织入Weaving
- 把通知应用到目标对象上的过程
- 代理对象Proxy
- 一个目标对象被织入通知后产生的新对象
- 目标对象Target
- 被织入通知的对象
使用Spring的AOP
Spring对AOP的实现包括以下3种方式:
- Spring框架结合AspectJ框架实现的AOP,基于注解方式。
- Spring框架结合AspectJ框架实现的AOP,基于XML方式。
- Spring框架自己实现的AOP,基于XML配置方式。
AOP的实际案例
- 编程式事务
- 安全日志:记录一些危险操作
Spring对事务的支持
事务概述
什么是事务
- 在一个业务流程当中,通常需要多条DML(insert、delete、update)语句共同联合才能完成,这多条DML语句必须同时成功,或者同时失败,这样才能保证数据的安全。
- 多条DML要么同时成功,要么同时失败,这就叫做事务。
- 事务:Transaction
事务的四个处理过程
- 第一步:开启事务(start transaction)
- 第二步:执行核心业务代码
- 第三步:提交事务(如果核心业务处理过程中没有出现异常)(commit transaction)
- 第四步:回滚事务(如果核心业务处理过程中出现异常)(rollback transaction)
事务的四个特性
- A 原子性:事务是最小的工作单元,不可再分。
- C 一致性:事务要求要么同时成功,要么同时失败,事务前和事务后的总量不变。
- I 隔离性:事务和事务之间因为有隔离性,才可以保证互不干扰。
- D 持久性:持久性是事务结束的标志。
Spring实现事务的两种方式
- 编程式事务
- 通过编写代码的方式来实现事务的管理。
- 声明式事务
- 基于注解方式
- 基于XML配置方式
事务隔离级别
事务隔离级别类似于教室A和教室B之间的那道墙,隔离几杯越高表示墙体越厚,隔音效果越好。
数据库中读取数据存在的三大问题:
- 脏读:读取到没有提交到数据库的数据,叫做脏读。
- 不可重复读:在同一个事务当中,第一次和第二次读取的数据不一样。
- 幻读:读到的数据是假的。
事务隔离级别包括四个级别:
- 读未提交:READ_UNCOMMITTED
- 这种隔离级别,存在脏读问题,所谓的脏读表示能够读取到其他事务未提交的数据。
- 读提交:READ_COMMITTED
- 解决了脏读问题,其他事务提交之后才能读到,但存在不可重复读问题。
- 可重复读:REPEATABLE_READ
- 解决了不可重复读,可以达到可重复读效果,只要当前事务不结束,读取到的数据一直都是一样的,但存在幻读问题。
- 序列化:SERIALIZABLE
- 解决了幻读问题,事务排队执行,不支持并发。
事务超时
@Transactional(timeout = 0)
以上代码表示设置事务的超时时间为10秒。
表示超过10秒如果该事务中所有的DML语句还没有执行完毕的话,最终结果会选择回滚。
默认值-1,表示没有时间限制。
事务的超时时间是指哪段时间?
在当前事务当中,最后一条DML语句执行之前的时间,如果最后一条DML语句后面很多很多业务逻辑,这些业务代码执行的时间不被计入超时时间。
只读事务
@Transaction(readOnly = true)
将当前事务设置为只读事务,在该事务执行过程中只允许 select 语句执行, delete 、 insert 、 update 均不可执行。
该特性的作用:启动spring的优化策略,提高 select 语句执行效率。
如果该事务中确实没有增删改操作,建议设置为只读事务。
设置哪些异常回滚事务
@Transaction(rollbackFor = RuntimeException.class)
表示只有发生 RuntimeException 异常或该异常的子类才回滚。
设置哪些异常不回滚
@Transaction(noRollbackFor = NullPointerException.class)
表示发生 NullPointerException 或该异常的子类异常不回滚,其他异常回滚。
Spring整合MyBatis
实现步骤
- 第一步:准备数据库表
- 使用xx表
- 第二步:IDEA中创建一个模块,并引入依赖
- spring-context
- spring-jdbc
- mysql驱动
- mybatis
- mybatis-spring:mybatis提供的与spring框架集成的依赖
- druid连接池
- junit
- 第三步:基于三层架构实现,所以提前创建好所有的包
- com.xxx.mapper
- com.xxx.service
- com.xxx.service.impl
- com.xxx.pojo
- 第四步:编写pojo
- 属性私有化,提供公开的setter、getter和toString
- 第五步:编写mapper接口
- 定义方法
- 第六步:编写mapper配置文件
- 在配置文件中配置命名空间,以及每一个方法对应的sql
- 第七步:编写service接口和service接口实现类
- xxService
- xxServiceImpl
- 第八步:编写jdbc.properties配置文件
- 数据库连接池相关信息
- 第九步:编写mybatis-config.xml配置文件
- 该文件可以没有,大部分配置可以转移到spring配置文件中
- 如果遇到mybatis相关的系统级配置,还是需要这个文件
- 第十步:编写spring.xml
- 组件扫描
- 引入外部的属性文件
- 数据源
- SqlSessionFactoryBean配置
- 注入mybatis核心配置文件路径
- 指定别名包
- 注入数据库
- Mapper扫描配置器
- 指定扫描的包
- 事务管理器DataSourceTransactionManager
- 注入数据源
- 启用事务注解
- 注入事务管理器
- 第十一步:编写测试程序,并添加事务,进行测试
Spring框架的八大设计模式
简单工厂模式
BeanFactory的getBean()方法,通过唯一标识来获取Bean对象,是典型的简单工厂模式(静态工厂模式)。
工厂方法模式
FactoryBean是典型的工厂方法模式,在配置文件中通过factory-method属性来指定工厂方法,该方法是一个实例方法。
单例模式
Spring用的是双重判断加锁的单例模式。
代理模式
Spring的AOP就是使用了动态代理实现的。
装饰器模式
希望能在尽可能少修改原有类代码的情况下,动态切换不同的数据源:
- Spring根据每次请求的不同,将dataSource属性设置成不同的数据源,以达到切换数据源的目的。
Spring中类名带有:Decorator和Wrapper单词的类,就是装饰器模式。
观察者模式
定义对象间的一对多的关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。Spring中观察者模式一般用在listener的实现。
策略模式
策略模式是行为性模式,调用不同的方法,适应行为的变化,强调父类调用子类的特性。
getHandler是HandlerMapping接口中的唯一方法,用于根据请求找到匹配的处理器。
模板方法模式
Spring中的JdbcTemplate类就是一个模版类,它是一个模板方法设计模式的体现。在模板类的模板方法execute中编写核心算法,具体实现步骤在子类中完成。
总结
这节的学习跟着 动力节点 的视频学的,真的讲的挺好的!