一、功能测试
Aop:【动态代理】指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式;
1、导入aop模块:Spring AOP:(spring-aspects)
2、定义一个业务逻辑类(MatchCalculator);在业务逻辑运行的时候将日志进行打印(方法运行前,方法运行后,方法出现异常等)
3、定义一个日志切面类(LogAspects);切面类里面的方法需要动态感知MatchCalculator.div运行到哪里,然后执行
通知方法:
前置通知(@Before):logStart,在目标方法(div)运行结束之前运行
后置通知(@After):logEnd:在目标方法(div)运行结束之后运行
返回通知(@AfterReturning):logReturn:在目标方法(div)正常返回之后运行
异常通知(@AfterThrowing):logException:在目标方法(div)出现异常以后运行
环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.proceed)
4、给切面类的目标方法标注何时(通知注解)
5、将切面类和业务逻辑类(目标方法所在类)都加入到容器中;
6、必须告诉spring哪个类是切面类(给切面类上加入注解@Aspect)
7、给配置类加@EnableAspectJAutoProxy【开启基于注解的AOP模式】
在Spring中很多的@Enablexxxx
三步:
1)、将业务逻辑组件和切面类都加入到容器中,告诉Spring哪个是切面类(@Aspect)
2)、在切面类上的每一个通知方法上标注通知注解,告诉Spring核实何地运行(切入点表达式)
3)、开启基于注解的aop模式;EnableAspectJAutoProxy
例:
业务逻辑类
public class MathCalculator { public int div(int i,int j) { return i/j; } }
切入类
@Aspect public class LogAspect { //抽取公共的切入点表达式 //1、本类引用,方法名就行pointCut() //2、其它的切面引用,需要全路径名com.suxiaodong.annotation.LogAspect.pointCut() @Pointcut("execution(public int com.suxiaodong.annotation.MathCalculator.div(int, int))") public void pointCut() { } @Before("pointCut()") public void logStart(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); String methodName = joinPoint.getSignature().getName(); System.out.println(methodName + "方法运行之前,@Before执行,参数{" + Arrays.asList(args) + "}"); } @After("com.suxiaodong.annotation.LogAspect.pointCut()") public void logEnd(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); String methodName = joinPoint.getSignature().getName(); System.out.println(methodName + "方法运行之后,@After执行,参数{" + Arrays.asList(args) + "}"); } //注意,JoinPoint joinPoint必须放在第一个参数的位置 @AfterReturning(value="pointCut()", returning="ret") public void logAfter(JoinPoint joinPoint, Object ret) { Object[] args = joinPoint.getArgs(); String methodName = joinPoint.getSignature().getName(); System.out.println(methodName + "方法返回结果之后运行,@AfterReturning执行,参数{" + Arrays.asList(args) + "},返回值{" + ret + "}"); } @AfterThrowing(value = "pointCut()", throwing="exception") public void logThrowing(JoinPoint joinPoint, Exception exception) { Object[] args = joinPoint.getArgs(); String methodName = joinPoint.getSignature().getName(); System.out.println(methodName + "方法返回结果之后运行,@AfterReturning执行,参数{" + Arrays.asList(args) + "},异常{" + exception + "}"); } }
spring bean配置类
@EnableAspectJAutoProxy @Configuration public class MathConfigurationBean { @Bean public MathCalculator mathCalculator() { return new MathCalculator(); } @Bean public LogAspect logAspect() { return new LogAspect(); } }
测试类
public class MainTest { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext( MathConfigurationBean.class); MathCalculator calculator = applicationContext.getBean(MathCalculator.class); calculator.div(3, 1); applicationContext.close(); } }
运行结果
div方法运行之前,@Before执行,参数{[3, 1]}
div方法运行之后,@After执行,参数{[3, 1]}
div方法返回结果之后运行,@AfterReturning执行,参数{[3, 1]},返回值{3}
二、Spring 事务
1给方法上标注@Transactional表示当前方法是一个事务方法;
2@EnableTransactionManagement开启基于注解的事务管理功能;
3配置事务管理器来控制事务
例:
@Configuration //加上这个注解,使得支持事务 @EnableTransactionManagement public class MyBatis implements TransactionManagementConfigurer { @Autowired private DataSource dataSource; @Override public PlatformTransactionManager annotationDrivenTransactionManager() { return new DataSourceTransactionManager(dataSource); } }
三、AOP原理












四、@Transactional自调用失效问题
serviceA自身有方法a和方法b,方法a不加注解,方法b加事务注解,方法a调方法b不会开启事务。因为方法a里的this调用,this对象是被代理对象本身,并不是代理对象,不会执行aop通知逻辑,解决方法
1:不用this调用,在serviceA里注入自身,或用ApplicationContext.getBean从容器里拿到ServiceA对象再调用方法b
2:使用 @EnableAspectJAutoProxy 注解时,指定 exposeProxy 为 true,再用AopContext.currentProxy()获取代理对象,再调用方法b
例:
@Override public String testTransaction() { IAsyncTestService asyncTestService = (IAsyncTestService) applicationContext.getBean("asyncTestService"); return asyncTestService.selfTrans(); } @Transactional @Override public String selfTrans() { int row = jdbcTemplate.update("update stock set stock = 10 where id=1"); int a = 10/0; if(row > 0) { jdbcTemplate.update("update stock set stock = 3 where id=1"); } return "成功"; }
五、SPRING配置事务拦截指定方法(不常用,不灵活)
@Aspect @Configuration public class TxAdviceInterceptor { private static final int TX_METHOD_TIMEOUT = 20; private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.mytest.service.*.*(..))"; @Autowired private PlatformTransactionManager transactionManager; @Bean public TransactionInterceptor txAdvice() { NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource(); RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute(); readOnlyTx.setReadOnly(true); readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED); RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute(); requiredTx.setRollbackRules( Collections.singletonList(new RollbackRuleAttribute(Exception.class))); requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); requiredTx.setTimeout(TX_METHOD_TIMEOUT); Map<String, TransactionAttribute> txMap = new HashMap<>(); txMap.put("add*", requiredTx); txMap.put("apply*", requiredTx); txMap.put("save*", requiredTx); txMap.put("insert*", requiredTx); txMap.put("update*", requiredTx); txMap.put("delete*", requiredTx); txMap.put("del*", requiredTx); txMap.put("create*", requiredTx); txMap.put("redeploy*", requiredTx); txMap.put("rebuild*", requiredTx); txMap.put("get*", readOnlyTx); txMap.put("list*", readOnlyTx); source.setNameMap(txMap); TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, source); return txAdvice; } @Bean public Advisor txAdviceAdvisor() { AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression(AOP_POINTCUT_EXPRESSION); return new DefaultPointcutAdvisor(pointcut, txAdvice()); } }