Springjs css html

SpringAOP

2022-11-27  本文已影响0人  h2coder

AOP简介和作用

AOP的一些概念

image-20210730144903209.png image-20210730154740528.png

SpringAOP快速入门

<dependencies>
    <!--spring-context-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>

    <!--aspectjweaver(spring面向切面开发必须的第三方组件)-->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>

    <!--junit,spring对junit4的要求必须是4.12版本以上-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
        <scope>test</scope>
    </dependency>

    <!--spring-test-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
</dependencies>
//Dao层接口类
public interface BookDao {
    void save();

    void update();

    void delete();
}

//Dao层实现类
@Repository
public class BookDaoImpl implements BookDao {
    @Override
    public void save() {
        System.out.println("book dao save ...");
    }

    @Override
    public void update() {
        System.out.println("book dao update ...");
    }

    @Override
    public void delete() {
        System.out.println("book dao delete ...");
    }
}
/**
 * 通知类
 * 注意:需要将切面类加入Spring-IOC容器
 */
@Component //需要将切面类加入Spring-IOC容器
@Aspect //指定为切面类,Spring框架就会对这个类进行AOP操作
public class MyAdvice {
    /**
     * 定义切入点表达式,指定对 BookDaoImpl 的 save()、update()方法进行增强
     */
    //最完整的写法,必须3个参数必写:返回值、方法名、参数
    @Pointcut("execution(void com.itheima.dao.BookDao.save()) || execution(void com.itheima.dao.BookDao.update())")
    public void pt() {
    }

    /**
     * 通知方法
     * <p>
     * 注解@Before,前置通知,会在业务方法前,调用该增强方法
     * 属性 @Before("pt()"),绑定切入点,目的是让增强方法和业务方法绑定关系
     */
    @Before(value = "pt()")
    public void before() {
        System.out.println("写入日志");
    }
}
@Configuration
@ComponentScan("com.itheima")
//开启AOP注解扫描(开启后,才会扫描AOP注解,才能使用AOP面向切面功能)
@EnableAspectJAutoProxy
public class SpringConfig {
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class BookDaoTest {
    /**
     * 注入BookDao,这个对象是代理对象,AOP底层就是动态代理,调用方法时,先调用增强的代码,再调用我们的业务代码
     */
    @Autowired
    private BookDao bookDao;

    @Test
    public void test() {
        System.out.println("运行 =======> 需要增强的方法");
        bookDao.save();
        bookDao.update();

        System.out.println("运行 =======> 执行没有增强的方法");
        bookDao.delete();
    }
}

AOP工作流程

AOP核心概念

AOP切入点表达式

语法格式

execution(void com.itheima.dao.BookDao.update())
execution(void com.itheima.dao.impl.BookDaoImpl.update())
execution(public User com.itheima.service.UserService.findById(int))

通配符

通配符:*

匹配com.itheima包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法

execution(public * com.itheima.*.UserService.find*(*))

通配符 ..

匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法

execution(public User com..UserService.findById(..))

通配符 +

execution(* *..*Service+.*(..))

书写技巧

AOP通知类型

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

AOP通知共分为5种类型

AOP通知详解

前置通知

@Before("pt()")
public void before() {
    System.out.println("before advice ...");
}

最终通知

@After("pt()")
public void after() {
    System.out.println("after advice ...");
}

后置通知

@AfterReturning("pt()")
public void afterReturning() {
    System.out.println("afterReturning advice ...");
}

异常通知

@AfterThrowing("pt()")
public void afterThrowing() {
    System.out.println("afterThrowing advice ...");
}

环绕通知

@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;
}

环绕通知注意事项

AOP切入点数据获取

获取参数

前置通知中,获取方法参数

@Before("pt()")
public void before(JoinPoint jp) {
    Object[] args = jp.getArgs(); //获取连接点方法的参数们
    System.out.println(Arrays.toString(args));
}

环绕通知中,获取方法参数

@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    Object[] args = pjp.getArgs(); //获取连接点方法的参数们
    System.out.println(Arrays.toString(args));
    Object ret = pjp.proceed();
    return ret;
}

获取返回值

异常通知中,获取返回值

@AfterReturning(value = "pt()",returning = "ret")
public void afterReturning(Object ret) { //变量名要和returning="ret"的属性值一致
    System.out.println("afterReturning advice ..."+ret);
}

环绕通知中,获取返回值

@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    // 手动调用连接点方法,返回值就是连接点方法的返回值
    Object ret = pjp.proceed();
    return ret;
}

获取异常信息

异常通知可以获取切入点方法中出现的异常信息,使用形参可以接收对应的异常对象

@AfterThrowing(value = "pt()",throwing = "t")
public void afterThrowing(Throwable t) {//变量名要和throwing = "t"的属性值一致
    System.out.println("afterThrowing advice ..."+ t);
}

异常通知可以获取切入点方法运行的异常信息,使用形参可以接收运行时抛出的异常对象

@Around("pt()")
public Object around(ProceedingJoinPoint pjp)  {
    Object ret = null;
    //此处需要try...catch处理,catch中捕获到的异常就是连接点方法中抛出的异常
    try {
        ret = pjp.proceed();
    } catch (Throwable t) {
        t.printStackTrace();
    }
    return ret;
}

Spring事务管理

image-20210801190820853.png

Spring事务作用

转账案例

环境准备

<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>8.0.27</version>
    </dependency>

    <!-- Spring-JDBC依赖包,事务功能也在这里 -->
    <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>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
</dependencies>
-- 创建数据表,账户表
CREATE TABLE tbl_account (
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(10),
    money DOUBLE  -- 金额
);

-- 添加数据
INSERT INTO tbl_account (NAME, money) VALUES ('Jack', 1000), ('Rose', 1000);

SELECT * FROM tbl_account;

-- 还原金额
update tbl_account set money=1000;
jdbc.username=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/springdb2?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
//Spring配置类
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class, MybatisConfig.class})
//注解 @EnableTransactionManagement 开启事务注解扫描(当前包名下都扫描,所以不需要配置包名)
@EnableTransactionManagement
public class SpringConfig {
}
//Druid连接池配置
public class JdbcConfig {
    @Value("${jdbc.driverClassName}")
    private String driverClassName;
    @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(driverClassName);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }

    /**
     * 配置spring提供的事务切面类:里面增强了事务管理功能,里面有事务提交和事务回滚功能
     */
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        DataSourceTransactionManager ptm = new DataSourceTransactionManager();
        ptm.setDataSource(dataSource);
        return ptm;
    }
}
//MyBatis配置类
public class MybatisConfig {
    @Bean  //不仅可以将返回值加入IOC容器,而且可以实现方法参数进行依赖注入,参数默认会根据类型从IOC容器中获取对象自动注入
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("com.itheima.domain"); //告诉mybatis,设置实体类包别名
        ssfb.setDataSource(dataSource); //将IOC容器中连接池给到Mybatis

        //注意:必须导入org.apache.ibatis.session.Configuration;
        Configuration configuration = new Configuration();
        //设置整合mybatis驼峰命名映射
        configuration.setMapUnderscoreToCamelCase(true);
        //设置打印日志
        configuration.setLogImpl(StdOutImpl.class);

        ssfb.setConfiguration(configuration);
        return ssfb;
    }

    //设置事务管理器,并将事务管理器添加到IOC容器中
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        //告诉mybatis扫描指定的接口映射包
        mapperScannerConfigurer.setBasePackage("com.itheima.dao");
        return mapperScannerConfigurer;
    }
}

编写代码

public interface AccountDao {
    /**
     * 转入
     *
     * @param name  账号名
     * @param money 金额
     */
    @Update("update tbl_account set money = money + #{money} where name = #{name}")
    void inMoney(@Param("name") String name, @Param("money") Double money);

    /**
     * 转出
     *
     * @param name  账号名
     * @param money 金额
     */
    @Update("update tbl_account set money = money - #{money} where name = #{name}")
    void outMoney(@Param("name") String name, @Param("money") Double money);
}
//业务层接口
//(推荐这种)写到类头,那么所有实现的所有方法,都有了事务
@Transactional
public interface AccountService {
    /**
     * 转账
     *
     * @param out   转出给谁
     * @param in    转入到谁
     * @param money 金额
     */
    //在方法上,添加 @Transactional 注解,那么所有的实现类的这个方法都有了事务
    //@Transactional
    void transfer(String out, String in, Double money) throws IOException;
}

//业务层实现类
@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    public void transfer(String out, String in, Double money) throws FileNotFoundException {
        accountDao.outMoney(out, money);
        //如果没有事务,这个异常会导致结束程序,导致下面的转入没有执行
        //int a = 1 / 0;
        accountDao.inMoney(in, money);
    }
}

注意事项

Spring事务角色

image-20210801192453227.png

Spring事务相关配置

image-20210802151553053.png

Spring事务的传播行为

image-20210802153014296.png

传播行为的种类

传播行为种类解释

Spring的事务传播行为案例

默认事务传播行为的问题

image-20210802153216460.png

环境准备

CREATE TABLE tbl_log(
    id INT PRIMARY KEY AUTO_INCREMENT,
    info VARCHAR(255),
    create_date DATETIME
);

编写代码

/**
 * 转账日志记录-持久层
 */
public interface LogDao {
    /**
     * 增加转账日志
     *
     * @param info 日志信息
     */
    @Insert("insert into tbl_log (info,create_date) values(#{info},now())")
    void log(String info);
}
/**
 * 转账日志业务层接口
 */
public interface LogService {
    /**
     * 日志方法,无论调用当前方法是否是否有事务,当前方法都创建一个新的事务
     * <p>
     * Propagation.REQUIRES_NEW,就是无论调用方是否有事务,都开启一个新的事务
     * 如果不设置传播行为,那么就会加入到调用方的事务,调用方出异常回滚了,就会将当前log记录也一起回滚!
     * 注解@Transactional(propagation = Propagation.REQUIRES_NEW)
     *
     * @param out   谁转出转出
     * @param in    转出给谁
     * @param money 金额
     */
    @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);
    }
}
@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    @Autowired
    private LogService logService;

    public void transfer(String out, String in, Double money) throws FileNotFoundException {
        try {
            accountDao.outMoney(out, money);
            //如果没有事务,这个异常会导致结束程序,导致下面的转入没有执行
            //int a = 1 / 0;
            accountDao.inMoney(in, money);
        } finally {
            //调用日志业务,记录日志(调用了另外一个业务方法,要求是一个独立的事务,就是不加入当前事务)
            logService.log(out, in, money);
        }
    }
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {
    @Autowired
    private AccountService accountService;

    @Test
    public void testTransfer() throws IOException {
        accountService.transfer("Jack", "Rose", 500d);
    }
}
上一篇 下一篇

猜你喜欢

热点阅读