Spring注解驱动开发(四)
注:此笔记为尚硅谷Spring注解驱动教程(雷丰阳源码级讲解)学习笔记,并同时参考[https://blog.csdn.net/xjhqre/article/details/123264069]博主文章,其中包含个人的笔记和理解,仅做学习笔记之用。
14、声明式事务
Spring声明式事务是通过AOP(面向切面编程)来实现的,它允许开发者将事务管理的代码从业务逻辑中分离出来,以提高代码的模块化和可维护性。在Spring中,声明式事务是通过注解或XML配置来定义的,而不是直接在代码中编写事务管理的逻辑。
核心概念:
事务(Transaction)
: 事务是一系列的操作,要么全部成功执行,要么全部失败回滚。在数据库中,事务通常包括一组SQL语句。
事务管理器(Transaction Manager)
: 事务管理器负责协调事务的开始、提交和回滚。Spring支持多种事务管理器,包括JDBC事务管理器、Hibernate事务管理器、JTA事务管理器等。
切点(Pointcut)
: 切点定义了在哪里开始事务的范围。通常,切点定义在业务层方法上,以确定哪些方法需要事务支持。
通知(Advice)
: 通知是在切点上执行的代码,包括"前置通知"、“后置通知”、"异常通知"等。在声明式事务中,通知包括开启事务、提交事务、回滚事务等。
事务属性(Transaction Attributes)
: 事务属性定义了事务的属性,例如隔离级别、传播行为、超时时间等。这些属性可以通过注解或XML配置来指定。
原理:
事务代理
: Spring使用AOP在运行时生成代理对象,这些代理对象包装了被事务管理的目标对象。当目标对象的方法被调用时,代理对象将负责管理事务。
切面
: 切面是一组通知和切点的组合。在声明式事务中,切面包含了开启事务、提交事务、回滚事务等通知,并定义了切点,确定在哪些方法上应用这些通知。
事务管理器
: 声明式事务需要与事务管理器协同工作。在Spring中,可以配置不同的事务管理器来支持不同的数据源和事务管理策略。
事务通知
: 在切点匹配时,相应的事务通知将被执行。通常有三种主要的事务通知:前置通知(开启事务)、后置通知(提交事务)、异常通知(回滚事务)。
14.1、环境搭建
- 导入相关依赖:数据源、数据库驱动、Spring-jdbc模块
- 配置数据源、JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据
14.1.1、创建数据库
DROP TABLE IF EXISTS `tbl_user`;
CREATE TABLE `tbl_user` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`age` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
14.1.2、编写UserDao
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void insert() {
String sql = "INSERT INTO `tbl_user`(username,age) VALUES(?,?)";
String username = UUID.randomUUID().toString().substring(0, 5);
jdbcTemplate.update(sql, username, 19);
}
}
14.1.3、编写UserService
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void insertUser() {
userDao.insert();
System.out.println("插入完成...");
}
}
14.1.4、编写配置类
@Configuration
@ComponentScan("com.kdz.tx")
@PropertySource("classpath:/db.properties")
public class TxConfig implements EmbeddedValueResolverAware {
private StringValueResolver valueResolver;
@Bean
public DataSource dataSource() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(valueResolver.resolveStringValue("${jdbc.username}"));
dataSource.setPassword(valueResolver.resolveStringValue("${jdbc.password}"));
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/springtx?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true");
dataSource.setDriverClass(valueResolver.resolveStringValue("${jdbc.driverClassName}"));
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate() throws Exception{
//Spring对@Configuration类会特殊处理;给容器中加组件的方法,多次调用都只是从容器中找组件
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
return jdbcTemplate;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.valueResolver = resolver;
}
}
14.1.5、测试
@Test
public void test13() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TxConfig.class);
UserService userService = applicationContext.getBean(UserService.class);
userService.insertUser();
}
14.2、事务测试
14.2.1、给方法标注@Transactional
给方法上标注 @Transactional 表示当前方法是一个事务方法;
并在方法中设置异常
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Transactional
public void insertUser() {
userDao.insert();
int i = 1 / 0;
System.out.println("插入完成...");
}
}
只有@Transactional,不行
14.2.2、开启注解事务管理功能
在配置类标注**@EnableTransactionManagement** 开启基于注解的事务管理功能;
@Configuration // 告诉Spring这是一个配置类
@EnableTransactionManagement
@ComponentScan("com.kdz.tx")
@PropertySource("classpath:/db.properties")
public class TxConfig implements EmbeddedValueResolverAware {
// ...
}
14.2.3、配置事务管理器
在配置类中加入事务管理器bean
//注册事务管理器在容器中
@Bean
public PlatformTransactionManager transactionManager() throws Exception{
return new DataSourceTransactionManager(dataSource());
}
14.2.4、测试
@Test
public void test13() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TxConfig.class);
UserService userService = applicationContext.getBean(UserService.class);
userService.insertUser();
}
事务测试成功
测试结果:
控制台打印除 0 异常,数据库未增加新记录
事务使用步骤
第三步:在对应需要执行事务的方法上添加事务注解@Transactional
14.3、事务源码分析
事务执行原理:
1.标注**@EnableTransactionManagement**,spring利用TransactionManagementConfigurationSelector给容器 中会导入两个组件:AutoProxyRegistrar和ProxyTransactionManagementConfiguration
2.AutoProxyRegistrar功能:
1)给容器中注册一个 InfrastructureAdvisorAutoProxyCreator 组件;该组件利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用;
3.ProxyTransactionManagementConfiguration功能:
给容器中注册事务增强器:
1)事务增强器要用事务注解的信息,利用AnnotationTransactionAttributeSource解析事务注解
2)事务拦截器:TransactionInterceptor;保存了事务属性信息,事务管理器;他是一个 MethodInterceptor,在目标方法执行的时候执行拦截器链:
①先获取事务相关的属性
②再获取PlatformTransactionManager,如果事先没有添加指定任何transactionmanger,最终会从容器中按照类型获取一个PlatformTransactionManager;
③执行目标方法,如果异常,获取到事务管理器,利用事务管理回滚操作;如果正常,利用事务管理器,提交事务
AutoProxyRegistrar
ProxyTransactionManagementConfiguration
给容器中注册事务增强器
? 1)事务增强器要用事务注解的信息,利用**AnnotationTransactionAttributeSource ** 解析事务注解
? 2)事务拦截器:TransactionInterceptor;保存了事务属性信息,事务管理器;
他是一个 MethodInterceptor,在目标方法执行的时候执行拦截器链:
3.执行目标方法,如果异常,获取到事务管理器,利用事务管理回滚操作;如果正常,利用事务管理器,提交事务
15、扩展原理
15.1、BeanFactoryPostProcessor
BeanPostProcessor:bean后置处理器,bean创建对象初始化前后进行拦截工作的
BeanFactoryPostProcessor:beanFactory的后置处理器:
1.在BeanFactory标准初始化之后调用,来定制和修改BeanFactory的内容
2.所有的bean定义已经保存加载到beanFactory,但是bean的实例还未创建
15.1.1、编写Dog类
public class Dog {
public Dog() {
System.out.println("创建狗对象");
}
@PostConstruct
public void init() {
System.out.println("初始化狗对象");
}
@PreDestroy
public void destroy() {
System.out.println("销毁狗对象");
}
}
15.1.2、编写MyBeanFactoryPostProcessor
编写MyBeanFactoryPostProcessor方法,实现接口BeanFactoryPostProcessor,重写postProcessBeanFactory方法
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("执行postProcessBeanFactory");
int count = beanFactory.getBeanDefinitionCount();
String[] names = beanFactory.getBeanDefinitionNames();
System.out.println("当前BeanFactory中有" + count + " 个Bean");
System.out.println(Arrays.asList(names));
}
}
15.1.3、编写配置类
@ComponentScan("com.kdz.ext")
@Configuration
public class ExtConfig {
@Bean
public Dog dog() {
return new Dog();
}
}
15.1.4、测试
@Test
public void test14() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);
applicationContext.close();
}
测试结果:
执行postProcessBeanFactory
当前BeanFactory中有8 个Bean
创建狗对象
初始化狗对象
销毁狗对象
debug分析
1.debug入口
2.具体过程
3.BeanFactoryPostProcessor原理:
1.ioc容器创建对象
2.执行refresh()方法中的invokeBeanFactoryPostProcessors(beanFactory);
3.直接在BeanFactory中找到所有类型是BeanFactoryPostProcessor的组件,并执行他们的方法
4.在初始化创建其他组件前面执行
15.2、BeanDefinitionRegistryPostProcessor
BeanDefinitionRegistryPostProcessor 继承自BeanFactoryPostProcessor
其优先于BeanFactoryPostProcessor执行;在所有bean定义信息将要被加载,bean实例还未创建的时候执行
利用BeanDefinitionRegistryPostProcessor给容器中再额外添加一些组件
BeanDefinitionRegistryPostProcessor执行流程:
1.ioc创建对象
2.refresh() --> invokeBeanFactoryPostProcessors(beanFactory);
3.从容器中获取到所有的BeanDefinitionRegistryPostProcessor组件
1)依次触发所有的postProcessBeanDefinitionRegistry()方法
2)再来触发postProcessBeanFactory()方法;
4.再来从容器中找到BeanFactoryPostProcessor组件;然后依次触发postProcessBeanFactory()方法
15.2.1、编写MyBeanDefinitionRegistryPostProcessor
其他配置类和测试类同上
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("执行MyBeanDefinitionRegistryPostProcessor...bean的数量:" + beanFactory.getBeanDefinitionCount());
}
//BeanDefinitionRegistry Bean定义信息的保存中心,以后BeanFactory就是按照BeanDefinitionRegistry里面保存的每一个bean定义信息创建bean实例;
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
System.out.println("执行postProcessBeanDefinitionRegistry...bean的数量:" + registry.getBeanDefinitionCount());
// 手动注册bean对象
// 方法一:RootBeanDefinition beanDefinition = new RootBeanDefinition(Cat.class);
// 方法二:
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Cat.class).getBeanDefinition();
registry.registerBeanDefinition("car", beanDefinition);
}
}
15.2.2、编写Cat类
public class Cat implements InitializingBean, DisposableBean {
public Cat() {
System.out.println("创建猫对象");
}
@Override
public void destroy() throws Exception {
System.out.println("销毁猫对象");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("初始化猫对象");
}
}
15.2.3、测试
测试结果:
执行postProcessBeanDefinitionRegistry…bean的数量:9
执行MyBeanDefinitionRegistryPostProcessor…bean的数量:10
执行postProcessBeanFactory
当前BeanFactory中有10 个Bean
创建狗对象
初始化狗对象
创建猫对象
初始化猫对象
销毁猫对象
销毁狗对象
结果分析:
先执行MyBeanDefinitionRegistryPostProcessor里的postProcessBeanDefinitionRegistry,在输出bean对象的数量后又创建了一个Cat类的bean
在执行MyBeanDefinitionRegistryPostProcessor里的postProcessBeanFactory方法。输出的bean数量加1
然后执行MyBeanFactoryPostProcessor里的postProcessBeanFactory方法
以上方法都执行完成后开始创建bean对象
注意方法的执行顺序
debug分析
为什么BeanDefinitionRegistryPostProcessor会比BeanFactoryPostProcessor先执行???
1.debug入口
16、事件监听
ApplicationListener:监听容器中发布的事件。事件驱动模型开发;
我们需要自己写一个监听器,该监听器必须实现ApplicationListener接口,用于监听ApplicationEvent 及其下面的子事件;
步骤:
1.写一个监听器(ApplicationListener实现类)来监听某个事件(ApplicationEvent及其子类)
2.把监听器加入到容器;
3.只要容器中有相关事件的发布,我们就能监听到这个事件;
1)ContextRefreshedEvent:容器刷新完成(所有bean都完全创建)会发布这个事件;
2)ContextClosedEvent:关闭容器会发布这个事件;
4.发布一个事件
applicationContext.publishEvent()
16.1、编写监听器
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
//当容器中发布此事件以后,方法触发
@Override
public void onApplicationEvent(ApplicationEvent event) {
// TODO Auto-generated method stub
System.out.println("收到事件:" + event);
}
}
16.2、测试
@Test
public void test14() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);
applicationContext.publishEvent(new ApplicationEvent(new String("我发布的事件")) {
});
applicationContext.close();
}
测试结果:
收到事件:org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@6bc168e5, started on Wed Mar 02 15:04:16 CST 2022]
收到事件:SpringTest$1[source=我发布的事件]
收到事件:org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@6bc168e5, started on Wed Mar 02 15:04:16 CST 2022]
16.3、源码分析
上面结果分析:
debug分析
1.debug入口:
事件发布顺序
- ContextRefreshedEvent事件
- 容器创建对象:refresh();
- finishRefresh();容器刷新完成会发布ContextRefreshedEvent事件
- 自己发布的事件执行
- 容器关闭会发布ContextClosedEvent
事件发布流程
1.容器创建对象:refresh();
2.finishRefresh();
3.publishEvent(new ContextRefreshedEvent(this));
1)获取事件的多播器(派发器):getApplicationEventMulticaster()
2)multicastEvent派发事件:
3)获取到所有的ApplicationListener;for (final ApplicationListener<?> listener : getApplicationListeners(event, type))
①如果有Executor,可以支持使用Executor进行异步派发;
②否则,同步的方式直接执行listener方法;invokeListener(listener, event);拿到listener回调onApplicationEvent方法;
事件多播器(派发器)创建流程
1.容器创建对象:refresh();
2.initApplicationEventMulticaster();初始化ApplicationEventMulticaster;
1.先去容器中找有没有id=“applicationEventMulticaster”的组件;
2.如果没有则直接创建this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);并且加入到容器中,我们就可以在其他组件要派发事件,自动注入到派发器applicationEventMulticaster中;
监听器创建流程
1.容器创建对象:refresh();
2**.registerListeners()**;从容器中拿到所有的监听器,把他们注册到applicationEventMulticaster中;
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
将监听器listener注册到派发器ApplicationEventMulticaster中:getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
16.4、@EventListener
通过@EventListener注解,我们也可以用来监听事件
16.4.1、编写UserService类
@Service
public class UserService {
@EventListener(classes = {ApplicationEvent.class})
public void listen(ApplicationEvent event) {
System.out.println("UserService。。监听到的事件:" + event);
}
}
16.4.2、测试
@Test
public void test14() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);
applicationContext.publishEvent(new ApplicationEvent(new String("我发布的事件")) {
});
applicationContext.close();
}
测试结果:
UserService监听到的事件:org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@6bc168e5, started on Wed Mar 02 15:37:36 CST 2022]
UserService监听到的事件:SpringTest$1[source=我发布的事件]
UserService监听到的事件:org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@6bc168e5, started on Wed Mar 02 15:37:36 CST 2022]
16.4.3、源码分析
spring使用EventListenerMethodProcessor
处理器来解析方法上的@EventListener;其实现了SmartInitializingSingleton
接口
SmartInitializingSingleton原理:
? 1.ioc容器创建对象并refresh();
? 2.finishBeanFactoryInitialization(beanFactory);
初始化剩下的单实例bean;
? 1.先创建所有的单实例bean:getBean()
;
? 2.获取所有创建好的单实例bean,判断是否是SmartInitializingSingleton
类型的;如果是就调用afterSingletonsInstantiated();
Spring注解驱动开发(四)的学习笔记到此完结,笔者归纳、创作不易,大佬们给个3连再起飞吧
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!