spring 是目前主流的 Java Web 开发框架,是 Java 世界最为成功的框架。该框架是一个轻量级的开源框架,具有很高的凝聚力和吸引力。
Spring 由 Rod Johnson 创立,2004 年发布了 Spring 框架的第一版,其目的是用于简化企业级应用程序开发的难度和周期。
Spring 是分层的 Java SE/EE 一站式轻量级开源框架,以 IoC(Inverse of Control,控制反转)和 AOP(Aspect Oriented Programming,面向切面编程)为内核。
IoC 指的是将对象的创建权交给 Spring 去创建。使用 Spring 之前,对象的创建都是由我们使用 new 创建,而使用 Spring 之后,对象的创建都交给了 Spring 框架。AOP 用来封装多个类的公共行为,将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,减少系统的重复代码,降低模块间的耦合度。另外,AOP 还解决一些系统层面上的问题,比如日志、事务、权限等
1)方便解耦,简化开发
Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给 Spring 管理。
2)方便集成各种优秀框架
Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如 Struts2、Hibernate、MyBatis 等)的直接支持。
3)降低 Java EE API 的使用难度
Spring 对 Java EE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等)都提供了封装,使这些 API 应用的难度大大降低。
4)方便程序的测试
Spring 支持 JUnit4,可以通过注解方便地测试 Spring 程序。
5)AOP 编程的支持
Spring 提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。
6)声明式事务的支持
只需要通过配置就可以完成对事务的管理,而无须手动编程
Spring 框架采用分层的理念,根据功能的不同划分成了多个模块,这些模块大体可分为 Data Access/Integration(数据访问与集成)、Web、AOP、Aspects、Instrumentation(检测)、Messaging(消息处理)、Core Container(核心容器)和 Test。如下图所示(以下是 Spring Framework 4.x 版本后的系统架构图)
上图中包含了 Spring 框架的所有模块,这些模块可以满足一切企业级应用开发的需求,在开发过程中可以根据需求有选择性地使用所需要的模块
数据访问/集成层包括 JDBC、ORM、OXM、JMS 和 Transactions 模块,具体介绍如下。
Spring 的 Web 层包括 Web、Servlet、WebSocket 和 Portlet 组件,具体介绍如下。
Spring 的核心容器是其他模块建立的基础,由 Beans 模块、Core 核心模块、Context 上下文模块和 SpEL 表达式语言模块组成,没有这些核心容器,也不可能有 AOP、Web 等上层的功能。具体介绍如下。
在 Core Container 之上是 AOP、Aspects 等模块,具体介绍如下:
Test 模块:Spring 支持 Junit 和 TestNG 测试框架,而且还额外提供了一些基于 Spring 的测试功能,比如在测试 Web 框架时,模拟 Http 请求的功能。
spring框架的核心作用是管理(创建,使用,销毁)项目中的组件对象
使用 Spring 之前需要安装 JDK、Tomcat 和 Eclipse
下载Apache Common Logging API
Common Logging 是使用 Spring 的必要组件。Apache Common Logging API 下载地址:https://commons.apache.org/proper/commons-logging/download_logging.cgi
下载完成后,将压缩包解压到相应位置。该文件包含以下 jar 文件和其它支持文档,目录结构如下。
下载Spring
Spring 下载地址:https://repo.spring.io/ui/native/libs-release-local/org/springframework/spring/5.3.9/
该文件目录结构如下:
名称 | 作用 |
---|---|
docs | 包含 Spring 的 API 文档和开发规范 |
libs | 包含开发需要的 jar 包和源码包 |
schema | 包含开发所需要的 schema 文件,在这些文件中定义了 Spring 相关配置文件的约束 |
在 libs 目录中,包含了 Spring 框架提供的所有 jar 文件,其中有 4 个 jar 文件是 Spring 框架的基础包,分别对应 Spring 容器的四个模块,具体如表 2 所示。
名称 | 作用 |
---|---|
spring-core-x.x.xx.RELEASE.jar | 包含 Spring 框架基本的核心工具类,Spring 其他组件都要用到这个包中的类,是其他组件的基本核心。 |
spring-beans-x.x.xx.RELEASE.jar | 所有应用都要用到的,它包含访问配置文件、创建和管理 Bean 以及进行 Inversion of Control(IoC)或者 Dependency Injection(DI)操作相关的所有类。 |
spring-context-x.x.xx.RELEASE.jar | Spring 提供在基础 IoC 功能上的扩展服务,此外还提供许多企业级服务的支持,如邮件服务、任务调度、JNDI 定位、EJB 集成、远程访问、缓存以及各种视图层框架的封装等 |
spring-expression-x.x.xx.RELEASE.jar | 定义了 Spring 的表达式语言。 需要注意的是,在使用 Spring 开发时,除了 Spring 自带的 JAR 包以外,还需要一个第三方 JAR 包 commons.logging 处理日志信息 |
使用 Spring 框架时,只需将 Spring 的 4 个基础包以及 commons-logging-1.2.jar 包复制到项目的 lib 目录,并发布到类路径中即可
1.创建maven的javaweb项目
2.引入配置文件
<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">
beans>
IoC 容器是 Spring 的核心,也可以称为 Spring 容器。Spring 通过 IoC 容器来管理对象的实例化和初始化,以及对象从创建到销毁的整个生命周期。
Spring 中使用的对象都由 IoC 容器管理,不需要我们手动使用 new 运算符创建对象。由 IoC 容器管理的对象称为 Spring Bean,Spring Bean 就是 Java 对象,和使用 new 运算符创建的对象没有区别。
Spring 通过读取 XML 或 Java 注解中的信息来获取哪些对象需要实例化。Spring 提供 2 种不同类型的 IoC 容器,即 BeanFactory 和 ApplicationContext 容器
1. BeanFactory 容器
BeanFactory 是最简单的容器,由 org.springframework.beans.factory.BeanFactory 接口定义,采用懒加载(lazy-load),所以容器启动比较快。BeanFactory 提供了容器最基本的功能,配置文件加载时不会创建对象,在获取bean时才会创建对象
为了能够兼容 Spring 集成的第三方框架(如 BeanFactoryAware、InitializingBean、DisposableBean),所以目前仍然保留了该接口。简单来说,BeanFactory 就是一个管理 Bean 的工厂,它主要负责初始化各种 Bean,并调用它们的生命周期方法。BeanFactory 接口有多个实现类 org.springframework.beans.factory.xml.XmlBeanFactory最常见
使用 BeanFactory 需要创建 XmlBeanFactory 类的实例,通过 XmlBeanFactory 类的构造函数来传递 Resource 对象。如下所示。
Resource resource = new ClassPathResource("applicationContext.xml");
BeanFactory factory = new XmlBeanFactory(resource);
2. ApplicationContext 容器
ApplicationContext 继承了 BeanFactory 接口,由 org.springframework.context.ApplicationContext 接口定义,对象在启动容器时加载。ApplicationContext 在 BeanFactory 的基础上增加了很多企业级功能,例如 AOP、国际化、事件支持等。
ApplicationContext 接口有两个常用的实现类,具体如下。
1)ClassPathXmlApplicationContext
该类从类路径 ClassPath 中寻找指定的 XML 配置文件,并完成 ApplicationContext 的实例化工作,具体如下所示。
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(String configLocation);
配置文件加载,则创建对象
在上述代码中,configLocation 参数用于指定 Spring 配置文件的名称和位置,如 Beans.xml。
2)FileSystemXmlApplicationContext
该类从指定的文件系统路径中寻找指定的 XML 配置文件,并完成 ApplicationContext 的实例化工作,具体如下所示。
ApplicationContext applicationContext = new FileSystemXmlApplicationContext(String configLocation);
二者的主要区别在于,如果 Bean 的某一个属性没有注入,使用 BeanFacotry 加载后,第一次调用 getBean() 方法时会抛出异常,而 ApplicationContext 则会在初始化时自检,这样有利于检查所依赖的属性是否注入。
因此,在实际开发中,通常都选择使用 ApplicationContext,只有在系统资源较少时,才考虑使用 BeanFactory。
spring容器加载多个配置文件
1.使用字符串参数,逗号分隔
//加载spring的配置文件 启动spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext("config/student.xml","config/springConfig.xml");
2.使用字符串数组
String[] config = {"config/student.xml","config/springConfig.xml"};
//加载spring的配置文件 启动spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
3.使用导入
<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">
<import resource="student.xml"/>
<import resource="springConfig.xml"/>
beans>
//加载spring的配置文件 启动spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext("config/applicationContext.xml");
Spring Bean定义
由 Spring IoC 容器管理的对象称为 Bean,Bean 根据 Spring 配置文件中的信息创建。
可以把 Spring IoC 容器看作是一个大工厂,Bean 相当于工厂的产品,如果希望这个大工厂生产和管理 Bean,则需要告诉容器需要哪些 Bean,以及需要哪种方式装配 Bean。
Spring 配置文件支持两种格式,即 XML 文件格式和 Properties 文件格式。
通常情况下,Spring 的配置文件使用 XML 格式。XML 配置文件的根元素是 ,该元素包含了多个子元素 。每一个 元素都定义了一个 Bean,并描述了该 Bean 如何被装配到 Spring 容器中 元素中可以包含很多属性,其常用属性如下表所示。
属性名称 | 描述 |
---|---|
id | Bean 的唯一标识符,Spring 容器对 Bean 的配置和管理都通过该属性完成。id 的值必须以字母开始,可以使用字母、数字、下划线等符号。 |
name | name 属性中可以为 Bean 指定多个名称,每个名称之间用逗号或分号隔开。Spring 容器可以通过 name 属性配置和管理容器中的 Bean。 |
class | 该属性指定了 Bean 的具体实现类,它必须是一个完整的类名,即类的全限定名。 |
scope | 用于设定 Bean 实例的作用域,属性值可以为 singleton(单例)、prototype(原型)、request、session 和 global Session。其默认值是 singleton |
constructor-arg | 元素的子元素,可以使用此元素传入构造参数进行实例化。该元素的 index 属性指定构造参数的序号(从 0 开始),type 属性指定构造参数的类型 |
property | 元素的子元素,用于调用 Bean 实例中的 setter 方法来属性赋值,从而完成依赖注入。该元素的 name 属性用于指定 Bean 实例中相应的属性名 |
ref | 和 等元素的子元索,该元素中的 bean 属性用于指定对某个 Bean 实例的引用 |
value | 和 等元素的子元素,用于直接指定一个常量值 |
list | 用于封装 List 或数组类型的依赖注入 |
set | 用于封装 Set 类型的依赖注入 |
map | 用于封装 Map 类型的依赖注入 |
entry | |
init-method | 容器加载 Bean 时调用该方法,类似于 Servlet 中的 init() 方法 |
destroy-method | 容器删除 Bean 时调用该方法,类似于 Servlet 中的 destroy() 方法。该方法只在 scope=singleton 时有效 |
lazy-init | 懒加载,值为 true,容器在首次请求时才会创建 Bean 实例;值为 false,容器在启动时创建 Bean 实例。该方法只在 scope=singleton 时有效 |
IOC创建对象的方式
1.无参构造器创建对象 (默认)
2.有参构造创建对象
<!-- 使用索引匹配参数-->
<bean id="student" class="cn.kgc.spring.entity.Student">
<constructor-arg index="0" value="20"/>
<constructor-arg index="1" value="李四"/>
<constructor-arg index="2" value="20210814"/>
<constructor-arg index="3" value="2021/08/14"/>
</bean>
<!--使用数据类型匹配参数 有相同的参数类型可配合索引一起使用-->
<bean id="student" class="cn.kgc.spring.entity.Student">
<constructor-arg type="int" value="20"/>
<constructor-arg type="java.lang.String" index="1" value="李四"/>
<constructor-arg type="java.lang.String" value="20210814"/>
<constructor-arg type="java.util.Date" value="2021/08/14"/>
</bean>
<!--使用属性名赋值-->
<bean id="student" class="cn.kgc.spring.entity.Student">
<constructor-arg name="age" value="20"/>
<constructor-arg name="name" value="李四"/>
<constructor-arg name="stuNo" value="20210814"/>
<constructor-arg name="birth" value="2021/08/14" />
</bean>
创建对象时属性的其它注入方式
set注入
bean id="studentService" class="cn.kgc.spring.service.StudentServiceImpl">
<property name="studentDao" ref="studentDao"/>
<property name="age" value="20"/>
<property name="stuName" value="张三"/>
<property name="price" value="20.4"/>
<property name="score" value="80.3"/>
<property name="birth" value="2021/8/13"/>
<property name="str" >
<array>
<value>张三value>
<value>李四value>
<value>王五value>
array>
property>
<property name="lis">
<list>
<value>抽烟value>
<value>喝酒value>
<value>烫头value>
list>
property>
<property name="lis2">
<list>
<ref bean="studentDao">ref>
<ref bean="studentDao">ref>
<ref bean="studentDao">ref>
list>
property>
<property name="books">
<set>
<value>javavalue>
<value>phpvalue>
<value>c#value>
set>
property>
<property name="scores">
<map>
<entry key="math" value="89" >entry>
<entry key="english" value="90" >entry>
<entry key="java" value="80" >entry>
map>
property>
<property name="map">
<map>
<entry key="01" value-ref="studentDao">entry>
<entry key="02" value-ref="studentDao">entry>
<entry key="03" value-ref="studentDao">entry>
map>
property>
<property name="ps">
<props>
<prop key="driver">com.mysql.jdbc.Driverprop>
<prop key="url">jdbc:mysql:///mybatisprop>
<prop key="username">rootprop>
<prop key="password">roootprop>
props>
property>
bean>
使用set注入,每个属性必须含有对应的set方法,否则无法进行属性的注入
Spring的依赖注入之p命名空间和c命名空间
p命名空间是set注入的一种快捷实现方式,想要使用p命名空间注入,需要注意一下几点。
实体类中必须有set方法;
实体类中必须有无参构造器(默认存在);
必须导入p命名空间注入方式依赖。
xml依赖代码:
xmlns:p="http://www.springframework.org/schema/p"
导入后即可使用
c命名空间是构造器注入的一种快捷实现方式,想要使用c命名空间,需要注意一下几点。
实体类中必须存在有参构造器;
必须导入c命名空间注入方式依赖。
xml依赖代码:
xmlns:c="http://www.springframework.org/schema/c"
导入后即可使用
<!--需要有有参构造方法-->
<bean id="student" class="cn.kgc.spring.entity.Student" c:age="30" c:name="王五" c:birth="1991/12/08" c:stuNo="20210814001"></bean>
作用:自定义注入参数和实体类中类型的匹配方式
import org.springframework.core.convert.converter.Converter;
public class MyConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
Date parse = simpleDateFormat.parse(source);
return parse;
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
xml文件配置:
<bean id="convert" class="cn.kgc.spring.basic.convert.MyConverter">bean>
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="convert">ref>
set>
property>
bean>
1.autowire="byName"在容器的上下文中寻找与类中属性对应的set方法名字相同的id属性值进行装配
<bean id="teacher" class="cn.kgc.spring.entity.Teacher">
<property name="name" value="李老师"/>
<property name="teaNo" value="001"/>
bean>
<bean id="classRoom1" class="cn.kgc.spring.entity.ClassRoom">
<property name="address" value="学思楼1楼"/>
<property name="classNo" value="1"/>
bean>
<bean id="student" class="cn.kgc.spring.entity.Student" autowire="byName" >bean>
2.autowire="byType"在容器的上下文中寻找与类中属性类型相同的Bean进行装配
<bean id="teacher" class="cn.kgc.spring.entity.Teacher">
<property name="name" value="李老师"/>
<property name="teaNo" value="001"/>
bean>
<bean id="classRoom1" class="cn.kgc.spring.entity.ClassRoom">
<property name="address" value="学思楼1楼"/>
<property name="classNo" value="1"/>
bean>
<bean id="student" class="cn.kgc.spring.entity.Student" autowire="byType" >bean>
3.使用注解自动装配
1.导入context约束
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
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
http://www.springframework.org/schema/context/spring-context.xsd" >
2.开启注解支持
<context:annotation-config/>
public class Student {
@Value("2021")
private String stuNo;
@Value("wangwu")
private String name;
@Value("20")
private int age;
@Value("2021/12/08")
private Date birth;
@Autowired
private Teacher teacher;
@Resource
private ClassRoom classRoom;
}
获取配置文件中的值
public class Aoo {
@Value("${test.boolean}")
private Boolean testBoolean;
@Value("${test.string}")
private String testString;
@Value("${test.integer}")
private Integer testInteger;
@Value("${test.long}")
private Long testLong;
@Value("${test.float}")
private Float testFloat;
@Value("${test.double}")
private Double testDouble;
@Value("#{'${test.array}'.split(',')}")
private String[] testArray;
@Value("#{'${test.list}'.split(',')}")
private List<String> testList;
@Value("#{'${test.set}'.split(',')}")
private Set<String> testSet;
@Value("#{${test.map}}")
private Map<String, Object> testMap;
}
配置文件 properties
test.boolean=true
test.string=abc
test.integer=123
test.long=123
test.float=1.2345678123456
test.double=1.2345678123456
test.array=1,3,4,5,6,1,2,3
test.list=1,3,4,5,6,1,2,3
test.set=1,3,4,5,6,1,2,3
test.map={name:"zhangsan", age:18}
1.FactoryBean
public class ConnectionFactoryBean implements FactoryBean<Connection> {
@Override
public Connection getObject() throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql:///java2215?serverTimezone=UTC", "root", "root");
return connection;
}
@Override
public Class<?> getObjectType() {
return Connection.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
xml配置方式:
<bean id="conn" class="cn.kgc.spring.ioc.entity.ConnectionFactoryBean">bean>
2.实例工厂
public class ConnectionFactoryBean {
public Connection getConnection() throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql:///java2215?serverTimezone=UTC&useSSL=false", "root", "root");
return connection;
}
}
xml配置方式:
<bean id="conn" class="cn.kgc.spring.ioc.entity.ConnectionFactoryBean">bean>
<bean id="connection" factory-bean="conn" factory-method="getConnection">bean>
3.静态工厂
public class ConnectionFactoryBean {
public static Connection getConnection() throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql:///java2215?serverTimezone=UTC&useSSL=false", "root", "root");
return connection;
}
}
xml配置方式:
<bean id="conn" class="cn.kgc.spring.ioc.entity.ConnectionFactoryBean" factory-method="getConnection">bean>
1.singleton 容器启动的时候创建对象,容器正常关闭时销毁对象
2.prototype 获取对象的时候创建对象,spring容器不负责对象的销毁
生命周期的过程:
1.调用无参创建对象
2.调用set方法初始化属性
3.调用初始化方法
4.对象创建完成,使用对象
5.关闭容器,调用销毁的方法
在配置文件中,除了可以定义 Bean 的属性值和相互之间的依赖关系,还可以声明 Bean 的作用域。例如,如果每次获取 Bean 时,都需要一个 Bean 实例,那么应该将 Bean 的 scope 属性定义为 prototype,如果 Spring 需要每次都返回一个相同的 Bean 实例,则应将 Bean 的 scope 属性定义为 singleton。
作用域的种类
Spring 容器在初始化一个 Bean 实例时,同时会指定该实例的作用域。Spring 5 支持以下 6 种作用域。
默认值,单例模式,表示在 Spring 容器中只有一个 Bean 实例,Bean 以单例的方式存在。
<bean id="..." class="..." scope="singleton"/>
原型模式,表示每次通过 Spring 容器获取 Bean 时,容器都会创建一个 Bean 实例。
<bean id="..." class="..." scope="prototype"/>
每次 HTTP 请求,容器都会创建一个 Bean 实例。该作用域只在当前 HTTP Request 内有效。
同一个 HTTP Session 共享一个 Bean 实例,不同的 Session 使用不同的 Bean 实例。该作用域仅在当前 HTTP Session 内有效。
同一个 Web 应用共享一个 Bean 实例,该作用域在当前 ServletContext 内有效。
类似于 singleton,不同的是,singleton 表示每个 IoC 容器中仅有一个 Bean 实例,而同一个 Web 应用中可能会有多个 IoC 容器,但一个 Web 应用只会有一个 ServletContext,也可以说 application 才是 Web 应用中货真价实的单例模式。
websocket 的作用域是 WebSocket ,即在整个 WebSocket 中有效
spring注解开发
1.开启注解的包扫描
<context:component-scan base-package="cn.kgc.spring"/>
2.常用的注解
@Repository dao层
@Component 组件通用
@Service service层
@Controller 控制层
四个注解的功能是一样的,只是使用不同的注解可以看出层次结构
3.作用域注解
@scope("singleton")
@scope("prototype")
1.代理模式讲解
作用:
通过代理类为原始类增加额外的功能
代理分类:
1.1静态代理
静态代理优缺点
1)优点 :在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展
2)缺点 :因为代理对象需要与目标对象实现一样的接口,所以会很多代理类 ,一旦接口增加方法,目标对象与代理对象都要维护
开发代理对象的原则:
1.代理对象和目标对象实现相同的接口
2.代理对象依赖于目标对象
1.2动态代理(反射)
程序运行的过程中,通过jdk提供代理技术动态的为某个类产生动态代理对象的过程
开发代理对象的原则:
1)代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用JDK动态代理。
2)代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象。
3)动态代理也叫做 :JDK代理、接口代理
@Test
public void test4() { //jdk的动态代理
// 被代理对象
UserServiceImpl userService = new UserServiceImpl();
InvocationHandler handler = new InvocationHandler() {
/**
* @param proxy 生成的代理对象 可忽略
* @param method 原始对象中的方法
* @param args 原始对象中的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------log---------");
Object invoke = method.invoke(userService, args);
return invoke;
}
};
/**
*参数1:类加载器,动态代理对象没有类加载器 需要借助别的类的加载器实现类的加载
* 类加载器的作用:
* 1.加载class文件到JVM虚拟机
* 2.创建类的class对象,进而创建类的实例化对象
*
* 参数2:原始类实现的所有接口 基于jdk的动态代理要求代理对象和原始对象要实现相同的接口
*
* 参数3:代理对象额外增加的功能 InvocationHandler
*
*/
UserService o =(UserService) Proxy.newProxyInstance(TestStaticProxy.class.getClassLoader(), userService.getClass().getInterfaces(), handler);
o.login();
o.register();
}
运行结果:
基于CGLib的动态代理
开发代理对象的原则:
1.代理对象无需和原始类对象实现相同的接口
2.代理对象和原始类对象要存在父子类关系
实现步骤
@Test
public void test5(){ //基于cglib的动态代理
UserServiceImpl userService = new UserServiceImpl();
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(this.getClass().getClassLoader());
enhancer.setSuperclass(userService.getClass());
//设置额外的功能
MethodInterceptor callback = new MethodInterceptor() {
//等价于 jdk代理中 invocationHandler 中的 invoke 方法
/**
* @param o 代理对象
* @param method 原始对象中的方法
* @param objects 原始对象中的参数
* @param methodProxy 代理方法
* @return 原始方法的返回值
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("---------cglib log------------");
return method.invoke(userService, objects);
}
};
enhancer.setCallback(callback);
// 创建代理对象
UserService usrService =(UserService) enhancer.create();
usrService.login();
usrService.register();
}
运行结果
2.AOP概述
AOP为Aspect Oriented Programming的缩写,是面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型
AOP可以分离业务代码和关注点代码(重复代码),在执行业务代码时,动态的注入关注点代码。切面就是关注点代码形成的类。Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口
AOP 要达到的效果是,保证开发者不修改源代码的前提下,去为系统中的业务组件添加某种通用功能,按照 AOP 框架修改源代码的时机,可以将其分为两类:
横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等…
切面(ASPECT) :横切关注点被模块化的特殊对象。即,它是一个类
通知(Advice) :切面必须要完成的工作。即,它是类中的一个方法
前置通知 后置通知 环绕通知 异常通知 最终通知
目标(Target) ;被通知对象
代理(Proxy) :向目标对象应用通知之后创建的对象
切入点(PointCut) :切面通知执行的“地点"的定义
连接点JointPoint) :与切入点匹配的执行点
快速使用
1.基于接口形式实现
使用Spring aop接口方式实现aop, 可以通过自定义通知来供Spring AOP识别对应实现的接口是:
通知的类型
Spring 方面可以使用下面提到的五种通知工作:
通知 | 描述 |
---|---|
前置通知 | 在一个方法执行之前,执行通知。 |
后置通知 | 在一个方法执行之后,不考虑其结果,执行通知。 |
返回后通知 | 在一个方法执行之后,只有在方法成功完成时,才能执行通知。 |
抛出异常后通知 | 在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知。 |
环绕通知 | 在一个方法调用之前和之后,执行通知。 |
案例
业务接口
/**
* 使用接口方式实现AOP, 默认通过JDK的动态代理来实现. 非接口方式, 使用的是cglib实现动态代理
*/
public interface IBaseCalculate {
int add(int numA, int numB);
int sub(int numA, int numB);
int div(int numA, int numB);
int multi(int numA, int numB);
}
实现类
public class BaseCalculate implements IBaseCalculate {
@Override
public int add(int numA, int numB) {
System.out.println("执行目标方法: add");
return numA + numB;
}
@Override
public int sub(int numA, int numB) {
System.out.println("执行目标方法: sub");
return numA - numB;
}
@Override
public int multi(int numA, int numB) {
System.out.println("执行目标方法: multi");
return numA * numB;
}
@Override
public int div(int numA, int numB) {
System.out.println("执行目标方法: div");
return numA / numB;
}
}
通知类
public class BaseAdvice implements MethodBeforeAdvice ,AfterReturningAdvice,ThrowsAdvice,MethodInterceptor{
/**
*
* @param method 切入的方法
* @param args 切入方法的参数
* @param target 目标对象
* @throws Throwable
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("===========进入beforeAdvice()============");
System.out.println("目标对象:" + target);
System.out.println("方法名: "+method);
System.out.println("即将进入切入点方法");
}
/**
*
* @param returnValue 切入点执行完方法的返回值,但不能修改
* @param method 切入点方法
* @param args 切入点方法的参数数组
* @param target 目标对象
* @throws Throwable
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("==========进入afterReturning()=========== \n");
System.out.println("切入点方法执行完成");
System.out.println("后置通知--目标对象:" + target);
System.out.println("后置通知--方法名: "+method);
System.out.println("后置通知--方法入参: "+ args.toString());
System.out.println("后置通知--方法返回值: "+ returnValue);
}
/**
异常通知 接口中没有抽象方法 利用反射实现
*/
public void afterThrowing(Exception e){
System.out.println("异常为:"+e.getMessage());
}
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("=================环绕通知开始===================");
String method = methodInvocation.getMethod().getName();
System.out.println("方法名:"+method);
Object[] arguments = methodInvocation.getArguments();
System.out.println("方法参数:"+Arrays.toString(arguments));
Object aThis = methodInvocation.getThis();
System.out.println("目标对象:"+aThis);
System.out.println("=================前置通知===================");
//执行目标方法
Object proceed = methodInvocation.proceed();
System.out.println("=================后置通知===================");
System.out.println("返回值:"+proceed);
System.out.println("=================环绕通知结束===================");
return proceed;
}
}
基于Aspectj实现AOP操作
1.基于xml配置文件
2.基于注解的形式实现
切入点表达式
作用:声明对哪个类中的哪个方法进行增强
语法:
execution([访问权限修饰符] [ 返回值 ] [ 类的全路径名 ] [ 方法名 ] (参数列表))
方式1:
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println(method.getName()+"方法被执行,返回值是:"+o);
}
}
public class Log implements MethodBeforeAdvice {
/**
*
* @param method 目标对象的方法
* @param objects 参数
* @param o 目标对象
* @throws Throwable
*/
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"方法被执行");
}
}
<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"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="log" class="cn.kgc.spring.service.Log"></bean>
<bean id="afterlog" class="cn.kgc.spring.service.AfterLog"/>
<bean id="studentServiceImpl" class="cn.kgc.spring.service.StudentServiceImpl"/>
<!--配置aop-->
<aop:config>
<!-- 切入点execution() 要执行的位置-->
<aop:pointcut id="pointcut" expression="execution(* cn.kgc.spring.service.StudentServiceImpl.*(..))"/>
<!-- 执行环绕增强-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
方式2:
public class PointCut {
//前置通知
public void beforeMethod(){
System.out.println("开启事务");
}
//后置通知 切点方法正常执行之后执行
public void afterMethod(){
System.out.println("关闭事务");
}
//环绕通知
public void around(ProceedingJoinPoint jp) {
try {
System.out.println("------环绕前------");
Object proceed = jp.proceed();
System.out.println("------环绕后------");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("异常通知");
}finally {
System.out.println("最终通知");
}
}
//最终通知 无论切点方法是否正常执行 都会在其后执行
public void fy(){
System.out.println("最终通知");
}
//异常通知 和后置通知永远只能执行一个
public void exceptionMethod(){
System.out.println("异常通知");
}
}
<aop:config>
<aop:aspect ref="point">
<aop:pointcut id="pt" expression="execution(* cn.kgc.spring.service.StudentServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="pt"/>
<aop:after method="after" pointcut-ref="pt"/>
aop:aspect>
aop:config>
方式3:注解方式
<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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="cn.kgc.spring"/>
<aop:aspectj-autoproxy/>
beans>
@Component
@Aspect
public class PointCut2 {
//前置通知
@Before("execution(* cn.kgc.spring.service.StudentServiceImpl.*(..))")
public void beforeMethod(){
System.out.println("开启事务");
}
//后置通知 切点方法正常执行之后执行
@AfterReturning("execution(* cn.kgc.spring.service.StudentServiceImpl.*(..))")
public void afterMethod(){
System.out.println("关闭事务");
}
//环绕通知
@Around("execution(* cn.kgc.spring.service.StudentServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp){
try {
System.out.println("------环绕前------");
Object proceed = jp.proceed();
System.out.println("------环绕后------");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("异常通知");
}finally {
System.out.println("最终通知");
}
}
//最终通知 无论切点方法是否正常执行 都会在其后执行
@After("execution(* cn.kgc.spring.service.StudentServiceImpl.*(..))")
public void fy(){
System.out.println("最终通知");
}
//异常通知 和后置通知永远只能执行一个
@AfterThrowing("execution(* cn.kgc.spring.service.StudentServiceImpl.*(..))")
public void exceptionMethod(){
System.out.println("异常通知");
}
}
1.引入依赖
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.44version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.10version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.7version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.3.9version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.5version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.3.9version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.3.9version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.16version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>5.2.1version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.11version>
<scope>testscope>
dependency>
dependencies>
-----------------------------------------------------------------------------------
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-beansartifactId>
<version>5.3.7version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>5.3.7version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.3.7version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-expressionartifactId>
<version>5.3.7version>
dependency>
<dependency>
<groupId>commons-logginggroupId>
<artifactId>commons-loggingartifactId>
<version>1.2version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.7version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.5version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.3.7version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>5.2.1version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.12version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.3.7version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.22version>
dependency>
2.配置spring的文件
<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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="db.properties"/>
<bean id="druid" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
bean>
<bean id="ssf" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="druid"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.kgc.spring.dao"/>
bean>
beans>
3.配置mybatis文件
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="logImpl" value="LOG4J"/>
settings>
<typeAliases>
<package name="cn.kgc.mybatis.entity"/>
typeAliases>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql" />
<property name="pageSizeZero" value="true" />
<property name="reasonable" value="true" />
plugin>
plugins>
configuration>
日志文件 (log4j.properties)
log4j.rootLogger=DEBUG,Console
#Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
log4j.logger.org.apache=ERROR
log4j.logger.org.mybatis=ERROR
log4j.logger.org.springframework=ERROR
#这个需要
log4j.logger.log4jdbc.debug=ERROR
log4j.logger.com.gk.mapper=ERROR
log4j.logger.jdbc.audit=ERROR
log4j.logger.jdbc.resultset=ERROR
#这个打印SQL语句非常重要
log4j.logger.jdbc.sqlonly=DEBUG
log4j.logger.jdbc.sqltiming=ERROR
log4j.logger.jdbc.connection=FATAL
4.编写测试类 测试
/**
*
* junit版本要求4.12以上
* @author mosin
* date 2021/11/15
* @version 1.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:config/applicationcontext.xml")
public class TestStudent {
@Autowired
private StudentService studentService;
@Test
public void test01(){
// Integer integer = studentService.delOne(500062);
// System.out.println(integer);
// Student student = studentService.selectOne(500062);
// System.out.println(student);
//
// Integer integer1 = studentService.addOne(Student.builder().stuName("lisi").stuBirth(new Date()).stuSex("男").stuNo("2021111501").build());
// System.out.println(integer1);
// Student student2 = studentService.selectOne(500073);
// System.out.println(student2);
// PageHelper.startPage(1, 20);
// List students = studentService.selectAll();
// PageInfo studentPageInfo = new PageInfo<>(students);
//
// System.out.println(studentPageInfo.getList());
studentService.updateStudent(Student.builder().stuName("李四").stuBirth(new Date()).stuSex("男").stuNo("2021111501").id(500073).build());
}
}
1.配置式事务
<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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druid"/>
bean>
<tx:advice id="tx" transaction-manager="dataSourceTransactionManager">
<tx:attributes>
<tx:method name="save"/>
<tx:method name="delete"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="servicePoint" expression="execution(* cn.kgc.spring.service.*.*(..))"/>
<aop:advisor advice-ref="tx" pointcut-ref="servicePoint"/>
aop:config>
beans>
2.声明式事务
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
@Service("studentService")
@Transactional
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentDao studentDao;
@Override
public List<Student> getAll() {
List<Student> all = studentDao.getAll();
return all;
}
@Override
public int save(Student student){
int line = studentDao.save(student);
int a = 1/0;
return line;
}
}
事务传播方式
事务最重要的两个特性,是事务的传播级别和数据隔离级别。传播级别定义的是事务的控制范围,事务隔离级别定义的是事务在数据库读写方面的控制范围。
以下是事务的7种传播级别:
1) *PROPAGATION_REQUIRED* ,默认的spring事务传播级别,使用该级别的特点是,如果上下文中已经存在事务,那么就加入到事务中执行,如果当前上下文中不存在事务,则新建事务执行。所以这个级别通常能满足处理大多数的业务场景。
2)*PROPAGATION_SUPPORTS* ,从字面意思就知道,supports,支持,该传播级别的特点是,如果上下文存在事务,则支持事务加入事务,如果没有事务,则使用非事务的方式执行。所以说,并非所有的包在transactionTemplate.execute中的代码都会有事务支持。这个通常是用来处理那些并非原子性的非核心业务逻辑操作。应用场景较少。
3)*PROPAGATION_MANDATORY* , 该级别的事务要求上下文中必须要存在事务,否则就会抛出异常!配置该方式的传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段。比如一段代码不能单独被调用执行,但是一旦被调用,就必须有事务包含的情况,就可以使用这个传播级别。
4)*PROPAGATION_REQUIRES_NEW* ,从字面即可知道,new,每次都要一个新事务,该传播级别的特点是,每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
这是一个很有用的传播级别,举一个应用场景:现在有一个发送100个红包的操作,在发送之前,要做一些系统的初始化、验证、数据记录操作,然后发送100封红包,然后再记录发送日志,发送日志要求100%的准确,如果日志不准确,那么整个父事务逻辑需要回滚。
怎么处理整个业务需求呢?就是通过这个PROPAGATION_REQUIRES_NEW 级别的事务传播控制就可以完成。发送红包的子事务不会直接影响到父事务的提交和回滚。
5)*PROPAGATION_NOT_SUPPORTED* ,这个也可以从字面得知,not supported ,不支持,当前级别的特点就是上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务。
这个级别有什么好处?可以帮助你将事务极可能的缩小。我们知道一个事务越大,它存在的风险也就越多。所以在处理事务的过程中,要保证尽可能的缩小范围。比如一段代码,是每次逻辑操作都必须调用的,比如循环1000次的某个非核心业务逻辑操作。这样的代码如果包在事务中,势必造成事务太大,导致出现一些难以考虑周全的异常情况。所以这个事务这个级别的传播级别就派上用场了。用当前级别的事务模板抱起来就可以了。
6)*PROPAGATION_NEVER* ,该事务更严格,上面一个事务传播级别只是不支持而已,有事务就挂起,而PROPAGATION_NEVER传播级别要求上下文中不能存在事务,一旦有事务,就抛出runtime异常,强制停止执行!这个级别上辈子跟事务有仇。
7)*PROPAGATION_NESTED* ,字面也可知道,nested,嵌套级别事务。该传播级别特征是,如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。
@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class)
student){
int line = studentDao.save(student);
int a = 1/0;
return line;
}
}
**事务传播方式**
事务最重要的两个特性,是事务的传播级别和数据隔离级别。传播级别定义的是事务的控制范围,事务隔离级别定义的是事务在数据库读写方面的控制范围。
以下是事务的7种传播级别:
1) ***\*PROPAGATION_REQUIRED\**** ,默认的spring事务传播级别,使用该级别的特点是,如果上下文中已经存在事务,那么就加入到事务中执行,如果当前上下文中不存在事务,则新建事务执行。所以这个级别通常能满足处理大多数的业务场景。
2)***\*PROPAGATION_SUPPORTS\**** ,从字面意思就知道,supports,支持,该传播级别的特点是,如果上下文存在事务,则支持事务加入事务,如果没有事务,则使用非事务的方式执行。所以说,并非所有的包在transactionTemplate.execute中的代码都会有事务支持。这个通常是用来处理那些并非原子性的非核心业务逻辑操作。应用场景较少。
3)***\*PROPAGATION_MANDATORY\**** , 该级别的事务要求上下文中必须要存在事务,否则就会抛出异常!配置该方式的传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段。比如一段代码不能单独被调用执行,但是一旦被调用,就必须有事务包含的情况,就可以使用这个传播级别。
4)***\*PROPAGATION_REQUIRES_NEW\**** ,从字面即可知道,new,每次都要一个新事务,该传播级别的特点是,每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
这是一个很有用的传播级别,举一个应用场景:现在有一个发送100个红包的操作,在发送之前,要做一些系统的初始化、验证、数据记录操作,然后发送100封红包,然后再记录发送日志,发送日志要求100%的准确,如果日志不准确,那么整个父事务逻辑需要回滚。
怎么处理整个业务需求呢?就是通过这个PROPAGATION_REQUIRES_NEW 级别的事务传播控制就可以完成。发送红包的子事务不会直接影响到父事务的提交和回滚。
5)***\*PROPAGATION_NOT_SUPPORTED\**** ,这个也可以从字面得知,not supported ,不支持,当前级别的特点就是上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务。
这个级别有什么好处?可以帮助你将事务极可能的缩小。我们知道一个事务越大,它存在的风险也就越多。所以在处理事务的过程中,要保证尽可能的缩小范围。比如一段代码,是每次逻辑操作都必须调用的,比如循环1000次的某个非核心业务逻辑操作。这样的代码如果包在事务中,势必造成事务太大,导致出现一些难以考虑周全的异常情况。所以这个事务这个级别的传播级别就派上用场了。用当前级别的事务模板抱起来就可以了。
6)***\*PROPAGATION_NEVER\**** ,该事务更严格,上面一个事务传播级别只是不支持而已,有事务就挂起,而PROPAGATION_NEVER传播级别要求上下文中不能存在事务,一旦有事务,就抛出runtime异常,强制停止执行!这个级别上辈子跟事务有仇。
7)***\*PROPAGATION_NESTED\**** ,字面也可知道,nested,嵌套级别事务。该传播级别特征是,如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。
```java
@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class)