Spring
初识spring
Spring Framework
Spring Framework系统架构
学习路线
核心容器
核心概念(IoC/DI)
当前遇到的问题
当前遇到的问题
- 业务层需要调用数据层的方法,就需要在业务层new数据层的对象
- 如果数据层的实现类发生变化,那么业务层的代码也需要跟着改变,发生变更后,都需要进行编译打包和重部署
- 所以,现在代码在编写的过程中存在的问题是:耦合度偏高
IoC(Inversion of Control)控制反转
控制反转
使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转
Spring技术对IOC思想进行了实现
Spring技术
Spring提供了一个容器,称为IOC容器,用来充当IOC思想中的"外部"
IoC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IoC容器中统称为Bean
DI(Dependency Injection)依赖注入
DI(Dependency Injection)依赖注入
在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入
- 业务层要用数据层的类对象,以前是自己
new
的 - 现在自己不new了,靠
别人[外部其实指的就是IOC容器]
来给注入进来 - 这种思想就是依赖注入
目标:充分解耦
充分解耦
- 使用IOC容器管理bean(IOC)
- 在IOC容器内将有依赖关系的bean进行关系绑定(DI)
- 最终结果为:使用对象时不仅可以直接从IOC容器中获取,并且获取到的bean已经绑定了所有的依赖关系.
小结
小结
什么IOC/DI思想?
- IOC:控制反转,控制反转的是对象的创建权
- DI:依赖注入,绑定对象与对象之间的依赖关系
什么是IOC容器?
- Spring创建了一个容器用来存放所创建的对象,这个容器就叫IOC容器
什么是Bean?
- 容器中所存放的一个个对象就叫Bean或Bean对象
IoC入门案例
导入spring的坐标spring-context,对应版本5.2.10.RELEASE
配置bean
- bean标签标示配置bean
- id属性标示给bean起名字
- class属性表示给bean定义类型
获取IoC容器 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
获取bean
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
小结
IoC入门案例(XML版)
DI入门案例
删除业务层中使用new的方式创建的dao对象
提供对应的set方法
javapublic void setBookDao(BookDao bookDao) { this.bookDao = bookDao; }
配置server与dao的关系
name="bookDao"中bookDao的作用是让Spring的IOC容器在获取到名称后,将首字母大写,前面加set找对应的setBookDao()方法进行对象注入 ref="bookDao"中bookDao的作用是让Spring能在IOC容器中找到id为bookDao的Bean对象给bookService进行注入
xml<!--dao放到service里--> <bean id = "bookService" class = "com.itheima.service.impl.BookServiceImpl"> <!--配置server与dao的关系--> <!--property标签表示配置当前bean的属性 name属性表示配置哪一个具体的属性 ref属性表示参照哪一个bean --> <property name="bookDao" ref="bookDao1"/> </bean>
小结
DI入门案例(XML版)
IOC相关内容
bean配置
bean基础配置
bean别名配置
bean作用范围配置
思考
为什么bean默认为单例?
- bean为单例的意思是在Spring的IOC容器中只会有该类的一个对象
- bean对象只有一个就避免了对象的频繁创建与销毁,达到了bean对象的复用,性能高
bean在容器中是单例的,会不会产生线程安全问题?
如果对象是有状态对象,即该对象有成员变量可以用来存储数据的,
因为所有请求线程共用一个bean对象,所以会存在线程安全问题。
如果对象是无状态对象,即该对象没有成员变量没有进行数据存储的,因方法中的局部变量在方法调用完成后会被销毁,所以不会存在线程安全问题。
哪些bean对象适合交给容器进行管理?
- 表现层对象
- 业务层对象
- 数据层对象
- 工具对象
哪些bean对象不适合交给容器进行管理?
- 封装实例的域对象
bean实例化
Spring容器在创建对象的时候也走的是构造函数,Spring底层用的是反射,Spring底层使用的是类的无参构造方法。
构造方法(常用)
静态工厂
实例工厂
使用FactoryBean实例化bean
bean生命周期
- 生命周期:从创建到消亡的完整过程
- bean生命周期:bean从创建到销毁的整体过程bean
- 生命周期控制:在bean创建后到销毁前做一些事情
public class AppForLifeCycle {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// ctx.registerShutdownHook();//注册关闭钩子 在容器未关闭之前,提前设置好回调函数,让JVM在退出之前回调此函数来关闭容器 在任何时间都可以
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
ctx.close(); // 暴力关闭
}
}
或者
bean在初始化过程中的阶段
bean在初始化过程中的阶段
- 初始化容器
- 1.创建对象(内存分配)
- 2.执行构造方法
- 3.执行属性注入(set操作)
- 4.执行bean初始化方法
- 使用bean
- 1.执行业务操作
- 关闭/销毁容器
- 1.执行bean销毁方法
bean销毁时机
DI相关内容
依赖注入
setter注入
构造器注入
选择
如何选择
介绍完两种参数的注入方式,具体我们该如何选择
- 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现强制依赖指对象在创建的过程中必须要注入指定的参数
- 可选依赖使用setter注入进行,灵活性强
- 可选依赖指对象在创建过程中注入的参数可有可无
- Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
- 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
- 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
- 自己开发的模块推荐使用setter注入
自动装配
IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
方式
- 按类型(常用)
- 按名称
- 按构造方法
- 不启用自动装配
特征
- 自动装配用于引用类型依赖注入,不能对简单类型进行操作
- 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
- 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
- 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
集合注入
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" scope="prototype">
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
<property name="list">
<list>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
</list>
</property>
<property name="set">
<set>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>boxuegu</value>
</set>
</property>
<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="henan"/>
<entry key="city" value="zhengzhou"/>
</map>
</property>
<property name="properties">
<props>
<prop key="country">china</prop>
<prop key="province">henan</prop>
<prop key="city">zhengzhou</prop>
</props>
</property>
</bean>
案例:数据源对象管理
加载properties文件
容器
总结
容器相关
- BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载
- ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载
- ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能
- ApplicationContext接口常用初始化类
- ClassPathXmlApplicationContext(常用)
- FileSystemXmlApplicationContext
bean相关
依赖注入相关
注解开发
注解开发定义bean
注解开发bean管理
bean作用范围
bean生命周期
依赖注入
读取properties文件
第三方bean管理
第三方bean管理
第三方bean依赖注入
总结
注解开发总结
整合
spring整合Mybatis
Sprirng整合Junit
AOP
简介
- AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构。
- OOP(Object Oriented Programming)面向对象编程
我们都知道OOP是一种编程思想,那么AOP也是一种编程思想,编程思想主要的内容就是指导程序员该如何编写程序,所以它们两个是不同的编程范式
。
AOP作用
- 作用:在不惊动原始设计的基础上为其进行功能增强,前面有技术就可以实现这样的功能即代理模式。
前面有技术就可以实现这样的功能即代理模式
。
AOP核心概念
入门案例
AOP工作流程
流程1:Spring容器启动
- 容器启动就需要去加载bean,哪些类需要被加载呢?
- 需要被增强的类,如:BookServiceImpl
- 通知类,如:MyAdvice
- 注意此时bean对象还没有创建成功
流程2:读取所有切面配置中的切入点
- 上面这个例子中有两个切入点的配置,但是第一个
ptx()
并没有被使用,所以不会被读取。
流程3:初始化bean,
判定bean对应的类中的方法是否匹配到任意切入点
注意第1步在容器启动的时候,bean对象还没有被创建成功。
要被实例化bean对象的类中的方法和切入点进行匹配
- 匹配失败,创建原始对象,如
UserDao
- 匹配失败说明不需要增强,直接调用原始对象的方法即可。
- 匹配成功,创建原始对象(==目标对象==)的==代理==对象,如:
BookDao
- 匹配成功说明需要对其进行增强
- 对哪个类做增强,这个类对应的对象就叫做目标对象
- 因为要对目标对象进行功能增强,而采用的技术是动态代理,所以会为其创建一个代理对象
- 最终运行的是代理对象的方法,在该方法中会对原始方法进行功能增强
- 匹配失败,创建原始对象,如
流程4:获取bean执行方法
- 获取的bean是原始对象时,调用方法并执行,完成操作
- 获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作
AOP切入点表达式
通知类型
前置通知
后置通知
环绕通知(重点)
返回后通知(了解 )
抛 出异常后通知(了解)
AOP通知获取数据
获取参数
获取返回值
获取异常
AOP总结
AOP的核心概念
- 概念:AOP(Aspect Oriented Programming)面向切面编程,一种编程范式
- 作用:在不惊动原始设计的基础上为方法进行功能==增强==
- 核心概念
- 代理(Proxy):SpringAOP的核心本质是采用代理模式实现的
- 连接点(JoinPoint):在SpringAOP中,理解为任意方法的执行
- 切入点(Pointcut):匹配连接点的式子,也是具有共性功能的方法描述
- 通知(Advice):若干个方法的共性功能,在切入点处执行,最终体现为一个方法
- 切面(Aspect):描述通知与切入点的对应关系
- 目标对象(Target):被代理的原始对象成为目标对象
切入点表达式
切入点表达式标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)
execution(* com.itheima.service.*Service.*(..))
切入点表达式描述通配符:
- 作用:用于快速描述,范围描述
*
:匹配任意符号(常用)..
:匹配多个连续的任意符号(常用)+
:匹配子类类型
切入点表达式书写技巧
1.按==标准规范==开发 2.查询操作的返回值建议使用*匹配 3.减少使用..的形式描述包 4.==对接口进行描述==,使用*表示模块名,例如UserService的匹配描述为*Service 5.方法名书写保留动词,例如get,使用*表示名词,例如getById匹配描述为getBy* 6.参数根据实际情况灵活调整
五种通知类型
- 前置通知
- 后置通知
- 环绕通知(重点)
- 环绕通知依赖形参ProceedingJoinPoint才能实现对原始方法的调用
- 环绕通知可以隔离原始方法的调用执行
- 环绕通知返回值设置为Object类型
- 环绕通知中可以对原始方法调用过程中出现的异常进行处理
- 返回后通知
- 抛出异常后通知
通知中获取参数
- 获取切入点方法的参数,所有的通知类型都可以获取参数
- JoinPoint:适用于前置、后置、返回后、抛出异常后通知
- ProceedingJoinPoint:适用于环绕通知
- 获取切入点方法返回值,前置和抛出异常后通知是没有返回值,后置通知可有可无,所以不做研究
- 返回后通知
- 环绕通知
- 获取切入点方法运行异常信息,前置和返回后通知是不会有,后置通知可有可无,所以不做研究
- 抛出异常后通知
- 环绕通知
事务
简介
事务作用:在数据层保障一系列的数据库操作同成功同失败
Spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败
举个简单的例子,
- 转账业务会有两次数据层的调用,一次是加钱一次是减钱
- 把事务放在数据层,加钱和减钱就有两个事务
- 没办法保证加钱和减钱同时成功或者同时失败
- 这个时候就需要将事务放在业务层进行处理。
Spring为了管理事务,提供了一个平台事务管理器PlatformTransactionManager
commit是用来提交事务,rollback是用来回滚事务。
PlatformTransactionManager只是一个接口,Spring还为其提供了一个具体的实现:
转账案例-需求分析
需求: 实现任意两个账户间转账操作
需求微缩: A账户减钱,B账户加钱
为了实现上述的业务需求,我们可以按照下面步骤来实现下: ①:数据层提供基础操作,指定账户减钱(outMoney),指定账户加钱(inMoney)
②:业务层提供转账操作(transfer),调用减钱与加钱的操作
③:提供2个账号和操作金额执行转账操作
④:基于Spring整合MyBatis环境搭建上述操作
转账案例-环境搭建
事务角色
- 事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法
- 事务协调员:加入事务方,在Spring中通常指代数据层方法,也可以是业务层方法
目前的事务管理是基于DataSourceTransactionManager
和SqlSessionFactoryBean
使用的是同一个数据源。