Spring注解开发系列Ⅵ --- AOP&事务
注解开发 --- AOP
AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,横向重复,纵向抽取。详细的AOP介绍请看这里,本篇主要是讨论AOP在spring注解开发中的运用。
AOP的使用
1.导入aop模块(spring-aspects)
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.3.12.RELEASE</version> </dependency>
2.定义一个业务逻辑类(com.wang.aop.MathCalculator),在业务逻辑运行时将日志进行打印(方法之前,方法结束,方法异常都会打印)。
public class MathCalculator { public int div(int x,int y){ return x/y; } }
3.定义一个日志切面类(com.wang.aop.LogAspects),切面类的方法需要动态感知MathCalculator的div方法运行到哪里,然后执行通知方法。
通知方法:
1).前置通知(@Before):logStart(),在目标方法运行之前运行
2).后置通知(@After):logEnd(),在目标方法运行之后运行(无论方法正常结束或异常结束都会调用)
3).返回通知(@AfterReturning):logRet(),在目标方法正常返回之后执行
4).异常通知(@AfterThrowing):logException(),在目标方法运行异常之后运行
5).环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())
4.给切面类的方法标注通知注解
/** * 切面类 */ @Aspect public class LogAspects { //抽取公共表达式 //本类引用:pointCut() //其他切面类引用:com.wang.aop.LogAspects.pointCut() //@Pointcut("execution(public int com.wang.aop.MathCalculator.div(int ,int ))") @Pointcut("execution(public int com.wang.aop.MathCalculator.*(..))") public void pointCut(){ } //在目标方法之前切人,切入点表达式 @Before("pointCut()") public void logStart(JoinPoint joinPoint){ System.out.println(joinPoint.getSignature().getName()+"方法开始运行...@Before,参数列表是"+Arrays.asList(joinPoint.getArgs())); } @After("pointCut()") public void logEnd(JoinPoint joinPoint){ System.out.println(joinPoint.getSignature().getName()+"方法结束...@After,参数列表是"+Arrays.asList(joinPoint.getArgs())); } @AfterReturning(value = "pointCut()",returning = "result") public void logRet(JoinPoint joinPoint,Object result){ //joinPoint必须放在参数第一位,否则则会报错 System.out.println(joinPoint.getSignature().getName()+"方法结果打印...@AfterReturning,运行结果"+result); } @AfterThrowing(value = "pointCut()",throwing = "exception") public void logException(Exception exception){ System.out.println("方法异常...@AfterThrowing,异常结果"+exception); } }
5.将切面类和业务逻辑类(目标方法所在类)都加入到容器中
@Configuration @EnableAspectJAutoProxy //开启spring的aop注解功能 public class AOPConfig { @Bean public MathCalculator calculator(){ return new MathCalculator(); } @Bean public LogAspects logAspects(){ return new LogAspects(); } }
6.必须告诉spring哪个类是切面类(给切面类加上注解@Aspect)(@Aspect)
7.给配置类中添加@EnableAspectJAutoProxy,开启基于注解的AOP模式
注意:
1.使用aop的对象不能自己new创建,需要去spring容器中获取,否则AOP方法不会执行
2.JoinPoint必须放在参数第一位,否则会报错
注解开发 --- 事务
Spring事务其实就是Spring AOP,底层创建动态代理对象,在代码的开头结尾封装了开启事务和事务回滚操作,关于事务的基本使用。
声明式事务
1.环境搭建:导入相关依赖:数据源,数据库驱动
<dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.3.12.RELEASE</version> </dependency>
2.配置数据源,spring-jdbc模块(jdbctmplate,也可以导入mybatis以及hibernate,jpa)
@Bean public DataSource dataSource() throws PropertyVetoException { ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource(); comboPooledDataSource.setUser("root"); comboPooledDataSource.setPassword("123456"); comboPooledDataSource.setDriverClass("com.mysql.jdbc.Driver"); comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test"); return comboPooledDataSource; }
3.给方法标注:@Transactional 表示当前方法是一个事务方法
@Repository public class UserDao { @Autowired private JdbcTemplate jdbcTemplate; public void insert(){ String sql = "INSERT INTO `tb_user`(username,age) VALUES(?,?)"; String substring = UUID.randomUUID().toString().substring(0, 5); jdbcTemplate.update(sql,substring,11); } }
@Service public class UserService { @Autowired private UserDao userDao; @Transactional public void insertUser(){ userDao.insert(); int i = 1/0; System.out.println("插入成功"); } }
4.配置类上加上@EnableTransactionManagement开启基于注解的事务管理
@ComponentScan({"com.wang.tx","com.wang.service","com.wang.dao"}) @Configuration @EnableTransactionManagement public class TxConfig { @Bean public DataSource dataSource() throws PropertyVetoException { ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource(); comboPooledDataSource.setUser("root"); comboPooledDataSource.setPassword("123456"); comboPooledDataSource.setDriverClass("com.mysql.jdbc.Driver"); comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test"); return comboPooledDataSource; } @Bean public JdbcTemplate jdbcTemplate(DataSource dataSource){ //spring对Configuration类有特殊处理,给容器中加组件的方法,多次调用都会从容器中找组件 JdbcTemplate template = new JdbcTemplate(dataSource); return template; } @Bean public PlatformTransactionManager platformTransactionManager(DataSource dataSource){ return new DataSourceTransactionManager(dataSource); } }
5.在配置类配置事务管理器控制事务
测试结果:查看数据库,若抛出异常没有数据插入到数据库说明事务注解生效了
public class TxTest { @Test public void txTest(){ AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(TxConfig.class); UserService userservice = annotationConfigApplicationContext.getBean(UserService.class); userservice.insertUser(); annotationConfigApplicationContext.close(); } }