Spring
Spring
1.简介
spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架
导入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
</dependencies>
优点
- Spring是一个开源免费的框架(容器)!
- Spring是一个轻量级的、非入侵式的框架!
- 控制反转(IOC),面向切面编程(AOP)!
- 支持事务的处理,对框架整合的支持!
2.IOC
-
Ioc本质
控制反转IOC(Inversion of Control),是一种设计思想,
DI(依赖注入)是实现IOC的一种方法,也有人认为DI只是IOC的另一种说法.
没有Ioc的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系
完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建
转移给第三方,所谓控制反转就是:获得依赖对象的方式反转了. -
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用
注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在
实现类中,从而达到零配置的目的.
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定
对象的方式.在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection,DI).
3.HelloSpring
1.导入Spring相关jar包
2.编写相关代码
2.1编写一个Hello实体类
public class Hello {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}
2.2、编写我们的spring文件 , 这里我们命名为beans.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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--bean就是java对象 , 由Spring创建和管理-->
<bean id="hello" class="com.prq.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
</beans>
3、我们可以去进行测试了 .
@Test
public void test(){
//解析beans.xml文件 , 生成管理相应的Bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//getBean : 参数即为spring配置文件中bean的id .
Hello hello = (Hello) context.getBean("hello");
hello.show();
}
4.Spring配置
<!--导入-->
<import resource="beans1.xml"/>
<!--第一种,下标赋值!-->
<bean id="user" class="com.prq.pojo.User">
<constructor-arg index="0" value="狂神说java"/>
</bean>
<!--第二种方式:通过类型创建,不建议使用!-->
<bean id="user" class="com.prq.pojo.User">
<constructor-arg type="java.lang.String" value="qinjiang"/>
</bean>
<!--第三种,直接通过参数名来设置-->
<bean id="user" class="com.prq.pojo.User">
<constructor-arg name="name" value="秦疆"/>
</bean>
<!--别名,如果添加了别名,我们也可以使用别名获取到这个对象-->
<alias name="user" alias="userNew"/>
<!--
id : bean的唯一标识符,也就是相当于我们学的对象名
class : bean 对象所对应的全限定名 : 报名 + 类名
name : 也是别名而且name 可以同时取多个别名
-->
<bean id="userT" class="com.prq.pojo.UserT" name="user2 u2,u3;u4;u5,u">
<property name="name" value="西部开源"/>
</bean>
5.DI注入
<!--第一种,普通值注入,value-->
<property name="name" value="秦疆"/>
<!--第二种,bean注入,ref-->
<property name="address" ref="address"/>
<!--数组注入-->
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>水浒传</value>
<value>三国演义</value>
</array>
</property>
<!--List注入-->
<property name="hobbys">
<list>
<value>听歌</value>
<value>敲代码</value>
<value>看电影</value>
</list>
</property>
<!--Map注入-->
<property name="card">
<map>
<entry key="身份证" value="111111222222223333"/>
<entry key="银行卡" value="1321223112232123231"/>
</map>
</property>
<!--Set注入-->
<property name="games">
<set>
<value>LOL</value>
<value>COC</value>
<value>BOB</value>
</set>
</property>
<!--null注入-->
<property name="wife">
<null/>
</property>
<!--Properties注入
key=value
key=value
key=value
-->
<property name="info">
<props>
<prop key="driver">20190525</prop>
<prop key="url">男</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
p命名空间注入 c命名空间注入
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--p命名空间注入,可以直接注入属性的值:property-->
<bean id="user" class="com.prq.pojo.User" p:name="秦疆" p:age="18"/>
<!--c命名空间注入,通过构造器注入:construct-args-->
<bean id="user2" class="com.prq.pojo.User" c:age="18" c:name="狂神" scope="prototype"/>
<!--
单例模式(Spring默认机制)
原型模式:每次从容器中get的时候都会产生一个新对象!
-->
Bean的作用域
在Spring中,那些组成应用程序的主体及由Spring
IoC容器所管理的对象,被称之为bean.简单地讲,
bean就是由IoC容器初始化 装配及管理的对象 .
Singleton
- 当一个bean的作用域为Singleton,那么Spring IoC容器中
只会存在一个共享的bean实例,并且所有对bean的请求,只要
id与该bean定义相匹配,则只会返回bean的同一实例。
Singleton是单例类型,就是在创建起容器时就同时
自动创建了一个bean的对象,不管你是否使用,他都存在了,
每次获取到的对象都是同一个对象。
Prototype
- 当一个bean的作用域为Prototype,表示一个bean定义对应
多个对象实例。Prototype作用域的bean会导致在每次对该
bean请求(将其注入到另一个bean中,或者以程序的方式调用
容器的getBean()方法)时都会创建一个新的bean实例。
Prototype是原型类型,它在我们创建容器的时候并没有
实例化,而是当我们获取bean的时候才会去创建一个对象,
而且我们每次获取到的对象都不是同一个对象。根据经验,
对有状态的bean应该使用prototype作用域,而对无状态的
bean则应该使用singleton作用域。
Request
- 当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,
它们依据某个bean定义创建而成。该作用域仅在基于
web的Spring ApplicationContext情形下有效 - 针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全
新的LoginAction bean实例,且该loginAction bean
实例仅在当前HTTP request内有效,因此可以根据需要放心
的更改所建实例的内部状态,而其他请求中根据loginAction
bean定义创建的实例,将不会看到这些特定于某个请求的
状态变化。当处理请求结束,request作用域的bean实例将
被销毁。
Session
- 当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于
web的Spring ApplicationContext情形下有效。 - 针对某个HTTP Session,Spring容器会根据userPreferences
bean定义创建一个全新的userPreferences bean实例,
且该userPreferences bean仅在当前HTTP Session内
有效。与request作用域一样,可以根据需要放心的更改所
创建实例的内部状态,而别的HTTP Session中根据
userPreferences创建的实例,将不会看到这些特定于某
个HTTP Session的状态变化。当HTTP Session最终被废
弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。
6.自动装配
-
自动装配是使用spring满足bean依赖的一种方法
-
spring会在应用上下文中为某个bean寻找其依赖的bean。
Spring中bean有三种装配机制,分别是: 1.在xml中显式配置; 2.在java中显式配置; 3.隐式的bean发现机制和自动装配。 Spring的自动装配需要从两个角度来实现,或者说是两个操作: 1.组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean; 2.自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI; 组件扫描和自动装配组合发挥巨大威力,使得显示的配置降低到最少。 当一个bean节点带有 autowire byName的属性时。 1.将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。 2.去spring容器中寻找是否有此字符串名称id的对象。 3.如果有,就取出注入;如果没有,就报空指针异常。
使用注解
<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">
<!--开启注解的支持-->
<context:annotation-config/>
<!--
byName:会自动在容器上下文中查找,
和自己对象set方法后面的值对应的beanid!
byType会自动在容器上下文中查找,
和自己对象属性类型相同的bean!
-->
<!--
byName的时候,需要保证所有bean的id唯一,
并且这个bean需要和自动注入的属性的set方法一致!
byType的时候,需要保证所有bean的class唯一,
并且这个bean需要和自动注入的属性的类型一致!
-->
@Autowired
直接在属性上使用即可!也可以在set方式上使用
使用Autowired我们就可以不用编写set方法了,
前提是你这个自动装配的属性在IOC(Spring)容器中存在,
且符合符合名字byname!
如果显示定义了Autowired的属性为false,
说明这个对象可以为null,否则不允许为空
如果@Autowired自动装配的环境比较复杂,
自动装配无法通过一个注解[@Autowired]完成的时候、
我们可以使用@Qualifier(value="xxx")去配置
@Autowired的使用,指定一个唯一的bean对象注入!
注解说明
- @Autowired:自动装配通过类型、名字
如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value = "xxx") - @Nullable
字段标记了这个注解,说明这个字段可以为null - @Resource :自动装配通过名字。类型
- @Component : 组件,放在类上,说明这个类被Spring管理了,就是bean!
- @Qualifier : @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
@Qualifier不能单独使用。
@Resource和@Autowired的区别
- 1.都是用来自动装配的,都可以放在属性字段上,或者set方法上
- 2.@Autowired默认按类型装配(属于spring规范)
,默认情况下必须要求依赖对象必须存在,如果要允
许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用 - 3.@Autowired通过byType的方式实现,
而且必须要求这个对象存在!【常用】
@Resource默认通过byname的方式实现,
如果找不到名字则通过byType实现!
如果两个都找不到的情况下,就报错!【常用】 - 4.@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
- 5.执行顺序不同:@Autowired通过byType的方式实现。
@Resource默认通过byname的方式实现
7.使用注解开发
- 在spring4之后,想要使用注解形式,必须得要引入aop的包
- 在配置文件当中,还得要引入一个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
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
我们之前都是使用 bean 的标签进行bean注入,但是实际开发中,我们一般都会使用注解!
//这里这个注解的意思,就是说明这个类被Spring接管了,注册到了容器
@Component
public class User {
private String name;
public String getName() {
return name;
}
@Value("秦疆")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
//这个也会被Spring容器托管,注册到容器中,因为他本来就是一个@Component
// @Configuration代表这是一个配置类,就和我们之前看的beans、xml
@Configuration
@ComponentScan("com.prq.pojo")
@Import(KuangConfig2.class)
public class KuangConfig {
//注册一个bean,就相当于我们之前写的一个bean标签
//这个方法的名字,就相当于bean标签中的id属性
//这个方法的返回值,就相当于bean标签中的class属性
@Bean
public User user(){
return new User();//就是返回要注入到bean的对象!
}
}
@Component三个衍生注解
为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。
- @Controller:web层
- @Service:service层
- @Repository:dao层
写上这些注解,就相当于将这个类交给Spring管理装配了!
@scope
- singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
- prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
xml与注解
xml与注解
1.xml更加万能,适用于任何场合!维护简单方便
2.注解 不是自己类使用不了,维护相对复杂
xml与注解最佳实践
1.xml用来管理bean
2.注解只负责完成属性的注入;
3.我们使用的过程中,只需要注意一个问题:必须让注解生效,
就需要开启注解的支持
<!--指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.prq"/>
<context:annotation-config/>
作用:
- 进行注解驱动注册,从而使注解生效
- 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册
- 如果不扫描包,就需要手动配置bean
- 如果不加注解驱动,则注入的值为null!
8.代理模式
- 静态代理
- 动态代理
静态代理角色分析
- 抽象角色 : 一般使用接口或者抽象类来实现
- 真实角色 : 被代理的角色
- 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
- 客户 : 使用代理角色来进行一些操作 .
静态代理的好处:
- 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
- 公共的业务由代理来完成 . 实现了业务的分工 ,
- 公共业务发生扩展时变得更加集中和方便 .
缺点 :
- 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .
- 我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !
动态代理
动态代理的角色和静态代理的一样 .
- 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
- 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
- 基于接口的动态代理----JDK动态代理
- 基于类的动态代理--cglib
- 现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist
- 我们这里使用JDK的原生代码来实现,其余的道理都是一样的!、
JDK的动态代理需要了解两个类
核心 : InvocationHandler 和 Proxy , 打开JDK帮助文档看看
【InvocationHandler:调用处理程序】
Object invoke(Object proxy, 方法 method, Object[] args);
//参数
//proxy - 调用该方法的代理实例
//method -所述方法对应于调用代理实例上的接口
// 方法的实例。方法对象的声明类将是该方法声明的接口,
// 它可以是代理类继承该方法的代理接口的超级接口。
//args -包含的方法调用传递代理实例的参数值的对象
// 的阵列,或null如果接口方法没有参数。原始类型的
// 参数包含在适当的原始包装器类的实例中,
// 例如java.lang.Integer或java.lang.Boolean 。
核心:一个动态代理 , 一般代理某一类业务 ,
一个动态代理可以代理多个类,代理的是接口!、
动态代理的好处
静态代理有的它都有,静态代理没有的,它也有!
- 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
- 公共的业务由代理来完成 . 实现了业务的分工 ,
- 公共业务发生扩展时变得更加集中和方便 .
- 一个动态代理 , 一般代理某一类业务
- 一个动态代理可以代理多个类,代理的是接口!
9.什么是AOP
AOP(Aspect Oriented Programming)意为:
面向切面编程,通过预编译方式和运行期动态代理实现
程序功能的统一维护的一种技术。AOP是OOP的延续,是软
件开发中的一个热点,也是Spring框架中的一个重要内容,
是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的
各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度
降低,提高程序的可重用性,同时提高了开发的效率。
Aop在Spring中的作用
提供声明式事务;允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ....
- 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
- 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
- 目标(Target):被通知对象。
- 代理(Proxy):向目标对象应用通知之后创建的对象。
- 切入点(PointCut):切面通知 执行的 “地点”的定义。
- 连接点(JointPoint):与切入点匹配的执行点。
使用Spring实现Aop
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
第一种方式
- 通过 Spring API 实现
首先编写我们的业务接口和实现类
public interface UserService {
public void add();
public void delete();
public void update();
public void search();
}
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加用户");
}
@Override
public void delete() {
System.out.println("删除用户");
}
@Override
public void update() {
System.out.println("更新用户");
}
@Override
public void search() {
System.out.println("查询用户");
}
}
然后去写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强
public class Log implements MethodBeforeAdvice {
//method : 要执行的目标对象的方法
//objects : 被调用的方法的参数
//Object : 目标对象
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被执行了");
}
}
public class AfterLog implements AfterReturningAdvice {
//returnValue 返回值
//method被调用的方法
//args 被调用的方法的对象的参数
//target 被调用的目标对象
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了" + target.getClass().getName()
+"的"+method.getName()+"方法,"
+"返回值:"+returnValue);
}
}
spring的文件中注册 , 并实现aop切入实现 , 注意导入约束 .
<?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: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-->
<bean id="userService" class="com.prq.service.UserServiceImpl"/>
<bean id="log" class="com.prq.log.Log"/>
<bean id="afterLog" class="com.prq.log.AfterLog"/>
<!--aop的配置-->
<aop:config>
<!--切入点 expression:表达式匹配要执行的方法-->
<aop:pointcut id="pointcut" expression="execution(* com.prq.service.UserServiceImpl.*(..))"/>
<!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
测试
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
userService.search();
}
}
Aop的重要性 : 很重要 . 一定要理解其中的思路 , 主要是思想的理解这一块 .
Spring的Aop就是将公共的业务 (日志 , 安全等)
和领域业务结合起来 , 当执行领域业务时 , 将会把
公共业务加进来 . 实现公共业务的重复利用 . 领域
业务更纯粹 , 程序猿专注领域业务 , 其本质还是动态代理 .
第二种方式
- 自定义类来实现Aop
目标业务类不变依旧是userServiceImpl
第一步 : 写我们自己的一个切入类
public class DiyPointcut {
public void before(){
System.out.println("---------方法执行前---------");
}
public void after(){
System.out.println("---------方法执行后---------");
}
}
去spring中配置
<!--注册bean-->
<bean id="diy" class="com.kuang.config.DiyPointcut"/>
<!--aop的配置-->
<aop:config>
<!--第二种方式:使用AOP的标签实现-->
<aop:aspect ref="diy">
<aop:pointcut id="diyPonitcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
<aop:before pointcut-ref="diyPonitcut" method="before"/>
<aop:after pointcut-ref="diyPonitcut" method="after"/>
</aop:aspect>
</aop:config>
测试:
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
第三种方式
- 使用注解实现
第一步:编写一个注解实现的增强类
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class AnnotationPointcut {
@Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("---------方法执行前---------");
}
@After("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("---------方法执行后---------");
}
@Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
System.out.println("签名:"+jp.getSignature());
//执行目标方法proceed
Object proceed = jp.proceed();
System.out.println("环绕后");
System.out.println(proceed);
}
}
第二步:在Spring配置文件中,注册bean,并增加支持注解的配置
<bean id="annotationPointcut" class="com.kuang.config.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>
aop:aspectj-autoproxy:说明
- 通过aop命名空间的<aop:aspectj-autoproxy />
声明自动为spring容器中那些配置@aspectJ切面的
bean创建代理,织入切面。当然,spring 在内部依
旧采用AnnotationAwareAspectJAutoProxyCreator
进行自动代理的创建工作,但具体实现的细节已经被
<aop:aspectj-autoproxy />隐藏起来了 - <aop:aspectj-autoproxy />有一个
proxy-target-class属性,默认为false,
表示使用jdk动态代理织入增强,当配为
<aop:aspectj-autoproxy poxy-target-class="true"/>时,
表示使用CGLib动态代理技术织入增强。
不过即使proxy-target-class设置为false,如
果目标类没有声明接口,则spring将自动使用CGLib动态代理。
10.声明式事务
回顾事务
- 事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!
- 事务管理是企业级应用程序开发中必备技术,用来确保数据
的完整性和一致性。 - 事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。
事务四个属性ACID
- 原子性(atomicity)
- 事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用
- 一致性(consistency)
- 一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中
- 隔离性(isolation)
- 可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏
- 持久性(durability)
- 事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中
Spring中的事务管理
- Spring在不同的事务管理API之上定义了一个抽象层,
使得开发人员不必了解底层的事务管理API就可以使用
Spring的事务管理机制。Spring支持编程式事务管理
和声明式的事务管理。
编程式事务管理
- 将事务管理代码嵌到业务方法中来控制事务的提交和回滚
- 缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码
声明式事务管理
- 一般情况下比编程式事务好用。
- 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
- 将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。
使用Spring管理事务,注意头文件的约束导入 : tx
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
事务管理器
- 无论使用Spring的哪种事务管理策略(编程式或者声明式)事务管理器都是必须的。
- 就是 Spring的核心事务管理抽象,管理封装了一组独立于技术的方法。
spring事务传播特性:
事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:
- propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
- propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
- propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
- propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
- propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
- propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作
Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。
假设 ServiveX#methodX() 都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)