• 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();
        }
    }
  • 相关阅读:
    Windows 8将替换Win32 API
    密码强度检测:passwordStrength
    整数溢出与程序安全
    编程经验谈:如何正确使用内存
    C/C++指针学习的两个经典实例
    VC调试入门
    一些电子书籍的网站
    BMP文件格式分析(zz)
    C/C++ 跨平台I/O操作技巧
    Windows下C语言网络编程快速入门
  • 原文地址:https://www.cnblogs.com/wangxiayun/p/10137895.html
Copyright © 2020-2023  润新知