java AOP+事务的讲解

1.什么是AOP?

2.为什么使用AOP?

3.如何使用AOP?

4.什么是事务?

5.spring如何实现事务管理

1.什么是AOP?

在Java中,AOP(面向切面编程)是一种编程范式,用于在应用程序中实现横切关注点(Cross-cutting Concerns)的模块化和重用。横切关注点是指那些横跨应用程序多个模块的功能或行为,如日志记录、安全性、性能监测等,它们通常割裂了程序的模块性,使得代码难以维护和扩展。

AOP通过对应用程序进行解耦和模块化,使得我们可以将横切关注点从核心业务逻辑中剥离出来,从而实现更好的代码组织和可维护性。

AOP的核心概念是切面(Aspect),切面是横切关注点的模块化单元。切面通过定义一系列的切点(Pointcut)和通知(Advice),在应用程序执行过程中根据切点来插入通知,从而实现对横切关注点的处理。

2.为什么使用AOP?

 AOP:面向切面编程,相对于OOP面向对象编程。Spring的AOP的存在目的是为了解耦。AOP可以让一组类共享相同的行为。在OOP中只能继承和实现接口,且类继承只能单继承,阻碍更多行为添加到一组类上,AOP弥补了OOP的不足。还有就是为了清晰的逻辑,让业务逻辑关注业务本身,不用去关心其它的事情,比如事务。

AOP应用场景:
  1. 统一管理日志:在系统中记录日志是很常见的需求,如果把日志操作放到每个方法中,那么会产生很多冗余代码。使用AOP,我们可以定义一个日志切面,然后通过AOP框架将该切面织入到所有方法中,这样可以避免冗余代码的产生,同时也方便统一管理日志。

  2. 统一异常处理:在系统中,处理异常也是很常见的需求。如果将异常处理代码放到每个方法中,那么不仅会产生冗余代码,而且也会增加代码的复杂度。使用AOP,我们可以定义一个异常处理切面,将其织入到所有方法中,这样可以方便统一处理异常。

  3. 安全检查:在系统中进行安全检查是很必要的事情。如果将安全检查代码放到每个方法中,那么会产生很多冗余代码,而且也会增加代码的复杂度。使用AOP,我们可以定义一个安全检查切面,将其织入到所有方法中,这样可以方便统一进行安全检查。

AOP的结构:

AOP要做的三件事:

1,在哪里切入?

也就是权限校验,等非业务操作在哪里执行,

2,什么时候切入?

是业务执行代码后还是执行前

3.切入后做什么是?

比如做权限校验,日志记录等。

 

Aspect:切面

PointCut:切点:---方式:(1)路径表达式(2)注解形式

Advice:处理的机制。

3.如何使用AOP?

3.1路径表达式

(1)提前写的接口:MathService.java,代码省略,参照以下该接口的实现类。

提前写的实现类:MathServiceImpl.java,代码如下所示。

@Service  //<bean id="mathServiceImp" class="">
public class MathServiceImpl implements MathService {
    public double add(double a, double b) {
        double result=a+b;
        return result;
    }
​
    public double mul(double a, double b) {
        double result=a-b;
        return result;
    }
​
    public double cheng(double a, double b) {
        double result=a*b;
        return result;
    }
​
    public double div(double a, double b) {
        double result=a/b;
        return result;
    }
}

(2)引入相关依赖

<dependencies>
        <!--引入spring核心依赖库-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.12.RELEASE</version>
        </dependency>
​
        <!--引入spring切面依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.12.RELEASE</version>
        </dependency>
    </dependencies>

(3)创建一个切面类

@Aspect //标记该类为切面类
@Component //该类对象的创建教于spring容器来创建,---等同于@Service,@Controller
public class MyAspect  {
    //定义为切点
    @Pointcut(value = "execution(public double com.aaa.date0816.d1.MathServiceImpl.add(double,double))")
    private void test01(){
​
    }
​
    @After(value = "test01()")
    private void test001(){
        System.out.println("我是after'''''''''''之后执行");
    }
}

(4)测试

public class Test01 {
    public static void main(String[] args) {
        //加载spring配置文件
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("classpath:spring.xml");
        MathService mathServiceImpl = (MathService)app.getBean("mathServiceImpl");
​
        mathServiceImpl.add(2,9);
​
    }
}

使用通配符来通配类路径

@Aspect //标记该类为切面类
@Component //该类对象的创建教于spring容器来创建,---等同于@Service,@Controller
public class MyAspect  {
    //定义为切点
    /**
     * 第一个* 表示任意修饰符,任意返回值。
     * 第二个* 表示该包下的所有类
     * 第三个* 表示类下所有的方法
     * ..:表示任意参数
     *
     * 建议包就别使用通配符
     */
    @Pointcut(value = "execution(* com.aaa.date0816.d1.*.*(..))")
    private void test01(){
​
    }
​
    @After(value = "test01()")
    private void test001(){
        System.out.println("我是after'''''''''''之后执行");
    }
}
3.2注解模式

1.自定义注解

@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "";
}

修改切面类

    //自定义注解
    @Pointcut(value = "@annotation(com.aaa.date0816.d1.MyAnnotation)")
    private void test02(){
    }
​
    //在使用MyAnnotation注解的方法之后执行内容
    @After(value="test02()")
    private void test002(){
        System.out.println("我是after02~~~~~之后执行我");
    }
3.3AOP切面通知的类型

@Before 前置通知. 被代理的方法执行前--执行

@After: 后置通知: 被代理的方法执行完后--执行

@AfterReturning: 后置返回通知: 被代理的方法碰到return.--才会执行

@AfterThrowing: 后置异常通知: 当被代理的方法出现异常时--才会执行。

@Around: 环绕通知。

@Aspect //标记该类为切面类
@Component //该类对象的创建教于spring容器来创建,---等同于@Service,@Controller
public class MyAspect  {
    //定义为切点
    /**
     * 第一个* 表示任意修饰符,任意返回值。
     * 第二个* 表示该包下的所有类
     * 第三个* 表示类下所有的方法
     * ..:表示任意参数
     *
     * 建议包就别使用通配符
     */
//    @Pointcut(value = "execution(* com.aaa.date0816.d1.*.*(..))")
//    private void test01(){
//
//    }
//
//    @After(value = "test01()")
//    private void test001(){
//        System.out.println("我是after'''''''''''之后执行");
//    }
​
    //自定义注解
    @Pointcut(value = "@annotation(com.aaa.date0816.d1.MyAnnotation)")
    private void test02(){
    }
​
//    //在使用MyAnnotation注解的方法之后执行内容,无论如何都执行
//    @After(value="test02()")
//    private void test002(){
//        System.out.println("我是after02~~~~~最后执行我");
//    }
//
//    //前置通知
//    @Before(value = "test02()")
//    public void test003(){
//        System.out.println("方法执行前执行切面的内容 前置通知~~~~~~~~");
//    }
//
//    //后置返回通知, 碰到return ,如果方法出现异常,这种通知不会执行
//    @AfterReturning(value = "test02()",returning="r")
//    public void test004(Object r){//参数名必须和returning的名称一致
//        System.out.println("----------后置返回通知"+r);
//    }
//
//    //异常通知
//    @AfterThrowing(value = "test02()")
//    public void Test005(){
//        System.out.println("异常通知---------异常之后会走我");
//    }
    
    //环绕通知
    @Around(value = "test02()")
    public Object Test006(ProceedingJoinPoint joinPoint){//连接点, 可以理解为被执行方法的对象
        System.out.println("开始的我");
        try {
            joinPoint.proceed();//执行你的连接点
            System.out.println("执行内容。。。。。。");
        } catch (Throwable e) {
            e.printStackTrace();
            System.out.println("方法出现异常执行我");
        }finally {
            System.out.println("无论如何都会执行我");
        }
        return 0.0;
    }
}

4.什么是事务

在Java编程中,事务是指一系列数据库操作的逻辑单元,要么全部成功执行,要么全部回滚。事务的目的是确保一组操作的一致性和完整性,例如在数据库中执行多个操作时,要么全部操作成功提交,要么全部操作失败回滚,以保证数据的正确性。

举例1, A -->B, A向B转钱。

JDBC--模式事务是自动提交

--设置手动提交

public class Test03 {
    public static void main(String[] args) throws ClassNotFoundException {
        Connection connection = null;
        Class<?> aClass = Class.forName("com.mysql.cj.jdbc.Driver");
        try {
           connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mysql5_1?serverTimezone=Asia/Shanghai", "root", "123456");
           connection.setAutoCommit(false);//把自动提交关闭,就是手动提交
            PreparedStatement preparedStatement = connection.prepareStatement("update account set money=money-500 where id=8");
            PreparedStatement preparedStatement1 = connection.prepareStatement("update account set money=money+500 where id=9");
            preparedStatement1.executeUpdate();
            preparedStatement.executeUpdate();
            connection.commit();//提交
        } catch (SQLException e) {
            e.printStackTrace();
            //事务回滚
            try {
                connection.rollback();
            } catch (SQLException ex) {
                throw new RuntimeException(ex);
            }
        }
​
​
    }
​
        }
​

5.spring如何实现事务管理

spring框架会提供一个事务切面类。【1】前置通知---开启手动事务 [2]后置返回通知[事务提交] [3]异常通知[事务回滚]

1(依赖)

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.12.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.12.RELEASE</version>
        </dependency>
        <!--spring事务依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.2.12.RELEASE</version>
        </dependency>
​
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.15.RELEASE</version>
        </dependency>
​
        <!--mybatis的依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>
        <!--mybatis和spring整合的依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.7</version>
        </dependency>
        <!--druid的连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.8</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.28</version>
        </dependency>
    </dependencies>

(2)spring 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
​
    <!--springmvc的配置-->
    <!--包扫描 扫描com.ykq以及该包下的子包-->
    <context:component-scan base-package="com.aaa"/>
​
​
​
    <!--spring整合mybatis的配置-->
​
    <!--数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <!--mysql驱动为8.0以后必须使用时区-->
        <property name="url" value="jdbc:mysql://localhost:3306/mysql5_1?serverTimezone=Asia/Shanghai"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
​
    <!--spring封装了一个类SqlSessionFactoryBean类,可以把mybatis中的配置-->
    <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mapperLocations" value="classpath:/mapper/*.xml"/>
    </bean>
​
    <!--为指定dao包下的接口生产代理实现类-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sessionFactory"/>
        <!--它会为com.ykq.dao包下的所有接口生产代理实现类-->
        <property name="basePackage" value="com.aaa.dao"/>
    </bean>
​
    <!---================以下内容是关于事务的配置===================-->
    <!--事务切面管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
​
    <!--开启事务管理注解的驱动-->
    <tx:annotation-driven/>
</beans>

(3)dao和xml

public interface AccountDao {
    //修改余额
    public void updateBalance(@Param("id") int id,@Param("money") int money);
​
}
​
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace必须和dao接口的名称一模一样-->
<mapper namespace="com.aaa.dao.AccountDao">
​
    <update id="updateBalance">
        update  account set money=money+#{money} where id=#{id}
    </update>
​
</mapper>

(4)service

package com.ykq.service.impl;
​
import com.ykq.dao.UserDao;
import com.ykq.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
​
/**
 * @program: qy168-transation
 * @description:
 * @author: 闫克起2
 * @create: 2023-08-16 17:17
 **/
@Service
public class UserServieImpl implements UserService {
    @Autowired
    private UserDao userDao;
​
    @Transactional //该方法交于spring的事务来管理了---默认spring不识别该注解
    public void zhuanzhang(int id, int uid, double money) {
​
        //1.扣钱
        userDao.updateBalance(id,-money);
        //int c=10/0;
        //2.收钱
        userDao.updateBalance(uid,money);
    }
    //---事务管理: (1)自己写事务管理 --自己手动定义事务切面类
    //           (2)使用spring提供的事务切面类。
    //
}
​

(5)测试

public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext con = new ClassPathXmlApplicationContext("spring.xml");
        AccountService accountServiceImpl = (AccountService) con.getBean("accountServiceImpl");
        accountServiceImpl.zhuanzhang(8,9,500);
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值