Spring AOP和事务学习

Spring AOP和事务学习

一、Spring AOP介绍

1.AOP(Aspect Oriented Programming) 面向切面编程,一种编程范式,指导开发者如何组织程
序结构。 AOP 是一种编程思想,编程思想主要的内容就是指导程序员该 如何编写程序。AOP则利用一种称为"横切的技术,剖开对象内部,并将公共行为封装到可重用模块,从而减少重复代码,降低耦合。

二、Aop的作用

1.不修改源码的情况下,进行功能增强,通过动态代理实现的
2.减少重复代码,提高开发效率,降低耦合度方便维护

三、AOP相关术语

目标对象(Target):需要代理的/要增强的目标对象。

代理对象(Proxy):目标对象被AOP织入增强后,就得到一个代理对象

连接点(JoinPoint):能够被拦截到的点,在Spring里指的是方法

切入点(PointCut):要对哪些连接点进行拦截的定义

通知/增强(Advice):拦截到连接点之后要做的事情

切面(Aspect):是切入点和通知的结合。 告诉Spring的AOP:要对哪个方法,做什么样的增强

织入(Weaving):把增强/通知应用到目标对象来创建代理对象的过程在这里插入图片描述

大多数用于描述的AOP功能的术语并不直观,所以为了理解AOP,我们必须了解这些术语。

四、AOP快速入门

1.需要编写的内容

  • 编写核心业务代码(目标类的目标方法)
  • 编写切面类,切面类中有通知(增强功能方法)
  • 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合
  1. 切点表达式的写法

    语法:

    execution([修饰符]返回值类型包名.类名.方法名(参数))
    
    • 访问修饰符可以省略
    • 返回值类型、包名、类名、方法名可以使用星号*代表任意
    • 包名与类名之间一个点.代表当前包下的类,两个点…表示当前包及其子包下的类
    • 参数列表可以使用两个点…表示任意个数,任意类型的参数列表

    例如:

    execution(public void com.iflytek.aop.Target.method())
    execution(void com.iflytek.aop.Target.* ( ..))
    execution(* com.iflytek.aop.*.*( ..))
    execution(* com.iflytek.aop..*.* (..))
    execution(* *..*.*(..))
    
  2. 通知的类型

    通知的配置语法:

    <!--配置文件方式-->
    <aop:通知类型 method="切面类中方法名" pointcut="切点表达式"></aop:通知类型>
    
    //注解方式
    @通知注解("切点表达式")
    
    名称标签注解说明
    前置通知<aop:before>@Before用于配置前置通知。指定增强的方法在切入点方法之前执行
    后置通知<aop:AfterReturning>@AfterReturning用于配置后置通知。指定增强的方法在切入点方法之后执行
    环绕通知<aop:Around>@Around用于配置环绕通知。指定增强的方法在切入点方法之前和之后都执行
    异常抛出通知<aop:AfterThrowing>@AfterThrowing用于配置异常抛出通知。指定增强的方法在出现异常时执行
    最终通知<aop:After>@After用于配置最终通知。无论增强方式执行是否有异常都会执行

四、实验步骤

  1. 基于配置文件方式实现日志打印

    (1)导入AOP相关坐标

        <dependencies>
         <dependency>
             <groupId>org.aspectj</groupId>
             <artifactId>aspectjweaver</artifactId>
             <version>1.8.6</version>
         </dependency>
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-test</artifactId>
             <version>5.3.8</version>
         </dependency>
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <version>4.13.1</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-context</artifactId>
             <version>5.3.8</version>
         </dependency>
     </dependencies>
    
    

    (2)创建DAO接口和实现类(内部有切点)

    • 在com.cqgcxy.service包下创建目标接口StudyService

      public interface StudyService {void study();
      

}

  • 在com.cqgcxy.service.impl包下创建目标类StudyServiceImpl
    public class PhoneServiceImpl implements PhoneService {
@Override
public void study() {
    System.out.println("SpringAOP学习");

}
 ```

(3)定义通知类(切面类),制作通知方法

  • 在com.cqgcxy.aop包下创建切面类LogManager
package com.cqgcxy.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;

import java.time.LocalDateTime;

public class LogManager {
    public Object printLog(ProceedingJoinPoint pjp) throws Throwable {
        Signature signature = pjp.getSignature();
        System.out.println(LocalDateTime.now()+"==============>"+signature+"===============>开始执行!");
        Object proceed = pjp.proceed();
        System.out.println(LocalDateTime.now()+"==============>"+signature+"===============>结束执行!");
        return proceed;
    }
}

(4)将目标类和切面类的对象创建权交给spring

	<bean id="logManager" class="com.cqgcxy.aop.LogManager"></bean>
    <bean id="studyService" class="com.cqgcxy.service.impl.StudyServiceImpl"></bean>

(5)在applicationContext.xml中配置织入关系

  • 方式一:

    	<aop:config>
            <aop:aspect ref="logManager">
                <aop:around method="printLog" pointcut="execution(* com.cqgcxy.service..*.*(..))"></aop:around>
            </aop:aspect>
        </aop:config>
    
  • 方式二:

    	<aop:config>
            <aop:aspect ref="logManager">
                <aop:pointcut id="logPointcut" expression="execution(* com.cqgcxy.service..*.*(..))"/>
                <aop:around method="printLog" pointcut-ref="logPointcut"></aop:around>
            </aop:aspect>
        </aop:config>
    

(6)测试代码

package com.cqgcxy.service.impl;

import com.cqgcxy.service.PhoneService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import static org.junit.Assert.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class PhoneServiceImplTest {

    @Autowired
    private StudyService studyService;

    @Test
    public void study() {
       studyService.study();
    }
}
  1. 基于注解方式实现日志打印

    (1)创建目标接口和目标类(内部有切点)

    • 在com.cqgcxy.service包下创建目标接口PhoneService

      public interface StudyService { void study();}
      
      
    • 在com.cqgcxy.service.impl包下创建目标类StudyServiceImp
      @Service
      public class StudyServiceImpl implements StudyService {
      @Override
      public void study() {
      System.out.println(“SpringAop学习”);
      }

    (2)创建切面类(内部有增强方法),并配置织入关系

    • 方式一:
      @Aspect
      @Component
      public class LogManager {
      @Around(“execution(* com.cqgcxy.service….(…))”)
      public Object printLog(ProceedingJoinPoint pjp) throws Throwable {
      Signature signature= pjp.getSignature();
      System.out.println(LocalDateTime.now()+“=>"+ signature + "=>开始执行方法");
      Object proceed= pjp.proceed();
      System.out.println(LocalDateTime.now()+"
      =>"+ signature + "=>结束执行方法”);
      return proceed;

    }

    • 方式二:
    • @Aspect
      @Component
      public class LogManager {
      @Pointcut(“execution(* com.cqgcxy.service….(…))”)
      public void logPointcut(){
      }
      @Around(“LogManager.logPointcut()”)
      public Object printLog(ProceedingJoinPoint pjp) throws Throwable {
      Signature signature= pjp.getSignature();
      System.out.println(LocalDateTime.now()+“=>"+ signature + "=>开始执行方法");
      Object proceed= pjp.proceed();
      System.out.println(LocalDateTime.now()+"
      =>"+ signature + "=>结束执行方法”);
      return proceed;

    }

    (3)将目标类和切面类的对象创建权交给spring,并在配置文件中开启组件扫描和AOP的自动代理

    @Configuration
    @ComponentScan(“com.cqgcxy”)
    @Component
    @EnableAspectJAutoProxy
    public class SpringConfiguration {
    }

    (4)测试

    package com.cqgcxy.service.impl;
    
    import com.cqgcxy.service.PhoneService;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import static org.junit.Assert.*; 
    

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(“classpath:applicationContext.xml”)
public class StudyServiceImplTest {
@Autowired
private StudyService studyService;
@Test
public void dawj() {
studyService.study();
}
}
``

五、实验结果

  1. 基于配置文件方式和注解方式实现日志打印的测试效果截图如下:
    在这里插入图片描述

至此,AOP的基础入门就完成了!!

Spring事务管理

一、事务管理的介绍

1.1:事务的简介:

举个简单的例子,
转账业务会有两次数据层的调用,一次是加钱一次是减钱
把事务放在数据层,加钱和减钱就有两个事务
没办法保证加钱和减钱同时成功或者同时失败
这个时候就需要将事务放在业务层进行处理。
Spring为了管理事务,提供了一个平台事务管理器PlatformTransactionManager。

1.2事务的概念:事务是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作,这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行;事务是一组不可再分割的操作集合。

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

事务的ACID原则

1.原则性:一个事务已经是一个不可再分割的工作单位。事务中的全部操作要么都做;要么都不做
2.一致性:事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。
3.隔离性:事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正确性和完整性。同时,并行事务的修改必须与其他并行事务的修改相互独立。
4.持久性:一个事务一旦提交,它对数据库中数据的改变会永久存储起来。其他操作不会对它产生影响

事务的操作

开启事务(一组操作开始前,开启事务):start transaction/begin;
提交事务(这组操作全部成功后,提交事务):commit;
回滚事务(中间任何一个操作出现异常,回滚事务):rollback;

事务隔离级别

1.1.Spring事务隔离级别:隔离级别由低到高【读未提交】=>【读已提交】=>【可重复读】=>【序列化操作】
在这里插入图片描述
1.2:事务的传播行为
指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。
在这里插入图片描述

实现事务管理:

使用银行转账业务完成:
1.创建数据库:
DROP TABLE IF EXISTS account;
CREATE TABLE account (
id int NOT NULL AUTO_INCREMENT,
name varchar(11) COLLATE utf8mb4_general_ci NOT NULL,
money decimal(10,2) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
INSERT INTO account VALUES (‘1’, ‘messi’, ‘100.00’);
INSERT INTO account VALUES (‘2’, ‘pep’, ‘100.00’);
2.导入相关依赖:


<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
    <artifactId>spring-context</atifactId>
            <version>5.3.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.8</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.4</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.6</version>
        </dependency>
    </dependencies>

3.实体类的创建:

public class Account implements Serializable {
    private Integer id;
    private String name;
    private Double money;

get和set方法此处省去

4.创建DAo接口和实现类
1.1创建目标接口:

public interface AccountDao {
    void inMoney(String name,Double money);
    void outMoney(String name,Double money);
}

1.2创建目标类:

@Repository
public class AccountDaoImpl implements AccountDao {
    @Autowired
    private JdbcTemplate jt;
    public void inMoney(String name, Double money) {
        jt.update("update account set money = money + ? where name = ?",money,name);
    }

    public void outMoney(String name, Double money) {
        jt.update("update account set money = money - ? where name = ?",money,name);
    }

5.创建服务层接口和实现类:

服务层接口
public interface AccountService {
    /**
     * 转账
     * @param out 转出
     * @param in 转进
     * @param money 金额
     */
    public void transfer(String out,String in,Double money);
}

实现类
@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=3/0;
        accountDao.inMoney(in, money);
    }

6.配置连接数据库文件:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql:///gcxy_teach?serverTimezone=Asia/Shanghai&characterEncoding=utf-8
jdbc.username=root
jdbc.password=123456

7.配置SPring配置文件

通过xml方式:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     ![请添加图片描述](https://img-blog.csdnimg.cn/69964456e0e34ee493ed8d50e0ce4e47.png)
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
       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/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--开启组件扫描-->
    <context:component-scan base-package="com.cqgcxy"/>

    <context:property-placeholder location="classpath*:jdbc.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置平台事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--配置通知 事务增强-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    <!--配置通知的AOP织入-->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.cqgcxy.service.impl.*.*(..))"></aop:advisor>
    </aop:config>
</beans>

通过注解方式:
(1):Spring配置类SpringConfiguration

@Configuration
@ComponentScan("com.cqgcxy")
@Import({JdbcConfig.class})
//启动平台事务管理
@EnableTransactionManagement
public class SpringConfiguration {
}

(2)配置jdbcconfig类

@PropertySource("classpath:jdbc.properties")
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;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate=new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

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

(3):业务层实现修改方法:

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;


    @Transactional
    @Override
    public void transfer(String out, String in, Double money) {
        accountDao.inMoney(in,money);

        accountDao.outMoney(out,money);
    }

5.进行代码测试:

@RunWith(SpringJUnit4ClassRunner.class)
//1.xml配置文件方式
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceImplTest {
    @Autowired
    private AccountService accountService;

    @Test
    public void transfer() {
        accountService.transfer("messi","pep",20.0);
    }
}

学习心得:我进一步了解了Spring AOP和事务管理在实际开发中的重要性和作用。Spring AOP通过面向切面的编程方式,使得代码的横切关注点(如日志等)能够被集中处理,而不需要在每个业务方法中重复编写这些逻辑。这大大减少了代码的冗余,提升了代码的可维护性和可读性。Spring AOP还提供了环绕等多种增强,在目标方法执行前后可以自定义处理逻辑。对于事务管理,它对于确保数据操作一致性和完整性是至关重要的。在方法执行时,如果发生异常或者满足其他事务回滚的条件,事务管理器会自动回滚事务,保证了数据的一致性。而如果方法执行成功,则事务管理器会提交事务,将数据永久性地保存到数据库中。我相信通过熟练掌握和应用Spring AOP和事务管理,在实际的开发中将对项目的质量和开发效率带来明显的提升。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值