Spring学习(为了快速入门)
Spring
Spring快速入门
-
spring框架的概述,以及spring中基于XML的IOC配置
-
1、spring的概述
- 1.1、spring是什么
- spring是分层的java SE/EE 应用 full-stack 轻量级开源框架,以IOC(Inverse Of Control:控制反转)和AOP(Aspect Oriented Programming:面向切面编程)为内核
- 1.2、spring的两大核心
- IOC
- AOP
- 1.3、spring的发展历程和优势
- 方便解耦,简化开发
- AOP编程的支持
- 声明式事务的支持
- 方便程序的测试
- 方便集成各种优势框架
- 降低JavaEE API 的使用难度
- Spring对JavaEE API(JDBC、JavaMail、远程调用等)进行了薄薄的封装层
- Java源码式经典学习范例
- 1.4、spring体系结构
- 1.1、spring是什么
-
2、程序的耦合及解耦
- 程序的耦合
- 耦合:程序间的依赖关系
- 包括
- 类之间的依赖
- 方法间的依赖
- 包括
- 解耦:
- 降低程序间的依赖关系
- 实际开发中
- 应该做到:编译器不依赖,运行时才依赖。
- 解耦的思路:
- 第一步:使用反射来创建对象,从而避免使用new关键字。
- 第二部:通过读取配置文件来获取要创建的对象全限定类名
- 耦合:程序间的依赖关系
- 2.1、曾经案例中问题
- 2.2、工厂模式解耦
- 程序的耦合
-
3、IOC概念和spring中的IOC
-
3.1、spring中基于XML的IOC环境搭建
-
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="..." class="..."> <!-- 该id属性是标识单个bean定义的字符串。 该class属性定义bean的类型,并使用完全限定的类名。 --> <!-- collaborators and configuration for this bean go here --> </bean> <bean id="..." class="..."> <!-- collaborators and configuration for this bean go here --> </bean> <!-- more bean definitions go here --> </beans>
-
-
IOC:对象由Spring来创建,管理,装配。
-
-
4、依赖注入(Dependency Injection)
-
spring中基于注解的IOC 和ioc的案例‘
-
spring中的aop和基于XML以及注解的AOP配置
-
spring中的jdbcTemlate以及Spring事务控制
Spring能做什么?
1、Spring能帮我们根据配置文件创建及组装对象之间的依赖关系。
2、Spring面向切面编程能帮助我们无耦合的实现日志记录,性能统计,安全控制。
3、Spring能非常简单的帮我们管理数据库事务
Spring框架结构
- Data Access/Integration 层包含有JDBC、ORM、OXM、JMS和Transaction模块。
- Web层包含了Web、Web-Servlet、WebSocket、Web-Porlet模块。
- AOP模块提供了一个符合AOP联盟标准的面向切面编程的实现。
- Core Container(核心容器):包含有BEans、Core、Context、SpEl模块
- Test模块支持使用JUnit和TestNG对Spring组件进行测试
Spring IoC 和 DI 简介
IoC:Inverse of Control (控制反转)
或者读作“反转控制”,是一种设计思想,将原本在程序中手动创建对象的控制器,交由Spring框架来管理。
- 正控:使用某个对象,需要自己去负责对象的创建
- 反控:使用某个对象,只需要从 Spring 容器中获取需要使用的对象,不关心对象的创建过程,也就是把创建对象的控制权反转给Spring框架,,交给Spring来创建对象
主动创造:
去奶茶店
总结:
-
传统方式:
通过new关键字主动创建对象
-
IOC方式:
让Spring去创建对象管理,我们直接去Spring那里去获取对象,控制器交给Spring
DI:Dependency Injection(依赖注入)
- Spring在创建对象的过程中,将对象依赖属性(简单值,集合,对象)通过配置设值给该对象
IOC如何实现的?
1、读取标注或者配置文件,看看 JuiceMaker依赖的是哪个Source ,拿到类名
2、使用反射的API,基于类名实例化对应的对象实例。
3、将对象实例,通过构造函数或者setter,传递给JuiceMaker
IOC升级版的工厂模式
Spring AOP简介
在数据库事务中切面编程被广泛使用
AOP:Aspect Oriented Program 面向切面编程
-
在面向切面编程思想里,把功能划分为核心业务功能,和周边功能。
-
核心业务:登录、增加数据、删除数据。。。。
-
周边功能:性能统计、日志、事务管理等等。。
周边功能在 Spring 的面向切面编程AOP思想里被定义为切面。
核心业务功能和切面功能分别独立进行开发,然后把切面功能和核心业务功能“编织”在一起,这就叫AOP
AOP的目的?
AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(事务控制,日志管理,权限控制)封装起来,便于减少系统的重复代码,降低模块间的耦合,有利于未来的可扩展性和可维护性
AOP概念:
-
切入点(Pointcut)
在哪些类,哪些方法上切入(where)
-
通知(Advice)
在方法执行的什么时机(where:方法前/方法后/方法前后)做什么(what:增强的功能)
-
切面(Aspect)
切面 = 切入点 + 通知,简言之:在什么时机,什么地方,做什么增强
-
织入(Weaving)
把切面加入到对象,并创建出代理对象的过程(由Spring完成)
AOP入门概念代码
package com.yuanwu.service;
/**
* @Author YuanWu
* @ClassName ProductService
* @Date 2020/8/7
**/
public class ProductService {
public void doSomeService(){
System.out.println("doSomeService");
}
}
在xml文件中装配bean
<bean name="productService" class="com.yuanwu.service.ProductService"/>
测试
package com.yuanwu.Test;
import com.yuanwu.service.ProductService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Author YuanWu
* @ClassName TeseSpring
* @Date 2020/8/7
**/
public class TestSpring {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
ProductService ps = (ProductService) context.getBean("productService");
ps.doSomeService();
}
}
日志切面
package com.yuanwu.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* @Author YuanWu
* @ClassName LoggerAspect
* @Date 2020/8/7
**/
public class LoggerAspect {
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("start log:" + joinPoint.getSignature().getName());
Object object = joinPoint.proceed();
System.out.println("end log:" + joinPoint.getSignature().getName());
return object;
}
}
在xml文件中声明业务对象和日志切面
<?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-->
<!-- https://www.springframework.org/schema/beans/spring-beans.xsd">-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean name="productService" class="com.yuanwu.service.ProductService"/>
<bean name="loggerAspect" class="com.yuanwu.aspect.LoggerAspect" />
<!-- 配置AOP -->
<aop:config>
<!-- where: 在那些地方(包.类.方法)做增加-->
<aop:pointcut id="loggerCutpoint" expression="execution(* com.yuanwu.service.ProductService.*(..))"/>
<!-- what: 做什么增强-->
<aop:aspect id="logAspect" ref="loggerAspect">
<!-- when: 在什么时机(方法前/方法后)-->
<aop:around pointcut-ref="loggerCutpoint" method="log"/>
</aop:aspect>
</aop:config>
</beans>
再次测试
start log:doSomeService
doSomeService
end log:doSomeService
Spring IOC详解
Spring IOC 容器的设计
Spring IOC 容器的设计主要是基于两个接口
- BeanFactory
- ApplicationContext
ApplicationContext 是 BeanFactory的子接口之一
BeanFactory
位于设计的 最底层,提供了Spring IOC 最顶层的设计
-
【getBean】对应了多个方法来获取配置给Spring IOC容器的bean
1、按照类型拿bean:
bean = (Bean)factory.getBean(Bean.class);要求在Spring中只配置了一个这种类型的实例,否则报错
2、按照bean的名字拿bean:
bean = (Bean)factory.getBean("beanName");这种方法不太安全
3、按照名字和类型拿bean:推荐
bean = (Bean)factory.getBean("beanName",Bean.class);
ApplicationContext
常见实现类:
1、 ClassPathXmlApplicationContext :
读取classpath中的资源
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
2、 FileSystemXmlApplicationContext :
读取指定路径的资源
ApplicationContext ac = new FileSystemXmlApplicationContext("c:/applicationContext.xml");
3、 XmlWebApplicationContext :
需要在Web环境下才可以运行
XmlWebApplicationContext ac = new XmlWebApplicationContext(); // 这时并没有初始化容器
ac.setServletContext(servletContext); // 需要指定ServletContext对象
ac.setConfigLocation("/WEB-INF/applicationContext.xml"); // 指定配置文件路径,开头的斜线表示Web应用的根目录
ac.refresh(); // 初始化容器
Spring IOC 的容器初始化和依赖注入
Bean的定义 和 初始化 在Spring IOC 容器中是两大步骤,先定义,然后初始化和依赖注入
-
Bean的定义分三步:
1、Resource定位:
Spring IOC容器先根据开发者的配置,进行资源的定位,在Spring开发中,通过XML或者注解都是常见的方式,定位的内容由开发者提供。
2、BeanDefinition的载入:
这个时候只是将Resource定位到的信息,保存到Bean定义(BeanDefinition)中,此时并不会创建Bean的实例。
3、BeanDefinition的注册
这个过程就是将BeanDefinition的信息发布到Spring IOC 容器中
但是此时仍然没有对应的Bean实例
对于初始化和依赖注入,Spring Bean还有一个配置选项——【lazy-init】,其含义为是否初始化Spring Bean。在没有任何配置的情况下,它是默认值default,false,即Spring IOC 默认会自动初始化Bean,如果设置为true,则只有当我们使用Spring IOC 容器的getBean方法获取它时,这时才会进行Bean初始化,完成依赖注入
Spring Bean 详解
方式选择原则
1、最优先:通过隐式Bean 的发现机制和自动装配的原则。
基于约定大于配置的原则,这种方式应该是最优先的。
- 优点:减少程序开发者的决定权,简单,灵活
2、其次:Java 接口和类中配置实现配置
- 优点:避免XML配置的泛滥,更为容易
3、XML方式配置
- 对于初学者易懂
装配简易值
<bean id="c" class="pojo.Category">
<property name="name" value="测试" />
</bean>
- id:id属性是Spring能找到当前Bean的一个依赖的编号,唯一标识符
- class:class属性是一个类的全限定类名
- property:property元素是定义类的属性,name是定义属性的名称,value是值
例子::
<!-- 配置 srouce 原料 -->
<bean name="source" class="pojo.Source">
<property name="fruit" value="橙子"/>
<property name="sugar" value="多糖"/>
<property name="size" value="超大杯"/>
</bean>
<bean name="juickMaker" class="pojo.JuiceMaker">
<!-- 注入上面配置的id为srouce的Srouce对象 -->
<property name="source" ref="source"/>
</bean>
- 注入对象:使用
ref属性
..............................................未完待续
面向切面编程【AOP模块】
AOP的一个思想:让关注点代码与业务代码分离
使用注解来开发 Spring AOP
-
🎃 第一步:选择连接点
Spring是方法级别的AOP框架,选择哪一个类的哪一个方法用以增强功能
package com.yuanwu.pojo; import org.springframework.stereotype.Component; /** * @Author YuanWu * @ClassName Landlord * @Date 2020/8/8 **/ @Component("landlord") public class Landlord { public void service(){ System.out.println("签合同"); System.out.println("收房租"); } }选择Landlord类中的service()方法作为连接点
配置xml
在bean.xml中配置自动注入,并告诉Spring IOC容器去哪里扫描这两个Bean:
<?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" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="com.yuanwu.aspect"/> <context:component-scan base-package="com.yuanwu.pojo"/> <aop:aspectj-autoproxy/> </beans> -
🎃 第二步:创建切面
在Spring 中只需要使用 @Aspect 注解类,Spring IOC容器就会认为这是一个切面
package com.yuanwu.aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* @Author YuanWu
* @ClassName Broker
* @Date 2020/8/8
**/
@Component
@Aspect
public class Broker {
@Before("execution(* com.yuanwu.pojo.Landlord.service())")
public void before(){
System.out.println("带租客看房子");
System.out.println("谈价格");
}
@After("execution(* com.yuanwu.pojo.Landlord.service())")
public void after(){
System.out.println("交钥匙");
}
}
- 这里需要注意一下: 被定义为切面的类仍然是一个Bean,需要
@Component注解标注
Spring 中的AspectJ注解:
| 注解 | 说明 |
|---|---|
@Before |
前置通知,在连接点方法前调用 |
@Around |
环绕通知,它将覆盖原有方法,但是允许你通过反射调用原有方法 |
@After |
后置通知,在连接点方法后调用 |
@AfterReturning |
返货通知,在连接点方法执行并正常返回后调用,要求连接点方法在执行过程中没有发生异常 |
@AfterThrowing |
异常通知,当连接点方法异常时调用 |
注解中间使用了定义切点的正则式,告诉SpringAOP需要拦截说明对象的说明方法
-
🎃第三步:定义切点
Spring通过这个正则表达式判断具体要拦截的是哪一个类的哪一个方法
execution(* com.yuanwu.pojo.Landlord.service())
- execution :代表执行方法的时候会触发
- ***** : 代表任意返回类型的反法
- com.yuanwu.pojo.Landlord : 代表全限定类名
- service() :被拦截的方法名称
可以使用 @Pointcut 注解来定义一个切点,来避免很多重复的表达式
package com.yuanwu.aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* @Author YuanWu
* @ClassName Broker
* @Date 2020/8/8
**/
@Component
@Aspect
public class Broker {
@Pointcut("execution(* com.yuanwu.pojo.Landlord.service())")
public void lService(){
}
//@Before("execution(* com.yuanwu.pojo.Landlord.service())")
@Before("lService()")
public void before(){
System.out.println("带租客看房子");
System.out.println("谈价格");
}
//@After("execution(* com.yuanwu.pojo.Landlord.service())")
@After("lService()")
public void after(){
System.out.println("交钥匙");
}
}
-
🎃第四步:测试
-
带租客看房子 谈价格 签合同 收房租 交钥匙
🥨 环绕通知
Spring AOP 中最强大的通知,因为它集成了前置和后置通知,保留了连接点原有方法的功能
使用 @Around 注解来同时完成前置和后置通知
@Around("execution(* com.yuanwu.pojo.Landlord.service())")
public void around(ProceedingJoinPoint joinPoint){
System.out.println("带租客看房子");
System.out.println("谈价格");
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("交钥匙");
}
使用XML配置开发SpringIOC
| AOP 配置元素 | 用途 | 备注 |
|---|---|---|
aop:advisor |
定义 AOP 的通知 | 一种很古老的方式,很少使用 |
aop:aspect |
定义一个切面 | —— |
aop:before |
定义前置通知 | —— |
aop:after |
定义后置通知 | —— |
aop:around |
定义环绕通知 | —— |
aop:after-returning |
定义返回通知 | —— |
aop:after-throwing |
定义异常通知 | —— |
aop:config |
顶层的 AOP 配置元素 | AOP 的配置是以它为开始的 |
aop:declare-parents |
给通知引入新的额外接口,增强功能 | —— |
aop:pointcut |
定义切点 | —— |
<!-- 装配Bean -->
<bean name="productService" class="com.yuanwu.service.ProductService"/>
<bean name="loggerAspect" class="com.yuanwu.aspect.LoggerAspect" />
<!-- 配置AOP -->
<aop:config>
<!-- where: 在那些地方(包.类.方法)做增加-->
<aop:pointcut id="loggerCutpoint" expression="execution(* com.yuanwu.service.ProductService.*(..))"/>
<!-- what: 做什么增强-->
<aop:aspect id="logAspect" ref="loggerAspect">
<!-- when: 在什么时机(方法前/方法后)-->
<aop:around pointcut-ref="loggerCutpoint" method="log"/>
</aop:aspect>
</aop:config>
在Spring 中使用 AspectJ注解支持
1、导入jar 包
头文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.1.xsd">
<!-- 组件扫描 -->
<context:component-scan base-package="包名"></context:component-scan>
<!-- 基于注解使用AspectJ 主要作用是为切面中通知能作用到目标类生成代理-->
<aop:aspectJ-autoproxy/>
</beans>
定义一个日志切面类
前置通知
目标方法执行之前执行
@Component //标识为一个组件
@Aspect //标识为一个切面
public class LoggingAspect{
@Before("execution(public int 全类名.方法名(参数类型,参数类型))")
public void beforeMethod(){
System.out.println("日志记录");
}
}
后置通知
目标方法执行之后执行;不管目标方法有没有抛出异常都会执行,不能获取方法的结果
execution(* com.yuanwu.anntion.类名.*(..))
*:任意修饰符
*:任意类
*:任意方法
..:任意参数
获取方法名字
连接点对象 Joinpoint
getSignature()===》方法签名
public void afterMethod(Joinpoint joinpoinr){
String methodName = joinpoint.getSignature().getName();
System.out.println("日志记录");
}
@After("execution(* 全限定类名.*(..))")
public void afterMethod(){
System.out.println("日志记录");
}
返回通知
在目标方法正常执行结束后执行;可以获取到返回值
@AfterReturning(value="execution(* 全限定类名.*(..))" ,returning="result")
//指定名字必须与参数名一致
public void afterRetueningMethod(Object result){
System.out.println("日志记录" + result);
}
异常通知
在目标方法抛出异常后执行
可以通过形参异常的类型,去设置抛出指定异常,才执行异常通知;比如指定空指针异常,才会执行异常通知
public void afterThrowingMethod(NullPointerExecption EX){
System.out.println("日志记录" + EX);
}
@AfterThrowing(value="execution(* 全限定类名.*(..))" , throwing="EX")
//指定名字必须与参数名一致
public void afterThrowingMethod(Execption EX){
System.out.println("日志记录" + EX);
}
环绕通知
环绕目标方法执行,可以理解为:前置后置返回异常,四个通知的组合体,更像是动态代理的整个过程
@Around("execution(* 全限定类名.*(..))")
public Object aroundMethod(ProceedingJoinPoint pjp){
try{
//前置通知
...
//执行目标方法
pjp.proceed();
//返回通知
...
} catch(Throwable e){
//异常通知
...
e.printStackTrace();
}finally {
//后置通知
...
}
return null;
}
@Oreder();
优先级
==重用切入点表达式==
Spring 事务管理
事务的概念:
以转账为例,必定要有两个操作:先将A账户的金额减去要转账的数目,然后将B账户加上响应的金额数目。这两个操作必定要全部完成,才表示当前转账操作成功。若有任何一方失败,则另一方必须回滚(即全部失败)。即事务:这组操作是不可分割的,要么全部成功,要么全部失败。
所谓事务管理,就是“按照给定的事务规则来执行提交或者回滚操作”;
事务的特性:
具有ACID四个特性:
原子性(Atomicity):事务是不可分割的工作单位,要么都发生,要么都不发生。
一致性(Consistency):事务在完成后数据的完整性必须保持一致。
隔离性(Isolation):多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务干扰,多个并发事务之间的数据要相互隔离;
持久性(Durability):一个事务被提交,它对数据库数据的改变应该是永久性的,即使数据库发生故障也不应该对其有任何影响;
Spring 事务管理接口:
三个高层抽象接口:PlatformTransactionManager,TransactionDefinition,TransactionStatus
1、PlatformTransactionManager事务管理器
org.springframework.transaction.PlatformTransactionManager 事务管理器接口;事实上,Spring 框架并不直接管理事务,而是通过这个接口为不同的持久层框架提供了不同的PlatformTransactionManager接口实现类,将事务管理的职责 委托给Hibernate 或者iBatis等持久化框架的事务来实现;
PlatformTransactionManager源码:
public interface PlatformTransactionManager {
//事务管理器通过TransactionStatus获得"事务状态",从而管理事务
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
//根据状态提交
void commit(TransactionStatus status) throws TransactionException;
//根据状态回滚
void rollback(TransactionStatus status) throws TransactionException;
}
在使用JDBCC或者MyBatis进行数据持久化操作时,xml配置通常:
<!-- 事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource" />
</bean>
2、TransactionDefinition 定义事务基本属性
包: org.springframework.transaction.TransactionDefinition ;用于定义一个事务。
它定义了 Spring 事务管理的五大属性:隔离级别、传播行为、是否只读、事务超时、回滚规则
2.1、隔离级别
隔离级别是用来描述并发事务之间隔离程度的大小;
脏读: 一个事务读到了另一个事务的未提交的数据;
不可重复读: 一个事务读到了另一个事务已经提交的 update 的数据,导致多次查询结果不一致;
幻读: 一个事务读到了另一个事务已经提交的 insert 的数据,导致多次查询结果不一致;
Spring 事务管理定义的隔离级别::
ISOLATION_DEFAULT:使用数据库默认的隔离级别;
ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取已改变而没有提交的数据,可能会导致脏读、幻读或不可重复读;
ISOLATION_READ_COMMITTED:允许读取事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生;
ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据事务本身改变,可以阻止脏读和不可重复读,但幻读仍有可能发生;
ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别,确保不发生脏读、不可重复读、以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的;
2.2、传播行为
事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。
例如:methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。
Spring 事务管理传播机制规定了事务方法和事务方法 发生嵌套调用时事务如何进行传播,即协调已经有事务标识的方法之间发生调用时的事务上下文的规则;
Spring 定义了七种传播行为:方法之间发生嵌套调用时如何传播事务?
PROPAGATION_REQUIRED: A如果有事务,B将使用该事务;如果A没有事务,B将创建一个新的事务;
PROPAGATION_SUPPORTS: A如果有事务,B将使用该事务;如果A没有事务,B将以非事务执行;
PROPAGATION_MANDATORY: A如果有事务,B将使用该事务;如果A没有事务,B将抛出异常;
PROPAGATION_REQUIRES_NEW: A如果有事务,将A的事务挂起,B创建一个新的事务;如果A没有事务,B创建一个新的事务;
PROPAGATION_NOT_SUPPORTED: A如果有事务,将A的事务挂起,B将以非事务执行;如果A没有事务,B将以非事务执行;
PROPAGATION_NEVER:A如果有事务,B将抛出异常;A如果没有事务,B将以非事务执行;
PROPAGATION_NESTED:A和B底层采用保存点机制,形成嵌套事务;
2.3、是否只读
如果将事务设置为只读,表示这个事务只读取数据但不更新数据,这样可以帮助数据库引擎优化事务;
2.4、事务超时
事务超时就是事务的一个定时器,在特定的时间内事务如果没有执行完毕,就会自动回滚,而不是一致等待其结束。在TransactionDefinition 中以 int 的值表示超时时机,默认值是 -1 ,单位秒
2.5、回滚规则
回滚规则定义了哪些异常会导致事务回滚或哪些不会。默认情况下,事务只有遇到运行期异常才会回滚;
3、TransactionStatus事务状态
org.springframework.transaction.TransactionStatus 接口是用来记录事务的状态,用来获取或判断事务的相应状态信息:
TransactionStatus源码:
public interface TransactionStatus extends SavepointManager, Flushable {
boolean isNewTransaction(); //是否是新的事务
boolean hasSavepoint(); //是否有回复点
void setRollbackOnly(); //设置为只回滚
boolean isRollbackOnly(); //是否为只回滚
void flush(); //刷新
boolean isCompleted(); //是否已完成
}
Spring 事务管理实现方式
Spring 事务管理有两种方式:编程式事务管理;声明式事务管理;
编程式事务管理:通过TransactionTemplate 手动管理事务;
声明式事务管理:基于TransactionProxyFactoryBean的方式、基于Aspect的XML方式、基于注解(@Transactional)的方式;代码侵入性最小,实际是通过AOP实现的;(推荐使用)
配置事务管理器
数据源 让 事务管理器 进行管理
ref="dataSource" ==>>引用数据源 id名字
<!-- 事务管理器 -->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启事务注解 -->
<tx:annotation-driven transactionManager-manager="dataSourceTransactionManager"/>
xml方式配置事务
<!-- 基于xml配置事务管理, -->
<tx:advice transaction-manager="dataSourceTransactionManager" id="toAdvice">
<!-- 配置事务属性 -->
<tx:attributes>
<!-- 具体方法使用的事务属性 -->
<tx:method name="方法名" ...事务属性/>
<!-- 约定方法的名字 -->
<!-- select开头的方法 -->
<tx:method name="select*"/>
<!-- 上述指定之外的所有方法 -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 切入点表达式 -->
<aop:pointcut expression="executiom(* 全限定类名.*.*(..))" id="toPointCut"/>
<aop:advisor advice-ref="toAdvice" pointcut-ref="toPointCut"/>
</aop:config>
动态代理
动态代理原理:
代理设计模式原理: 使用一个代理类将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理,代理对象决定是否以及何时将方法调用转到原始对象上。
动态代理的方式:
基于接口实现动态代理:JDK动态代理
基于继承实现动态代理:Cglib、javassist动态代理
1、接口:ArithmeticCalculator
package com.yuanwu.spring.before;
public interface ArithmeticCalculator {
int add(int x, int y);
int sub(int x, int y);
int nub(int x, int y);
int del(int x, int y);
}
2、接口实现类:AritheticCalculatorImpl
package com.yuanwu.spring.before;
/**
* @Author YuanWu
* @ClassName AritheticCalculatorImpl
* @Date 2020/9/28
**/
public class AritheticCalculatorImpl implements ArithmeticCalculator{
@Override
public int add(int x, int y) {
return x + y;
}
@Override
public int sub(int x, int y) {
return x - y;
}
@Override
public int nub(int x, int y) {
return x * y;
}
@Override
public int del(int x, int y) {
return x / y;
}
}
3、AritheticCalculatorImpl 代理类
package com.yuanwu.spring.before;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/**
* @Author YuanWu
* @ClassName ArithmeticCalculatorProxy
* @Date 2020/9/28
*
* 生成代理对象。
*
* JDK动态代理
* 1、Proxy :是所有动态代理类的父类,专门用于生成代理类或者是代理对象
* 1、用于生成代理类的Class对象
* public static Class<?> getProxyClass(ClassLoader loader,
* Class<?>... interfaces)
*
* 2、用于生成代理对象
* public static Object newProxyInstance(ClassLoader loader,
* Class<?>[] interfaces,
* InvocationHandler h)
* 2、InvocationHandler :完成动态代理的整个过程
* public Object invoke(Object proxy, Method method, Object[] args)
* throws Throwable;
**/
public class ArithmeticCalculatorProxy {
//动态代理 :目标对象(是谁) 如何获取代理对象 代理要做什么
//目标对象
private ArithmeticCalculator target;
public ArithmeticCalculatorProxy(ArithmeticCalculator target) {
this.target = target ;
}
//获取代理对象的方法
public Object getProxy(){
//代理对象
/**
* loader: ClassLoader对象,类加载器。帮我们加载动态生成的代理类
*
* interfaces: 多个接口。提供目标对象的所有接口,
* 目的是让代理对象保证与目标对象都有接口中相同的方法。
*
* h:InvocationHandler类型的对象
* */
ClassLoader loader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
//返回代理类的一个实例,返回的代理类可以当作被代理类使用
Object proxy = Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
/**
* invoke : 代理对象调用代理方法,会回来调用invoke方法。
*
* proxy : 代理对象,在invoke方法中一般不会使用
* method: 正在被调用的方法对象
* args : 正在被调用的方法参数
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//将方法的调用转回到目标对象上。
String methodName = method.getName();
System.out.println("LoggingProxy==>" + methodName + "begin with" + Arrays.asList(args));
//目标对象执行目标方法,相当于执行ArithmeticCalculator - + * / 方法
Object result = method.invoke(target, args);
System.out.println("LoggingProxy==>" + methodName + "ends with" + Arrays.asList(args));
return result;
}
});
return proxy ;
}
}
JDK动态代理的一般实现步骤如下:
(1)创建一个实现InvocationHandler接口的类,它必须实现invoke方法
(2)创建被代理的类以及接口
(3)调用Proxy的静态方法newProxyInstance,创建一个代理类
(4)通过代理调用方法
动态代理具体步骤:
- 通过实现 InvocationHandler 接口创建自己的调用处理器;
- 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

浙公网安备 33010602011771号