SSM框架-AOP概述、Spring事务

16 spring整合mybatis

16.1 前情代码

  1. 实体类
public class Account {

    private Integer id;
    private String name;
    private Double money;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}
  1. AccountDao接口
public interface AccountDao {

    @Insert("insert into tbl_account(name,money)values(#{name},#{money})")
    void save(Account account);

    @Delete("delete from tbl_account where id = #{id} ")
    void delete(Integer id);

    @Update("update tbl_account set name = #{name} , money = #{money} where id = #{id} ")
    void update(Account account);

    @Select("select * from tbl_account")
    List<Account> findAll();

    @Select("select * from tbl_account where id = #{id} ")
    Account findById(Integer id);
}
  1. App实现
public class App {
    public static void main(String[] args) throws IOException {
        // 1. 创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        // 2. 加载SqlMapConfig.xml配置文件
        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        // 3. 创建SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        // 4. 获取SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 5. 执行SqlSession对象执行查询,获取结果User
        AccountDao accountDao = sqlSession.getMapper(AccountDao.class);

        Account ac = accountDao.findById(2);
        System.out.println(ac);

        // 6. 释放资源
        sqlSession.close();
    }
}
  1. AccountService接口
public interface AccountService {

    void save(Account account);

    void delete(Integer id);

    void update(Account account);

    List<Account> findAll();

    Account findById(Integer id);

}
  1. AccountServiceImpl接口实现类
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    public void save(Account account) {
        accountDao.save(account);
    }

    public void update(Account account){
        accountDao.update(account);
    }

    public void delete(Integer id) {
        accountDao.delete(id);
    }

    public Account findById(Integer id) {
        return accountDao.findById(id);
    }

    public List<Account> findAll() {
        return accountDao.findAll();
    }
}

16.2 整合

1. 分析

在这里插入图片描述

sqlSession是工厂造出来的,已经存在,现在调用而已

sqkSessionFactory是核心对象

下面配置是围绕此对象进行的
在这里插入图片描述

  1. pom准备
 <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>
    
      <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.16</version>
    </dependency>

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.6</version>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
    </dependency>
    
        <!--spring操作与数据库有关的-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>
    
    
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.0</version>
    </dependency>
    
        <!--spring整合一个mybits的jar包-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.0</version>
    </dependency>
  1. spring配置类
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {

}
  1. 自动状态JdbcConfig配置类
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;
    }
}
  1. 注解简化mybatis配置

在这里插入图片描述

public class MybatisConfig {
    //定义bean,SqlSessionFactoryBean,用于产生SqlSessionFactory对象
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("com.itheima.domain");
        ssfb.setDataSource(dataSource);
        return ssfb;
    }
    //定义bean,返回MapperScannerConfigurer对象
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.itheima.dao");
        return msc;
    }
}
  1. 原来App类不要了,重新写App2
public class App2 {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

        AccountService accountService = ctx.getBean(AccountService.class);

        Account ac = accountService.findById(1);
        System.out.println(ac);
    }
}

17 Spring整合junit

  1. maven准备
    <!--junit包-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
    </dependency>

    <!--spring整合junit-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>
  1. 写测试方法

junit就相当于不用走main方法直接执行

//设定类运行器
@RunWith(SpringJUnit4ClassRunner.class)
//配置上下文
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {
    //测业务层接口
    //自动装配
    @Autowired
    private AccountService accountService;

    //写测试方法
    @Test
    public void testFindById(){
        //直接掉方法就可以啦
        System.out.println(accountService.findById(2));
    }

}

18 AOP

18.1 AOP简介

  1. 简介

AOP面向切面编程,一种编程规范,指导开发者如何组织程序结构

  1. 作用

在不惊动原始设计的基础上为其进行功能增强

  1. 原理

将原来想要其他方法也拥有的功能代码抽取出来,把这块东西抽取出来成method单独的方法

给原来就有的方法取名叫做连接点

给需要追加功能的方法叫做切入点

追加的共性功能的方法(method)中叫做通知

通知方法所在的类叫做通知类

通知和切入点关系叫做切面

在这里插入图片描述

在这里插入图片描述

18.2 AOP入门案例

  1. 步骤分析
  • 导入AOP坐标(pom.xml)
  • 制作连接点方法(原始操作,Dao接口与实现类)
  • 制作共性功能(通知类与通知)
  • 定义切入点
  • 绑定切入点和通知关系(切面)
  1. pom包导入依赖

导入context的时候,aop的包也自动导入了

导入aspectj的包

    <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. BookDaoImpl,制作连接点方法
  • 接口
public interface BookDao {
    public void save();
    public void update();
}
  • 实现类
@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. 制作连接点方法以及定义切入点
  • 新建一个类advice写这个
//4.通知类必须配置成Spring管理的bean
@Component
//5.设置当前类为切面类类,spring就会把这个当作aop处理
@Aspect
public class MyAdvice {
    //2.定义切入点,要求配置在方法上方
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
    
    //3.设置在切入点pt()的前面运行当前操作(前置通知)
     @Before("pt()")

     //1.共性功能
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}
  • spring配置类也要写一个
@Configuration
@ComponentScan("com.itheima")
//开启注解开发AOP功能,跟@Aspect一起的
@EnableAspectJAutoProxy
public class SpringConfig {
}
  • App入口
public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = ctx.getBean(BookDao.class);
        bookDao.update();
            }
}
  • 输出结果
1668245211570
book dao save ...

18.3 AOP的工作流程

  1. 流程
  • Spring容器启动
  • 读取所有切面配置中的切入点
  • 初始化bean,判断bean对应的类中的方法是否匹配到任意切入点
    • 匹配失败,创建对象
    • 匹配成功,创建原始对象(目标对象)的代理对象
  • 执行bean执行方法
    • 获取bean,调用方法并执行,完成操作
    • 获取bean是代理对象时,根据代理对象的运行模式运行原始方法和增强方法的内容,完成操作
  1. 实操
  • 匹配的上

System.out.println(bookDao);打印出来是System.out.println(bookDao);

System.out.println(bookDao.getClass());打印出来是class com.sun.proxy.$Proxy19

  • 匹配不上

System.out.println(bookDao);打印出来是System.out.println(bookDao);

System.out.println(bookDao.getClass());打印出来是class com.itheima.dao.impl.BookDaoImpl

  • 总结

打印对象不准确,因为对tostring进行重写了,所以看到的是一个样的

打印.class发现是由代理完成的实现的

18.4 AOP切入点表达式

image-20221112210448849
  1. 语法格式
  • 类型

    • 按照接口描述

    • 按照接口实现类描述,但一般不用,耦合度高

  • 切入点表达式标准格式

在这里插入图片描述

  • 实例
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    @Pointcut("execution(void com.itheima.dao.impl.BookDaoImpl.update())")
  1. 通配符
  • *:表示任意
    @Pointcut("execution(* com.itheima.dao.impl.BookDaoImpl.update())")//出
	@Pointcut("execution(* com.itheima.dao.impl.BookDaoImpl.update(*))")//不出,因此*表示至少一个
  • …:表示多个连续的任意符号,即没有或者多个
    @Pointcut("execution(void *..update())")
    @Pointcut("execution(* *..*(..))")//表示匹配工程中的所有东西,电脑估计会炸,一般不用
    @Pointcut("execution(* *..u*(..))")//u开头的方法
  • +:专用于匹配子类类型
  1. 技巧与规范

在这里插入图片描述

18.5 AOP通知类型

  1. 概念

描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置

  1. 类型
  • 前置通知
  • 后置通知
  • 环绕通知(重点)
  • 返回后通知(了解)
  • 抛出异常后通知(了解)
  1. 运用
  • 前置
public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
    
    //@Before:前置通知,在原始方法运行之前执行
    @Before("pt()")
    public void before() {
        System.out.println("before advice ...");
    }
}
  • 后置
    //@After:后置通知,在原始方法运行之后执行
    @After("pt()")
    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需要返回Object,通过 pjp.proceed()进行返回值的接收

public interface BookDao {
    public int select();
}
public class BookDaoImpl implements BookDao {

    public int select() {
        System.out.println("book dao select is running ...");
//        int i = 1/0;
        return 100;
    }
}
    @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;
    }

在这里插入图片描述

  • 返回后通知(了解)

与After的区别是要求原始方法执行过程中未出现异常现象

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

18.6 测试业务层接口万次执行效率

  1. 需求

任意业务层接口执行均可显示其执行效率(执行时长)

  1. 分析
  • 业务功能:业务层接口执行前后分别记录时间,求差值得到执行效率
  • 通知类型选择前后均可以增强的类型—环绕通知
  1. 前情代码
  • jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
jdbc.username=root
jdbc.password=123456
  • 配置类JdbcConfig
package com.itheima.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;


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;
    }
}
  • 配置类MybatisConfig
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;

public class MybatisConfig {

    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("com.itheima.domain");
        ssfb.setDataSource(dataSource);
        return ssfb;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.itheima.dao");
        return msc;
    }
}
  • spring配置类SpringConfig
import org.springframework.context.annotation.*;

@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {
}
  • 实体类Account
package com.itheima.domain;

import java.io.Serializable;

public class Account implements Serializable {

    private Integer id;
    private String name;
    private Double money;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

  • AccountDao接口(使用mybatis)
public interface AccountDao {

    @Insert("insert into tbl_account(name,money)values(#{name},#{money})")
    void save(Account account);

    @Delete("delete from tbl_account where id = #{id} ")
    void delete(Integer id);

    @Update("update tbl_account set name = #{name} , money = #{money} where id = #{id} ")
    void update(Account account);

    @Select("select * from tbl_account")
    List<Account> findAll();

    @Select("select * from tbl_account where id = #{id} ")
    Account findById(Integer id);
}
  • AccountService接口
package com.itheima.service;

import com.itheima.domain.Account;

import java.util.List;

public interface AccountService {
    void save(Account account);
    void delete(Integer id);
    void update(Account account);
    List<Account> findAll();

    Account findById(Integer id);
}
  • AccountServiceImpl接口实现类
import java.util.List;
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;
    @Override
    public void save(Account account) {
        accountDao.save(account);
    }

    @Override
    public void delete(Integer id) {
        accountDao.delete(id);
    }

    @Override
    public void update(Account account) {
        accountDao.update(account);
    }

    @Override
    public List<Account> findAll() {
        return accountDao.findAll() ;
    }

    @Override
    public Account findById(Integer id) {
        return accountDao.findById(id);
    }
}
  • 测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTestCase {
    @Autowired
    private AccountService accountService;

    @Test
    public void testFindById(){
        Account ac = accountService.findById(2);
        System.out.println(ac);
    }

    @Test
    public void testFindAll(){
        List<Account> all = accountService.findAll();
        System.out.println(all);
    }
}
  1. 进行aop设置
  • 打开aop注解
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
@EnableAspectJAutoProxy
public class SpringConfig {
}
  • 写aop的类
@Component
//切片类
@Aspect
public class ProjectAdvice {
    //匹配业务层的所有方法
    @Pointcut("execution(* com.itheima.service.*Service.*(..))")
    private void servicePt(){};

    @Around("ProjectAdvice.servicePt()")
    public void runSpeed(ProceedingJoinPoint pjp) throws Throwable {

        //签名信息
        Signature signature = pjp.getSignature();
        //接口类型
        String className = signature.getDeclaringTypeName();
        //接口方法
        String methodName = signature.getName();

        //原始操作前来个记录时间
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            //执行原始操作,不需要返回值了,直接查询10000遍
            pjp.proceed();
        }
        //结束后
        long end = System.currentTimeMillis();
        System.out.println("万次执行:"+className+"."+methodName+"---->"+(end-start)+"ms");
    }
}
  • 测试代码
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;
    @Override
    public void save(Account account) {
        accountDao.save(account);
    }

    @Override
    public void delete(Integer id) {
        accountDao.delete(id);
    }

    @Override
    public void update(Account account) {
        accountDao.update(account);
    }

    @Override
    public List<Account> findAll() {
        return accountDao.findAll() ;
    }

    @Override
    public Account findById(Integer id) {
        return accountDao.findById(id);
    }
}
  • 结果
万次执行:com.itheima.service.AccountService.findAll---->2148ms
万次执行:com.itheima.service.AccountService.findById---->1280ms

18.7 AOP通知获取数据

在这里插入图片描述

  1. 代码
  • before
@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")
    private void pt(){}

    //JoinPoint:用于描述切入点的对象,必须配置成通知方法中的第一个参数,可用于获取原始方法调用的参数
    @Before("pt()")
    public void before(JoinPoint jp) {
        //用JoinPoint返回参数,返回的是数组
        Object[] args = jp.getArgs();
        System.out.println(Arrays.toString(args));
        System.out.println("before advice ..." );
    }
    }
  • around

正常运行是id为100,可以在中间更改参数,变成600

可以运用到参过来的参数有问题(例如数据格式、数据问题),可以处理

    //ProceedingJoinPoint:专用于环绕通知,是JoinPoint子类,可以实现对原始方法的调用,直接getArgs
    //@Around("pt()")
    public Object around(ProceedingJoinPoint pjp) {
        Object[] args = pjp.getArgs();
        System.out.println(Arrays.toString(args));
        //更改args数值,再作为参数扔进去
        args[0] = 666;
        Object ret = null;
        try {
            ret = pjp.proceed(args);
        } catch (Throwable t) {
            t.printStackTrace();
        }
        return ret;
    }
  • afterReturning

returning = "ret"与String ret以及JoinPoint和String同时存在

    //设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同
    @AfterReturning(value = "pt()",returning = "ret")
    //JoinPoint和String同时存在,JoinPoint得放前面
    public void afterReturning(JoinPoint jp,String ret) {
        System.out.println("afterReturning advice ..."+ret);
    }
  • AfterThrowing

throwing = "t"与(Throwable t)

    //设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同,即形参是返回对象
    @AfterThrowing(value = "pt()",throwing = "t")
    public void afterThrowing(Throwable t) {
        System.out.println("afterThrowing advice ..."+t);
    }
    public String findName(int id,String password) {
        System.out.println("id:" + id);
        //故意来个异常,给AfterThrowing接收
        if (true) throw new NullPointerException();
        return "itcast";
    }

输出

id:100
afterThrowing advice ...java.lang.NullPointerException
Exception in thread "main" java.lang.NullPointerException
  1. 总结
  • around用proceedingjoinpoint,其他用joinpoint

18.8 案例:百度网盘密码数据兼容处理

  1. 需求

在业务方法执行之前对所有的输入参数进行格式处理——trim()

  1. 分析

使用处理后的参数调用原始方法——环绕通知中存在对原始方法的调用

  1. 前情代码
  • maven
 <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.4</version>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
    </dependency>

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.16</version>
    </dependency>

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.6</version>
    </dependency>

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.0</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
  • 配置类
@Configuration
@ComponentScan("com.itheima")
public class SpringConfig {
}
  • ResourcesDao接口
public interface ResourcesDao {

    boolean readResources(String url, String password);
}
  • ResourcesDaoImpl接口实现类
@Repository
public class ResourcesDaoImpl implements ResourcesDao {
    public boolean readResources(String url, String password) {
        System.out.println(password.length());
        //模拟校验
        return password.equals("root");
    }
}
  • ResourcesService
public interface ResourcesService {
    public boolean openURL(String url, String password);
}
  • ResourcesServiceImpl接口实现类
@Service
public class ResourcesServiceImpl implements ResourcesService {
    @Autowired
    private ResourcesDao resourcesDao;

    public boolean openURL(String url, String password) {
        return resourcesDao.readResources(url,password);
    }
}
  • App入口
public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        ResourcesService resourcesService = ctx.getBean(ResourcesService.class);
        boolean flag = resourcesService.openURL("http://pan.baidu.com/haha", "root");
        System.out.println(flag);
    }
}
  1. 开工
  • 打开aop配置类
@Configuration
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy
public class SpringConfig {
}
  • DataAdvice类
@Component
@Aspect
public class DataAdvice {
    @Pointcut("execution(boolean com.itheima.service.*Service.*(*,*))")
    private void servicePt(){}

    @Around("com.itheima.aop.DataAdvice.servicePt()")
    public Object trimStr(ProceedingJoinPoint pjp) throws Throwable {
        Object[] args = pjp.getArgs();
        for (int i = 0; i < args.length; i++) {
            //args是个对象,不一定是字符产,不用foreach循环,用fori,需要用到索引值判断是否是字符串
            // 判断参数是不是字符串
            if(args[i].getClass().equals(String.class)){
                //用toString转成字符串
                args[i] = args[i].toString().trim();
            }
        }
        //改完放回去哦
        Object ret = pjp.proceed(args);
        return ret;
    }
}
  • 运行App入口类
public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        ResourcesService resourcesService = ctx.getBean(ResourcesService.class);
        boolean flag = resourcesService.openURL("http://pan.baidu.com/haha", "root  ");
        System.out.println(flag);
    }
}
  • 输出结果
4
true

19 Spring事务

19.1 spring事务简介

  1. 作用

在数据层保障一系列的数据库操作同失败同完成

spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败

在这里插入图片描述

  1. 需求

需求:实现任意两个账户间转账操作,A账户减钱,B账户加钱

  • JDBC配置
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配置
public class MybatisConfig {

    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("com.itheima.domain");
        ssfb.setDataSource(dataSource);
        return ssfb;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.itheima.dao");
        return msc;
    }
}
  • spring配置
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {
}
  • 数据层接口
public interface AccountDao {

    @Update("update tbl_account set money = money + #{money} where name = #{name}")
    void inMoney(@Param("name") String name, @Param("money") Double money);

    @Update("update tbl_account set money = money - #{money} where name = #{name}")
    void outMoney(@Param("name") String name, @Param("money") Double money);
}
  • 业务层接口
public interface AccountService {
    /**
     * 转账操作
     * @param out 传出方
     * @param in 转入方
     * @param money 金额
     */
    //配置当前接口方法具有事务
    @Transactional
    public void transfer(String out,String in ,Double money) ;
}
  • 业务层实现类
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    public void transfer(String out,String in ,Double money) {
        accountDao.outMoney(out,money);
        accountDao.inMoney(in,money);
    }
}
  • 测试用例
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void testTransfer() throws IOException {
        accountService.transfer("Tom","Jerry",100D);
    }
}
  1. 结果分析

如果中间出现异常,那么会出现A加,B不减的执行问题

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;
    
    public void transfer(String out,String in ,Double money) {
        accountDao.outMoney(out,money);
        int i = 1/0;
        accountDao.inMoney(in,money);
    }

}

因此在接口中开启事务

    @Transactional
    public void transfer(String out,String in ,Double money) ;
}

并且在JdbcConfig开启事务管理器

    //配置事务管理器,mybatis使用的是jdbc事务,dataSource这个丢进去
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }

并且在注解写上事务型驱动

@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
//开启注解式事务驱动
@EnableTransactionManagement
public class SpringConfig {
}

这样运行的时候就会保证同时成功或者失败,抛出异常java.lang.ArithmeticException: / by zero

19.2 Spring事务角色

  1. 场景

在这里插入图片描述

outMoney事务和inMoney事务都会加入到transfer事务中,就可以实现,transfer下的操作同时成功或者失败,不会出现inMoney事务出错,而outMoney事务不回滚的问题了

transfer叫做事务管理员,outMoney事务和inMoney事务叫做事务协调员

事务管理员就是发起事务方,就是发起事务的那个方法,例如transfer

事务协调员就是加入事务方,可以是数据层方法也可以是业务层方法

19.3 事务的配置

  1. 配置项

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值