一、配置过程
- 配置依赖项,注意的是切入点表达式解析的依赖包。
- 配置切面类:
@Aspect
注解,表示当前类是一个切面类
@Pointcut
替代xml文件中的切入点表达式,并且写入切入点表达式
@Before @AfterThrowing @AfterReturning @After @Around
分别替代xml文件中的前置、异常、后置、最终以及环绕通知,需要注意的是,环绕通知与前面几个通知选择其一即可,环绕通知需要写一个方法为public Object aopAround(ProceedingJoinPoint joinPoint)
,参数必须为要求的参数,然后通过joinPoint
来替代动态代理中的invoke
。
其余注解的配置与之前的配置一致,需要的注意的是在MyConfiguration
类前需要加注解@EnableAspectJAutoProxy
来开启AOP代理。
二、配置步骤
2.1 依赖项
4.0.0
1.8
1.8
org.example
spring-account-annoation
1.0-SNAPSHOT
jar
org.springframework
spring-context
5.0.2.RELEASE
org.springframework
spring-aop
5.0.2.RELEASE
org.springframework
spring-test
5.0.2.RELEASE
test
org.springframework
spring-context
5.0.2.RELEASE
junit
junit
4.12
test
mysql
mysql-connector-java
5.1.48
org.aspectj
aspectjweaver
1.8.7
commons-dbutils
commons-dbutils
1.7
c3p0
c3p0
0.9.1.2
2.2 配置切面类及通知方法
package com.util;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.sql.SQLException;
/**
* Created with IntelliJ IDEA.
*
* @author : gxm
* @date : 2020/5/5
* @time : 21:25
* @projectName : spring-account
* @description : 工具类,控制连接的事务,注解中配置为切面类
* 四个通知的注解使用会有顺序调用问题,开发时应选择环绕通知,手动控制通知
* To change this template use File | Settings | File Templates.
* Description:
**/
@Component("transactionManager")
@Aspect
public class TransactionManager {
@Resource(name = "connectionUtils")
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
/**
* @author gxm
* @date 2020/5/8 15:38
* @description 配置切入点表达式
* @param
* @return void
* @since version-1.0
*/
@Pointcut("execution(* *.*.*.AccountServiceImpl.*(..))")
private void pointCut(){}
/**
* @param
* @return void
* @author gxm
* @date 2020/5/5 21:30
* @description 关闭当前当前线程连接的自动提交
* @since version-1.0
*/
@Before("pointCut()")
public void beginTransaction() {
System.out.println("beginTransaction");
try {
connectionUtils.getThreadConnection().setAutoCommit(false);
} catch (SQLException throwables) {
throw new RuntimeException(throwables);
}
}
/**
* @param
* @return void
* @author gxm
* @date 2020/5/5 21:30
* @description 出错就回滚
* @since version-1.0
*/
@AfterThrowing("pointCut()")
public void rollBack() {
try {
connectionUtils.getThreadConnection().rollback();
} catch (SQLException throwables) {
throw new RuntimeException(throwables);
}
System.out.println("rollBack");
}
/**
* @param
* @return void
* @author gxm
* @date 2020/5/5 21:32
* @description 提交操作
* @since version-1.0
*/
@AfterReturning("pointCut()")
public void commit() {
System.out.println("commit");
try {
connectionUtils.getThreadConnection().commit();
} catch (SQLException throwables) {
throw new RuntimeException(throwables);
}
}
/**
* @param
* @return void
* @author gxm
* @date 2020/5/5 21:33
* @description This is description of method
* @since version-1.0
*/
@After("pointCut()")
public void release() {
System.out.println("release");
try {
connectionUtils.getThreadConnection().close();
connectionUtils.remove();
} catch (SQLException throwables) {
throw new RuntimeException(throwables);
}
}
/**
* @author gxm
* @date 22:44
* @description 环绕通知,类似于动态代理,手动实现aop过程
* @param joinPoint 1
* @return java.lang.Object
* @since version-1.0
*/
//@Around("pointCut()")
public Object aopAround(ProceedingJoinPoint joinPoint) {
Object proceed = null;
try {
// 1. 前置通知
beginTransaction();
// 2. 环绕通知
proceed = joinPoint.proceed(joinPoint.getArgs());
// 3. 后置通知
commit();
} catch (Throwable throwable) {
// 4. 异常通知
rollBack();
throw new RuntimeException(throwable);
}finally {
// 5. 最终通知
release();
}
return proceed;
}
}
2.3 开启AOP代理
@Configuration
@ComponentScan(value = "com")
@PropertySource("classpath:db.properties")
@Import(JdbcConfig.class)
@EnableAspectJAutoProxy
public class MyConfiguration {
}
2.4 测试AOP代理是否正常执行
package com.service.impl;
import com.config.MyConfiguration;
import com.domain.Account;
import com.service.IAccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import java.util.List;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MyConfiguration.class)
public class AccountServiceImplTest {
@Autowired
private IAccountService accountService;
@Qualifier("account")
@Autowired
private Account account1;
@Qualifier("account")
@Autowired
private Account account2;
// 更新数据库时,添加如下测试代码,报错后,并未提交到数据库,说明测试成功
//accounts1.forEach(this::updateAccount);
// int a = 1/0;
// accounts2.forEach(this::updateAccount);
@Test
public void transfer() {
account1.setId(17);
account2.setId(18);
accountService.transfer(account2, account1, 10f);
}
@Test
public void saveAccount() {
accountService.saveAccount(account1);
accountService.saveAccount(account2);
}
@Test
public void queryAll() {
List accounts = accountService.queryAll();
accounts.forEach((System.out::println));
}
@Test
public void queryById() {
List accounts = accountService.queryById(account1.getId());
accounts.forEach(System.out::println);
}
@Test
public void queryByName() {
List accounts = accountService.queryByName(account1.getName());
accounts.forEach(System.out::println);
}
@Test
public void updateAccount() {
account1.setBalance(account1.getBalance()+1);
accountService.updateAccount(account1);
}
@Test
public void deleteAccountById() {
accountService.deleteAccountById(account1.getId());
}
@Test
public void setAccountDao() {
}
}
2.5 github完整代码
https://github.com/GaoXiaoMing-BUPT/spring-account-annoation