SSM框架(二):AOP和事物


一、AOP的介绍

1.1 基本概念

在这里插入图片描述

在这里插入图片描述

1.2 AOP入门

  1. 导入坐标
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.4</version>
    </dependency>
  1. 定义dao接口和实现类
@Repository
public class BookDaoImpl implements BookDao {

    public void save() {
        System.out.println(System.currentTimeMillis());
        System.out.println("book dao save ...");
    }

    public void update(){
        System.out.println("book dao update ...");
    }
}
  1. 定义通知类:定义切入点,绑定切入点与通知的关系
//通知类必须配置成Spring管理的bean
@Component
//设置当前类为切面类类
@Aspect
public class MyAdvice {
    //设置切入点,要求配置在方法上方
    @Pointcut("execution(void com.itheima.dao.impl.BookDaoImpl.update())")
    private void pt(){}

    //设置在切入点pt()的前面运行当前操作(前置通知)
    @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}
  1. 开启Spring对AOP注解驱动的支持
@Configuration
@ComponentScan("com.itheima")
//开启注解开发AOP功能
@EnableAspectJAutoProxy
public class SpringConfig {
}

1.3 AOP工作流程

在这里插入图片描述
在这里插入图片描述

1.4 切入点表达式

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

1.5 AOP的通知类型

在这里插入图片描述

  1. @after:当前通知方法在原始切入点方法后运行
  2. @before:当前通知方法在原始切入点方法前运行
  3. @AfterReturning:当前通知方法在原始切入点方法正常执行完毕后执行
  4. @AfterThrowing:当前通知方法在原始切入点方法运行抛出异常后执行
  5. @Around:当前通知方法在原始切入点方法前后运行

在这里插入图片描述

演示:

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
    @Pointcut("execution(int com.itheima.dao.BookDao.select())")
    private void pt2(){}

    //@Before:前置通知,在原始方法运行之前执行
//    @Before("pt()")
    public void before() {
        System.out.println("before advice ...");
    }

    //@After:后置通知,在原始方法运行之后执行
//    @After("pt2()")
    public void after() {
        System.out.println("after advice ...");
    }

    //@Around:环绕通知,在原始方法运行的前后执行
//    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around before advice ...");
        //表示对原始操作的调用
        Object ret = pjp.proceed();
        System.out.println("around after advice ...");
        return ret;
    }

//    @Around("pt2()")
    public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("around before advice ...");
        //表示对原始操作的调用
        Integer ret = (Integer) pjp.proceed();
        System.out.println("around after advice ...");
        return ret;
    }

    //@AfterReturning:返回后通知,在原始方法执行完毕后运行,且原始方法执行过程中未出现异常现象
//    @AfterReturning("pt2()")
    public void afterReturning() {
        System.out.println("afterReturning advice ...");
    }

    //@AfterThrowing:抛出异常后通知,在原始方法执行过程中出现异常后运行
    @AfterThrowing("pt2()")
    public void afterThrowing() {
        System.out.println("afterThrowing advice ...");
    }
}

1.6 ProceedingJoinPoint

@Component
@Aspect
public class ProjectAdvice {
	//配置业务层的所有方法
	@Pointcut("execution(* com.itheima.service.*Service.*(..))")
	private void servicePt(){}
	//@Around("ProjectAdvice.servicePt()") 可以简写为下面的方式
	@Around("servicePt()")
	public void runSpeed(ProceedingJoinPoint pjp){
		//获取执行签名信息
		Signature signature = pjp.getSignature();
		//通过签名获取执行操作名称(接口名)
		String className = signature.getDeclaringTypeName();
		//通过签名获取执行操作名称(方法名)
		String methodName = signature.getName();
		long start = System.currentTimeMillis();
		for (int i = 0; i < 10000; i++) {
			pjp.proceed();
		}
		long end = System.currentTimeMillis();
		System.out.println("万次执行:"+ className+"."+methodName+"---->" +
		(end-start) + "ms");
	}
}

1.7 AOP通知获取参数数据

  1. 获取参数

在这里插入图片描述

  1. 获取返回值

在这里插入图片描述

  1. 获取异常

在这里插入图片描述

二、事物

2.1 基本介绍

在这里插入图片描述

  1. 在业务层接口的方法或者接口类(表示里面所有方法都事务管理)上添加事务管理注解@Transactional
public interface AccountService {
    /**
     * 转账操作
     * @param out 传出方
     * @param in 转入方
     * @param money 金额
     */
    //配置当前接口方法具有事务
    @Transactional
    public void transfer(String out,String in ,Double money) ;
}
  1. 设置事务管理器,创建一个事务管理的bean,例如:创建一个管理数据库的事物bean
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }

    //配置事务管理器,mybatis使用的是jdbc事务
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}
  1. 开启注解式事务驱动
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
//开启注解式事务驱动
@EnableTransactionManagement
public class SpringConfig {
}

2.2 事物角色

在这里插入图片描述
在这里插入图片描述

2.3 事物属性

当代码抛出IOException异常时,事物不会回滚,因此我们需要设置@Transactional(rollbackFor={IOException.class})来保证遇到IO异常时事物回滚。

在这里插入图片描述

2.4 事物的传播行为

在这里插入图片描述

1.REQUIRES_NEW解读:
由于多个事物协调员由一个事物管理员管理,当一个事物失败时,其他事物也不能执行;当我们需要保证一个事物协调员不会受其他事物影响时,设置@Transactional(propagation=Propagation.REQUIRES_NEW)。

2.REQUIRES_NEW应用场景:
当我们在转账时,无论成功失败,都需要向数据库中写入日志,首先把转账与写日志写进一个转账Service的方法中,并加上事务管理@Transactional;然后我们就需要在日志的Service接口的方法中加入@Transactional(propagation=Propagation.REQUIRES_NEW)。

日志接口与实现类:LogService、LogServiceImpl

public interface LogService {
    //propagation设置事务属性:传播行为设置为当前操作需要新事务
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    void log(String out, String in, Double money);
}

@Service
public class LogServiceImpl implements LogService {

    @Autowired
    private LogDao logDao;

    public void log(String out,String in,Double money ) {
        logDao.log("转账操作由"+out+"到"+in+",金额:"+money);
    }
}

转账接口和实现类:AccountService、AccountServiceImpl

public interface AccountService {
    //rollback:设置当前事务参与回滚的异常,默认非运行时异常不参与回滚
//    @Transactional(rollbackFor = IOException.class)
    @Transactional
    public void transfer(String out,String in ,Double money) throws IOException;
}

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Autowired
    private LogService logService;

    public void transfer(String out,String in ,Double money) {
        try{
            accountDao.outMoney(out,money);
            int i = 1/0;
            accountDao.inMoney(in,money);
        }finally {
            logService.log(out,in,money);
        }
    }

}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值