spring学习笔记-IOC,AOP,事务管理
目录
概述
什么是spring
侵入式的概念
spring的核心
spring的优势
注意
IOC控制反转
概述
核心
容器
DI,dependency injection依赖注入
概念
注入方式
循环依赖
spring如何解决循环依赖
spring生成Bean的方式
Bean属性注入(Bean属性赋值)
springBean生命周期
实例化Bean
属性赋值(依赖注入)
Aware接口回调
BeanPostProcessor前置处理器
InitializingBean(初始化)
BeanPostProcessor后置处理器
destroy-method销毁
springBean的作用域
springIOC注解
类注解
@Component
@Configuration
@Scope
@Scope具有以下几种作用域
@Scope注意点
属性注解
@Value
@AutoWire
编辑
@Resource
编辑
Resource总结
@Autowire和@Resource区别
方法注解
@Bean
@Scope
@Lazy
@Primary
@Profile
AOP面向切面
底层实现
代理
静态代理
动态代理
JDK 动态代理
Cglib 动态代理
动态代理注意点
AOP名词
Joinpoint(连接点)
Pointcut(切入点)
Advice(通知/增强)
Introduction(引介)
Aspect(切面)
Weaving(织入)
Proxy(代理)
简单的springAOP实现
@Aspect
@Pointcut("xxx")
@Before等
其他通知注解
切点表达式
依赖
实现
spring事务管理
概述
什么是事务
事务特性ACID
不考虑事务隔离引发的安全性问题
脏读
不可重复读
幻读
事务隔离级别
事务回滚
spring事务核心
PlatformTransactionManager平台事务管理器接口
TransactionDefinition事务管理的属性
transactionManager
isolation,隔离级别
propagation,传播行为
readOnly,是否只读事务
timeout,超时设置
rollbaclFor,何种异常执行回滚
怎么使用spring事务管理
依赖
简单demo
spring事务失效
数据库方面
spring方面
配置方面
代码编写方面
异常处理
通过springAOP实现自动事务,默认情况下不再需要对每个方法手动打注解
spring中运用的设计模式
概述
什么是spring
一站式轻量级分层框架,非侵入式
spring是非侵入式的框架
侵入式的概念
侵入式:实现特定接口,继承特定类,改变原有类的结构,即为侵入式,如struts2
非侵入式:对类原本结构没有影响,仍然增强了JavaBean的功能
spring的核心
核心是IOC控制反转和AOP面向切面
spring的优势
IOC,解耦开发
AOP,方便权限拦截,运行监控
声明式事务管理
方便集成其它框架
降低日常开发难度
注意
因为当下项目已经全面普及springboot,前后端分离,因此我们不再关注以前ssm项目中常见的xml配置文件,比如applicationContext.xml,只关注spring本身
IOC控制反转
概述
在没有spring的情况下,如果需要使用一个类的对象,需要手动的new Xxx(),那么当前类就和Xxx类形成了强耦合
于是有人想到用BeanFactory模式,通过BeanFactory获取对象,这样当前类就和Xxx解耦,比如
private XxDao xxDao = DaoFactory.getInstance().createDao("...dao.impl.XxDaoImpl", XxDao.class);
spring提供了更好的解决方案,即IOC控制反转,将对象的管理权反转给了spring,开发者不再需要手动创建Bean,而是直接从spring容器获取
核心
IOC的核心是spring容器,spring创建对象并负责管理它们完整的生命周期
IOC需要依赖注入(DI)的支持:创建A对象,A会用到其他的类,这时就需要依赖注入支持
容器
ApplicationContext
即时加载,加载applicationContext.xml(容器启动)时候就会创建对象
ClassPathXmlApplicationContext:加载类路径下 Spring 的配置文件.
FileSystemXmlApplicationContext:加载本地磁盘下 Spring 的配置文件
DI,dependency injection依赖注入
概念
依赖:指一个类使用到了另一个类,A类依赖B类,即A类使用到了B类
依赖注入:将类和类的依赖关系告诉spring容器
注入方式
构造方法:通过构造方法将另一个类的对象传入当前类
set方法:通过set方法将另一个类的对象传入当前类
注解注入:通过注解获取另一个类的对象
注解方式只能注入spring容器管理的类,因此要配置扫描包,springboot默认会扫描启动类所在包及其子包下的Bean,可以自定义配置
@ComponentScan(value = {"com.xxx.app.service.*", "com.xxx.app.controller.*"})
public class UserServiceImpl implements UserService {//注解注入@Autowiredprivate UserDao userDao;//使用构造方法实现依赖注入public UserServiceImpl(UserDao userDao){this.userDao = userDao;}//使用set方法实现依赖注入public void setUserDao(UserDao userDao) {this.userDao = userDao;}
}
循环依赖
定义:A依赖B,B依赖A
spring如何解决循环依赖
构造器的循环依赖:无法处理,直接抛出BeanCurrentlylnCreationException异常
单例模式下的setter循环依赖:通过“三级缓存”处理循环依赖
非单例循环依赖:无法处理
spring生成Bean的方式
通过无参构造创建Bean
使用BeanFactory:静态和非静态工厂
Bean属性注入(Bean属性赋值)
构造方法:通过构造方法给属性赋值
set方法:通过set方法给属性赋值
spel
springBean生命周期
实例化Bean
对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。
对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean
属性赋值(依赖注入)
实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成属性设置与依赖注入
Aware接口回调
Spring会检测该对象是否实现了xxxAware接口,如果Bean实现了Aware接口,容器会回调相应的方法,将容器相关的信息注入到Bean中
如果这个Bean实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,传入Bean的名字
如果这个Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例
如果这个Bean实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身
如果这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文
BeanPostProcessor前置处理器
容器会调用所有实现了BeanPostProcessor接口的类的postProcessBeforeInitialization方法,对Bean进行前置处理
InitializingBean(初始化)
如果Bean实现了InitializingBean接口,执行afeterPropertiesSet()方法
或在Bean定义中(配置)指定init-method方法
BeanPostProcessor后置处理器
如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法
由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术
在这一步后,Bean就被创建完成,可以开始使用这个Bean了
destroy-method销毁
当容器关闭时,会调用Bean的销毁方法
如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法
或在Bean定义中指定destroy-method方法
springBean的作用域
singleton:默认作用域,单例bean,每个容器中只有一个bean的实例
prototype:为每一个bean请求创建一个实例
request:为每一个request请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收
session:与request范围类似,同一个session会话共享一个实例,不同会话使用不同的实例
global-session:全局作用域,所有会话共享一个实例。如果想要声明让所有会话共享的存储变量的话,那么这全局变量需要存储在global-session中
springIOC注解
刚刚我们说,IOC是将对象的控制从开发者反转给spring,我们通过spring提供的注解来通知spring管理哪些类,将哪些类以哪种作用域向容器中注入Bean
类注解
@Component
向spring容器注入当前Bean
@Component("user"),相当于xml配置了<bean name="user"...></bean>,现在是否理解xml中的注入?其实就是通过xml的方式向spring容器注入了一个Bean
Spring 中提供@Component 的三个衍生注解,功能和@Component一样
@Controller 控制层
@Service 业务层
加在接口的实现类上,不加在接口上
对于有多个实现类的接口,要指定name,自动装配要使用@Resource(name="xxx")
@Repository 持久层
说明:为了区别于类型,通常配置name属性为开头小写
实现类通常配置name=接口名,存在多个实现类则根据区别配置不同name
@Configuration
标记这是一个配置类,一般会配合@Bean,@Scope,@Lazy使用,等价于原本的xml配置文件注入Bean
@Configuration
public class AppConfig {@Beanpublic TransferService transferService() {return new TransferServiceImpl();}
}相当于xml
<beans><bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
@Scope
@Scope作用在类上,表示给定容器中Bean的作用域
@Scope(scopeName="prototype");
@Scope具有以下几种作用域
singleton 单实例的(单例)(默认),全局有且仅有一个实例
prototype 多实例的(多例),每次获取Bean的时候会有一个新的实例
reqeust 同一次请求,每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效
session 同一个会话级别,每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效
@Scope注意点
1.尽量不要直接使用字符串配置,使用spring提供的参数
ConfigurableBeanFactory.SCOPE_PROTOTYPE
ConfigurableBeanFactory.SCOPE_SINGLETON
WebApplicationContext.SCOPE_REQUEST
WebApplicationContext.SCOPE_SESSION
2.每一个Bean实例,都会有一个initMethod() 和 destroyMethod() 方法,我们可以在Bean 类中自行定义,也可以使用 Spring 默认提供的这两个方法
也可以通过@Bean的属性指定@Bean(initMethod = "initUser", destroyMethod = "destroyUser")
3.思考这么一个场景:当Controller中定义了非静态成员变量,如果不设置成多例,那么多次调用操作同一个变量,会不断的改变变量的值,与我们调用方法的预期不符
属性注解
@Value
用于注入普通类型
@Value(value="zs"),value=可以省略
作用在指定成员的成员变量上或者对应的set()上,区别在于
作用成员变量上,通过反射Field注入
作用在set()上,通过set()注入
可以获取配置文件的数据进行注入,比如.properties
@AutoWire
@Autowire默认按照类型(by-type)装配
默认情况下要求依赖对象必须存在,如果找到多个,再按照名称装配
@Autowire
private StudentService studentService;
如果允许依赖对象为null,需设置required属性为false,即
@Autowire(required=false)
private StudentService studentService;
如果使用按照名称(by-name)装配,需结合@Qualifier注解使用,即
@Autowire
@Qualifier("studentService") //这里的参数是bean的name,studentService
private StudentService studentService;
@Resource
默认按照名称(by-name)装配,名称可以通过name属性指定
当按照名称(by-name)装配未匹配时,按照类型(by-type)装配,此时就是用成员的类型去容器中bean的类型匹配
@Resource(name="studentService") //用这里的参数studentService去容器和bean的name匹配
当显式指定name属性后,只能按照名称(by-name)装配
如果没有指定name
当@Resource注解在成员变量上时,默认取name=成员变量名称装配,这里是字段名称不是字段类型,如下就是用studentService去和容器中bean的name匹配
当@Resource注解在setter方法上时,默认取name=属性名称装配
@Resource
private StudentService studentService;@Resource
public void setXXX() {...}
Resource总结
如果同时指定name和type属性,则找到唯一匹配的bean装配,未找到则抛异常
如果指定name属性,则按照名称(by-name)装配,未找到则抛异常
如果指定type属性,则按照类型(by-type)装配,未找到或者找到多个则抛异常(一个type可能有多个实现类,所以可能找到多个结果)
既未指定name属性,又未指定type属性,则按照名称(by-name)装配,如果未找到,则按照类型(by-type)装配
@Autowire和@Resource区别
1.来源不同:@Autowire是spring提供的注解,@Resource是jdk提供的注解
2.默认的依赖查找顺序不同:
@Autowire默认根据类型查找,如果找到多个,再根据名称查找
@Resource默认根据名称查找,找不到再根据类型查找
3.支持的参数不同
@Autowire仅支持require参数
@Resource支持多个参数,比如name,type
4.支持的注入方式不同
@Autowire支持属性注入,setter注入,构造注入
@Resource不支持构造注入,即这个标签不能打在构造方法上实现注入
方法注解
@Bean
方法级别注解,通常使用在@Configuration的配置类中使用
向容器注入一个Bean,id为方法名,类型为方法返回值
@Configuration
public class AppConfig {@Beanpublic TransferService transferService() {return new TransferServiceImpl();}
}相当于xml
<beans><bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
@Scope
@Scope作用在方法上,表示当前Bean作用域
@Lazy
@Lazy作用在方法上,表示当前Bean在注入容器时是懒加载模式
@Primary
当某个类型的Bean有多个候选者可以注入时,应当优先加载@Primary标记的Bean
@Profile
当spring.profile.active为对应的值时,才向容器注入当前Bean
AOP面向切面
切面编程并不是spring开创的,来源是java拦截器思想,但spring给切面编程提供了更好的解决方式
抽取程序执行过程中多个部分功能一致的代码,比如:权限校验,日志记录,性能监控,事务控制
底层实现
通过代理机制,springAOP会用到两种代理机制,只能选一种不能混用
代理
什么是代理:将原本的调用,比如person.run();封装一层代理,在代理对象中调用person.run();
静态代理
代码运行前,我们就编写好代理类,编译后生成class文件
缺点十分明显:每一个被代理对象都需要建一个代理类去代理,代码冗杂
动态代理
代理类是在运行过程中产生的,不需要给每一个被代理类编写单独的代理类
JDK 动态代理
针对接口的实现类产生代理
生成接口的实现类,在实现类中通过method.invoke(...)调用被代理对象的方法
Cglib 动态代理
针对没有实现接口的类产生代理
对目标进行继承代理,应用的是字节码增强生成当前类的子类对象
Enhancer类的create()生成代理对象
实现MethodInterceptor接口,重写intercept(...)
methodProxy.invoke(target,objects)调用代理对象方法
如果没有Spring,那么需要我们手动的Proxy.newProxyInstance(xx.xx.xx);
简单来说,就是在目标方法执行时,会通过代理的方式生成一个子类,重写目标方法,在方法体前后执行AOP代码
动态代理注意点
如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
如果目标对象实现了接口,可以强制使用CGLIB实现AOP
如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
Spring AOP的原理是 JDK 动态代理和CGLIB字节码增强技术,前者需要被代理类实现相应接口,也只有接口中的方法可以被JDK动态代理技术所处理;后者实际上是生成一个子类,来覆盖被代理类,那么父类的final方法就不能代理,因为父类的final方法不能被子类所覆盖。一般而言Spring默认优先使用JDK动态代理技术,只有在被代理类没有实现接口时,才会选择使用CGLIB技术来实现AOP,这样就可能造成代理混用的问题,spring提供了一个配置强制使用cglib代理来解决混用问题:<aop:config proxy-target-class="true" />
AOP名词
Joinpoint(连接点)
目标对象中所有可以增强的方法都是连接点
所谓连接点是指那些可以被拦截到的点,因为spring只支持方法类型的连接点
Pointcut(切入点)
目标对象中已经被增强的方法
所谓切入点是指我们要对哪些连接点Joinpoint进行拦截的定义
Advice(通知/增强)
负责增强的代码
所谓通知是指拦截到Joinpoint之后所要做的事情
通知分为前置通知,后置返回通知,后置通知(最终通知),环绕通知(切面要完成的功能),异常通知
后置返回通知:目标方法出现异常不会执行
后置通知(最终通知):目标出现异常也会执行
Introduction(引介)
引介是一种特殊的通知,在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或 Field
Aspect(切面)
是切入点和通知(引介)的结合
Target(目标对象)
被代理的目标对象
Weaving(织入)
将通知应用到切点的过程就是织入
是指把增强应用到目标对象来创建新的代理对象的过程,织入完成生成代理对象
Proxy(代理)
通知织入到目标对象以后,形成代理对象
一个类被AOP织入增强后,就产生一个结果代理类
简单的springAOP实现
@Aspect
定义切面,表示该类是切面类
这个注解只是声明这是个切面类,并没有放进Spring容器,通常使用@Component将该类交给spring容器
切面类中可以定义切点,通知
@Pointcut("xxx")
定义切点,两种方式
execution 指定方法,@Pointcut("execution(* com.coolway.*(..))")
@annotation 指定注解,@Pointcut("@annotation(com.coolway.annotation.RequiresLogin)")
@Before等
定义通知,即指定切点处要执行的AOP代码
要给通知注解传入切点
要么使用上面@Pointcut("xxx")定义好的切点
要么重新传入切点,比如@Around("@annotation(com.coolway.annotation.RequiresLogin)")
其他通知注解
@AfterReturning
@Around
@AfterThrowing
@After
切点表达式
完整的表达式:execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)modifiers-pattern:方法的可见性修饰符,必须是public,通常省略
void com.coolway.service.impl.StudentServiceImpl.getStudent()ret-type-pattern:方法的返回值类型,如 int,void 等;通常不对返回值做要求,用*表示任意类型返回值
* com.coolway.service.impl.StudentServiceImpl.getStudent()declaring-type-pattern:方法所在类的全路径名,类名可以定义为部分匹配,比如以ServiceImpl结尾
* com.coolway.service.impl.*ServiceImpl.*(..)可以配置成包括子包下符合的,使用..符号,如service下任意子包下的impl包的ServiceImpl类的任意方法(任意参数)
* com.coolway.service..impl.*ServiceImpl.*(..)name-pattern:方法名,如 getOrderDetail();可以用*表示该类的任意方法
* com.coolway.service.impl.StudentServiceImpl.*()param-pattern:方法的参数类型,如 java.lang.String;..表示任意参数
* com.coolway.service.impl.StudentServiceImpl.*(..)throws-pattern:方法抛出的异常类型,如 java.lang.Exception;示例:execution(public void com.coolway.service.impl.StudentServiceImpl.getStudent())
依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
实现
@Aspect
public class AspectDemo {//定义切点,指定方法@Pointcut("execution(* com.coolway.*(..))")public void loginAspectPointcut() {}//使用定义好的切点@Before("loginAspectPointcut()")public void beforeLogin() {System.out.println("开始访问,查看是否登录");}//指定切点为注解@After("@annotation(com.coolway.annotation.RequiresLogin)")public void afterLogin() {System.out.println("结束访问");}
}
spring事务管理
概述
什么是事务
逻辑上的一组操作,一起成功,一起失败
显然,通常执行一系列的增删改操作,需要开启事务
spring事务管理依托于数据库事务,是通过数据库连接Connection来实现的,并不是spring自己提供的事务,spring提供了更好的方式处理事务
事务特性ACID
原子性:事务不可分割
一致性:事务执行前后数据完整性保持一致
隔离性:事务执行过程中不受其他事务的干扰
持久性:事务结束,数据持久化到数据库
不考虑事务隔离引发的安全性问题
脏读
读到了其他事务正在操作但未提交的数据
A事务访问了一条数据X,并对X做了修改,B事务也访问了X,读取到了A事务修改后的X值,而A事务执行了回滚,X值恢复到了修改前,此时B事务之前读的X就成了脏数据
不可重复读
重复查询时,读到了其他事务更新的数据,导致重复查询结果不一致
A事务在执行过程中多此访问数据X,因为其他事务在多次读取的间隙时间内修改了X值,导致A事务多次读取到的X值不一致
幻读
读到了其他事务插入的数据,导致重复查询结果不一致
在一个事务中,先后两次进行读取相同的数据(一般是范围查询),但由于另一个事务新增或者删除了数据,导致前后两次结果不一致,幻读不可以理解成不可重复读的一种,应当单独理解,因为幻读会严重影响后续操作
事务隔离级别
未提交读 :脏读,不可重复读,虚读都有可能发生 1
已提交读 :避免脏读。但是不可重复读和虚读有可能发生 2
可重复读 :避免脏读和不可重复读.但是虚读有可能发生 4
串行化的 :避免以上所有读问题 8
Mysql默认是repeatable read,级别4
oracle默认是read committed,级别2
事务回滚
代码出现异常会执行事务回滚,事务整体失败
这里的异常指的是没有被处理的异常,一旦使用了try catch finally或者是throw&throws,事务就不会执行回滚
spring事务核心
PlatformTransactionManager平台事务管理器接口
spring本身不直接管理事务,而是提供多种事务管理器,针对不同持久化平台提供不同的实现类,如JDBCTransactionManager,HibernateTransactionManager,JpaTransactionManager等
TransactionDefinition事务管理的属性
transactionManager
通常一个项目只用一个事务管理器,但有些项目会包含多个互不相关的数据源,这时候就需要使用多个事务管理器,通过这个属性指定事务管理器
@Transactional(transactionManager = "txManager#singleton")
isolation,隔离级别
ISOLATION_DEFAULT:这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别
ISOLATION_READ_UNCOMMITTED:读未提交,允许事务在执行过程中,读取其他事务未提交的数据
ISOLATION_READ_COMMITTED:读已提交,允许事务在执行过程中,读取其他事务已经提交的数据
ISOLATION_REPEATABLE_READ:可重复读,在同一个事务内,任意时刻的查询结果都是一致的
ISOLATION_SERIALIZABLE:所有事务逐个依次执行
未提交读:脏读,不可重复读,虚读都有可能发生 1
已提交读:避免脏读。但是不可重复读和虚读有可能发生 2
可重复读:避免脏读和不可重复读.但是虚读有可能发生 4
串行化的:避免以上所有读问题 8
propagation,传播行为
决定业务方法平行调用时,事务如何处理
PROPAGATION_REQUIRED required,支持当前事务,如果不存在 就新建一个(默认),最常用
A()调用B(),如果A没有开事务,那么会自动开启一个事务,如果A已经开了事务,就在这个事务中执行B,实际上99%的情况都是这种情况,相当于整体操作作为一个事务
PROPAGATION_SUPPORTS support,支持当前事务,如果不存在,就不使用事务
PROPAGATION_MANDATORY mandatory,支持当前事务,如果不存在,抛出异常
下列配置可以保证平行方法没有在同一个事务中执行
PROPAGATION_REQUIRES_NEW requires_new,如果有事务存在,挂起当前事务,创建一个新的事务,这是为了在事务嵌套的情况下,内部事务不影响外部事务,其他事务
PROPAGATION_NOT_SUPPORTED not_supported,以非事务方式运行,如果有事务存在,挂起当前事务
PROPAGATION_NEVER never,以非事务方式运行,如果有事务存在,抛出异常
PROPAGATION_NESTED nested,如果当前事务存在,则嵌套事务执行0
readOnly,是否只读事务
如果一个事务中所有的数据库操作都是只读的,应该给该事务设置只读属性,帮助数据库引擎优化事务,提升数据库读写效率
true,false
timeout,超时设置
设置事务持续时间,超时强制回滚,避免一个事务对连接占用过长的时间
rollbaclFor,何种异常执行回滚
默认情况下Spring会为所有的运行时异常进行回滚
怎么使用spring事务管理
此处不讨论xml配置的方式,只关注注解式
依赖
<dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId>
</dependency>
简单demo
@Transactional(isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRED, rollbaclFor = NullPointException)
public ...(){...}
//实际上也可以都使用默认配置,即
@Transactional
public ...(){...}
spring事务失效
数据库方面
1.数据库本身不支持事务,比如,mysql的myisam不支持事务,当然新版本的mysql已经移除了myisam引擎
spring方面
2.事务没有被spring管理,比如,在某个方法上使用了注解@Transactional,但是这个类没有被注入到spring容器中,那么这个事务其实是无效的
配置方面
3.数据源未配置事务管理器,未开启事务管理,配置transactionManager是开启spring事务管理的第一步,springboot无需手动开启,但xml配置的方式需要手动配置开启
4.事务传播性设置导致,PROPAGATION_NOT_SUPPORTED,PROPAGATION_NEVER,PROPAGATION_SUPPORTS有可能会导致事务失效
代码编写方面
5.方法不是public,@Transactional只有修饰public方法时才会生效,这是spring的明确要求
6.方法是final修饰,final方法无法被代理类重写,也就无法添加事务管理功能
7.同一个类中自身方法调用
比如,在类A的方法a上使用了注解@Transactional,在这个类A中有其他的方法b调用了这个方法a,那么a上标记的这个事务是无效的,因为spring事务管理是通过AOP实现的,而AOP是通过动态代理实现的,而同一个类中自身方法的调用是通过this,没有通过代理类,所以事务是无效的
解决方法:解决办法有多种
1.声明一个新的service,在service中调用这个@Transactional的方法
2.本类中注入自身
@AutowireXxxService xxxService;xxxService.do();
3.使用application.getBean()获取当前类对象,并调用目标方法
XxxService xxxService = applicationContext.getBean(XxxService.class);xxxService.do();
4.使用AopContext,获取当前的代理对象并调用
((XxxService)AopContext.currentProxy()).do();
8.使用cglib代理,cglib代理对接口层的@Transactional是无效的
9.多线程调用,多个线程获取到的数据库连接不一样,则分别属于两个不同的事务(因为spring事务是基于数据库连接实现),显然无法一起提交和回滚
异常处理
10.rollbackFor异常指定错误,如果发生了rollbaclFor指定以外的异常,事务就不会rollback了
11.异常被catch了,如果必须要catch,那么手动做throw,抛出异常
通过springAOP实现自动事务,默认情况下不再需要对每个方法手动打注解
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;/*** @description 通过AOP切面设置全局事务,拦截service包下面所有方法* AOP术语:通知(Advice)、连接点(Joinpoint)、切入点(Pointcut)、切面(Aspect)、目标(Target)、代理(Proxy)、织入(Weaving)*/
@Configuration
public class TransactionManagerConfig {private static final int TX_METHOD_TIMEOUT = 5000;/*定义切点变量*/private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.web.service.impl.*.*(..))";@Autowiredprivate PlatformTransactionManager transactionManager;/*** @description springBoot事务配置*/@Beanpublic TransactionInterceptor TxAdvice() {/*事务管理规则,声明具备事务管理的方法名*/NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();/*只读事物、不做更新删除等*//*当前存在事务就用当前的事务,当前不存在事务就创建一个新的事务*/RuleBasedTransactionAttribute readOnlyRule = new RuleBasedTransactionAttribute();/*设置当前事务是否为只读事务,true为只读*/readOnlyRule.setReadOnly(true);/* transactiondefinition 定义事务的隔离级别;* PROPAGATION_NOT_SUPPORTED事务传播级别5,以非事务运行,如果当前存在事务,则把当前事务挂起*/readOnlyRule.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);RuleBasedTransactionAttribute requireRule = new RuleBasedTransactionAttribute();/*抛出异常后执行切点回滚*/requireRule.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));/*PROPAGATION_REQUIRED:事务隔离性为1,若当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。 */requireRule.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);/*设置事务失效时间,如果超过5秒,则回滚事务*/requireRule.setTimeout(TX_METHOD_TIMEOUT);Map<String, TransactionAttribute> txMap = new HashMap<>();txMap.put("add*", requireRule);txMap.put("save*", requireRule);txMap.put("insert*", requireRule);txMap.put("update*", requireRule);txMap.put("delete*", requireRule);txMap.put("remove*", requireRule);txMap.put("import*", requireRule);txMap.put("get*", readOnlyRule);txMap.put("query*", readOnlyRule);txMap.put("find*", readOnlyRule);txMap.put("select*", readOnlyRule);source.setNameMap(txMap);TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, source);return txAdvice;}/*** @description 利用AspectJExpressionPointcut设置切面=切点+通知(写成内部bean的方式)*/@Beanpublic Advisor txAdviceAdvisor() {/* 声明切点的面* 切面(Aspect):切面就是通知和切入点的结合。通知和切入点共同定义了关于切面的全部内容——它的功能、在何时和何地完成其功能。* */AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();/*声明和设置需要拦截的方法,用切点语言描写*/pointcut.setExpression(AOP_POINTCUT_EXPRESSION);/*设置切面=切点pointcut+通知TxAdvice*/return new DefaultPointcutAdvisor(pointcut, TxAdvice());}
}
spring中运用的设计模式
工厂设计模式:Spring使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象
代理设计模式:Spring AOP 功能的实现
单例设计模式:Spring 中的 Bean 默认都是单例的
模板方法模式:Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式
包装器设计模式:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源
观察者模式:Spring 事件驱动模型就是观察者模式很经典的一个应用
适配器模式:Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller
相关文章:

spring学习笔记-IOC,AOP,事务管理
目录 概述 什么是spring 侵入式的概念 spring的核心 spring的优势 注意 IOC控制反转 概述 核心 容器 DI,dependency injection依赖注入 概念 注入方式 循环依赖 spring如何解决循环依赖 spring生成Bean的方式 Bean属性注入(Bean属性赋值…...

MYSQL中的触发器TRIGGER
1.概念 触发器是一个特殊的存储过程,当触发器保护的数据发生变更时就会触发。 2.特性 1.触发器与表息息相关,一般我们一个表创建六个触发器。 2.六个触发器其实是三种类六个 insert 类型 before | after insertupdate 类型 before | af…...

用人话讲解深度学习中CUDA,cudatookit,cudnn和pytorch的关系
参考链接 本人学习使用,侵权删谢谢。用人话讲解深度学习中CUDA,cudatookit,cudnn和pytorch的关系 CUDA CUDA是显卡厂商NVIDIA推出的运算平台。 CUDA™是一种由NVIDIA推出的通用并行计算架构,是一种并行计算平台和编程模型&…...

【JavaEE】Servlet API 详解(HttpServletRequest类)
一、HttpServletRequest Tomcat 通过 Socket API 读取 HTTP 请求(字符串), 并且按照 HTTP 协议的格式把字符串解析成 HttpServletRequest 对象(内容和HTTP请求报文一样) 1.1 HttpServletRequest核心方法 1.2 方法演示 WebServlet("/showRequest&…...

HTML页面的全屏显示及退出全屏案例
进入全屏 requestFullscreen 接收一个参数 options(可选), options 是一个对象, 但是只有一个字段 navigationUI, 用于控制是否在元素处于全屏模式时显示导航条. 可选值为 auto, hide, show, 默认值为 auto;当元素不在文档内时, 调用requestFullScreen回失败。 退出…...

layui弹出层点回车键无限弹出解决
$(document).keydown(function (event) {if (event.keyCode 13) {$("*").blur();//去掉焦点if ($(".layui-layer-btn0").length > 0)layer.closeAll();}});...

抖音测试付费短视频:从短剧领域拓展到知识、娱乐全品类
11月16日,抖音开始测试短视频内容付费,即用户在观看创作者的内容时,部分内容需要付费解锁才能全部观看,涉及范围不仅包括此前已经进行付费试水的短剧领域,还拓展至知识、娱乐等几乎全品类内容,用户可按单条…...

代码随想录算法训练营第五十五天 | LeetCode 583. 两个字符串的删除操作、72. 编辑距离、编辑距离总结
代码随想录算法训练营第五十五天 | LeetCode 583. 两个字符串的删除操作、72. 编辑距离、编辑距离总结 文章链接:两个字符串的删除操作、编辑距离、编辑距离总结 视频链接:两个字符串的删除操作、编辑距离 1. LeetCode 583. 两个字符串的删除操作 1.1 思…...

Excel vlookup 如何使用
Excel vlookup 如何使用 打开WX, 搜索 “程序员奇点” Excel vlookup可以说是利器,非常好用的工具,用来查询 Excel 或者进行数据匹配,十分方便。 VLookuP 如何使用,不常用的同学经常容易忘记,这次做个记录ÿ…...

Latex常用特殊字符汇总
本文汇总了博主在使用Latex写文档过程中遇到的所有常用疑难字符、表达式等等及对应的Latex形式 持续更新... 目录 常用字符波浪号1. 文本模式:~2. 数学模式: ∼ \sim ∼3. 字母上方的波浪号: a ˜ \~a a˜ 字母上方角标 (数学模式强调符)箭头…...

Day1跟李沐学AI-深度学习课程00-04【预告、课程安排、深度学习介绍、安装、数据操作+数据预处理】
00 预告 《动手学深度学习》https://github.com/d2l-ai/d2l-zh 01 课程安排 02 深度学习介绍 03 安装 本地安装 使用conda/miniconda环境 conda env remove d2l-zh conda create -n -y d2l-zh python3.8 pip conda activate d2l-zh 安装需要的包 pip install -y jupyter …...

借助拧紧曲线高效管理螺栓装配防错——SunTorque智能扭矩系统
拧紧曲线作为拧紧质量的“晴雨表”,在拧紧过程中,能够实时探知到拧紧状态是否存在异常,并根据曲线特质推测出拧紧过程中遇到了什么样的问题,今天SunTorque智能扭矩系统带您了解拧紧曲线在螺栓装配防错管理中如何发挥作用。 合格的…...

李开复再度回应争议;10 月中国游戏厂商及应用出海收入 30 强出炉丨 RTE 开发者日报 Vol.86
开发者朋友们大家好: 这里是 「RTE 开发者日报」 ,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE (Real Time Engagement) 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有…...

mysql undolog
undolog 日志介绍...

milvus数据库-管理数据库
一个 Milvus 集群最多支持 64 个数据库。 1.创建数据库 先连接数据库服务器,再创建 from pymilvus import connections, dbconn connections.connect(host"127.0.0.1", port19530)database db.create_database("book")2.连接数据库 可以改变…...

一键整合,万用万灵,Python3.10项目嵌入式一键整合包的制作(Embed)
我们知道Python是一门解释型语言,项目运行时需要依赖Python解释器,并且有时候需要安装项目中对应的三方依赖库。对于专业的Python开发者来说,可以直接通过pip命令进行安装即可。但是如果是分发给其他的Windows用户,特别是不熟悉Py…...

MAC地址注册的网络安全影响和措施分析
MAC地址注册对网络安全具有重要影响,同时也需要采取相应的措施来应对潜在的安全风险。以下是有关MAC地址注册的网络安全影响和应对措施的分析: 影响: 1. 身份验证:MAC地址注册可用于设备的身份验证,但MAC地址本身并不…...

某个订单项目记录,涉及MQ消息处理、分布式问题、幂等性等问题解决设计
文章目录 消息队列(Message Queue)什么场景下,使用消息队列?消息队列 概述 RabbitMQ 消息队列RabbitMQ 概念名词 概念RabbitMQ 流程 RabbitMQ 安装RabbitMQ 页面介绍Exchange 交换机类型Spring Boot 整合RabbitMQAmqpAdmin 与 Rab…...

excel中的OFFSET函数
介绍 OFFSET函数是确定从基点出发移动后的引用区域。它有5个参数: 第1个参数是引用的参考基点区域第2个参数是移动的行数,正数代表向下移动的行数,负数代表向上移动的行数第3个参数是移动的列数,正数代表向右移动的列数…...

力扣:168. Excel表列名称(Python3)
题目: 给你一个整数 columnNumber ,返回它在 Excel 表中相对应的列名称。 例如: A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> 27 AB -> 28 ... 来源:力扣(LeetCode) 链接:力扣&…...

短视频账号矩阵系统源码/技术源码分享/技术搭建架构
短视频账号矩阵系统----技术源码分享/技术搭建架构 抖音seo又叫抖音搜索引擎,只要能做到布词,和过去的百度seo优化一样,布词,布关键词,当搜索栏搜索时可以搜索到该短视频。优化视频关键词,做好关键词的优化…...

Nginx负载均衡时,验证码老是错误
问题 正式环境里,登录之后没有跳转到之前的页面,在测试服务器上测试了一下这个BUG,不存在这个问题 问题原因 我们的服务器做了负载均衡,问题出现之后,就问了运维,负载均衡的规则是什么,默认情况…...

Unity3D ugui获取ui控件屏幕坐标
local worldPos uiGo.Transform.position local uiCamera GetUICamera() local screenPos uiCamera:WorldToScreenPoint(worldPos)...

数字化转型的“支点”是什么?
当前,数字化转型在推动各行各业降本增效的过程中扮演着越来越重要的角色,数字化转型不仅可以提高企业的生产效率和降低企业生产经营成本,也成为了推动经济发展的新引擎。对于企业而言,想要成功推动企业数字化转型,不…...

Spring Task单机定时任务(使用及阻塞问题解决)
一、介绍 SpringTask是Spring自主研发的定时任务工具,并且存在于Spring体系中,不需要添加任何依赖 Spring Boot 默认在无任何第三方依赖的情况下使用 spring-context 模块下提供的定时任务工具 Spring Task。 我们只需要使用 EnableScheduling 注解就可…...

石原子科技亮相2023成都市信息领域新产品发布会
2023年11月13日至15日,由成都市互联网信息办公室、四川天府新区管委会、成都市经信局市新经济委、成都市农业农村局指导的以“信息创造价值 创新引领未来”为主题的成都市信息领域新产品发布会在科创生态岛1号馆举行。围绕人工智能、区块链、数字化绿色化、数字乡村…...

2023数维杯国际赛数学建模竞赛选题建议及D题思路讲解
大家好呀,2023年第九届数维杯国际大学生数学建模挑战赛今天早上开赛啦,在这里先带来初步的选题建议及思路。 目前团队正在写B题和D题完整论文,后续还会持续更新哈,大家三连关注一下防止迷路。 注意,本文只是比较简略…...

最新宝塔反代openai官方API开发接口详细搭建教程,解决502 Bad Gateway问题
一、前言 宝塔反代openai官方API接口详细教程,实现国内使用ChatGPT502 Bad Gateway问题解决, 此方法最简单快捷,没有复杂步骤,不容易出错,即最简单,零代码、零部署的方法。 二、实现前提 一台海外服务器…...

vue3 实现pdf预览
需要下载pdfjs-dist <template><a-modal class"fill-modal" v-model:open"state.visible" :title"state.modalTitle" width"50%" cancel"handleCancel"><div class"preview-btns-posi"><a-…...

【React】Redux基本使用
什么情况使用 Redux ? Redux 适用于多交互、多数据源的场景。简单理解就是复杂 从组件角度去考虑的话,当我们有以下的应用场景时,我们可以尝试采用 Redux 来实现 某个组件的状态需要共享时 一个组件需要改变其他组件的状态时 一个组件需要…...