目录
一.概述
我将提出用spring写事务和aop的程序的问题,然后写springBoot对于它们的优化,以及提出上一篇未曾提起的springboot的核心。
二.问题
首先,先提出问题:spring对于aop和事务的配置繁琐,需要导入打量jar包,编写配置文件。
我们导入jar包。
<!-- Spring核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.20</version> <!-- 版本可根据实际需求调整 -->
</dependency>
<!-- AOP依赖(Spring AOP + AspectJ支持) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.20</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version> <!-- AspectJ织入器,用于解析切面表达式 -->
</dependency>
<!-- 事务管理依赖(如果使用JDBC/MyBatis等,需额外引入对应数据源依赖) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.20</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId> <!-- 提供DataSourceTransactionManager -->
<version>5.3.20</version>
</dependency>
因为先设置aop,才能配置事务,我们先来看aop的配置。
AOP配置的顺序。
1.先开启aop的自动代理
2.定义切面类和切入点
3.定义通知
前提,要确保它们全部都已经注册进入容器。
第一步:
注解方式:
@Configuration
@ComponentScan("com.pxq") // 扫描组件(包括切面类)
@EnableAspectJAutoProxy // 开启AOP自动代理
public class SpringConfig {
}
xml方式:
<!-- 扫描组件(可选,若切面类用@Component标记) -->
<context:component-scan base-package="com.pxq"/>
<!-- 开启AOP自动代理 -->
<aop:aspectj-autoproxy/>
第二三步:
注解方式:
@Component // 纳入Spring容器管理
@Aspect // 标记为切面类
public class transactionAspect {
// 定义切入点(例如:匹配com.pxq.service包下所有类的所有方法)
@Pointcut("execution(* com.pxq.service.*.*(..))")
public void servicePointcut() {}
// 前置通知
@Before("servicePointcut()")
public void before(JoinPoint joinPoint) {
System.out.println("方法执行前:" + joinPoint.getSignature().getName());
}
}
xml方式:
<!-- 定义切面(若未用@Component,需手动注册) -->
<bean id="transactionAspect" class="com.pxq.aspect.transactionAspect"/>
<!-- 配置AOP切面、切入点、通知 -->
<aop:config>
<!-- 引用切面类 -->
<aop:aspect ref="transactionAspect">
<!-- 定义切入点 -->
<aop:pointcut id="servicePointcut"
expression="execution(* com.pxq.service.*.*(..))"/>
<!-- 配置前置通知 -->
<aop:before pointcut-ref="servicePointcut" method="before"/>
</aop:aspect>
</aop:config>
配置完aop,我们来看事务。
用简单的注解方式:
1.配置数据源和事务管理器
2.使用@Transactional标记事务方法(可以在方法上配置事务的属性)
@Configuration
@ComponentScan("com.pxq")
@EnableTransactionManagement // 开启注解驱动的事务管理
public class SpringConfig {
// 配置数据源(以Druid为例,需引入对应依赖)
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
// 配置事务管理器(依赖数据源)
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
@Service
public class UserService {
@Autowired
private UserDao userDao;
// 声明事务:默认传播行为REQUIRED,隔离级别DEFAULT
@Transactional
public void transferMoney(Long outUser, Long inUser, BigDecimal amount) {
userDao.decrease(outUser, amount);
//int n = 1/0;如果出现异常会回滚
userDao.increase(inUser, amount);
}
}
XML方式:
配置数据源和事务管理器
<!-- 配置数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
定义事务的切面切点,以及事务的属性
<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 匹配Service层的所有方法,设置事务属性 -->
<tx:method name="*" propagation="REQUIRED" isolation="DEFAULT"
read-only="false" rollback-for="Exception"/>
<!-- 单独配置查询方法为只读 -->
<tx:method name="get*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- AOP织入:将事务通知应用到切入点 -->
<aop:config>
<aop:pointcut id="servicePointcut"
expression="execution(* com.pxq.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="servicePointcut"/>
</aop:config>
可以看到aop和事务的配置有多繁琐。
如果想要给方法加事务,使用注解方法,在方法和类上加注解(一般在serviceImpl包下),需要在多处加注解,而xml却能覆盖整个包下的所有方法,虽然注解方法简单,却依然有其优势。
看完spring的配置我们来看springboot的配置
三.优化
先引入依赖。
<!-- Spring Boot Starter(核心依赖,包含自动配置) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- AOP依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 事务依赖(若涉及数据库操作,需引入此依赖,包含事务管理器) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 数据库驱动(以MySQL为例) -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
1.spring-boot-starter-aop:自动引入 AOP 相关依赖(包括aspectjweaver),并默认开启 AOP 代理(无需手动加@EnableAspectJAutoProxy)。
2.spring-boot-starter-jdbc:引入Spring JDBC 和事务管理器(DataSourceTransactionManager),并自动配置事务支持(无需手动加@EnableTransactionManagement)
我要提到一个重要知识点:springboot默认有很多自动配置类,比如上述的aop和事务,只要引入启动器,并开启组件扫描,自动配置类会生效。
当你需要自定义配置类时,可以在该类上标注@Configuration,如果本身项目中有该配置类,便会覆盖默认的自动配置类,执行你自定义的配置类。
更详细的内容web篇再来表述。
// 1. @Aspect:标记为切面类
// 2. @Component:纳入Spring容器管理
@Aspect
@Component
public class LogAspect {
// 定义切入点:匹配com.example.service包下所有类的所有方法
// execution表达式:修饰符(可选) 返回值 包名.类名.方法名(参数)
private static final String POINTCUT = "execution(* com.pxa.service.*.*(..))";
// 环绕通知:可以在方法执行前后添加逻辑,甚至控制方法是否执行
@Around(POINTCUT)
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
// 方法执行前
long start = System.currentTimeMillis();
// 执行目标方法(必须调用,否则目标方法不执行)
Object result = joinPoint.proceed();
// 方法执行后
long end = System.currentTimeMillis();
String methodName = joinPoint.getSignature().getName();
System.out.println("方法[" + methodName + "]执行耗时:" + (end - start) + "ms");
return result;
}
}
事务配置:
1.配置数据源
yaml代码:
yaml和properties一样写了很多基础信息,都是springboot的配置文件,但是语法不一样,都用于给配置类或组件注入具体的值。
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC
username: root
password: 123456
@Service
public class UserService {
private final UserMapper userMapper;
// 构造器注入(防止循环依赖)
public UserService(UserMapper userMapper) {
this.userMapper = userMapper;
}
// 声明事务:默认传播行为REQUIRED(当前无事务则创建,有则加入)
@Transactional
public void transfer(Long outUser, Long inUser, BigDecimal amount) {
// 扣减金额
userMapper.decreaseBalance(outUser, amount);
// 模拟异常(测试回滚)
// if (true) throw new RuntimeException("转账失败");
// 增加金额
userMapper.increaseBalance(inUser, amount);
}
// 只读事务(查询方法推荐使用,提高性能)
@Transactional(readOnly = true)
public User getUserById(Long id) {
return userMapper.selectById(id);
}
}
其实springboot和spring写这些代码的内涵是一致的,只是springboot可以简化重复的配置内容,让开发者更高效的写代码。
四.结尾
下一章我将更详细的阐述自动配置类等重要的内容,帮助大家更好的看到spring和springboot的区别,和便利。
1048

被折叠的 条评论
为什么被折叠?



