Spring笔记04_AOP注解开发_模板_事务

1. Spring基于AspectJ的注解的AOP开发

1. 1 SpringAOP的注解入门

  • 创建项目,导入jar包

    • 需要导入Spring基础包4+2

    • 需要导入AOP联盟包、AspectJ包、Spring整合Aspect包Spring-aop包

    • Spring整合单元测试包

      Spring笔记04_AOP注解开发_模板_事务_第1张图片
      image
  • 引入配置文件applicationContext.xml

    
    
      
    
    
  • 编写目标类并配置

    package com.itzhouq.spring.demo1;
    
    public class OrderDao {
      public void save() {
          System.out.println("保存订单。。。");
      }
      public void update() {
          System.out.println("修改订单。。。");
      }
      public void find() {
          System.out.println("查找订单。。。");
      }
      public String delete() {
          System.out.println("删除订单。。。");
          return "周杰伦";
      }
    }
    
  • 配置目标类,将目标类OrderDao交给Spring管理

    • 在applicationContext.xml中添加
    
      
    
  • 编写切面类并配置

    package com.itzhouq.spring.demo1;
    /*
     * 切面类:注解的切面类
     */
    public class MyAspectAnno {
      
      public void before() {
          System.out.println("前置增强===============");
      }
    }
    
    
      
    
  • 使用注解的AOP对目标类的方法进行增强

    • 首先在配置文件中打开注解的AOP开发

      
          
      
    • 在切面类上使用注解

      /*
       * 切面类:注解的切面类
       */
      @Aspect //标记该类为切面类
      public class MyAspectAnno {
          
          @Before(value="execution(* com.itzhouq.spring.demo1.OrderDao.save(..))")
          public void before() {//这个注解用来标记目标类的哪个方法使用何种增强
              System.out.println("前置增强===============");
          }
      }
      
  • 测试

    package com.itzhouq.spring.demo1;
    
    import javax.annotation.Resource;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /*
     * Spring的AOP注解开发测试
     */
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class SpringDemo1 {
      @Resource(name="orderDao")  //注入OrderDao
      private OrderDao orderDao;
      
      @Test
      public void test1() {
          orderDao.save();
          orderDao.update();
          orderDao.find();
          orderDao.delete();
    //        前置增强===============
    //        保存订单。。。
    //        修改订单。。。
    //        查找订单。。。
    //        删除订单。。。
    
      }
    }
    

1.2 Spring的AOP的注解通知类型

1.2.1 @Before:前置通知

1.2.2 @AfterReturning:后置通知

  • 在删除delete方法上使用后置通知

  • 在切面类中添加以下方法

    //后置通知
      @AfterReturning("execution(* com.itzhouq.spring.demo1.OrderDao.delete(..))")
      public void afterReturning() {
          System.out.println("后置增强==================");
      }
    
  • 不用修改目标类直接测试就能实现效果

    前置增强===============
    保存订单。。。
    修改订单。。。
    查找订单。。。
    删除订单。。。
    后置增强==================
    
  • 后置通知还可以使用返回值

    //后置通知
      @AfterReturning(value="execution(* com.itzhouq.spring.demo1.OrderDao.delete(..))", returning="result")
      public void afterReturning(Object result) {
          System.out.println("后置增强=================="+result);
      }
    
    前置增强===============
    保存订单。。。
    修改订单。。。
    查找订单。。。
    删除订单。。。
    后置增强==================周杰伦
    

1.2.3 环绕通知

  • 在切面类中添加以下方法:

    //环绕通知
      @Around(value="execution(* com.itzhouq.spring.demo1.OrderDao.update(..))")
      public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
          System.out.println("环绕前增强===========");
          Object obj = joinPoint.proceed();
          System.out.println("环绕前增强===========");
          return obj;
      }
    
  • 测试

    前置增强===============
    保存订单。。。
    环绕前增强===========
    修改订单。。。
    环绕前增强===========
    查找订单。。。
    删除订单。。。
    后置增强==================周杰伦
    

1.2.4 异常抛出通知

  • 异常抛出通知可以获得异常信息

  • 在切面类中添加方法

      //异常抛出通知
      @AfterThrowing(value="execution(* com.itzhouq.spring.demo1.OrderDao.find(..))",throwing="e")
      public void afterThrowing(Throwable e) {
          System.out.println("异常抛出通知============"+e);
      }
    
  • 在find方法中模拟异常

    public void find() {
          System.out.println("查找订单。。。");
          int i = 1 / 0;
      }
    
  • 测试

    前置增强===============
    保存订单。。。
    环绕前增强===========
    修改订单。。。
    环绕前增强===========
    查找订单。。。
    异常抛出通知============java.lang.ArithmeticException: / by zero
    

1.2.5 最终通知‘

  • 在切面类中添加方法

    //最终通知
      @After(value="execution(* com.itzhouq.spring.demo1.OrderDao.find(..))")
      public void after() {
          System.out.println("最终增强============");
      }
    
  • 测试

    前置增强===============
    保存订单。。。
    环绕前增强===========
    修改订单。。。
    环绕前增强===========
    查找订单。。。
    最终增强============
    异常抛出通知============java.lang.ArithmeticException: / by zero
    

1.3 Spring的AOP的注解的切入点的注解

  • 修改切面类

    package com.itzhouq.spring.demo1;
    
    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;
    
    /*
     * 切面类:注解的切面类
     */
    @Aspect   //标记该类为切面类
    public class MyAspectAnno {
      
      @Before(value="MyAspectAnno.pointcut2()")
      public void before() {//这个注解用来标记目标类的哪个方法使用何种增强
          System.out.println("前置增强===============");
      }
      
      //后置通知
      @AfterReturning(value="execution(* com.itzhouq.spring.demo1.OrderDao.delete(..))", returning="result")
      public void afterReturning(Object result) {
          System.out.println("后置增强=================="+result);
      }
      
      //环绕通知
      @Around(value="MyAspectAnno.pointcut3()")
      public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
          System.out.println("环绕前增强===========");
          Object obj = joinPoint.proceed();
          System.out.println("环绕前增强===========");
          return obj;
      }
      
      //异常抛出通知
      @AfterThrowing(value="MyAspectAnno.pointcut4()",throwing="e")
      public void afterThrowing(Throwable e) {
          System.out.println("异常抛出通知============"+e);
      }
      
      //最终通知
      @After(value="MyAspectAnno.pointcut1()")
      public void after() {
          System.out.println("最终增强============");
      }
      
      //切入点注解
      @Pointcut(value="execution(* com.itzhouq.spring.demo1.OrderDao.find(..))")
      private void pointcut1() {}
      @Pointcut(value="execution(* com.itzhouq.spring.demo1.OrderDao.save(..))")
      private void pointcut2() {}
      @Pointcut(value="execution(* com.itzhouq.spring.demo1.OrderDao.update(..))")
      private void pointcut3() {}
      @Pointcut(value="execution(* com.itzhouq.spring.demo1.OrderDao.delete(..))")
      private void pointcut4() {}
    }
    
    • 通过切入点的注解,可以简化注解的代码量
    • 注意:使用类名.pointcut1()时候不要忘记pointcut1()是一个方法,需要带上()才有效

2. Spring的JDBC的模板的使用

2.1 Spring的JDBC的模板

  • Spring的EE开发的一站式的框架,有EE开发中每一层的解决方案。Spring对持久层也提供了解决方案:ORM模块和JDBC的模块。

  • Spring提供了很多的模板用于简化开发。

    Spring笔记04_AOP注解开发_模板_事务_第2张图片
    image

2.1.1 JDBC模板使用的入门

  • 创建项目,引入jar包

    • 引入基本的4+2包

    • 数据库驱动包

    • Spring的JDBC模板的jar包:事务管理tx和jdbc的包

    • 单元测试包

      Spring笔记04_AOP注解开发_模板_事务_第3张图片
      image
  • 创建数据库和表

    create database spring4_day03;
    use spring4_day03;
    create table account(
      id int primary key auto_increment,
      name varchar(20),
      money double
    )
    
  • 使用JDBC模板:保存数据

    package com.itzhouq.spring.jdbc.demo1;
    
    import org.junit.Test;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.datasource.DriverManagerDataSource;
    
    /*
     * JDBC的模板使用
     */
    public class JdbcDemo1 {
      @Test   //JDBC的模板的使用类似于Dbutils
      public void test1() {
          //创建连接池 这里使用Spring默认的连接池
          DriverManagerDataSource dataSource = new DriverManagerDataSource();
          dataSource.setDriverClassName("com.mysql.jdbc.Driver");
          dataSource.setUrl("jdbc:mysql:///spring4_day03");
          dataSource.setUsername("root");
          dataSource.setPassword("2626");
          
          //创建jdbc模板
          JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
          jdbcTemplate.update("insert into account values (null, ?,?)", "周杰伦",10000d);
      }
    
    }
    
  • 将日志记录的配置文件jdbc.properties拷贝到src下

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/spring4_day03
    jdbc.username=root
    jdbc.password=2626
    
  • 测试能插入数据

2.1.2 将连接池和模板交给Spring管理

  • 引入aop的jar包

  • 引入Spring的配置文件applicationContext.xml

    
    
      
      
    
    
  • 配置Spring的内置连接池,将连接池交给Spring管理

    
      
          
          
          
          
          
      
    
  • 配置Spring的jdbc模板,将模板交给Spring管理

    
      
          
      
    
  • 使用JDBC的模板

    package com.itzhouq.spring.jdbc.demo1;
    
    import javax.annotation.Resource;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(value="classpath:applicationContext.xml")
    public class JdbcDemo2 {
      @Resource(name="jdbcTemplate")
      private JdbcTemplate jdbcTemplate;
      
      @Test
      public void test1() {
          jdbcTemplate.update("insert into account values (null, ?,?)", "赵雷",10000d);
      }
    }
    
  • 测试能插入数据

2.2 使用开源的数据库连接池

2.2.1 C3P0的使用

  • 导入jar包

    • com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar
  • 路径

    • ..\spring-framework-3.0.2.RELEASE-dependencies\com.mchange.c3p0\com.springsource.com.mchange.v2.c3p0
  • 配置C3P0连接池

    
      
          
          
          
          
          
       
      
      
          
      
    
    • 注意:C3P0连接池的核心类是:com.mchange.v2.c3p0.ComboPooledDataSource
    • 测试能插入数据

2.2.2 DBCP的使用

  • 引入jar包

    • com.springsource.org.apache.commons.dbcp-1.2.2.osgi.jar
    • com.springsource.org.apache.commons.pool-1.5.3.jar
  • 路径

    • ..\spring-framework-3.0.2.RELEASE-dependencies\org.apache.commons\com.springsource.org.apache.commons.dbcp
    • ..\spring-framework-3.0.2.RELEASE-dependencies\org.apache.commons\com.springsource.org.apache.commons.pool
  • 配置DBCP的连接池

    
      
          属性注入 ===============
          
          
          
          
      
      
      
      
          
      
    
  • 注意:DBCP的核心类为org.apache.commons.dbcp.BasicDataSource

  • 测试能插入数据

2.3 抽取配置到属性文件

  • 在src下新建配置文件jdbc.properties

    jdbc.driverClass=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/spring4_day03
    jdbc.username=root
    jdbc.password=2626
    
  • 在Spring的配置文件中引入属性文件

    
      
      
      
          
          
          
          
          
       
    
  • 测试能插入数据

2.4 使用JDBC模板进行CURD操作

2.4.1 增

package com.itzhouq.spring.jdbc.demo1;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value="classpath:applicationContext.xml")
public class JdbcDemo2 {
    @Resource(name="jdbcTemplate")
    private JdbcTemplate jdbcTemplate;
    
    @Test
    public void test1() {
        jdbcTemplate.update("insert into account values (null, ?,?)", "YYY",10000d);
    }
}

2.4.2 删

@Test
    public void test3() {//删除
        jdbcTemplate.update("delete from account where id = ?", 5);
    }

2.4.3 改

@Test
    public void test2() {//修改
        jdbcTemplate.update("update account set name = ?, money =  ? where id = ?", "何巨涛",10000d, 6);
    }

2.4.4 查

  • 查询某个属性

    @Test
      public void test4() {//查询某个属性
          String name = jdbcTemplate.queryForObject("select name from account where id = ?", String.class, 6);
          System.out.println(name);
      }
    
  • 查询个数

    @Test
      public void test5() {//查询个数
          Long count = jdbcTemplate.queryForObject("select count(*) from account", Long.class);
          System.out.println(count);
      }
    
  • 返回的是对象

    • 首先要创建一个实体Account

      @Test
          public void test6() {//封装到一个对象中
              Account  account = jdbcTemplate.queryForObject("select * from account where id = ?", new MyRowMapper(), 4);
              System.out.println(account);
          }
          
          class MyRowMapper  implements RowMapper{
      
              @Override
              public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
                  Account account = new Account();
                  account.setId(rs.getInt("id"));
                  account.setName(rs.getString("name"));
                  account.setMoney(rs.getDouble("money"));
                  return account;
              }
          }
      
    • queryForObject的第二个参数需要实现一个接口RowMapper

  • 查询多条记录

    @Test
      public void test7() {
          List listAccount = jdbcTemplate.query("select * from account", new MyRowMapper());
          for (Account account : listAccount) {
              System.out.println(account);
          }
      }
      
      class MyRowMapper  implements RowMapper{
    
          @Override
          public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
              Account account = new Account();
              account.setId(rs.getInt("id"));
              account.setName(rs.getString("name"));
              account.setMoney(rs.getDouble("money"));
              return account;
          }
      }
    

3. Spring的事务管理

3.1 事务的回顾

3.1.1 什么是事务

  • 事务:逻辑上的一组操作,组成这组操作的各个单元,要么全都成功,要么全都失败。

3.1.2 事务的特性

  • 原子性:事务不可分割
  • 一致性:事务执行前后数据完整性保持一致
  • 隔离性:一个事务的执行不应该受到其他事务的影响
  • 持久性:一旦事务结束,数据就持久化到数据库中

3.1.3 不考虑事务的隔离性引发安全问题

  • 读问题
    • 脏读:一个事务读到另一个事务未提交的数据
    • 不可重复读:一个事务读取到另一个事务已经提交的update的数据,导致一个事务中多次查询结果不一致
    • 虚读、幻读:一个事务读到另一个事务已经提交的insert数据,导致一个事务中多次查询结果不一致
  • 写问题
    • 丢失更新

3.1.4 解决读问题

  • 设置事务的隔离级别
    • read uncommited:未提交读,任何读问题都解决不了
    • Read commited:已提交读,解决脏读,但是不可重复读和虚读有可能发生
    • Repeatable read:重复读,解决脏读和不可重复读,但是虚读有可能发生
    • Serializable:解决所有读问题

3.2 Spring的事务管理的API

3.2. 1 PlatformTransactionManager:平台事务管理器

  • 平台事务管理器:接口,是Spring用于管理事务的真正的对象。
    • DataSourceTransactionManager:底层使用JDBC管理事务
    • HibernateTransactionManager:底层使用Hibernate管理事务

3.2.2 TransactionDefinition:事务定义信息

  • 事务定义:用于定义事务的相关的信息,隔离级别、超时信息、传播行为、是否只读

3.2.3 TransactionStatus:事务的状态

  • l 事务状态:用于记录在事务管理过程中,事务的状态的对象。

3.2.4 事务管理的API的关系

  • Spring进行事务管理的时候,首先平台事务管理器根据事务定义信息进行事务的管理,在事务管理过程中,产生各种状态,将这些状态的信息记录到事务状态的对象中。

3.3 事务的传播行为

  • Spring中提供了七种事务的传播行为

3.3.1 保证多个操作在同一个事务中

  • PROPAGATION_REQUIRED:默认值,如果A中有事务,使用A中的事务,如果A没有,创建一个新的事务,将操作包含进来
  • PROPAGATION_SUPPORTS:支持事务,如果A中有事务,使用A中的事务。如果A没有事务,不使用事务。
  • PROPAGATION_MANDATORY:如果A中有事务,使用A中的事务。如果A没有事务,抛出异常。

3.3.2 保证多个操作不在同一个事务中

  • PROPAGATION_REQUIRES_NEW:如果A中有事务,将A的事务挂起(暂停),创建新事务,只包含自身操作。如果A中没有事务,创建一个新事务,包含自身操作。
  • PROPAGATION_NOT_SUPPORTED:如果A中有事务,将A的事务挂起。不使用事务管理。
  • PROPAGATION_NEVER:如果A中有事务,报异常。

3.3.3 嵌套式事务

  • PROPAGATION_NESTED:嵌套事务,如果A中有事务,按照A的事务执行,执行完成后,设置一个保存点,执行B中的操作,如果没有异常,执行通过,如果有异常,可以选择回滚到最初始位置,也可以回滚到保存点。

3.4 事务的管理

3.4.1 案例:转账

  • 创建AccountService接口

    package com.itzhouq.spring.tx.demo1;
    /*
     * 转账的业务层的接口
     */
    public interface AccountService {
      public void transfer(String from, String to, Double money);
    }
    
  • 创建接口实现类AccountServiceImpl

    package com.itzhouq.spring.tx.demo1;
    /*
     * 转账的业务层的实现类
     */
    public class AccountServiceImpl implements AccountService {
      
      //注入DAO
      private AccountDao accountDao;
      public void setAccountDao(AccountDao accountDao) {
          this.accountDao = accountDao;
      }
    
      /**
       *  from:转出账户
       *  to:转入账户
       *  money:转账金额
       */
      @Override
      public void transfer(String from, String to, Double money) {
          accountDao.outMoney(from, money);
          int i = 1 / 0;
          accountDao.inMoney(to, money);
      }
    }
    
  • 创建AccountDao接口

    package com.itzhouq.spring.tx.demo1;
    /*
     * 转账的DAO的接口
     */
    public interface AccountDao {
      public void outMoney(String from, Double money);
      public void inMoney(String to, Double money);
    }
    
  • 创建AccountDao实现类AccountDaoImpl

    package com.itzhouq.spring.tx.demo1;
    
    import org.springframework.jdbc.core.support.JdbcDaoSupport;
    
    public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
      
      @Override
      public void outMoney(String from, Double money) {
          this.getJdbcTemplate().update("update account set money=money-? where name=?", money,from);
      }
    
      @Override
      public void inMoney(String to, Double money) {
          this.getJdbcTemplate().update("update account set money=money+? where name=?", money,to);
      }
    }
    
  • 配置Service和Dao:交给Spring管理

    • 复制applicationContext.xml文件,新建配置文件tx.xml

      
          
              
          
          
          
          
              
          
      
  • 在Dao中编写扣钱和加钱

    • 配置连接池和JDBC的模板

      
      
      
      
          
          
          
          
          
       
      
      
          
      
      
    • 在Dao中注入jdbc的模板

      • 方法一:在Dao中直接注入

        package com.itzhouq.spring.tx.demo1;
        
        import org.springframework.jdbc.core.JdbcTemplate;
        
        public class AccountDaoImpl implements AccountDao {
          
          private JdbcTemplate jdbcTemplate;
          public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
              this.jdbcTemplate = jdbcTemplate;
          }
        
          @Override
          public void outMoney(String from, Double money) {
              
        
          }
        
          @Override
          public void inMoney(String to, Double money) {
        
          }
        }
        
      • 方法二:AccountDaoImpl继承JdbcDaoSupport

        • JdbcDaoSupport类中提供了模板,也提供了set方法

          package com.itzhouq.spring.tx.demo1;
          
          import org.springframework.jdbc.core.support.JdbcDaoSupport;
          
          public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
              
              @Override
              public void outMoney(String from, Double money) {
          
              }
          
              @Override
              public void inMoney(String to, Double money) {
          
              }
          }
          
        • 所以直接在xml文件中的dao注入模板

          
              
                  
              
          
      • 方法三:继承JdbcDaoSupport类之后直接注入连接池,这样连接池可以自动创建模板

        • 不用配置模板,直接dao中注入连接池

          
          
              
                  
              
          
          
              
                  
              
          
  • 配置文件

    
    
        
        
        
            
        
        
        
        
            
         
        
        
        
        
        
            
            
            
            
            
         
        
        
            
    
    
    
  • 测试

    package com.itzhouq.spring.tx.demo1;
    
    import javax.annotation.Resource;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    /*
     * 测试转账
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:tx.xml")
    public class SpringDemo1 {
      @Resource(name="accountService")
      private AccountService accountService;
      
      @Test
      public void test1() {
          accountService.transfer("周杰伦", "邓超", 1000d);
      }
    }
    
    • 如果没有异常能够转账成功。但是没有事务控制,一旦转账过程中出现异常就会出现数据丢失的现象。

3.4.2 Spring的事务管理

  • 方式一:编程式事务,需要手动编码【了解】
  • 方式二:声明式事务,通过配置实现---AOP

3.4.3 声明式事务

  • XML方式的声明式的事务管理

    • 引入jar包

      • aop联盟包

      • aspectJ包

      • aop包

      • Spring整合aspectJ包

        Spring笔记04_AOP注解开发_模板_事务_第4张图片
        image
    • 配置事务管理器

      
          
              
          
      
    • 配置增强

      
          
              
                  
                  
                  
              
          
      
    • AOP的配置

      
          
              
              
          
      
      • 测试:如果转账出现异常,事务自动回滚
  • 注解方式声明事务

    • 引入aop的开发包

    • 配置事务管理

      
          
              
          
      
    • 开启注解事务

      
          
      
    • 在业务层添加注解

      @Transactional
      public class AccountServiceImpl implements AccountService {
          
      
    • 测试:如果转账过程中出现异常,则自动回滚。

你可能感兴趣的:(Spring笔记04_AOP注解开发_模板_事务)