Spring
1. Spring
1.1 框架介绍
Spring 是于 2003 年兴起的一个轻量级的 Java 开发框架,它是为了解决企业应用开发 的复杂性而创建的。
Spring 的核心是控制反转(IoC)和面向切面编程(AOP)。能实现模块之间,类之间的解耦合。Spring 是可 以在 Java SE/EE 中使用的轻量级开源框架。
Spring 的主要作用就是为代码**“解耦”**,降低代码间的耦合度。就是让对象和对象(模块和模块)之间关系不是使用代码关联,而是通过配置来说明。即在 Spring 中说明对象(模块)的关系。
Spring 根据代码的功能特点,使用 Ioc 降低业务对象之间耦合度。IoC 使得主业务在相互 调用过程中,不用再自己维护关系了,即不用再自己创建要使用的对象了。而是由 Spring 容器统一管理,自动“注入”,注入即赋值。 而 AOP 使得系统级服务得到了最大复用,且 不用再由程序员手工将系统级服务“混杂”到主业务逻辑中了,而是由 Spring 容器统一完成 “织入”。
官网:https://spring.io/
1.2 优点
-
(1) 轻量
Spring 框架使用的 jar 都比较小,一般在 1M 以下或者几百 kb。Spring 核心功能的所需 的 jar 总共在 3M 左右。 Spring 框架运行占用的资源少,运行效率高。不依赖其他 jar
-
(2) 针对接口编程,解耦合
Spring 提供了 Ioc 控制反转,由容器管理对象,对象的依赖关系。原来在程序代码中的 对象创建方式,现在由容器完成。对象之间的依赖解耦合。
-
(3) AOP 编程的支持
通过 Spring 提供的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现 的功能可以通过 AOP 轻松应付 在 Spring 中,开发人员可以从繁杂的事务管理代码中解脱出来,通过声明式方式灵活地 进行事务的管理,提高开发效率和质量。
-
(4) 方便集成各种优秀框架
Spring 不排斥各种优秀的开源框架,相反 Spring 可以降低各种框架的使用难度,Spring 提供了对各种优秀框架(如 Struts,Hibernate、MyBatis)等的直接支持。简化框架的使用。 Spring 像插线板一样,其他框架是插头,可以容易的组合到一起。需要使用哪个框架,就把 这个插头放入插线板。不需要可以轻易的移除。
1.3 体系结构图
Spring 由 20 多个模块组成,它们可以分为
数据访问/集成(Data Access/Integration)
Web
面向切面编程(AOP, Aspects)
提供JVM的代理(Instrumentation)
消息发送(Messaging)
核心容器(Core Container)
测试(Test)
2. IoC 控制反转
2.1 IoC (Inversion of Control) : 控制反转, 是一个理论,概念,思想。
把对象的创建,赋值,管理工作都交给代码之外的容器实现, 也就是对象的创建是有其它外部资源完成。
-
控制: 创建对象,对象的属性赋值,对象之间的关系管理。
-
反转: 把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现。 由容器代替开发人员管理对象。创建对象,
给属性赋值。正转(之前一直使用的):由开发人员在代码中,使用new 构造方法创建对象, 开发人员主动管理对象。
public static void main(String args[]){
Student student = new Student(); // 在代码中, 创建对象。–正转。} -
容器:是一个服务器软件, 一个框架(spring)
2.2 为什么要使用 ioc : 减少对代码的改动, 也能实现不同的功能。 实现解耦合。
2.3 java中创建对象有哪些方式:
1. 构造方法 , new Student()
2. 反射
3. 序列化
4. 克隆
5. ioc :容器创建对象
6. 动态代理
2.4 ioc的体现:
servlet:
1:创建类继承HttpServelt
2:在web.xml 注册servlet , 使用
<servlet-name> myservlet </servlet-name>
<servelt-class>xxx.xxx.controller.MyServlet1</servlet-class>
- 没有创建 Servlet对象, 没有 MyServlet myservlet = new MyServlet()
- Servlet 是Tomcat服务器创建的。 Tomcat也称为容器
Tomcat作为容器:里面存放的有Servlet对象,Listener ,Filter对象
2.5 IoC的技术实现 :
DI 是ioc的技术实现
DI(Dependency Injection):依赖注入, 只需要在程序中提供要使用的对象名称就可以, 至于对象如何在容器中创建,
赋值,查找都由容器内部实现。
spring是使用的di实现了ioc的功能, spring底层创建对象,使用的是反射机制。
spring是一个容器,管理对象,给属性赋值, 底层是反射创建对象。
spring-conetxt 和 spring-webmvc是spring中的两个模块
spring-context:是ioc功能的,创建对象的。
spring-webmvc做web开发使用的, 是servlet的升级。
spring-webmvc中也会用到spring-context中创建对象的功能的。
2.6基于 XML 的 DI
2.6.1 注入分类
bean 实例在调用无参构造器创建对象后,就要对 bean 对象的属性进行初始化。初始化 是由容器自动完成的,称为注入。 根据注入方式的不同,常用的有两类:set 注入、构造注入。
-
set 注入
执行顺序:无参构造创建对象—》set方法完成赋值
- 1 ) 简单类型的set注入:spring调用类的set方法, 可以在set方法中完成属性赋值
<bean id="xx" class="yyy">
<property name=" 属性名字”value=" 此属性的值"/>
一个property只能给一个属性赋值
<property....
</bean>
set注入找的是类中的set方法,即使没有声明的变量,有set方法,依然可以执行
-
2)引用类型的set注入 : spring调用类的set方法
<bean id="xxx" class= "yyy"> <property name="属性名称" ref="bean的id(对象的名称)"/> </bean>
- 构造注入
spring调用类有参数构造方法, 在创建对象的同时,在构造方法中给属性赋值。
构造注入使用 标签
<constructor-arg>标签:一个<constructor-arg>表示构造方法一个参数。
<constructor-arg>标签属性:
name :表示构造方法的形参名
index:表示构造方法的参数的位置,参 数从左往右位置是0,1, 2的顺序
value :构造方法的形参类型是简单类型的,使用value
ref :构造方法的形参类型是引用类型的,使用ref
2.6.2 引用类型属性自动注入
- byName 方式自动注入
byName(按名称注入) : java类中引用类型的属性名和spring容器中(配置文件) 的id名称一样,且数据类型是一致的, 这样的容器中的bean,spring能够赋值给引用类型。
语法:
<bean id="xx" class="yyy" autowire= "byName">
简单类型属性赋值
</bean>
- byType 方式自动注入
byType(按类型注入) : java类中引用类型的数据类型和spring容器中(配置文件) 的class属性是同源关系的,这样的bean能够赋值给引用类型
同源就是一类的意思:
1.java类中 引用类型的数据类型和bean的class的值 是一样的。
2.java类中 引用类型的数据类型和bean的class的值 父子类关系的。
3.java类中 引用类型的数据类型和bean的class的值 接口和实现类关系的
语法:
<bean id="xx" class="yyy" autowire="byType">
简单类型属性赋值
</bean>
2.6.3 为应用指定多个 Spring 配置文件
在实际应用里,随着应用规模的增加,系统中 Bean 数量也大量增加,导致配置文件变 得非常庞大、臃肿。为了避免这种情况的产生,提高配置文件的可读性与可维护性,可以将 Spring 配置文件分解成多个配置文件。
包含关系的配置文件:
多个配置文件中有一个总文件,总配置文件将各其它子文件通过引入。在 Java 代码中只需要使用总配置文件对容器进行初始化即可。
在包含关系的配置文件中,可以通配符( * :表示任意字符)
注意:主配置文件名称不能包含在通配符的范围内
- 多个配置优势
- 每个文件的大小比一个文件要小很多。效率高
- 避免多人竞争带来的冲突。
如果你的项目有多个模块(相关的功能在一起) ,一个模块一个配置文件。
学生考勤模块一个配置文件, 张三
学生成绩一个配置文件, 李四
多文件的分配方式:
- 按功能模块,一个模块一个配置文件
- 按类的功能,数据库相关的配置一个文件配置文件, 做事务的功能一个配置文件, 做service功能的一个配置文件等
2.7 基于注解的 DI
通过注解完成java对象创建,属性赋值。
使用注解的步骤:
-
加入maven的依赖 spring-context ,在你加入spring-context的同时, 间接加入spring-aop的依赖。
使用注解必须使用spring-aop依赖 -
在类中加入spring的注解(多个不同功能的注解)
-
在spring的配置文件中,加入一个组件扫描器的标签,说明注解在你的项目中的位置.
学习的注解:
1.@Component
2.@Respotory
3.@Service
4.@Controller
5.@Value
6.@Autowired
7.@Resource
@Component:
@Component: @Component:创建对象的,等同于的功能
属性:value 就是对象的名称,也就是bean的id值,
value的值是唯一的 ,创建的对象在整个spring容器中就个
位置:在类的上面@component(value = "myStudent ”)等同于
spring中和@Component功能一致 ,创建对象的注解还有:
@Repository (用在持久层类的上面) :放在dao的实现类上面,表示创建dao对象, dao对象是能访问数据库的。@Service(用在业务层类的上面) :放在service的实现关上面创建service对象, service对象是做业务处理,可以有事务等功能的。
@Controller(用在控制器的上面) : 放在控制器(处理器)类的上面,创建控制器对象的,控制器对象,能够接受用户提交的参数,显示请求的处理结果。
以上三个注解的使用语法Component一样的。都能创建对象,但是这三个注解还有额外的功能。
@Repository,@Service,@Controller是给项目对象分层的。
@Value:
@Value: 简单类型的属性赋值
属性: value 是string类型的 ,表示简单类型的属性值
位置: 1.在属性定义的上面,无需set方法,推荐使用。
2.在set方法的上面,类中无需 setter。当然,若属性有 setter,则也可将其加 到 setter 上。
Autowired:
引用类型
@Autowired: spring框架提供的注解,实现引用类型的赋值。
spring中通过注解给引用类型赋值,使用的是自动注入原理,支持byName,byType
@Autowired:默认使用的是byType自动注入。属性:required , 是一个boolean类型的,默认true
required=true :表示引用类型赋值失败,程序报错,并终止执行。
required=false :引用类型如果赋值失败,程序正常执行,引用类型是null位置:1 )在属性定义的上面,无需set方法,推荐使用
2 )在set方法的上面如果要使用byName方式,需要做的是:
1.在属性上面加@Autowired
2.在属性上面加@Qualifier(value= “bean的id”) :表示使用指定名称的bean完成赋值
JDK 注解@Resource 自动注入:
引用类型
@Resource:来自jdk 中的注解,spring 框架提供对这个注解的功能支持,可以使用它给引用类型赋值,使用的也是自动注入原理,支持byName, byType ,默认是byName位置: 1.在属性定义的上面, 无需set方法,推荐使用。
2.在set方法的上面默认是byName :先使用byName 自动注入,如果byName赋值失败, 再使用byType
@Resource只使用byName方式, 需要增加一个属性name
name的值是bean的id(名称)
2.7.1 注解与 XML 的对比
- 注解优点是:
方便 直观 高效(代码少,没有配置文件的书写那么复杂)。
缺点:
以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的。
- XML 方式优点是:
配置和代码是分离的 在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。
缺点:
编写麻烦,效率低,大型项目过于复杂。
3.AOP
3.1 概述
AOP(Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程序运行过程。
AOP 底层,就是采用动态代理模式实现的。
采用了两种代理:JDK 的动态代理,与 CGLIB 的动态代理。
实现方式:jdk动态代理,使用jdk中的Proxy,Method,InvocaitonHanderl创建代理对象。
jdk动态代理要求目标类必须实现接口cglib动态代理:第三方的工具库,创建代理对象,原理是继承。 通过继承目标类,创建子类。
子类就是代理对象。 要求目标类不能是final的, 方法也不能是final的
AOP 是 Spring 框架中的一个重要内容。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
面向切面编程对有什么好处?
1.减少重复;
2.专注业务;
怎么理解面向切面编程 ?
1)需要在分析项目功能时,找出切面。
2)合理的安排切面的执行时间(在目标方法前, 还是目标方法后)
3)合理的安全切面执行的位置,在哪个类,哪个方法增加增强功能
注意:面向切面编程只是面向对象编程的一种补充。 使用 AOP 减少重复代码,专注业务实现:
AOP 编程术语
(1) 切面(Aspect) 切面泛指交叉业务逻辑。例如事务处理、日志处理就可以理解为切面。常用的切面 是通知(Advice)。实际就是对主业务逻辑的一种增强。
(2) 连接点(JoinPoint) 连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。 连接业务方法和切面的位置。 就某类中的业务方法。
(3) 切入点(Pointcut) 切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。 被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。
(4) 目标对象(Target) 目标对象指要被增强的对象 。即包含主业务逻辑的类的对象。上例中 的 StudentServiceImpl 的对象若被增强,则该类称为目标类,该类对象称为目标对象。当然, 不被增强,也就无所谓目标不目标了。
(5) 通知(Advice) 通知表示切面的执行时间,Advice 也叫增强。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。 切入点定义切入的位置,通知定义切入的时间。
切面有三个关键的要素:
1)切面的功能代码,切面干什么
2)切面的执行位置,使用Pointcut表示切面执行的位置
3)切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。
3.2 AspectJ
对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向 切面编程。然而,AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷,使用更为方便, 而且还支持注解式开发。所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框架中。
在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式。
官网地址:http://www.eclipse.org/aspectj/
aspectJ框架实现aop有两种方式:
1.使用xml的配置文件 : 配置全局事务
2.使用注解,我们在项目中要做aop功能,一般都使用注解, aspectj有5个注解。
aspectj框架的使用:
1)切面的执行时间, 这个执行时间在规范中叫做Advice(通知,增强)
在aspectj框架中使用注解表示的。也可以使用xml配置文件中的标签
1)@Before
2)@AfterReturning
3)@Around
4)@AfterThrowing
5)@After2)表示切面执行的位置,使用的是切入点表达式。
com.service.impl
com.bjpowrnode.service.impl
cn.crm.bjpowernode.service
3.3 切入点表达式
表达式的原型是:
execution(modifiers-pattern? ret-type-pattern
declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
以上表达式共 4 个部分。 execution(访问权限 方法返回值 方法声明(参数) 异常类型)
切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中明显就 是方法的签名。注意,表达式中黑色文字表示可省略部分,各部分间用空格分开。在其中可 以使用以下符号:
举例:
3.4 AspectJ 基于注解的 AOP 实现
- @Aspect:是aspectj框架中的注解。
作用:表示当前类是切面类。
切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
位置:在类定义的上面定义方法,方法是实现切面功能的。
方法的定义要求:
1.公共方法 public
2.方法没有返回值
3.方法名称自定义
4.方法可以有参数,也可以没有参数。
如果有参数,参数不是自定义的,有几个参数类型可以使用。
- @Before: 前置通知注解-方法有 JoinPoint 参数
属性:value,是切入点表达式 ,表示切面的功能执行的位置。
位置:在方法的上面
特点:
1.在目标方法之前先执行的
2.不会改变目标方法的执行结果
3.不会影响目标方法的执行。
- 指定通知方法中的参数: JoinPoint
JoinPoint:业务方法,要加入切面功能的业务方法
作用是:可以在通知方法中获取方法执行时的信息,例如 方法名称 ,方法的实参。
如果你的切面功能中需要用到方法的信息,就加入JoinPoint.
这个JoinPoint参数的值是由框架赋予,必须是 第一个位置的参数
-
@AfterReturning:后置通知-注解有 returning 属性
属性: 1.value切入点表达式
2.returning自定义的变量,表示目标方法的返回值的。
自定义变量名必须和通知方法的形参名一样。
位置:在方法定义的上面
特点:
1.在目标方法之后执行的。
2.能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
3.可以修改这个返回值 -
@Around: 环绕通知-增强方法有 ProceedingJoinPoint 参数
环绕通知方法的定义格式
1.public
2.必须有个返回值, 推荐使用Object
3.方法名称自定义
4.方法有参数,固定的参数ProceedingJoinPoint属性:value切入点表达式
位置:在方法的定义什么
特点:
1.它是功能最强的通知
2.在日标方法的前和后都能增强功能。
3.控制目标方法是否被调用执行
4.修改原来的目标方法的执行结果。 影响最后的调用结果环绕通知,等同jdk动态代理的,InvocationHandler接口
参数:ProceedingJoinPoint 就等同FMe thod
作用:执行目标方法的
返回值:就是目标方法的执行结果,可以被修改。
环绕通知:经常做事务,在目标方法之前开启事务,执行目标方法,在目标方法之后提交事务.
- @AfterThrowing 异常通知-注解中有 throwing 属性
属性:1. value切入点表达式
2. throwinng 自定义的变量,表示目标方法抛出的异常对象。变量名必须和方法的参数名一样
特点:
1.在目标方法抛出异常时执行的
2.可以做异常的监控程序,监控目标方法执行时是 不是有异常。
如果有异常,可以发送邮件,短信进行通知。
- @After 最终通知
最终通知方法的定义格式
1.public
2.没有返回值
3.方法名称自定义
4.方法没有参数,
如果有是JoinPoint属性: value 切入点表达式
位置:在方法的上面
特点:
1.总是会执行
2.在目标方法之后执行的
- @Pointcut 定义切入点
定义和管理切入点,如果 你的项目中有多个切入点表达式是重复的,可以复用的。可以使用@Pointcut
属性:value切入点表达式
位置:在自定义的方法上面
特点:
当使用Pointcut定义在一个方法的上面, 此时这个方法的名称就是切入点表达式的别名。
其它的通知中, value属性就可以使用这个方法名称,代替切入点表达式了。这个使用@Pointcut 注解 的方法一般使用 private 的标识方法,即没有实际作用的方法。
4. Spring 集成 MyBatis
把mybatis框架和spring集成在一起,向一个框架一样使用。
将 MyBatis 与 Spring 进行整合,主要解决的问题就是将 SqlSessionFactory 对象交由 Spring 来管理。所以,该整合,只需要将 SqlSessionFactory 的对象生成器 SqlSessionFactoryBean 注 册在 Spring 容器中,再将其注入给 Dao 的实现类即可完成整合。
实现 Spring 与 MyBatis 的整合常用的方式:扫描的 Mapper 动态代理
Spring 像插线板一样,mybatis 框架是插头,可以容易的组合到一起。插线板 spring 插 上 mybatis,两个框架就是一个整体。
- 用的技术是:ioc 。
- 为什么ioc:能把mybatis和spring集成在一起,像一个框架, 是因为ioc能创建对象。
可以把mybatis框架中的对象交给spring统一创建, 开发人员从spring中获取对象。
开发人员就不用同时面对两个或多个框架了, 就面对一个spring
mybatis使用步骤,对象
1.定义dao接口 ,StudentDao
2.定义mapper文件 StudentDao.xml
3.定义mybatis的主配置文件 mybatis.xml
4.创建dao的代理对象, StudentDao dao = SqlSession.getMapper(StudentDao.class);
List<Student> students = dao.selectStudents();
要使用dao对象,需要使用getMapper()方法
怎么能使用getMapper()方法,需要哪些条件
- 获取SqlSession对象, 需要使用SqlSessionFactory的openSession()方法。
- 创建SqlSessionFactory对象。 通过读取mybatis的主配置文件,能创建SqlSessionFactory对象
需要SqlSessionFactory对象, 使用Factory能获取SqlSession ,有了SqlSession就能有dao , 目的就是获取dao对象
Factory创建需要读取主配置文件
我们会使用独立的连接池类替换mybatis默认自己带的, 把连接池类也交给spring创建。
主配置文件:
1.数据库信息
<environment id="mydev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">//POOLED,连接池
<!--数据库的驱动类名-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--连接数据库的url字符串-->
<property name="url" value="jdbc:mysql://localhost:3306/springdb"/>
<!--访问数据库的用户名-->
<property name="username" value="root"/>
<!--密码-->
<property name="password" value="123456"/>
</dataSource>
-
mapper文件的位置
<mappers> <mapper resource="xxx/xxx/dao/StudentDao.xml"/> <!--<mapper resource="xxx/xxx/dao/SchoolDao.xml" />--> </mappers>
通过以上的说明,我们需要让spring创建以下对象
- 独立的连接池类的对象, 使用阿里的druid连接池
- SqlSessionFactory对象
- 创建出dao对象
需要学习就是上面三个对象的创建语法,使用xml的bean标签。
5. spring的事务处理
事务原本是数据库中的概念,在 Dao 层。但一般情况下,需要将事务提升到业务层, 即 Service 层。这样做是为了能够使用事务的特性来管理具体的业务。
在 Spring 中通常可以通过以下两种方式来实现对事务的管理:
(1)使用 Spring 的事务注解管理事务
(2)使用 AspectJ 的 AOP 配置管理事务
1. 什么是事务
mysql的时候,提出了事务。 事务是指一组sql语句的集合, 集合中有多条sql语句 可能是insert , update ,select ,delete, 我们希望这些多个sql语句都能成功,或者都失败, 这些sql语句的执行是一致的,作为一个整体执行。
2.在什么时候想到使用事务
当我的操作,涉及得到多个表,或者是多个sql语句的insert,update,delete。需要保证这些语句都是成功才能完成我的功能,或者都失败,保证操作是符合要求的。
在java代码中写程序,控制事务,此时事务应该放在那里呢?
service类的业务方法上,因为业务方法会调用多个dao方法,执行多个sql语句
3.通常使用JDBC访问数据库, 还是mybatis访问数据库怎么处理事务
jdbc访问数据库,处理事务 Connection conn ; conn.commit(); conn.rollback();
mybatis访问数据库,处理事务, SqlSession.commit(); SqlSession.rollback();
hibernate访问数据库,处理事务, Session.commit(); Session.rollback();
4. 3问题中事务的处理方式,有什么不足
- 不同的数据库访问技术,处理事务的对象,方法不同,需要了解不同数据库访问技术使用事务的原理
- 掌握多种数据库中事务的处理逻辑。什么时候提交事务,什么时候回滚事务
- 处理事务的多种方法。
总结: 就是多种数据库的访问技术,有不同的事务处理的机制,对象,方法。
5.怎么解决不足
spring提供一种处理事务的统一模型, 能使用统一步骤,方式完成多种不同数据库访问技术的事务处理。
使用spring的事务处理机制,可以完成mybatis访问数据库的事务处理
使用spring的事务处理机制,可以完成hibernate访问数据库的事务处理。
6.处理事务,需要怎么做,做什么
spring处理事务的模型,使用的步骤都是固定的。把事务使用的信息提供给spring就可以了
-
事务内部提交,回滚事务,使用的事务管理器对象,代替你完成commit,rollback
事务管理器是一个接口和他的众多实现类。接口:PlatformTransactionManager ,定义了事务重要方法 commit ,rollback
实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了。
mybatis访问数据库—spring创建好的是DataSourceTransactionManager
hibernate访问数据库----spring创建的是HibernateTransactionManager
怎么使用:你需要告诉spring 你用是那种数据库的访问技术,怎么告诉spring呢?
声明数据库访问技术对于的事务管理器实现类, 在spring的配置文件中使用声明就可以了
例如,你要使用mybatis访问数据库,你应该在xml配置文件中
<bean id=“xxx" class="...DataSourceTransactionManager">
- 你的业务方法需要什么样的事务,说明需要事务的类型。
- 说明方法需要的事务:
事务的隔离级别:有4个值。
DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ; Oracle默认为 READ_COMMITTED。
➢ READ_UNCOMMITTED:读未提交。未解决任何并发问题。
➢ READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
➢ REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读
➢ SERIALIZABLE:串行化。不存在并发问题。
-
事务的超时时间: TIMEOUT_DEFAULT 定义了事务底层默认的超时时限,sql 语句的执行时长。如果方法执行时超过了时间,事务就回滚。单位是秒, 整数值, 默认是 -1.
-
事务的传播行为 : 控制业务方法是不是有事务的, 是什么样的事务的。
7个传播行为,表示你的业务方法调用时,事务在方法之间是如果使用的。
PROPAGATION_REQUIRED
PROPAGATION_REQUIRES_NEW
PROPAGATION_SUPPORTS
以上三个需要掌握的
PROPAGATION_MANDATORY
PROPAGATION_NESTED
PROPAGATION_NEVER
PROPAGATION_NOT_SUPPORTED
a、 PROPAGATION_REQUIRED: 指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事务,则创建一个新事务。这种传播行为是最常见的选择,也是 Spring 默认的事务传播行为。
b、PROPAGATION_SUPPORTS 指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。
c、 PROPAGATION_REQUIRES_NEW 总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕。
- 事务提交事务,回滚事务的时机
-
当你的业务方法,执行成功,没有异常抛出,当方法执行完毕,spring在方法执行后提交事务。事务管理器commit
-
当你的业务方法抛出运行时异常或ERROR, spring执行回滚,调用事务管理器的rollback
运行时异常的定义: RuntimeException 和他的子类都是运行时异常, 例如NullPointException , NumberFormatException -
当你的业务方法抛出非运行时异常, 主要是受查异常时,提交事务
受查异常:在你写代码中,必须处理的异常。例如IOException, SQLException
Throwable 类是 Java 语言中所有错误或异常的超类。只有当对象是此类(或其子类之一) 的实例时,才能通过 Java 虚拟机或者 Java 的 throw 语句抛出。
Error 是程序在运行过程中出现的无法处理的错误,比如 OutOfMemoryError、 ThreadDeath、NoSuchMethodError 等。当这些错误发生时,程序是无法处理(捕获或抛出) 的,JVM 一般会终止线程。
程序在编译和运行时出现的另一类错误称之为异常,它是 JVM 通知程序员的一种方式。 通过这种方式,让程序员知道已经或可能出现错误,要求程序员对其进行处理。
异常分为运行时异常与受查异常。 运行时异常,是 RuntimeException 类或其子类,即只有在运行时才出现的异常。如, NullPointerException、ArrayIndexOutOfBoundsException、IllegalArgumentException 等均属于 运行时异常。这些异常由 JVM 抛出,在编译时不要求必须处理(捕获或抛出)。但,只要代 码编写足够仔细,程序足够健壮,运行时异常是可以避免的。
受查异常,也叫编译时异常,即在代码编写时要求必须捕获或抛出的异常,若不处理, 则无法通过编译。如 SQLException,ClassNotFoundException,IOException 等都属于受查异常。
RuntimeException 及其子类以外的异常,均属于受查异常。当然,用户自定义的 Exception 的子类,即用户自定义的异常也属受查异常。程序员在定义异常时,只要未明确声明定义的 为 RuntimeException 的子类,那么定义的就是受查异常。
总结spring的事务
1.管理事务的是 事务管理和他的实现类
2.spring的事务是一个统一模型
1)指定要使用的事务管理器实现类,使用
2)指定哪些类,哪些方法需要加入事务的功能
3)指定方法需要的隔离级别,传播行为,超时你需要告诉spring,你的项目中类信息,方法的名称,方法的事务传播行为。
7.spring框架中提供的事务处理方案
- 适合中小项目使用的, 注解方案。
spring框架自己用aop实现给业务方法增加事务的功能, 使用**@Transactional注解**增加事务。
@Transactional注解是spring框架自己注解,放在public方法的上面,表示当前方法具有事务。可以给注解的属性赋值,表示具体的隔离级别,传播行为,异常信息等等
使用@Transactional的步骤:
- 1.需要声明事务管理器对象
<bean id="xx" class="DataSourceTransactionManager">
- 2.开启事务注解驱动, 告诉spring框架,我要使用注解的方式管理事务。
spring使用aop机制,创建@Transactional所在的类代理对象,给方法加入事务的功能。
spring给业务方法加入事务:
在你的业务方法执行之前,先开启事务,在业务方法之后提交或回滚事务,使用aop的环绕通知
@Around("你要增加的事务功能的业务方法名称")
Object myAround(){
开启事务,spring给你开启
try{
buy(1001,10);
spring的事务管理器.commit();
}catch(Exception e){
spring的事务管理器.rollback();
}
}
- 3.在你的方法的上面加入@Trancational
- **适合大型项目,**有很多的类,方法,需要大量的配置事务,使用aspectj框架功能,在spring配置文件中
声明类,方法需要的事务。这种方式业务方法和事务配置完全分离。
实现步骤: 都是在xml配置文件中实现。
-
1.要使用的是aspectj框架,需要加入依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.5.RELEASE</version> </dependency>
-
2.声明事务管理器对象
<bean id="xx" class="DataSourceTransactionManager">
- 3.声明方法需要的事务类型(配置方法的事务属性【隔离级别,传播行为,超时】)
- 4.配置aop:指定哪些哪类要创建代理。
6. web项目中怎么使用容器对象。
-
做的是javase项目有main方法的,执行代码是执行main方法的,
在main里面创建的容器对象
ApplicationContext ctx = new ClassPathXmlApplicationContext(“applicationContext.xml”); -
web项目是在tomcat服务器上运行的。 tomcat一起动,项目一直运行的。
需求:
web项目中容器对象只需要创建一次, 把容器对象放入到全局作用域ServletContext中。
怎么实现:
使用监听器 当全局作用域对象被创建时 创建容器 存入ServletContext
监听器作用:
1)创建容器对象,执行 ApplicationContext ctx = new ClassPathXmlApplicationContext(“applicationContext.xml”);
2)把容器对象放入到ServletContext, ServletContext.setAttribute(key,ctx)
监听器可以自己创建,也可以使用框架中提供好的ContextLoaderListener
源代码:
private WebApplicationContext context;
public interface WebApplicationContext extends ApplicationContext
ApplicationContext:javase项目中使用的容器对象
WebApplicationContext:web项目中的使用的容器对象
把创建的容器对象,放入到全局作用域
key: WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
value:this.context
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
代码:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--
把数据库的配置信息,写在一个独立的文件,编译修改数据库的配置内容
spring知道jdbc.properties文件的位置-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--声明数据源DataSource, 作用是连接数据库的-->
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<!--set注入给DruidDataSource提供连接数据库信息-->
<!--使用属性配置文件中的数据,语法${key}-->
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="${jdbc.max}"/>
</bean>
<!--声明的是mybatis中提供的SqlSessionFactoryBean类, 这个类内部创建SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--set注入,把数据库连接池付给了dataSource属性-->
<property name="dataSource" ref="myDataSource"/>
<!-- mybatis主配置文件的位置
configLocation属性是Resource类型,读取配置文件
它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置
-->
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<!-- 创建dao对象 ,使用SqlSession的getMapper (studentDao.class )
MapperScannerConfigurer:在内部调用getMapper( )生成每个dao接口的代理对象。
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定sqlSessionFactory对象的id-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--指定包名,包名是dao接口所在的包名。
MapperScannerConfigurer会扫描这个包中的所有接口,把每一个接口都执行
一次getMapper()方法 得到每个接口的dao对象。
创建好的dao对象放入到spring的容器中的。dao对象的默认名称是接口名首字母小写
-->
<property name="basePackage" value="org.example.dao"/>
</bean>
<!--声明service-->
<bean id="studentService" class="org.example.service.impl.StudentServiceImpl">
<property name="studentDao" ref="studentDao"/>
</bean>
</beans>